內核版本:linux2.6.32.2
硬件資源:s3c2440
參考: 韋東山SPI視頻教程
內容概括:
1、I2C 驅動框架回顧
2、SPI 框架簡單介紹
3、master 驅動框架
3.1 驅動側
3.2 設備側
4、SPI 設備驅動框架
4.1 設備冊
4.2 驅動側
5、設備驅動程序實例
1、I2C 驅動框架回顧
在前面學習 I2C 驅動程序的時候我們知道,I2C 驅動框架分為兩層,一層是控制器驅動程序 i2c_adapter,它一般是由芯片廠商寫好的,主要提供一個 algorithm 底層的 i2c 協議的收發函數。i2c_adapter 驅動是基於 platform 模型,在driver側的probe函數里,取出資源信息進行設置,最后將adapter注冊到i2c_bus_type,注冊時會調用 i2c_scan_static_board_info,掃描並使用 i2c_new_device 創建設備(設備層的設備)。我們還提到了4種創建device的方式。
另一層是設備驅動層,基於 i2c_bus_type ,這個就很簡單了,在設備驅動層 device 只需要提供一個從設備地址和名字,在 driver 里使用 i2c_smbus_read_byte_data 等類似的函數進行收發即可了,i2c_smbus_read_byte_data 等函數最終就會調用到 我們的 i2c_adapter->algorithm 里的收發函數進行收發。
2、SPI 框架簡單介紹
對於SPI的大框架,與I2C是完全一致的,也分為兩層,控制器驅動程序層叫 spi_master ,主要提供transfer函數,進行spi協議的收發。spi_master 也是基於 Platform 模型的,注冊 spi_master 時也會掃描一個鏈表進行注冊設備,簡直太相似了。
另一層是設備驅動層,基於 spi_bus_type,相比 i2c 在device中需要提供的信息多一些,需要有名字、片選、最大速率、模式、中斷號等等,在driver里則使用spi_read、spi_writer 等函數,最終也會調用到 master->transfer 函數進行發送接收。
相比 I2C ,SPI驅動的框架是要簡單的,因為它少了兩種注冊device的方式,另外它不需要像I2C一樣去發送Start信號和設備地址去探測設備,SPI只需要片選選中就行了。但是它的底層收發的控制,相對I2C要復雜一點,畢竟4根線。
3、master 驅動框架
之前,分析的驅動程序都是 S3C2410\S3C2440 平台的,由於我的開發板 Mini2440 沒有SPI設備,因此廠商帶的內核里關於 SPI 驅動部分不完整,而且在s3c23xx_spi_probe函數里注冊master的時候寫的十分復雜,干擾信息太多,不適合分析學習,因此,我搜索了一下其他平台的代碼,發現 atmel_spi.c (drivers\spi),里 atmel 實現的底層控制器驅動簡單清晰多了,因此就拿它開刀,分析Master驅動框架。
3.1 驅動側
前面簡介里,我提到 master 驅動框架是基於 platform 平台的(我分析的這倆都是,其它的不清楚),那么肯定就要注冊platform_driver了,下面我們就開看看。
分配一個platfrom_driver結構
- static struct platform_driver atmel_spi_driver = {
- .driver = {
- .name = "atmel_spi",
- .owner = THIS_MODULE,
- },
- .suspend = atmel_spi_suspend,
- .resume = atmel_spi_resume,
- .remove = __exit_p(atmel_spi_remove),
- };
將 atmel_spi_driver 注冊到 platform_bus_type ,匹配設備 probe
- static int __init atmel_spi_init(void)
- {
- return platform_driver_probe(&atmel_spi_driver, atmel_spi_probe);
- }
我們之前都是將 probe 函數,直接放在driver結構體里,這里不是,而是調用了 platform_driver_probe ,就不貼代碼了,還看段函數介紹,大致了解下什么意思。
/**
* platform_driver_probe - register driver for non-hotpluggable device
* @drv: platform driver structure
* @probe: the driver probe routine, probably from an __init section
*
* Use this instead of platform_driver_register() when you know the device
* is not hotpluggable and has already been registered, and you want to
* remove its run-once probe() infrastructure from memory after the driver
* has bound to the device.
*
* One typical use for this would be with drivers for controllers integrated
* into system-on-chip processors, where the controller devices have been
* configured as part of board setup.
*
* Returns zero if the driver registered and bound to a device, else returns
* a negative error code and with the driver not registered.
*/
1、適用於非熱插拔設備
2、通常Probe位於__init段3、當你知道device是非熱拔插的,而且設備已經被注冊了,而且你想在probe函數調用一次之后就銷毀它節省空間,使用 platform_driver_probe 而非 platform_driver_register。
4、一個典型的應用是,用在完整的控制器驅動,控制器設備被當作 board setup 的一部分(在板子初始化的時候,設備就已經被注冊了,放在board_info里)
5、返回0 ,如果driver注冊成功且匹配到一個device ,以后再也無法被別的device probe了。
6、否則,返回一個錯誤,且driver未注冊。
顯然,我們寫的正式一個控制器驅動程序,設備側確實是早已注冊(后邊會講)。
疑問:有人說使用 platform_driver_probe 時 driver 只能被一個 device 匹配綁定,之后再也無法被別的device probe,難道說,我有倆spi控制器還需要寫兩個控制器驅動程序么?我認為這種說法是不對的,我猜大概是driver注冊時,會匹配一遍device鏈表,把能支持的device都probe,之后再有deivce注冊進來就不行了。這個有待驗證。
i2c驅動框架里,是在driver->probe 分配設置注冊adapter,想必spi也是在driver->probe里分配設置注冊master。
- static int __init atmel_spi_probe(struct platform_device *pdev)
- {
- struct resource *regs;
- int irq;
- struct clk *clk;
- int ret;
- struct spi_master *master;
- struct atmel_spi *as;
-
-
- regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- irq = platform_get_irq(pdev, 0);
-
-
- clk = clk_get(&pdev->dev, "spi_clk");
-
-
- master = spi_alloc_master(&pdev->dev, sizeof *as);
-
-
- master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
- master->bus_num = pdev->id;
- master->num_chipselect = 4;
- master->setup = atmel_spi_setup;
- master->transfer = atmel_spi_transfer;
- master->cleanup = atmel_spi_cleanup;
-
-
- platform_set_drvdata(pdev, master);
-
-
- as = spi_master_get_devdata(master);
- as->buffer = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE,
- &as->buffer_dma, GFP_KERNEL);
- spin_lock_init(&as->lock);
- INIT_LIST_HEAD(&as->queue);
- as->pdev = pdev;
- as->regs = ioremap(regs->start, (regs->end - regs->start) + 1);
- as->irq = irq;
- as->clk = clk;
-
-
- ret = request_irq(irq, atmel_spi_interrupt, 0,
- dev_name(&pdev->dev), master);
- clk_enable(clk);
-
-
- spi_writel(as, CR, SPI_BIT(SWRST));
- spi_writel(as, CR, SPI_BIT(SWRST));
- spi_writel(as, MR, SPI_BIT(MSTR) | SPI_BIT(MODFDIS));
- spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
- spi_writel(as, CR, SPI_BIT(SPIEN));
-
-
- ret = spi_register_master(master);
-
- return 0;
- }
對於master的設置過程注釋已經說的很明白了,我們還得看看分配和注冊過程。
- struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
- {
- struct spi_master *master;
-
- master = kzalloc(size + sizeof *master, GFP_KERNEL);
-
- device_initialize(&master->dev);
- master->dev.class = &spi_master_class;
- master->dev.parent = get_device(dev);
- spi_master_set_devdata(master, &master[1]);
-
- return master;
- }
1、spi_alloc_master 實際申請的內存大小為一個struct master + struct atmel_spi,並用master->dev->p->driver_data 指向這個多出來的 struct atmel_spi 空間,用來存放 master 的中斷 、寄存器等東西。
2、初始化 master->dev ,設置它的父設備等。
- int spi_register_master(struct spi_master *master)
- {
-
- dev_set_name(&master->dev, "spi%u", master->bus_num);
- status = device_add(&master->dev);
-
-
- scan_boardinfo(master);
- }
1、設置 master->dev 的名字,例如 spi0、spi1 ...
2、device_add 注冊設備
3、掃描spi設備信息:scan_boardinfo(master)
- static void scan_boardinfo(struct spi_master *master)
- {
- struct boardinfo *bi;
-
- mutex_lock(&board_lock);
- list_for_each_entry(bi, &board_list, list) {
- struct spi_board_info *chip = bi->board_info;
- unsigned n;
-
- for (n = bi->n_board_info; n > 0; n--, chip++) {
- if (chip->bus_num != master->bus_num)
- continue;
-
- (void) spi_new_device(master, chip);
- }
- }
- mutex_unlock(&board_lock);
- }
掃描 board_list ,取出每一個 boardinfo ,比對,如果 boardinfo 里的 bus_num 和 master 的 bus_num 相等,則認為這個spi設備在硬件物理連接上是接到這個控制器的,則使用 spi_new_device 創建 spi 設備。這個過程和i2c是多么的相似。至於在哪里填充的 board_list ,到后邊設備層驅動框架時再說不遲。
3.2 設備側
有 platform_driver 必然有 platform_device 與之對應,At91sam9260_devices.c 中定義
- static struct resource spi0_resources[] = {
- [0] = {
- .start = AT91SAM9260_BASE_SPI0,
- .end = AT91SAM9260_BASE_SPI0 + SZ_16K - 1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = AT91SAM9260_ID_SPI0,
- .end = AT91SAM9260_ID_SPI0,
- .flags = IORESOURCE_IRQ,
- },
- };
資源文件,提供寄存器范圍,spi中斷。
- static struct platform_device at91sam9260_spi0_device = {
- .name = "atmel_spi",
- .id = 0,
- .dev = {
- .dma_mask = &spi_dmamask,
- .coherent_dma_mask = DMA_BIT_MASK(32),
- },
- .resource = spi0_resources,
- .num_resources = ARRAY_SIZE(spi0_resources),
- };
與 driver 所配對的設備,顯然名字是一樣的。一般會有兩個spi控制器,at91sam9260_spi1_device 和 at91sam9260_spi0_device 一樣一樣的,這里就不貼代碼了。
既然分配了 platform_device 那么肯定會在某個地方調用 platform_device_register 將它注冊到 platform_bus_type , 就是在 at91_add_device_spi 。
- void __init at91_add_device_spi(struct spi_board_info *devices, int nr_devices)
- {
- ...
- spi_register_board_info(devices, nr_devices);
-
-
- if (enable_spi0) {
- ...
- platform_device_register(&at91sam9260_spi0_device);
- }
- if (enable_spi1) {
- ...
- platform_device_register(&at91sam9260_spi1_device);
- }
- }
1、添加 spi 設備信息,這應該是在設備驅動層要說的東西~就是前邊的填充 Board_list 鏈表。
2、將我們的 master 的設備側 at91sam9260_spi0_device 注冊到 platform_bus_type
思考:這樣做有什么好處呢?
這樣可以保證,master driver 與 device 匹配成功調用 probe 函數 scan_boardinfo 時,spi設備已經被添加到board_list中去~!如果先注冊成功了 master 驅動,再添加spi設備信息就無用了。根 i2c 也是一樣的。
至此,Master 驅動的框架就分析完了,至於 master 的那些設置,我們到下篇文件寫 master 驅動里細究。
4、SPI 設備驅動框架
設備驅動層,參考韋東山老師的 SPI Flash 驅動來分析,設備驅動層,device driver 都是注冊到spi_bus_type的,因此,我們現在看看 spi_bus_type->match 函數,看看它們如何匹配。
- static int spi_match_device(struct device *dev, struct device_driver *drv)
- {
- const struct spi_device *spi = to_spi_device(dev);
- const struct spi_driver *sdrv = to_spi_driver(drv);
-
- if (sdrv->id_table)
- return !!spi_match_id(sdrv->id_table, spi);
-
- return strcmp(spi->modalias, drv->name) == 0;
- }
如果,driver里有id_table,則根據id_table進行匹配,沒有就根據 spi->moadlias (設備名),和 driver->name 進行比較了。一樣則配對成功。
4.1 設備層
設備層比較簡單,先來分析它吧,目的只有一個分配 spi_board_info 設置 注冊。
- static struct spi_board_info spi_info_jz2440[] = {
- {
- .modalias = "100ask_spi_flash",
- .max_speed_hz = 80000000,
- .bus_num = 1,
- .mode = SPI_MODE_0,
- .chip_select = S3C2410_GPG(2),
- }
- };
- static int spi_info_jz2440_init(void)
- {
- return spi_register_board_info(spi_info_jz2440, ARRAY_SIZE(spi_info_jz2440));
- }
注冊 list_add_tail(&spi_info_jz2440->list, &board_list)
前面我們提到,master注冊成功時會掃描 board_list 注冊 spi 設備,現在來看看 spi 設備的注冊過程,雖然沒有啥重要的。
- struct spi_device *spi_new_device(struct spi_master *master,
- struct spi_board_info *chip)
- {
- struct spi_device *proxy;
- int status;
-
- proxy = spi_alloc_device(master);
-
- proxy->chip_select = chip->chip_select;
- proxy->max_speed_hz = chip->max_speed_hz;
- proxy->mode = chip->mode;
- proxy->irq = chip->irq;
- strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
- proxy->dev.platform_data = (void *) chip->platform_data;
- proxy->controller_data = chip->controller_data;
- proxy->controller_state = NULL;
-
- status = spi_add_device(proxy);
-
- return proxy;
- }
-
- struct spi_device *spi_alloc_device(struct spi_master *master)
- {
- struct spi_device *spi;
- struct device *dev = master->dev.parent;
-
- spi = kzalloc(sizeof *spi, GFP_KERNEL);
-
- spi->master = master;
- spi->dev.parent = dev;
- spi->dev.bus = &spi_bus_type;
- spi->dev.release = spidev_release;
- device_initialize(&spi->dev);
- return spi;
- }
-
- int spi_add_device(struct spi_device *spi)
- {
- static DEFINE_MUTEX(spi_add_lock);
- struct device *dev = spi->master->dev.parent;
- int status;
-
-
- if (spi->chip_select >= spi->master->num_chipselect) {
- dev_err(dev, "cs%d >= max %d\n",
- spi->chip_select,
- spi->master->num_chipselect);
- return -EINVAL;
- }
-
-
- dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),
- spi->chip_select);
-
- status = spi_setup(spi);
-
-
-
-
-
-
-
- status = device_add(&spi->dev);
- }
有一點,需要留意的吧,我們在寫 master 驅動時,master->num_chipselect 要大於我們將要注冊進來的spi設備的 chip_select 。
4.2 驅動側
- static struct spi_driver spi_flash_drv = {
- .driver = {
- .name = "100ask_spi_flash",
- .owner = THIS_MODULE,
- },
- .probe = spi_flash_probe,
- .remove = __devexit_p(spi_flash_remove),
- };
分配一個 spi_driver ,沒有 id_table ,要根據名字進行匹配了,顯然跟前面的設備是一樣的。
- static int spi_flash_init(void)
- {
- return spi_register_driver(&spi_flash_drv);
- }
注冊到 spi_bus_type ,匹配成功好調用 probe 函數,韋東山老師是將spi flash 作為一個mtd設備來使用的,因此在probe函數中分配、設置、注冊 mtd_info
- static int __devinit spi_flash_probe(struct spi_device *spi)
- {
- int mid, did;
-
- spi_flash = spi;
-
- s3c2410_gpio_cfgpin(spi->chip_select, S3C2410_GPIO_OUTPUT);
- SPIFlashInit();
- SPIFlashReadID(&mid, &did);
- printk("SPI Flash ID: %02x %02x\n", mid, did);
- memset(&spi_flash_dev, 0, sizeof(spi_flash_dev));
-
-
-
-
-
-
- spi_flash_dev.name = "100ask_spi_flash";
- spi_flash_dev.type = MTD_NORFLASH;
- spi_flash_dev.flags = MTD_CAP_NORFLASH;
- spi_flash_dev.size = 0x200000;
- spi_flash_dev.writesize = 1;
- spi_flash_dev.writebufsize = 4096;
- spi_flash_dev.erasesize = 4096;
-
- spi_flash_dev.owner = THIS_MODULE;
- spi_flash_dev._erase = spi_flash_erase;
- spi_flash_dev._read = spi_flash_read;
- spi_flash_dev._write = spi_flash_write;
-
- mtd_device_register(&spi_flash_dev, NULL, 0);
-
- return 0;
- }
在 i2c 設備驅動程序中,我們使用 i2c_read 等函數調用 adapter 里的底層收發函數進行與i2c設備通信,spi肯定也有相應的函數,例如 spi_read、spi_write ,下面我們來仔細看看,這個很重要~不然我們怎么寫設備驅動呢,光寫個框架不能收發那不白扯么。
- static inline int spi_write(struct spi_device *spi, const u8 *buf, size_t len)
- {
- struct spi_transfer t = {
- .tx_buf = buf,
- .len = len,
- };
- struct spi_message m;
-
- spi_message_init(&m);
- spi_message_add_tail(&t, &m);
- return spi_sync(spi, &m);
- }
以 spi_write 為例,看看它是如何調用到底層收發函數的。它構造了一個 spi_message 並由 spi_transfer組成,然后調用 spi_sync(spi, &m)->spi_async(spi,&m)->master->transfer(spi, &m).過程我們了解了,那么如何組織 spi_messgae ,它對應於時序圖怎樣的一個過程我們還不明白。
還記得,i2c 是通過構造 i2c_msg ,然后傳遞多個 i2c_msg 給底層的發送函數,多個i2c_msg 組成start信號和p信號之間的發送過程,每一個i2c_msg 都有一個start信號。大概 i2c_msg 就類比於spi里的 spi_transfer ,但是通常情況下,整個 spi_messgae 傳輸過程我們只片選一次就夠了。下面看個實例分析。
上圖是 spi flash 讀取任意字節的時序圖,片選選中之后,我們要先發送一個0x03 命令(1字節),再發送24bit(3字節)地址(先發送高位),然后讀取len,最后取消片選。
- void SPIFlashRead(unsigned int addr, unsigned char *buf, int len)
- {
- unsigned char tx_buf[4];
- struct spi_transfer t[] = {
- {
- .tx_buf = tx_buf,
- .len = 4,
- },
- {
- .rx_buf = buf,
- .len = len,
- },
- };
- struct spi_message m;
-
- tx_buf[0] = 0x03;
- tx_buf[1] = addr >> 16;
- tx_buf[2] = addr >> 8;
- tx_buf[3] = addr & 0xff;
-
- spi_message_init(&m);
- spi_message_add_tail(&t[0], &m);
- spi_message_add_tail(&t[1], &m);
- spi_sync(spi_flash, &m);
- }
韋老大的程序里,構造一個 struct spi_transfer 類型的數組,兩個成員,第一個有一個tx_buf(表示寫),長度為4,用來發送1字節命令和3字節地址。第二個成員 有一個rx_buf(表示讀),長度由參數決定,用來讀取長度len字節的內容。然后分配一個struct spi_message 並使用 spi_message_init 進行初始化,然后將 spi_transfer 數組成員依次添加到 spi_message 中去,最后 spi_sync(spi_flash, &m)
紙上談兵一大堆,現在來看看,我們在寫一個spi設備驅動的時候需要做哪些工作。
設備側:
1、分配一個 spi_board_info 結構體
2、設置 spi_board_info 里的名字、最大頻率、控制器編號、模式、片選
3、注冊 spi_register_board_info
驅動側:
1、分配一個 spi_driver 結構
2、設置名字、probe等函數
3、注冊 spi_register_driver
4、使用spi_write等系統調用,搞明白 spi_transfer spi_message ,會使用它們進行收發
一個 spi_message 對應於一個不可打斷的spi傳輸過程,可以理解為片選選中直到取消選中的過程(特殊情況下,一個spi_message里面是可以取消片選再選中的),而 spi_message 由 spi_transfer 組成,根據 tx_buf rx_buf 是否為空來判斷是 寫還是讀 操作。
至此,整個 spi 的框架分析完畢
5、設備驅動程序實例
- #include <linux/init.h>
- #include <linux/fs.h>
- #include <linux/slab.h>
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/device.h>
- #include <sound/core.h>
- #include <linux/spi/spi.h>
- #include <asm/uaccess.h>
- #include <linux/timer.h>
-
- #include <mach/hardware.h>
- #include <mach/regs-gpio.h>
-
- #include <linux/gpio.h>
- #include <plat/gpio-cfg.h>
-
- #include <linux/mtd/cfi.h>
- #include <linux/mtd/mtd.h>
- #include <linux/mtd/partitions.h>
-
-
-
-
-
-
- static struct spi_device *spi_flash;
-
-
-
-
- void SPIFlashReadID(int *pMID, int *pDID)
- {
- unsigned char tx_buf[4];
- unsigned char rx_buf[2];
-
- tx_buf[0] = 0x90;
- tx_buf[1] = 0;
- tx_buf[2] = 0;
- tx_buf[3] = 0;
-
- spi_write_then_read(spi_flash, tx_buf, 4, rx_buf, 2);
-
- *pMID = rx_buf[0];
- *pDID = rx_buf[1];
- }
-
- static void SPIFlashWriteEnable(int enable)
- {
- unsigned char val = enable ? 0x06 : 0x04;
- spi_write(spi_flash, &val, 1);
- }
-
- static unsigned char SPIFlashReadStatusReg1(void)
- {
- unsigned char val;
- unsigned char cmd = 0x05;
-
- spi_write_then_read(spi_flash, &cmd, 1, &val, 1);
-
- return val;
- }
-
- static unsigned char SPIFlashReadStatusReg2(void)
- {
- unsigned char val;
- unsigned char cmd = 0x35;
-
- spi_write_then_read(spi_flash, &cmd, 1, &val, 1);
-
- return val;
- }
-
- static void SPIFlashWaitWhenBusy(void)
- {
- while (SPIFlashReadStatusReg1() & 1)
- {
-
-
-
-
-
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ/100);
- }
- }
-
- static void SPIFlashWriteStatusReg(unsigned char reg1, unsigned char reg2)
- {
- unsigned char tx_buf[4];
-
- SPIFlashWriteEnable(1);
-
- tx_buf[0] = 0x01;
- tx_buf[1] = reg1;
- tx_buf[2] = reg2;
-
- spi_write(spi_flash, tx_buf, 3);
-
- SPIFlashWaitWhenBusy();
- }
-
- static void SPIFlashClearProtectForStatusReg(void)
- {
- unsigned char reg1, reg2;
-
- reg1 = SPIFlashReadStatusReg1();
- reg2 = SPIFlashReadStatusReg2();
-
- reg1 &= ~(1<<7);
- reg2 &= ~(1<<0);
-
- SPIFlashWriteStatusReg(reg1, reg2);
- }
-
- static void SPIFlashClearProtectForData(void)
- {
-
- unsigned char reg1, reg2;
-
- reg1 = SPIFlashReadStatusReg1();
- reg2 = SPIFlashReadStatusReg2();
-
- reg1 &= ~(7<<2);
- reg2 &= ~(1<<6);
-
- SPIFlashWriteStatusReg(reg1, reg2);
- }
-
-
- void SPIFlashEraseSector(unsigned int addr)
- {
- unsigned char tx_buf[4];
- tx_buf[0] = 0x20;
- tx_buf[1] = addr >> 16;
- tx_buf[2] = addr >> 8;
- tx_buf[3] = addr & 0xff;
-
- SPIFlashWriteEnable(1);
-
- spi_write(spi_flash, tx_buf, 4);
-
- SPIFlashWaitWhenBusy();
- }
-
-
- void SPIFlashProgram(unsigned int addr, unsigned char *buf, int len)
- {
- unsigned char tx_buf[4];
- struct spi_transfer t[] = {
- {
- .tx_buf = tx_buf,
- .len = 4,
- },
- {
- .tx_buf = buf,
- .len = len,
- },
- };
- struct spi_message m;
-
- tx_buf[0] = 0x02;
- tx_buf[1] = addr >> 16;
- tx_buf[2] = addr >> 8;
- tx_buf[3] = addr & 0xff;
-
- SPIFlashWriteEnable(1);
-
- spi_message_init(&m);
- spi_message_add_tail(&t[0], &m);
- spi_message_add_tail(&t[1], &m);
- spi_sync(spi_flash, &m);
-
- SPIFlashWaitWhenBusy();
- }
-
- void SPIFlashRead(unsigned int addr, unsigned char *buf, int len)
- {
-
-
-
-
- unsigned char tx_buf[4];
- struct spi_transfer t[] = {
- {
- .tx_buf = tx_buf,
- .len = 4,
- },
- {
- .rx_buf = buf,
- .len = len,
- },
- };
- struct spi_message m;
-
- tx_buf[0] = 0x03;
- tx_buf[1] = addr >> 16;
- tx_buf[2] = addr >> 8;
- tx_buf[3] = addr & 0xff;
-
- spi_message_init(&m);
- spi_message_add_tail(&t[0], &m);
- spi_message_add_tail(&t[1], &m);
- spi_sync(spi_flash, &m);
- }
-
- static void SPIFlashInit(void)
- {
- SPIFlashClearProtectForStatusReg();
- SPIFlashClearProtectForData();
- }
-
-
-
-
-
-
-
-
-
-
- static struct mtd_info spi_flash_dev;
-
- static int spi_flash_erase(struct mtd_info *mtd, struct erase_info *instr)
- {
- unsigned int addr = instr->addr;
- unsigned int len = 0;
-
-
- if ((addr & (spi_flash_dev.erasesize - 1)) || (instr->len & (spi_flash_dev.erasesize - 1)))
- {
- printk("addr/len is not aligned\n");
- return -EINVAL;
- }
-
- for (len = 0; len < instr->len; len += 4096)
- {
- SPIFlashEraseSector(addr);
- addr += 4096;
- }
-
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
- return 0;
- }
-
- static int spi_flash_read(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf)
- {
- SPIFlashRead(from, buf, len);
- *retlen = len;
- return 0;
- }
-
- static int spi_flash_write(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf)
- {
- unsigned int addr = to;
- unsigned int wlen = 0;
-
-
- if ((to & (spi_flash_dev.erasesize - 1)) || (len & (spi_flash_dev.erasesize - 1)))
- {
- printk("addr/len is not aligned\n");
- return -EINVAL;
- }
-
- for (wlen = 0; wlen < len; wlen += 256)
- {
- SPIFlashProgram(addr, (unsigned char *)buf, 256);
- addr += 256;
- buf += 256;
- }
-
- *retlen = len;
- return 0;
- }
-
- static int __devinit spi_flash_probe(struct spi_device *spi)
- {
- int mid, did;
-
- spi_flash = spi;
-
- s3c2410_gpio_cfgpin(spi->chip_select, S3C2410_GPIO_OUTPUT);
- SPIFlashInit();
- SPIFlashReadID(&mid, &did);
- printk("SPI Flash ID: %02x %02x\n", mid, did);
- memset(&spi_flash_dev, 0, sizeof(spi_flash_dev));
-
-
-
-
-
-
- spi_flash_dev.name = "100ask_spi_flash";
- spi_flash_dev.type = MTD_NORFLASH;
- spi_flash_dev.flags = MTD_CAP_NORFLASH;
- spi_flash_dev.size = 0x200000;
- spi_flash_dev.writesize = 1;
- spi_flash_dev.writebufsize = 4096;
- spi_flash_dev.erasesize = 4096;
-
- spi_flash_dev.owner = THIS_MODULE;
- spi_flash_dev._erase = spi_flash_erase;
- spi_flash_dev._read = spi_flash_read;
- spi_flash_dev._write = spi_flash_write;
-
- mtd_device_register(&spi_flash_dev, NULL, 0);
-
- return 0;
- }
-
- static int __devexit spi_flash_remove(struct spi_device *spi)
- {
- mtd_device_unregister(&spi_flash_dev);
- return 0;
- }
-
- static struct spi_driver spi_flash_drv = {
- .driver = {
- .name = "100ask_spi_flash",
- .owner = THIS_MODULE,
- },
- .probe = spi_flash_probe,
- .remove = __devexit_p(spi_flash_remove),
- };
-
- static int spi_flash_init(void)
- {
- return spi_register_driver(&spi_flash_drv);
- }
-
- static void spi_flash_exit(void)
- {
- spi_unregister_driver(&spi_flash_drv);
- }
-
- module_init(spi_flash_init);
- module_exit(spi_flash_exit);
- MODULE_DESCRIPTION("Flash SPI Driver");
- MODULE_AUTHOR("weidongshan@qq.com,www.100ask.net");
- MODULE_LICENSE("GPL");
-
-
- <strong style="color: rgb(255, 0, 0);">
- </strong>
轉自:http://blog.csdn.net/lizuobin2/article/details/51735963