日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】PowerPC + Linux2.6.25平台下的SPI驱动架构分析

發布時間:2024/9/21 linux 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】PowerPC + Linux2.6.25平台下的SPI驱动架构分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

PowerPC + Linux2.6.25平臺下的SPI驅動架構分析

?

Sailor_forever? sailing_9806#163.com

(本原創文章發表于Sailor_forever 的個人blog,未經本人許可,不得用于商業用途。任何個人、媒體、其他網站不得私自抄襲;網絡媒體轉載請注明出處,增加原文鏈接,否則屬于侵權行為。如 有任何問題,請留言或者發郵件給sailing_9806#163.com)
http://blog.csdn.net/sailor_8318/archive/2010/10/31/5977733.aspx

?

?

【摘要】本文以PowerPC+Linux 2.6.25 平臺為例,詳細分析了SPI總線的驅動架構。首先介紹了SPI的總體架構,從用戶的角度將其分為三個層面,不同的開發者只需要關注相應的層面即可。然后分析了主要數據結構及其之間的相互關系,接著分析了不同層的具體實現,最后以一款SPI接口的時鐘芯片為例講述了如何在用戶空間訪問SPI驅動。對于ARM + Linux平臺,只有平臺依賴層即總線控制器驅動有差異。

【關鍵字】PowerPC, SPI, Master, Slave, spidev

目錄
1??? SPI概述??? 3
2??? SPI總體架構??? 3
2.1??? 硬件抽象層??? 3
2.2??? 平臺依賴層??? 3
2.3??? 用戶接口層??? 3
3??? 主要的數據結構??? 4
3.1??? Spi_master??? 4
3.2??? SPI_driver??? 5
3.3??? Spi_device??? 6
4??? 平臺依賴層-總線控制器驅動??? 7
4.1??? platform device??? 8
4.2??? platform driver??? 11
4.3??? SPI Master??? 14
5??? 硬件抽象層-SPI core??? 14
5.1??? 總線初始化??? 14
5.2??? Master注冊??? 15
5.3??? 驅動注冊??? 19
5.4??? 數據傳輸??? 19
6??? 用戶接口層-SPI設備驅動??? 21
6.1??? 統一的設備模型??? 21
6.1.1??? 關鍵數據結構??? 21
6.1.2??? 初始化??? 22
6.1.3??? Open及release??? 24
6.1.4??? 數據收發??? 25
6.2??? 特定的設備驅動??? 36
6.2.1??? 關鍵數據結構??? 37
6.2.2??? 初始化??? 38
6.2.3??? 數據收發??? 42
7??? 驅動訪問示例??? 42
7.1.1??? 寫操作??? 43
7.1.2??? 讀操作??? 43
8??? 參考鳴謝??? 44


1??? SPI概述
SPI的通信原理很簡單,它以主從方式工作,這種模式通常有一個主設備和一個或多個從設備,需要至少4根線,事實上3根也可以(單向傳輸時或者硬件復用兩根數據線),也是所有基于SPI的設備共有的,它們是MISO、MOSI、SCK、CS。
(1)SDO – 主設備數據輸出,從設備數據輸入
(2)MISO– 主設備數據輸入,從設備數據輸出
(3)SCK – 時鐘信號,由主設備產生
(4)CS – 從設備使能信號,由主設備控制
其中CS是控制芯片是否被選中的,也就是說只有片選信號為預先規定的使能信號時(高電位或低電位),對此芯片的操作才有效,這就允許在同一總線上連接多個SPI設備成為可能。接下來就負責通訊的3根線了,通訊是通過數據交換完成的。SPI是串行通訊協議,也就是說數據是一位一位從MSB或者LSB開始傳輸的,這就是SCK時鐘線存在的原因,由SCK提供時鐘脈沖,MISO、MOSI則基于此脈沖完成數據傳輸。 SPI支持4-32bits的串行數據傳輸,支持MSB和LSB,每次數據傳輸時當從設備的大小端發生變化時需要重新設置SPI Master的大小端。

2??? SPI總體架構
在2.6的Linux內核中,SPI的驅動架構分為如下三個層次:硬件抽象層、平臺依賴層和用戶接口層。
2.1??? 硬件抽象層
SPI-bitbang.c和SPI.c為其主體框架代碼,提供了核心數據結構的定義、SPI控制器驅動和設備驅動的注冊、注銷管理等API。其為硬件平臺無關層,向下屏蔽了物理總線控制器的差異,定義了統一的訪問策略和接口;其向上提供了統一的接口,以便SPI設備驅動通過總線控制器進行數據收發。
2.2??? 平臺依賴層
SPI總線Master就是一條SPI總線的控制器(所謂控制是相對于本CPU來說的),在物理上連接若干SPI從設備。在Linux驅動中,每種處理器平臺有自己的控制器驅動,屬于平臺移植相關層。PowerPC平臺來說,其是spi_mpc83xx.c。其按照核心層定義的接口實現了spi_master。
2.3??? 用戶接口層
設備驅動層為用戶接口層,其為用戶提供了通過SPI總線訪問具體設備的接口。

3??? 主要的數據結構
3.1??? Spi_master
spi_master是對某一條SPI總線的抽象,是特定總線的相關屬性的集合。
/**
?* struct spi_master - interface to SPI master controller
?* @dev: device interface to this driver
?* @bus_num: board-specific (and often SOC-specific) identifier for a
?*??? given SPI controller.
?* @num_chipselect: chipselects are used to distinguish individual
?*??? SPI slaves, and are numbered from zero to num_chipselects.
?*??? each slave has a chipselect signal, but it's common that not
?*??? every chipselect is connected to a slave.
?* @setup: updates the device mode and clocking records used by a
?*??? device's SPI controller; protocol code may call this.? This
?*??? must fail if an unrecognized or unsupported mode is requested.
?*??? It's always safe to call this unless transfers are pending on
?*??? the device whose settings are being modified.
?* @transfer: adds a message to the controller's transfer queue.
?* @cleanup: frees controller-specific state
?*
?* Each SPI master controller can communicate with one or more @spi_device
?* children.? These make a small bus, sharing MOSI, MISO and SCK signals
?* but not chip select signals.? Each device may be configured to use a
?* different clock rate, since those shared signals are ignored unless
?* the chip is selected.
?*
?* The driver for an SPI controller manages access to those devices through
?* a queue of spi_message transactions, copying data between CPU memory and
?* an SPI slave device.? For each such message it queues, it calls the
?* message's completion function when the transaction completes.
?*/
struct spi_master {
??? struct device??? dev;

??? /* other than negative (== assign one dynamically), bus_num is fully
??? ?* board-specific.? usually that simplifies to being SOC-specific.
??? ?* example:? one SOC has three SPI controllers, numbered 0..2,
??? ?* and one board's schematics might show it using SPI-2.? software
??? ?* would normally use bus_num=2 for that controller.
??? ?*/
??? s16??? ??? ??? bus_num;

??? /* chipselects will be integral to many controllers; some others
??? ?* might use board-specific GPIOs.
??? ?*/
??? u16??? ??? ??? num_chipselect;

??? /* setup mode and clock, etc (spi driver may call many times) */
??? int??? ??? ??? (*setup)(struct spi_device *spi);

??? /* bidirectional bulk transfers
??? ?*
??? ?* + The transfer() method may not sleep; its main role is
??? ?*?? just to add the message to the queue.
??? ?* + For now there's no remove-from-queue operation, or
??? ?*?? any other request management
??? ?* + To a given spi_device, message queueing is pure fifo
??? ?*
??? ?* + The master's main job is to process its message queue,
??? ?*?? selecting a chip then transferring data
??? ?* + If there are multiple spi_device children, the i/o queue
??? ?*?? arbitration algorithm is unspecified (round robin, fifo,
??? ?*?? priority, reservations, preemption, etc)
??? ?*
??? ?* + Chipselect stays active during the entire message
??? ?*?? (unless modified by spi_transfer.cs_change != 0).
??? ?* + The message transfers use clock and SPI mode parameters
??? ?*?? previously established by setup() for this device
??? ?*/
??? int??? ??? ??? (*transfer)(struct spi_device *spi,
??? ??? ??? ??? ??? ??? struct spi_message *mesg);

??? /* called on release() to free memory provided by spi_master */
??? void??? ??? ??? (*cleanup)(struct spi_device *spi);
};

3.2??? SPI_driver
http://lxr.linux.no/#linux+v2.6.25/include/linux/SPI.h#L105
/**
?* struct spi_driver - Host side "protocol" driver
?* @probe: Binds this driver to the spi device.? Drivers can verify
?*??? that the device is actually present, and may need to configure
?*??? characteristics (such as bits_per_word) which weren't needed for
?*??? the initial configuration done during system setup.
?* @remove: Unbinds this driver from the spi device
?* @shutdown: Standard shutdown callback used during system state
?*??? transitions such as powerdown/halt and kexec
?* @suspend: Standard suspend callback used during system state transitions
?* @resume: Standard resume callback used during system state transitions
?* @driver: SPI device drivers should initialize the name and owner
?*??? field of this structure.
?*
?* This represents the kind of device driver that uses SPI messages to
?* interact with the hardware at the other end of a SPI link.? It's called
?* a "protocol" driver because it works through messages rather than talking
?* directly to SPI hardware (which is what the underlying SPI controller
?* driver does to pass those messages).? These protocols are defined in the
?* specification for the device(s) supported by the driver.
?*
?* As a rule, those device protocols represent the lowest level interface
?* supported by a driver, and it will support upper level interfaces too.
?* Examples of such upper levels include frameworks like MTD, networking,
?* MMC, RTC, filesystem character device nodes, and hardware monitoring.
?*/
struct spi_driver {
??? int??? ??? ??? (*probe)(struct spi_device *spi);
??? int??? ??? ??? (*remove)(struct spi_device *spi);
??? void??? ??? ??? (*shutdown)(struct spi_device *spi);
??? int??? ??? ??? (*suspend)(struct spi_device *spi, pm_message_t mesg);
??? int??? ??? ??? (*resume)(struct spi_device *spi);
??? struct device_driver??? driver;
};
Driver是為device服務的,SPI_driver注冊時會掃描SPI bus上的設備,進行驅動和設備的綁定。
3.3??? Spi_device
http://lxr.linux.no/#linux+v2.6.25/include/linux/SPI.h#L168
/**
?* struct spi_device - Master side proxy for an SPI slave device
?* @dev: Driver model representation of the device.
?* @master: SPI controller used with the device.
?* @max_speed_hz: Maximum clock rate to be used with this chip
?*??? (on this board); may be changed by the device's driver.
?*??? The spi_transfer.speed_hz can override this for each transfer.
?* @chip_select: Chipselect, distinguishing chips handled by @master.
?* @mode: The spi mode defines how data is clocked out and in.
?*??? This may be changed by the device's driver.
?*??? The "active low" default for chipselect mode can be overridden
?*??? (by specifying SPI_CS_HIGH) as can the "MSB first" default for
?*??? each word in a transfer (by specifying SPI_LSB_FIRST).
?* @bits_per_word: Data transfers involve one or more words; word sizes
?*??? like eight or 12 bits are common.? In-memory wordsizes are
?*??? powers of two bytes (e.g. 20 bit samples use 32 bits).
?*??? This may be changed by the device's driver, or left at the
?*??? default (0) indicating protocol words are eight bit bytes.
?*??? The spi_transfer.bits_per_word can override this for each transfer.
?* @irq: Negative, or the number passed to request_irq() to receive
?*??? interrupts from this device.
?* @controller_state: Controller's runtime state
?* @controller_data: Board-specific definitions for controller, such as
?*??? FIFO initialization parameters; from board_info.controller_data
*
?* A @spi_device is used to interchange data between an SPI slave
?* (usually a discrete chip) and CPU memory.
?*
?* In @dev, the platform_data is used to hold information about this
?* device that's meaningful to the device's protocol driver, but not
?* to its controller.? One example might be an identifier for a chip
?* variant with slightly different functionality; another might be
?* information about how this particular board wires the chip's pins.
?*/
struct spi_device {
??? struct device??? ??? dev;
??? struct spi_master??? *master;
??? u32??? ??? ??? max_speed_hz;
??? u8??? ??? ??? chip_select;
??? u8??? ??? ??? mode;
#define??? SPI_CPHA??? 0x01??? ??? ??? /* clock phase */
#define??? SPI_CPOL??? 0x02??? ??? ??? /* clock polarity */
#define??? SPI_MODE_0??? (0|0)??? ??? ??? /* (original MicroWire) */
#define??? SPI_MODE_1??? (0|SPI_CPHA)
#define??? SPI_MODE_2??? (SPI_CPOL|0)
#define??? SPI_MODE_3??? (SPI_CPOL|SPI_CPHA)
#define??? SPI_CS_HIGH??? 0x04??? ??? ??? /* chipselect active high? */
#define??? SPI_LSB_FIRST??? 0x08??? ??? ??? /* per-word bits-on-wire */
#define??? SPI_3WIRE??? 0x10??? ??? ??? /* SI/SO signals shared */
#define??? SPI_LOOP??? 0x20??? ??? ??? /* loopback mode */
??? u8??? ??? ??? bits_per_word;
??? int??? ??? ??? irq;
??? void??? ??? ??? *controller_state;
??? void??? ??? ??? *controller_data;
。。。
};

spi_device對應著SPI總線上某個特定的slave。每個slave都有特定的大小端、速率及傳輸位寬,各個slave相互之間無干擾。


4??? 平臺依賴層-總線控制器驅動
總線控制器驅動,本質上就是實現了具體的總線傳輸算法并向核心層注冊了控制器。主要分為三個層面,platform device,platform driver及與SPI core的接口層。

Linux內核的所有SPI控制器驅動程序都在driver/SPI/下面,MPC8xxx驅動是spi_mpc83xx.c。
4.1??? platform device
2.6內核中硬件資源的注冊都采用了platform device的機制。對于PowerPC來說,其硬件資源是通過DTS來描述的。
spi@7000 {
??? cell-index = <0>;
??? compatible = "fsl,spi";
??? reg = <0x7000 0x1000>;
??? interrupts = <16 0x8>;
??? interrupt-parent = <&ipic>;
??? mode = "cpu";
};
中斷號、中斷觸發電平、寄存器的基地址及范圍等信息會在設備樹中描述了,此后只需利用platform_get_resource等標準接口自動獲取即可,實現了驅動和資源的分離。Cell-index標識了總線編號,也就是SPI master的編號。

隨后在系統啟動階段會解析DTB文件,將相關資源注冊到Platform bus上。
http://lxr.linux.no/#linux+v2.6.25/arch/powerpc/sysdev/fsl_soc.c#L454

int __init fsl_spi_init(struct spi_board_info *board_infos,
??? ??? ??? unsigned int num_board_infos,
??? ??? ??? void (*activate_cs)(u8 cs, u8 polarity),
??? ??? ??? void (*deactivate_cs)(u8 cs, u8 polarity))
{
??? u32 sysclk = -1;
??? int ret;
。。。。。
??? if (sysclk == -1) {
??? ??? struct device_node *np;
??? ??? const u32 *freq;
??? ??? int size;

??? ??? np = of_find_node_by_type(NULL, "soc"); //獲得SOC中注冊的總線頻率
??? ??? if (!np)
??? ??? ??? return -ENODEV;

??? ??? freq = of_get_property(np, "clock-frequency", &size);
??? ??? if (!freq || size != sizeof(*freq) || *freq == 0) {
??? ??? ??? freq = of_get_property(np, "bus-frequency", &size);
??? ??? ??? if (!freq || size != sizeof(*freq) || *freq == 0) {
??? ??? ??? ??? of_node_put(np);
??? ??? ??? ??? return -ENODEV;
??? ??? ??? }
??? ??? }

??? ??? sysclk = *freq;
??? ??? of_node_put(np);
??? }

??? ret = of_fsl_spi_probe(NULL, "fsl,spi", sysclk, board_infos,
??? ??? ??? ?????? num_board_infos, activate_cs, deactivate_cs);
??? if (!ret)
??? ??? of_fsl_spi_probe("spi", "fsl_spi", sysclk, board_infos,
??? ??? ??? ??? ?num_board_infos, activate_cs, deactivate_cs);

??? return spi_register_board_info(board_infos, num_board_infos);? //將SPI board info注冊進系統SPI設備列表中
}


static int __init of_fsl_spi_probe(char *type, char *compatible, u32 sysclk,
??? ??? ??? ??? ?? struct spi_board_info *board_infos,
??? ??? ??? ??? ?? unsigned int num_board_infos,
??? ??? ??? ??? ?? void (*activate_cs)(u8 cs, u8 polarity),
??? ??? ??? ??? ?? void (*deactivate_cs)(u8 cs, u8 polarity))
{
??? struct device_node *np;
??? unsigned int i = 0;

??? for_each_compatible_node(np, type, compatible) { //根據compatible屬性"fsl,spi"查找相關device node節點
??? ??? int ret;
??? ??? unsigned int j;
??? ??? const void *prop;
??? ??? struct resource res[2];
??? ??? struct platform_device *pdev;
??? ??? struct fsl_spi_platform_data pdata = { //板級相關的SPI片選實現函數
??? ??? ??? .activate_cs = activate_cs,
??? ??? ??? .deactivate_cs = deactivate_cs,
??? ??? };

??? ??? memset(res, 0, sizeof(res));

??? ??? pdata.sysclk = sysclk;

??? ??? prop = of_get_property(np, "reg", NULL);
??? ??? if (!prop)
??? ??? ??? goto err;
??? ??? pdata.bus_num = *(u32 *)prop; //reg是寄存器的范圍,如何與總線編號掛鉤的呢?

??? ??? prop = of_get_property(np, "cell-index", NULL);
??? ??? if (prop)
??? ??? ??? i = *(u32 *)prop;

??? ??? prop = of_get_property(np, "mode", NULL);
??? ??? if (prop && !strcmp(prop, "cpu-qe"))
??? ??? ??? pdata.qe_mode = 1;

??? ??? for (j = 0; j < num_board_infos; j++) { //根據板級移植相關的board info指定的bus num進行匹配
??? ??? ??? if (board_infos[j].bus_num == pdata.bus_num)
??? ??? ??? ??? pdata.max_chipselect++;
??? ??? }

??? ??? if (!pdata.max_chipselect)
??? ??? ??? continue;

??? ??? ret = of_address_to_resource(np, 0, &res[0]);
??? ??? if (ret)
??? ??? ??? goto err;

??? ??? ret = of_irq_to_resource(np, 0, &res[1]);
??? ??? if (ret == NO_IRQ)
??? ??? ??? goto err;

??? ??? pdev = platform_device_alloc("mpc83xx_spi", i); //以mpc83xx_spi為name申請platform device,后續的platform driver將以mpc83xx_spi為匹配因子
??? ??? if (!pdev)
??? ??? ??? goto err;

??? ??? ret = platform_device_add_data(pdev, &pdata, sizeof(pdata)); //將pdata等特定的屬性添加到platform device中,以供相應的platform driver檢測。
??? ??? if (ret)
??? ??? ??? goto unreg;

??? ??? ret = platform_device_add_resources(pdev, res,
??? ??? ??? ??? ??? ??? ??? ARRAY_SIZE(res));
??? ??? if (ret)
??? ??? ??? goto unreg;

??? ??? ret = platform_device_add(pdev); //將SPI相關的platform device添加到platform bus上
??? ??? if (ret)
??? ??? ??? goto unreg;

??? ??? goto next;
unreg:
??? ??? platform_device_del(pdev);
err:
??? ??? pr_err("%s: registration failed/n", np->full_name);
next:
??? ??? i++;
??? }

??? return i;
}

4.2??? platform driver
static struct platform_driver mpc83xx_spi_driver = {
??? .remove = __exit_p(mpc83xx_spi_remove),
??? .driver = {
??? ??? .name = "mpc83xx_spi",
??? ??? .owner = THIS_MODULE,
??? },
};

static int __init mpc83xx_spi_init(void)
{
??? return platform_driver_probe(&mpc83xx_spi_driver, mpc83xx_spi_probe);
}

static void __exit mpc83xx_spi_exit(void)
{
??? platform_driver_unregister(&mpc83xx_spi_driver);
}
mpc83xx_spi_driver注冊時會掃描platform bus上的所有設備,匹配因子是mpc83xx_spi,匹配成功后調用mpc83xx_spi_probe將設備和驅動綁定起來,具體過程參加《詳解Linux2.6內核中基于platform機制的驅動模型》,隨后向系統注冊一個adapter。
http://blog.csdn.net/sailor_8318/archive/2010/01/29/5267698.aspx

static int __init mpc83xx_spi_probe(struct platform_device *dev)
{
??? struct spi_master *master;
??? struct mpc83xx_spi *mpc83xx_spi;
??? struct fsl_spi_platform_data *pdata;
??? struct resource *r;
??? u32 regval;
??? int ret = 0;


??? /* Get resources(memory, IRQ) associated with the device */
??? master = spi_alloc_master(&dev->dev, sizeof(struct mpc83xx_spi)); // 分配一個SPI Master

??? if (master == NULL) {
??? ??? ret = -ENOMEM;
??? ??? goto err;
??? }

??? platform_set_drvdata(dev, master);
??? pdata = dev->dev.platform_data;? //獲得platform device注冊的特定資源

??? if (pdata == NULL) {
??? ??? ret = -ENODEV;
??? ??? goto free_master;
??? }


??? r = platform_get_resource(dev, IORESOURCE_MEM, 0);
??? if (r == NULL) {
??? ??? ret = -ENODEV;
??? ??? goto free_master;
??? }

??? mpc83xx_spi = spi_master_get_devdata(master);
??? mpc83xx_spi->bitbang.master = spi_master_get(master);
??? mpc83xx_spi->bitbang.chipselect = mpc83xx_spi_chipselect;
??? mpc83xx_spi->bitbang.setup_transfer = mpc83xx_spi_setup_transfer;
??? mpc83xx_spi->bitbang.txrx_bufs = mpc83xx_spi_bufs;
??? mpc83xx_spi->activate_cs = pdata->activate_cs;
??? mpc83xx_spi->deactivate_cs = pdata->deactivate_cs; // 將特定的片選實現函數保存到mpc83xx_spi中
??? mpc83xx_spi->qe_mode = pdata->qe_mode;
??? mpc83xx_spi->get_rx = mpc83xx_spi_rx_buf_u8;
??? mpc83xx_spi->get_tx = mpc83xx_spi_tx_buf_u8;
??? mpc83xx_spi->spibrg = pdata->sysclk;

??? mpc83xx_spi->rx_shift = 0;
??? mpc83xx_spi->tx_shift = 0;
??? if (mpc83xx_spi->qe_mode) {
??? ??? mpc83xx_spi->rx_shift = 16;
??? ??? mpc83xx_spi->tx_shift = 24;
??? }

??? mpc83xx_spi->bitbang.master->setup = mpc83xx_spi_setup;
??? init_completion(&mpc83xx_spi->done);

??? mpc83xx_spi->base = ioremap(r->start, r->end - r->start + 1);
??? if (mpc83xx_spi->base == NULL) {
??? ??? ret = -ENOMEM;
??? ??? goto put_master;
??? }

??? mpc83xx_spi->irq = platform_get_irq(dev, 0); //從platform device中獲得相應的寄存器和中斷等信息

??? if (mpc83xx_spi->irq < 0) {
??? ??? ret = -ENXIO;
??? ??? goto unmap_io;
??? }

??? /* Register for SPI Interrupt */
??? ret = request_irq(mpc83xx_spi->irq, mpc83xx_spi_irq,
??? ??? ??? ? 0, "mpc83xx_spi", mpc83xx_spi);

??? if (ret != 0)
??? ??? goto unmap_io;

??? master->bus_num = pdata->bus_num;
??? master->num_chipselect = pdata->max_chipselect;

??? /* SPI controller initializations */
??? mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, 0);
??? mpc83xx_spi_write_reg(&mpc83xx_spi->base->mask, 0);
??? mpc83xx_spi_write_reg(&mpc83xx_spi->base->command, 0);
??? mpc83xx_spi_write_reg(&mpc83xx_spi->base->event, 0xffffffff);

??? /* Enable SPI interface */
??? regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
??? if (pdata->qe_mode)
??? ??? regval |= SPMODE_OP;

??? mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, regval);

??? ret = spi_bitbang_start(&mpc83xx_spi->bitbang);? //MPC83xx系列將在spi_bitbang_start中注冊SPI master

??? if (ret != 0)
??? ??? goto free_irq;

??? printk(KERN_INFO
??? ?????? "%s: MPC83xx SPI Controller driver at 0x%p (irq = %d)/n",
??? ?????? dev->dev.bus_id, mpc83xx_spi->base, mpc83xx_spi->irq);

??? return ret;

free_irq:
??? free_irq(mpc83xx_spi->irq, mpc83xx_spi);
unmap_io:
??? iounmap(mpc83xx_spi->base);
put_master:
??? spi_master_put(master);
free_master:
??? kfree(master);
err:
??? return ret;
}

4.3??? SPI Master
每一個SPI master都要實現setup、transfer及cleanup等。
static int mpc83xx_spi_setup(struct spi_device *spi)
static int mpc83xx_spi_bufs(struct spi_device *spi, struct spi_transfer *t)

mpc83xx_spi->bitbang.txrx_bufs = mpc83xx_spi_bufs;

MPC837x SPI Master的具體實現待續。
5??? 硬件抽象層-SPI core
5.1??? 總線初始化?
static int __init spi_init(void)
{
??? int??? status;

??? buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
??? if (!buf) {
??? ??? status = -ENOMEM;
??? ??? goto err0;
??? }

??? status = bus_register(&spi_bus_type);
??? if (status < 0)
??? ??? goto err1;

??? status = class_register(&spi_master_class);
??? if (status < 0)
??? ??? goto err2;
??? return 0;

err2:
??? bus_unregister(&spi_bus_type);
err1:
??? kfree(buf);
??? buf = NULL;
err0:
??? return status;
}

/* board_info is normally registered in arch_initcall(),
?* but even essential drivers wait till later
?*
?* REVISIT only boardinfo really needs static linking. the rest (device and
?* driver registration) _could_ be dynamically linked (modular) ... costs
?* include needing to have boardinfo data structures be much more public.
?*/
subsys_initcall(spi_init);

關于被subsys_initcall修飾的spi_init何時初始化,請參考下文《詳解Linux2.6內核中基于platform機制的驅動模型》
http://blog.csdn.net/sailor_8318/archive/2010/01/29/5267698.aspx
此時platform bus及platform device已經注冊完畢,因此SPI控制器的相關資源已經就緒。

5.2??? Master注冊
/**
?* spi_register_master - register SPI master controller
?* @master: initialized master, originally from spi_alloc_master()
?* Context: can sleep
?*
?* SPI master controllers connect to their drivers using some non-SPI bus,
?* such as the platform bus.? The final stage of probe() in that code
?* includes calling spi_register_master() to hook up to this SPI bus glue.
?*
?* SPI controllers use board specific (often SOC specific) bus numbers,
?* and board-specific addressing for SPI devices combines those numbers
?* with chip select numbers.? Since SPI does not directly support dynamic
?* device identification, boards need configuration tables telling which
?* chip is at which address.
?*/
int spi_register_master(struct spi_master *master)
{
??? static atomic_t??? ??? dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
??? struct device??? ??? *dev = master->dev.parent;
??? int??? ??? ??? status = -ENODEV;
??? int??? ??? ??? dynamic = 0;

??? if (!dev)
??? ??? return -ENODEV;

??? /* even if it's just one always-selected device, there must
??? ?* be at least one chipselect
??? ?*/
??? if (master->num_chipselect == 0)
??? ??? return -EINVAL;

??? /* convention:? dynamically assigned bus IDs count down from the max */
??? if (master->bus_num < 0) {
??? ??? /* FIXME switch to an IDR based scheme, something like
??? ??? ?* I2C now uses, so we can't run out of "dynamic" IDs
??? ??? ?*/
??? ??? master->bus_num = atomic_dec_return(&dyn_bus_id);
??? ??? dynamic = 1;
??? }

??? /* register the device, then userspace will see it.
??? ?* registration fails if the bus ID is in use.
??? ?*/
??? snprintf(master->dev.bus_id, sizeof master->dev.bus_id,
??? ??? "spi%u", master->bus_num);
??? status = device_add(&master->dev);
??? if (status < 0)
??? ??? goto done;
??? dev_dbg(dev, "registered master %s%s/n", master->dev.bus_id,
??? ??? ??? dynamic ? " (dynamic)" : "");

??? /* populate children from any spi device tables */
??? scan_boardinfo(master);
??? status = 0;
done:
??? return status;
}
EXPORT_SYMBOL_GPL(spi_register_master);

static void scan_boardinfo(struct spi_master *master)
{
??? struct boardinfo??? *bi;

??? mutex_lock(&board_lock);
??? list_for_each_entry(bi, &board_list, list) {//board list已經在platform device注冊時初始化
??? ??? 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)? //Master的總線號和相應slave所在的總線號匹配
??? ??? ??? ??? continue;
??? ??? ??? /* NOTE: this relies on spi_new_device to
??? ??? ??? ?* issue diagnostics when given bogus inputs
??? ??? ??? ?*/
??? ??? ??? (void) spi_new_device(master, chip);
??? ??? }
??? }
??? mutex_unlock(&board_lock);
}

/**
?* spi_new_device - instantiate one new SPI device
?* @master: Controller to which device is connected
?* @chip: Describes the SPI device
?* Context: can sleep
* Returns the new device, or NULL.
?*/
struct spi_device *spi_new_device(struct spi_master *master,
??? ??? ??? ??? ? struct spi_board_info *chip)
{
??? struct spi_device??? *proxy;
??? struct device??? ??? *dev = master->dev.parent;
??? int??? ??? ??? status;

??? /* NOTE:? caller did any chip->bus_num checks necessary.
??? ?*/

??? /* Chipselects are numbered 0..max; validate. */
??? if (chip->chip_select >= master->num_chipselect) {
??? ??? dev_err(dev, "cs%d > max %d/n",
??? ??? ??? chip->chip_select,
??? ??? ??? master->num_chipselect);
??? ??? return NULL;
??? }

??? if (!spi_master_get(master))
??? ??? return NULL;

??? proxy = kzalloc(sizeof *proxy, GFP_KERNEL);? //分配一個SPI device
??? if (!proxy) {
??? ??? dev_err(dev, "can't alloc dev for cs%d/n",
??? ??? ??? chip->chip_select);
??? ??? goto fail;
??? }

??? proxy->master = master;? // SPI slave所依附的SPI Master
??? proxy->chip_select = chip->chip_select;? // 保存board info中特定的屬性,板級移植需要關注
??? proxy->max_speed_hz = chip->max_speed_hz;
??? proxy->mode = chip->mode;
??? proxy->irq = chip->irq;
??? proxy->modalias = chip->modalias;

??? snprintf(proxy->dev.bus_id, sizeof proxy->dev.bus_id,
??? ??? ??? "%s.%u", master->dev.bus_id,
??? ??? ??? chip->chip_select);
??? proxy->dev.parent = dev;
??? proxy->dev.bus = &spi_bus_type;?? //此設備所依附的總線為SPI
??? proxy->dev.platform_data = (void *) chip->platform_data;
??? proxy->controller_data = chip->controller_data;
??? proxy->controller_state = NULL;
??? proxy->dev.release = spidev_release;

??? /* drivers may modify this initial i/o setup */
??? status = master->setup(proxy);
??? if (status < 0) {
??? ??? dev_err(dev, "can't %s %s, status %d/n",
??? ??? ??? ??? "setup", proxy->dev.bus_id, status);
??? ??? goto fail;
??? }

??? /* driver core catches callers that misbehave by defining
??? ?* devices that already exist.
??? ?*/
??? status = device_register(&proxy->dev);? // 將該SPI device掛接到SPI總線上
??? if (status < 0) {
??? ??? dev_err(dev, "can't %s %s, status %d/n",
??? ??? ??? ??? "add", proxy->dev.bus_id, status);
??? ??? goto fail;
??? }
??? dev_dbg(dev, "registered child %s/n", proxy->dev.bus_id);
??? return proxy;

fail:
??? spi_master_put(master);
??? kfree(proxy);
??? return NULL;
}
EXPORT_SYMBOL_GPL(spi_new_device);

5.3??? 驅動注冊
/**
?* spi_register_driver - register a SPI driver
?* @sdrv: the driver to register
?* Context: can sleep
?*/
int spi_register_driver(struct spi_driver *sdrv)
{
??? sdrv->driver.bus = &spi_bus_type;
??? if (sdrv->probe)
??? ??? sdrv->driver.probe = spi_drv_probe;
??? if (sdrv->remove)
??? ??? sdrv->driver.remove = spi_drv_remove;
??? if (sdrv->shutdown)
??? ??? sdrv->driver.shutdown = spi_drv_shutdown;
??? return driver_register(&sdrv->driver);
}
EXPORT_SYMBOL_GPL(spi_register_driver);

SPI driver采用內核通用的驅動模型,其流程和platform driver的注冊過程類似,請請參考下文《詳解Linux2.6內核中基于platform機制的驅動模型》
http://blog.csdn.net/sailor_8318/archive/2010/01/29/5267698.aspx
5.4??? 數據傳輸
/**
?* spi_sync - blocking/synchronous SPI data transfers
?* @spi: device with which data will be exchanged
?* @message: describes the data transfers
?* Context: can sleep
?*
?* This call may only be used from a context that may sleep.? The sleep
?* is non-interruptible, and has no timeout.? Low-overhead controller
?* drivers may DMA directly into and out of the message buffers.
?*
?* Note that the SPI device's chip select is active during the message,
?* and then is normally disabled between messages.? Drivers for some
?* frequently-used devices may want to minimize costs of selecting a chip,
?* by leaving it selected in anticipation that the next message will go
?* to the same chip.? (That may increase power usage.)
?*
?* Also, the caller is guaranteeing that the memory associated with the
?* message will not be freed before this call returns.
?*
?* It returns zero on success, else a negative error code.
?*/
int spi_sync(struct spi_device *spi, struct spi_message *message)
{
??? DECLARE_COMPLETION_ONSTACK(done);
??? int status;

??? message->complete = spi_complete;
??? message->context = &done;
??? status = spi_async(spi, message);
??? if (status == 0) {
??? ??? wait_for_completion(&done);
??? ??? status = message->status;
??? }
??? message->context = NULL;
??? return status;
}
EXPORT_SYMBOL_GPL(spi_sync);
同步接口,待數據收發完畢后才返回,只能在非中斷上下文中使用。

/**
?* spi_async - asynchronous SPI transfer
?* @spi: device with which data will be exchanged
?* @message: describes the data transfers, including completion callback
?* Context: any (irqs may be blocked, etc)
?*
?* This call may be used in_irq and other contexts which can't sleep,
?* as well as from task contexts which can sleep.
?*
?* The completion callback is invoked in a context which can't sleep.
?* Before that invocation, the value of message->status is undefined.
?* When the callback is issued, message->status holds either zero (to
?* indicate complete success) or a negative error code.? After that
?* callback returns, the driver which issued the transfer request may
?* deallocate the associated memory; it's no longer in use by any SPI
?* core or controller driver code.
?*
?* Note that although all messages to a spi_device are handled in
?* FIFO order, messages may go to different devices in other orders.
?* Some device might be higher priority, or have various "hard" access
?* time requirements, for example.
?*
?* On detection of any fault during the transfer, processing of
?* the entire message is aborted, and the device is deselected.
?* Until returning from the associated message completion callback,
?* no other spi_message queued to that device will be processed.
?* (This rule applies equally to all the synchronous transfer calls,
?* which are wrappers around this core asynchronous primitive.)
?*/
static inline int spi_async(struct spi_device *spi, struct spi_message *message)
{
??? message->spi = spi;
??? return spi->master->transfer(spi, message);
}
異步接口,根據spi device找到其所在的spi master,用master所特定的transfer函數實現數據收發。

核心層提供的通用數據收發接口,屏蔽了底層差異。

6??? 用戶接口層-SPI設備驅動
6.1??? 統一的設備模型
此驅動模型是針對SPI Slave的,只有注冊board info時modalias是spidev的才能由此驅動訪問。訪問各個slave的基本框架是一致的,具體的差異將由從設備號來體現。
6.1.1??? 關鍵數據結構
static struct spi_driver spidev_spi = {
??? .driver = {
??? ??? .name =??? ??? "spidev",
??? ??? .owner =??? THIS_MODULE,
??? },
??? .probe =??? spidev_probe,
??? .remove =??? __devexit_p(spidev_remove),
};
定義了一個標準的SPI_driver。

struct spidev_data {
??? struct device??? ??? dev;
??? struct spi_device??? *spi;
??? struct list_head??? device_entry;

??? struct mutex??? ??? buf_lock;
??? unsigned??? ??? users;
??? u8??? ??? ??? *buffer;
};

static LIST_HEAD(device_list);
static DEFINE_MUTEX(device_list_lock);
定義了一個SPI dev列表,每個slave對應一個struct spidev_data, 以device_entry為節點連接在device_list上。

static struct file_operations spidev_fops = {
??? .owner =??? THIS_MODULE,
??? .write =??? spidev_write,
??? .read =??? ??? spidev_read,
??? .ioctl =??? spidev_ioctl,
??? .open =??? ??? spidev_open,
??? .release =??? spidev_release,
};
任意一個需要和用戶空間通信的驅動必備的數據結構,其定義了具體的讀寫操作方法。

6.1.2??? 初始化
http://lxr.linux.no/#linux+v2.6.25/drivers/SPI/SPI-dev.c#L607

static int __init spidev_init(void)
{
??? int status;

??? /* Claim our 256 reserved device numbers.? Then register a class
??? ?* that will key udev/mdev to add/remove /dev nodes.? Last, register
??? ?* the driver which manages those device numbers.
??? ?*/
??? BUILD_BUG_ON(N_SPI_MINORS > 256);
??? status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
??? if (status < 0)
??? ??? return status;

??? status = class_register(&spidev_class);
??? if (status < 0) {
??? ??? unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
??? ??? return status;
??? }

??? status = spi_register_driver(&spidev_spi);
??? if (status < 0) {
??? ??? class_unregister(&spidev_class);
??? ??? unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
??? }
??? return status;
}
module_init(spidev_init);

首先注冊了一個字符型設備驅動,然后注冊spi_driver,其將在SPI總線上查找對應的設備,根據關鍵字spidev進行匹配,匹配成功后將調用spi_driver的probe方法,即spidev_probe,將驅動和每一個salve綁定起來,并建立對應的dev設備節點,同時維護了一個spidev_data鏈表。

static int spidev_probe(struct spi_device *spi)
{
??? struct spidev_data??? *spidev;
??? int??? ??? ??? status;
??? unsigned long??? ??? minor;

??? /* Allocate driver data */
??? spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
??? if (!spidev)
??? ??? return -ENOMEM;

??? /* Initialize the driver data */
??? spidev->spi = spi;
??? mutex_init(&spidev->buf_lock);

??? INIT_LIST_HEAD(&spidev->device_entry);

??? /* If we can allocate a minor number, hook up this device.
??? ?* Reusing minors is fine so long as udev or mdev is working.
??? ?*/
??? mutex_lock(&device_list_lock);
??? minor = find_first_zero_bit(minors, N_SPI_MINORS);
??? if (minor < N_SPI_MINORS) {
??? ??? spidev->dev.parent = &spi->dev;
??? ??? spidev->dev.class = &spidev_class;
??? ??? spidev->dev.devt = MKDEV(SPIDEV_MAJOR, minor);? //各個slave的從設備號是從0依次遞增的
??? ??? snprintf(spidev->dev.bus_id, sizeof spidev->dev.bus_id,
??? ??? ??? ??? "spidev%d.%d",
??? ??? ??? ??? spi->master->bus_num, spi->chip_select);
??? ??? status = device_register(&spidev->dev);
??? } else {
??? ??? dev_dbg(&spi->dev, "no minor number available!/n");
??? ??? status = -ENODEV;
??? }
??? if (status == 0) {
??? ??? set_bit(minor, minors);
??? ??? dev_set_drvdata(&spi->dev, spidev);
??? ??? list_add(&spidev->device_entry, &device_list);? // 將該SPI dev添加到SPI device列表中
??? }
??? mutex_unlock(&device_list_lock);

??? if (status != 0)
??? ??? kfree(spidev);

??? return status;
}
以SPI_MAJOR和動態從設備號為主從設備號注冊設備節點,如果系統有udev或者是hotplug,那么就會在/dev下自動創建相關的設備節點了,其名稱命名方式為spidevB.C,B為總線編號,C為片選序號。

6.1.3??? Open及release
static int spidev_open(struct inode *inode, struct file *filp)
{
??? struct spidev_data??? *spidev;
??? int??? ??? ??? status = -ENXIO;

??? mutex_lock(&device_list_lock);

??? list_for_each_entry(spidev, &device_list, device_entry) {
??? ??? if (spidev->dev.devt == inode->i_rdev) {? // 根據設備節點號進行匹配查找對應的spidev_data節點從而找到相應的從設備
??? ??? ??? status = 0;
??? ??? ??? break;
??? ??? }
??? }
??? if (status == 0) {
??? ??? if (!spidev->buffer) {
??? ??? ??? spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);? // 為每個slave分配了一段緩沖區
??? ??? ??? if (!spidev->buffer) {
??? ??? ??? ??? dev_dbg(&spidev->spi->dev, "open/ENOMEM/n");
??? ??? ??? ??? status = -ENOMEM;
??? ??? ??? }
??? ??? }
??? ??? if (status == 0) {
??? ??? ??? spidev->users++;? // 訪問該SPI slave的user加1
??? ??? ??? filp->private_data = spidev;?? //面向對象的典型應用,將特定數據保存在filp->private_data中,其他函數可以直接從filp->private_data中取出需要的東西,避免過多的使用全局變量來傳遞信息
??? ??? ??? nonseekable_open(inode, filp);
??? ??? }
??? } else
??? ??? pr_debug("spidev: nothing for minor %d/n", iminor(inode));

??? mutex_unlock(&device_list_lock);
??? return status;
}?


static int spidev_release(struct inode *inode, struct file *filp)
{
??? struct spidev_data??? *spidev;
??? int??? ??? ??? status = 0;

??? mutex_lock(&device_list_lock);
??? spidev = filp->private_data;
??? filp->private_data = NULL;
??? spidev->users--;
??? if (!spidev->users) {? // 無用戶再訪問該slave時才釋放其buffer
??? ??? kfree(spidev->buffer);
??? ??? spidev->buffer = NULL;
??? }
??? mutex_unlock(&device_list_lock);

??? return status;
}

Open操作是用戶空間程序和內核驅動交換的第一步,最終返回給用戶空間的就是struct file結構體。對于SPI 驅動來說,用戶空間所獲得的就是spidev這個關鍵信息。
6.1.4??? 數據收發
對于SPI驅動來說,數據收發有兩種途徑,read/write或者ioctl方式。但無論哪種方式,最終都是構造一個spi_message。

/**
?* struct spi_message - one multi-segment SPI transaction
?* @transfers: list of transfer segments in this transaction
?* @spi: SPI device to which the transaction is queued
?* @is_dma_mapped: if true, the caller provided both dma and cpu virtual
?*??? addresses for each transfer buffer
?* @complete: called to report transaction completions
?* @context: the argument to complete() when it's called
?* @actual_length: the total number of bytes that were transferred in all
?*??? successful segments
?* @status: zero for success, else negative errno
?* @queue: for use by whichever driver currently owns the message
?* @state: for use by whichever driver currently owns the message
?*
?* A @spi_message is used to execute an atomic sequence of data transfers,
?* each represented by a struct spi_transfer.? The sequence is "atomic"
?* in the sense that no other spi_message may use that SPI bus until that
?* sequence completes.? On some systems, many such sequences can execute
?* as single programmed DMA transfer.? On all systems, these messages are
?* queued, and might complete after transactions to other devices.? Messages
?* sent to a given spi_device are always executed in FIFO order.
?*
*/
struct spi_message {
??? struct list_head??? transfers;? // 所包含的spi_transfer鏈表
??? struct spi_device??? *spi;? // 消息所發往的對象
??? unsigned??? ??? is_dma_mapped:1;

??? /* completion is reported through a callback */
??? void??? ??? ??? (*complete)(void *context);? //消息發送完畢后的回調函數
??? void??? ??? ??? *context;
??? unsigned??? ??? actual_length;
??? int??? ??? ??? status;

??? /* for optional use by whatever driver currently owns the
??? ?* spi_message ...? between calls to spi_async and then later
??? ?* complete(), that's the spi_master controller driver.
??? ?*/
??? struct list_head??? queue;? // spi_message會由SPI Maser驅動統一組織成鏈表,順序處理,以此防止各個spi slave同時訪問總線而導致沖突
??? void??? ??? ??? *state;
};

spi_message 包含了一系列spi_transfer,在所有的spi_transfer發送完畢前,SPI總線一直被占據,其間不會出現總線沖突,因此一個spi_message是一個原子系列,這種特性非常利于復雜的SPI通信協議,如先發送命令字然后才能讀取數據。

/*
?* I/O INTERFACE between SPI controller and protocol drivers
?*
?* Protocol drivers use a queue of spi_messages, each transferring data
?* between the controller and memory buffers.
?*
?* The spi_messages themselves consist of a series of read+write transfer
?* segments.? Those segments always read the same number of bits as they
?* write; but one or the other is easily ignored by passing a null buffer
?* pointer.? (This is unlike most types of I/O API, because SPI hardware
?* is full duplex.)
?*
*/

/**
?* struct spi_transfer - a read/write buffer pair
?* @tx_buf: data to be written (dma-safe memory), or NULL
?* @rx_buf: data to be read (dma-safe memory), or NULL
?* @tx_dma: DMA address of tx_buf, if @spi_message.is_dma_mapped
?* @rx_dma: DMA address of rx_buf, if @spi_message.is_dma_mapped
?* @len: size of rx and tx buffers (in bytes)
?* @speed_hz: Select a speed other then the device default for this
?*????? transfer. If 0 the default (from @spi_device) is used.
?* @bits_per_word: select a bits_per_word other then the device default
?*????? for this transfer. If 0 the default (from @spi_device) is used.
?* @cs_change: affects chipselect after this transfer completes
?* @delay_usecs: microseconds to delay after this transfer before
?*??? (optionally) changing the chipselect status, then starting
?*??? the next transfer or completing this @spi_message.
?* @transfer_list: transfers are sequenced through @spi_message.transfers
?*
?* SPI transfers always write the same number of bytes as they read.
?* Protocol drivers should always provide @rx_buf and/or @tx_buf.
?*
?* If the transmit buffer is null, zeroes will be shifted out
?* while filling @rx_buf.? If the receive buffer is null, the data
?* shifted in will be discarded.? Only "len" bytes shift out (or in).
?* It's an error to try to shift out a partial word.? (For example, by
?* shifting out three bytes with word size of sixteen or twenty bits;
?* the former uses two bytes per word, the latter uses four bytes.)
?*
?* In-memory data values are always in native CPU byte order, translated
?* from the wire byte order (big-endian except with SPI_LSB_FIRST).? So
?* for example when bits_per_word is sixteen, buffers are 2N bytes long
?* (@len = 2N) and hold N sixteen bit words in CPU byte order.
?*
?* When the word size of the SPI transfer is not a power-of-two multiple
?* of eight bits, those in-memory words include extra bits.? In-memory
?* words are always seen by protocol drivers as right-justified, so the
?* undefined (rx) or unused (tx) bits are always the most significant bits.
?*
?* All SPI transfers start with the relevant chipselect active.? Normally
?* it stays selected until after the last transfer in a message.? Drivers
?* can affect the chipselect signal using cs_change.
?*
?* (i) If the transfer isn't the last one in the message, this flag is
?* used to make the chipselect briefly go inactive in the middle of the
?* message.? Toggling chipselect in this way may be needed to terminate
?* a chip command, letting a single spi_message perform all of group of
?* chip transactions together.
?*
?* (ii) When the transfer is the last one in the message, the chip may
?* stay selected until the next transfer.? On multi-device SPI busses
?* with nothing blocking messages going to other devices, this is just
?* a performance hint; starting a message to another device deselects
?* this one.? But in other cases, this can be used to ensure correctness.
?* Some devices need protocol transactions to be built from a series of
?* spi_message submissions, where the content of one message is determined
?* by the results of previous messages and where the whole transaction
?* ends when the chipselect goes intactive.
?*
?* The code that submits an spi_message (and its spi_transfers)
?* to the lower layers is responsible for managing its memory.
?* Zero-initialize every field you don't set up explicitly, to
?* insulate against future API updates.? After you submit a message
?* and its transfers, ignore them until its completion callback.
?*/
struct spi_transfer {
??? const void??? *tx_buf;
??? void??? ??? *rx_buf;
??? unsigned??? len;

??? dma_addr_t??? tx_dma;
??? dma_addr_t??? rx_dma;

??? unsigned??? cs_change:1;
??? u8??? ??? bits_per_word;
??? u16??? ??? delay_usecs;
??? u32??? ??? speed_hz;

??? struct list_head transfer_list;
};
一個spi_transfer是以某種特性如速率、延時及字長連續傳輸的最小單位。因為SPI是全雙工總線,只要總線上有數據傳輸,則MOSI和MISO上同時有數據采樣,對于SPI 控制器來說,其收發緩沖區中都有數據,但是對于用戶來說,其可以靈活的選擇tx_buf和rx_buf的設置。當發送數據時,tx_buf必須設置為待發送的數據,rx_buf可以為null或者指向無用的緩沖區,即不關心MISO的數據。而當接收數據時,rx_buf必須指向接收緩沖區,而tx_buf可以為Null,則MOSI上為全0,或者tx_buf指向不會引起從設備異常相應的數據。Len為待發送或者接收的數據長度。當一系列spi_transfer構成一個spi_message時,cs_change的設置非常講究。當cs_change為0即不變時,則可以連續對同一個設備進行數據收發;當cs_change為1時通常用來停止command的傳輸而隨后進行數據接收。因此cs_change可以利用SPI總線構造復雜的通信協議。

對于SPI總線協議來說,傳輸單位可以是4-32之間的任意bits,但對于SPI控制器來說,bits_per_word只能是8/16/32,即需要取整,待收發的數據在內存里都是以主機字節序右對齊存儲,SPI控制器會自動根據傳輸單位及大小端進行轉換。

對于read/write方式來說,如果不采用board info中的默認設置,則必須先通過ioctl方式設置該從設備特定的大小端、bits per word及速率等特性,后續數據傳輸時將一直采用此設置。

read/write與用戶空間的接口是buf和count,分別為收發的相應數據指針和數據長度。

/* Read-only message with current device setup */
static ssize_t spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
??? struct spidev_data??? *spidev;
??? struct spi_device??? *spi;
??? ssize_t??? ??? ??? status = 0;

??? /* chipselect only toggles at start or end of operation */? // 在count個數據發送期間,該slave的片選一直有效
??? if (count > bufsiz)
??? ??? return -EMSGSIZE;

??? spidev = filp->private_data;
??? spi = spidev->spi;

??? mutex_lock(&spidev->buf_lock); // 對同一個SPI slave的互斥訪問
??? status = spi_read(spi, spidev->buffer, count);
??? if (status == 0) {
??? ??? unsigned long??? missing;

??? ??? missing = copy_to_user(buf, spidev->buffer, count);
??? ??? if (count && missing == count)
??? ??? ??? status = -EFAULT;
??? ??? else
??? ??? ??? status = count - missing;
??? }
??? mutex_unlock(&spidev->buf_lock);

??? return status;
}


/**
?* spi_read - SPI synchronous read
?* @spi: device from which data will be read
?* @buf: data buffer
?* @len: data buffer size
?* Context: can sleep
?*
?* This reads the buffer and returns zero or a negative error code.
?* Callable only from contexts that can sleep.
?*/
static inline int spi_read(struct spi_device *spi, u8 *buf, size_t len)
{
??? struct spi_transfer??? t = {
??? ??? ??? .rx_buf??? ??? = buf,
??? ??? ??? .len??? ??? = len,
??? ??? };
??? struct spi_message??? m;

??? spi_message_init(&m);
??? spi_message_add_tail(&t, &m);
??? return spi_sync(spi, &m);
}
自動構建一個spi_message,其只包含一個spi_transfer。

/* Write-only message with current device setup */
static ssize_t spidev_write(struct file *filp, const char __user *buf,
??? ??? size_t count, loff_t *f_pos)
{
??? struct spidev_data??? *spidev;
??? struct spi_device??? *spi;
??? ssize_t??? ??? ??? status = 0;
??? unsigned long??? ??? missing;

??? /* chipselect only toggles at start or end of operation */
??? if (count > bufsiz)
??? ??? return -EMSGSIZE;

??? spidev = filp->private_data;
??? spi = spidev->spi;

??? mutex_lock(&spidev->buf_lock);
??? missing = copy_from_user(spidev->buffer, buf, count); // 將待發送的數據從用戶空間拷貝至內核空間
??? if (missing == 0) {
??? ??? status = spi_write(spi, spidev->buffer, count);
??? ??? if (status == 0)
??? ??? ??? status = count;
??? } else
??? ??? status = -EFAULT;
??? mutex_unlock(&spidev->buf_lock);

??? return status;
}

對于ioctl方式,默認情況下就是進行數據收發。

IO ctrl方式與用戶空間的接口為spi_ioc_transfer,其數據結構如下:
/**
?* struct spi_ioc_transfer - describes a single SPI transfer
?* @tx_buf: Holds pointer to userspace buffer with transmit data, or null.
?*??? If no data is provided, zeroes are shifted out.
?* @rx_buf: Holds pointer to userspace buffer for receive data, or null.
?* @len: Length of tx and rx buffers, in bytes.
?* @speed_hz: Temporary override of the device's bitrate.
?* @bits_per_word: Temporary override of the device's wordsize.
?* @delay_usecs: If nonzero, how long to delay after the last bit transfer
?*??? before optionally deselecting the device before the next transfer.
?* @cs_change: True to deselect device before starting the next transfer.
?*
?* This structure is mapped directly to the kernel spi_transfer structure;
?* the fields have the same meanings, except of course that the pointers
?* are in a different address space (and may be of different sizes in some
?* cases, such as 32-bit i386 userspace over a 64-bit x86_64 kernel).
?* Zero-initialize the structure, including currently unused fields, to
?* accomodate potential future updates.
?*
?* SPI_IOC_MESSAGE gives userspace the equivalent of kernel spi_sync().
?* Pass it an array of related transfers, they'll execute together.
?* Each transfer may be half duplex (either direction) or full duplex.
?*
?*??? struct spi_ioc_transfer mesg[4];
?*??? ...
?*??? status = ioctl(fd, SPI_IOC_MESSAGE(4), mesg);
?*
?* So for example one transfer might send a nine bit command (right aligned
?* in a 16-bit word), the next could read a block of 8-bit data before
?* terminating that command by temporarily deselecting the chip; the next
?* could send a different nine bit command (re-selecting the chip), and the
?* last transfer might write some register values.
?*/
struct spi_ioc_transfer {
??? __u64??? ??? tx_buf;
??? __u64??? ??? rx_buf;

??? __u32??? ??? len;
??? __u32??? ??? speed_hz;

??? __u16??? ??? delay_usecs;
??? __u8??? ??? bits_per_word;
??? __u8??? ??? cs_change;
??? __u32??? ??? pad;

};
spi_ioc_transfer可以動態的改變各種傳輸特性,并且可以將多個spi_ioc_transfer組織成一個message連續傳輸,比read/write方式提供了更多靈活性。

static int spidev_ioctl(struct inode *inode, struct file *filp,
??? ??? unsigned int cmd, unsigned long arg)
{
??? int??? ??? ??? err = 0;
??? int??? ??? ??? retval = 0;
??? struct spidev_data??? *spidev;
??? struct spi_device??? *spi;
??? u32??? ??? ??? tmp;
??? unsigned??? ??? n_ioc;
??? struct spi_ioc_transfer??? *ioc;

??? /* Check type and command number */
??? if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)
??? ??? return -ENOTTY;

??? /* Check access direction once here; don't repeat below.
??? ?* IOC_DIR is from the user perspective, while access_ok is
??? ?* from the kernel perspective; so they look reversed.
??? ?*/
??? if (_IOC_DIR(cmd) & _IOC_READ)
??? ??? err = !access_ok(VERIFY_WRITE,
??? ??? ??? ??? (void __user *)arg, _IOC_SIZE(cmd));
??? if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE)
??? ??? err = !access_ok(VERIFY_READ,
??? ??? ??? ??? (void __user *)arg, _IOC_SIZE(cmd));
??? if (err)
??? ??? return -EFAULT;

??? spidev = filp->private_data;
??? spi = spidev->spi;? // 找到對應的struct spi_device

??? switch (cmd) {
??? /* read requests */? // 讀取該slave的相關屬性
??? case SPI_IOC_RD_MODE:

??? case SPI_IOC_RD_LSB_FIRST:

??? case SPI_IOC_RD_BITS_PER_WORD:

??? case SPI_IOC_RD_MAX_SPEED_HZ:
??? ??? retval = __put_user(spi->max_speed_hz, (__u32 __user *)arg);
??? ??? break;

??? /* write requests */
??? case SPI_IOC_WR_MODE:
??? ??? retval = __get_user(tmp, (u8 __user *)arg);
??? ??? if (retval == 0) {
??? ??? ??? u8??? save = spi->mode;

??? ??? ??? if (tmp & ~SPI_MODE_MASK) {
??? ??? ??? ??? retval = -EINVAL;
??? ??? ??? ??? break;
??? ??? ??? }

??? ??? ??? tmp |= spi->mode & ~SPI_MODE_MASK;
??? ??? ??? spi->mode = (u8)tmp;? //更新相關屬性
??? ??? ??? retval = spi_setup(spi);? //使相關屬性生效,在此不設置也可以,因為每次傳輸時都會重新設置SPI master
??? ??? ??? if (retval < 0)
??? ??? ??? ??? spi->mode = save;
??? ??? ??? else
??? ??? ??? ??? dev_dbg(&spi->dev, "spi mode %02x/n", tmp);
??? ??? }
??? ??? break;
??? case SPI_IOC_WR_LSB_FIRST:
??? ??? 。。。
??? case SPI_IOC_WR_BITS_PER_WORD:
。。。
??? case SPI_IOC_WR_MAX_SPEED_HZ:
。。。

??? default:
??? ??? /* segmented and/or full-duplex I/O request */
??? ??? if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))
??? ??? ??? ??? || _IOC_DIR(cmd) != _IOC_WRITE)
??? ??? ??? return -ENOTTY;

??? ??? tmp = _IOC_SIZE(cmd);
??? ??? if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) {
??? ??? ??? retval = -EINVAL;
??? ??? ??? break;
??? ??? }
??? ??? n_ioc = tmp / sizeof(struct spi_ioc_transfer);
??? ??? if (n_ioc == 0)
??? ??? ??? break;

??? ??? /* copy into scratch area */
??? ??? ioc = kmalloc(tmp, GFP_KERNEL);
??? ??? if (!ioc) {
??? ??? ??? retval = -ENOMEM;
??? ??? ??? break;
??? ??? }
??? ??? if (__copy_from_user(ioc, (void __user *)arg, tmp)) {
??? ??? ??? kfree(ioc);
??? ??? ??? retval = -EFAULT;
??? ??? ??? break;
??? ??? }

??? ??? /* translate to spi_message, execute */
??? ??? retval = spidev_message(spidev, ioc, n_ioc);
??? ??? kfree(ioc);
??? ??? break;
??? }
??? return retval;
}


static int spidev_message(struct spidev_data *spidev,
??? ??? struct spi_ioc_transfer *u_xfers, unsigned n_xfers)
{
??? struct spi_message??? msg;
??? struct spi_transfer??? *k_xfers;
??? struct spi_transfer??? *k_tmp;
??? struct spi_ioc_transfer *u_tmp;
??? struct spi_device??? *spi = spidev->spi;
??? unsigned??? ??? n, total;
??? u8??? ??? ??? *buf;
??? int??? ??? ??? status = -EFAULT;

??? spi_message_init(&msg);
??? k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL);
??? if (k_xfers == NULL)
??? ??? return -ENOMEM;

??? /* Construct spi_message, copying any tx data to bounce buffer.
??? ?* We walk the array of user-provided transfers, using each one
??? ?* to initialize a kernel version of the same transfer.
??? ?*/
??? mutex_lock(&spidev->buf_lock);
??? buf = spidev->buffer;
??? total = 0;
??? for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
??? ??? ??? n;
??? ??? ??? n--, k_tmp++, u_tmp++) {
??? ??? k_tmp->len = u_tmp->len;

??? ??? total += k_tmp->len;
??? ??? if (total > bufsiz) {
??? ??? ??? status = -EMSGSIZE;
??? ??? ??? goto done;
??? ??? }

??? ??? if (u_tmp->rx_buf) {
??? ??? ??? k_tmp->rx_buf = buf;
??? ??? ??? if (!access_ok(VERIFY_WRITE, (u8 __user *)
??? ??? ??? ??? ??? ??? (uintptr_t) u_tmp->rx_buf,
??? ??? ??? ??? ??? ??? u_tmp->len))
??? ??? ??? ??? goto done;
??? ??? }
??? ??? if (u_tmp->tx_buf) {
??? ??? ??? k_tmp->tx_buf = buf;
??? ??? ??? if (copy_from_user(buf, (const u8 __user *)
??? ??? ??? ??? ??? ??? (uintptr_t) u_tmp->tx_buf,
??? ??? ??? ??? ??? u_tmp->len))
??? ??? ??? ??? goto done;
??? ??? }
??? ??? buf += k_tmp->len;
??? ??? // 將用戶指定的傳輸屬性保存起來,構成內核 spi_transfer
??? ??? k_tmp->cs_change = !!u_tmp->cs_change;??
??? ??? k_tmp->bits_per_word = u_tmp->bits_per_word;
??? ??? k_tmp->delay_usecs = u_tmp->delay_usecs;
??? ??? k_tmp->speed_hz = u_tmp->speed_hz;

??? ??? spi_message_add_tail(k_tmp, &msg);? // 將多個spi_transfer組織為spi_message
??? }

??? status = spi_sync(spi, &msg);
??? if (status < 0)
??? ??? goto done;

??? /* copy any rx data out of bounce buffer */
??? buf = spidev->buffer;
??? for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {
??? ??? if (u_tmp->rx_buf) {? //當用戶設置了rx buf時才將數據拷貝到用戶空間
??? ??? ??? if (__copy_to_user((u8 __user *)
??? ??? ??? ??? ??? (uintptr_t) u_tmp->rx_buf, buf,
??? ??? ??? ??? ??? u_tmp->len)) {
??? ??? ??? ??? status = -EFAULT;
??? ??? ??? ??? goto done;
??? ??? ??? }
??? ??? }
??? ??? buf += u_tmp->len;
??? }
??? status = total;

done:
??? mutex_unlock(&spidev->buf_lock);
??? kfree(k_xfers);
??? return status;
}

6.2??? 特定的設備驅動
原則上所有的SPI從設備都可以通過上述統一的設備模型spidev來訪問,但SPI 總線上傳輸的是透明的數據,而對于某些SPI從設備,其數據傳輸需要遵循一定的格式,如果用spidev來訪問,則用戶需要組織好所有的spi_transfer,對一般用戶來說可能比較復雜。為了提供更加user friendly的接口,可以對特定SPI從設備的訪問進行再次封裝。

如SPI接口的MMC卡,其數據的轉換較為復雜,為了減少用戶空間的轉換,提供了專用的MMC驅動供用戶程序訪問,其向上屏蔽了具體MMC芯片的差異。

下面以drivers/mmc/host/mmc_spi.c為例來進行講述。
6.2.1??? 關鍵數據結構
struct mmc_spi_host {
??? struct mmc_host??? ??? *mmc;
??? struct spi_device??? *spi;? // MMC卡對應的SPI 設備

??? unsigned char??? ??? power_mode;
??? u16??? ??? ??? powerup_msecs;

??? struct mmc_spi_platform_data??? *pdata;

??? /* for bulk data transfers */
??? struct spi_transfer??? token, t, crc, early_status;? //SD/MMC卡協議對應的各種transfer
??? struct spi_message??? m;

??? /* for status readback */
??? struct spi_transfer??? status;
??? struct spi_message??? readback;

??? /* underlying DMA-aware controller, or null */
??? struct device??? ??? *dma_dev;

??? /* buffer used for commands and for message "overhead" */
??? struct scratch??? ??? *data;
??? dma_addr_t??? ??? data_dma;

??? /* Specs say to write ones most of the time, even when the card
??? ?* has no need to read its input data; and many cards won't care.
??? ?* This is our source of those ones.
??? ?*/
??? void??? ??? ??? *ones;
??? dma_addr_t??? ??? ones_dma;
};?
mmc_spi_host描述了一個基于SPI總線的MMC卡設備。

static struct spi_driver mmc_spi_driver = {
??? .driver = {
??? ??? .name =??? ??? "mmc_spi",
??? ??? .bus =??? ??? &spi_bus_type,
??? ??? .owner =??? THIS_MODULE,
??? },
??? .probe =??? mmc_spi_probe,
??? .remove =??? __devexit_p(mmc_spi_remove),
};

特定的mmc_spi_driver,但其本質上是spi_driver,和SPI 設備匹配的關鍵字是mmc_spi。

6.2.2??? 初始化
static int __init mmc_spi_init(void)
{
??? return spi_register_driver(&mmc_spi_driver);
}
module_init(mmc_spi_init);


static void __exit mmc_spi_exit(void)
{
??? spi_unregister_driver(&mmc_spi_driver);
}

調用spi_register_driver向SPI總線上注冊mmc_spi_driver,根據mmc_spi進行匹配,匹配成功后將自動調用mmc_spi_probe。

static int mmc_spi_probe(struct spi_device *spi)
{
??? void??? ??? ??? *ones;
??? struct mmc_host??? ??? *mmc;
??? struct mmc_spi_host??? *host;
??? int??? ??? ??? status;

??? /* MMC and SD specs only seem to care that sampling is on the
??? ?* rising edge ... meaning SPI modes 0 or 3.? So either SPI mode
??? ?* should be legit.? We'll use mode 0 since it seems to be a
??? ?* bit less troublesome on some hardware ... unclear why.
??? ?*/
??? spi->mode = SPI_MODE_0;
??? spi->bits_per_word = 8;

??? status = spi_setup(spi);
??? if (status < 0) {
??? ??? dev_dbg(&spi->dev, "needs SPI mode %02x, %d KHz; %d/n",
??? ??? ??? ??? spi->mode, spi->max_speed_hz / 1000,
??? ??? ??? ??? status);
??? ??? return status;
??? }

??? /* We can use the bus safely iff nobody else will interfere with us.
??? ?* Most commands consist of one SPI message to issue a command, then
??? ?* several more to collect its response, then possibly more for data
??? ?* transfer.? Clocking access to other devices during that period will
??? ?* corrupt the command execution.
??? ?*
??? ?*/
??? if (spi->master->num_chipselect > 1) {
??? ??? struct count_children cc;

??? ??? cc.n = 0;
??? ??? cc.bus = spi->dev.bus;
??? ??? status = device_for_each_child(spi->dev.parent, &cc,
??? ??? ??? ??? maybe_count_child);
??? ??? if (status < 0) {
??? ??? ??? dev_err(&spi->dev, "can't share SPI bus/n");
??? ??? ??? return status;
??? ??? }

??? ??? dev_warn(&spi->dev, "ASSUMING SPI bus stays unshared!/n");
??? }

??? /* We need a supply of ones to transmit.? This is the only time
??? ?* the CPU touches these, so cache coherency isn't a concern.
??? ?*
??? ?* NOTE if many systems use more than one MMC-over-SPI connector
??? ?* it'd save some memory to share this.? That's evidently rare.
??? ?*/
??? status = -ENOMEM;
??? ones = kmalloc(MMC_SPI_BLOCKSIZE, GFP_KERNEL);
??? if (!ones)
??? ??? goto nomem;
??? memset(ones, 0xff, MMC_SPI_BLOCKSIZE);

??? mmc = mmc_alloc_host(sizeof(*host), &spi->dev);
??? if (!mmc)
??? ??? goto nomem;

??? mmc->ops = &mmc_spi_ops;
??? mmc->max_blk_size = MMC_SPI_BLOCKSIZE;

??? /* As long as we keep track of the number of successfully
??? ?* transmitted blocks, we're good for multiwrite.
??? ?*/
??? mmc->caps = MMC_CAP_SPI | MMC_CAP_MULTIWRITE;

??? /* SPI doesn't need the lowspeed device identification thing for
??? ?* MMC or SD cards, since it never comes up in open drain mode.
??? ?* That's good; some SPI masters can't handle very low speeds!
??? ?*
??? ?* However, low speed SDIO cards need not handle over 400 KHz;
??? ?* that's the only reason not to use a few MHz for f_min (until
??? ?* the upper layer reads the target frequency from the CSD).
??? ?*/
??? mmc->f_min = 400000;
??? mmc->f_max = spi->max_speed_hz;

??? host = mmc_priv(mmc);
??? host->mmc = mmc;
??? host->spi = spi;

??? host->ones = ones;

??? /* Platform data is used to hook up things like card sensing
??? ?* and power switching gpios.
??? ?*/
??? host->pdata = spi->dev.platform_data;
??? if (host->pdata)
??? ??? mmc->ocr_avail = host->pdata->ocr_mask;
??? if (!mmc->ocr_avail) {
??? ??? dev_warn(&spi->dev, "ASSUMING 3.2-3.4 V slot power/n");
??? ??? mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
??? }
??? if (host->pdata && host->pdata->setpower) {
??? ??? host->powerup_msecs = host->pdata->powerup_msecs;
??? ??? if (!host->powerup_msecs || host->powerup_msecs > 250)
??? ??? ??? host->powerup_msecs = 250;
??? }

??? dev_set_drvdata(&spi->dev, mmc);

??? /* preallocate dma buffers */
??? host->data = kmalloc(sizeof(*host->data), GFP_KERNEL);
??? if (!host->data)
??? ??? goto fail_nobuf1;

??? if (spi->master->dev.parent->dma_mask) {
??? ??? struct device??? *dev = spi->master->dev.parent;

??? ??? host->dma_dev = dev;
??? ??? host->ones_dma = dma_map_single(dev, ones,
??? ??? ??? ??? MMC_SPI_BLOCKSIZE, DMA_TO_DEVICE);
??? ??? host->data_dma = dma_map_single(dev, host->data,
??? ??? ??? ??? sizeof(*host->data), DMA_BIDIRECTIONAL);

??? ??? /* REVISIT in theory those map operations can fail... */

??? ??? dma_sync_single_for_cpu(host->dma_dev,
??? ??? ??? ??? host->data_dma, sizeof(*host->data),
??? ??? ??? ??? DMA_BIDIRECTIONAL);
??? }

??? /* setup message for status/busy readback */
??? spi_message_init(&host->readback);
??? host->readback.is_dma_mapped = (host->dma_dev != NULL);

??? spi_message_add_tail(&host->status, &host->readback);
??? host->status.tx_buf = host->ones;
??? host->status.tx_dma = host->ones_dma;
??? host->status.rx_buf = &host->data->status;
??? host->status.rx_dma = host->data_dma + offsetof(struct scratch, status);
??? host->status.cs_change = 1;

??? /* register card detect irq */
??? if (host->pdata && host->pdata->init) {
??? ??? status = host->pdata->init(&spi->dev, mmc_spi_detect_irq, mmc);
??? ??? if (status != 0)
??? ??? ??? goto fail_glue_init;
??? }

??? status = mmc_add_host(mmc);
??? if (status != 0)
??? ??? goto fail_add_host;

??? dev_info(&spi->dev, "SD/MMC host %s%s%s%s/n",
??? ??? ??? mmc->class_dev.bus_id,
??? ??? ??? host->dma_dev ? "" : ", no DMA",
??? ??? ??? (host->pdata && host->pdata->get_ro)
??? ??? ??? ??? ? "" : ", no WP",
??? ??? ??? (host->pdata && host->pdata->setpower)
??? ??? ??? ??? ? "" : ", no poweroff");
??? return 0;

fail_add_host:
??? mmc_remove_host (mmc);
fail_glue_init:
??? if (host->dma_dev)
??? ??? dma_unmap_single(host->dma_dev, host->data_dma,
??? ??? ??? ??? sizeof(*host->data), DMA_BIDIRECTIONAL);
??? kfree(host->data);

fail_nobuf1:
??? mmc_free_host(mmc);
??? dev_set_drvdata(&spi->dev, NULL);

nomem:
??? kfree(ones);
??? return status;
}
其主要功能是注冊一個struct mmc_host,最終的數據訪問是由其他子系統發起的。
6.2.3??? 數據收發
TBD

7??? 驅動訪問示例
下面以一款TI 的SPI接口的時鐘芯片62002為例來講述用戶空間如何訪問SPI設備。

62002支持32bits、LSB訪問模式。內部寄存器為28bits,其他4bits為地址位。
?
支持的讀寫命令如下:
?
Xxxx為待寫的數據,read command中AAAA為待訪問的寄存器地址。
7.1.1??? 寫操作
62002寫操作通常比較簡單,時序如下,
?

struct spi_ioc_transfer? write? = {
??? ??? .tx_buf = (int)dout,
??? ??? .rx_buf = NULL, //寫操作時,忽略MISO
??? ??? .len = sizeof(long),
??? ??? .delay_usecs = 0,
??? ??? .speed_hz = 500000,
??? ??? .bits_per_word = 32,
??? ??? .cs_change = 1,
};

Dout = data << 4 + address;? // 待發送的數據Dout由28bits的data field和4位地址域構成。

ret = ioctl(fd, SPI_IOC_MESSAGE(1), &write);
7.1.2??? 讀操作
62002的讀操作較復雜,需要先發送寫命令指定待讀的內部寄存器地址,然后再發起總線的讀操作。時序如下:
?
SPI Master發送讀命令后,抬起SPI片選,62002解碼讀命令中的寄存器地址。當SPI Master再次通過片選選中62002時,62002在MISO上發出待讀的數據。

因此需要在總線上連續進行兩次數據傳輸,即兩個spi_ioc_transfer 。
struct spi_ioc_transfer? read[2]? = {
??? {
??? ??? ??? .tx_buf = (int)dout,
??? ??? ??? .rx_buf = NULL,? //發送讀命令時,忽略MISO
??? ??? ??? .len = sizeof(long),
??? ??? ??? .delay_usecs = 0,
??? ??? ??? .speed_hz = 500000,
??? ??? ??? .bits_per_word = 32,
??? ??? ??? .cs_change = 1,
??? },
??? {
??? ??? ??? .tx_buf = NULL,? //讀取數據時,忽略MOSI,總線上默然發送的是0
??? ??? ??? .rx_buf = (int)din,
??? ??? ??? .len = sizeof(long),
??? ??? ??? .delay_usecs = 0,
??? ??? ??? .speed_hz = 500000,
??? ??? ??? .bits_per_word = 32,
??? ??? ??? .cs_change = 1,
??? }
}

第一個spi_ioc_transfer發送讀命令,第二個spi_ioc_transfer讀取數據。

ret = ioctl(fd, SPI_IOC_MESSAGE(2), read);
兩個spi_ioc_transfer將構成一個spi_message,其在內核中是一個原子序列,將連續占用總線直至兩個spi_ioc_transfer發送完畢。

第一個spi_ioc_transfer的cs_change一定要置為1,即spi_transfer發送完畢后片選要發生變化,由低到高,這樣才滿足62002的時序。

8??? 參考鳴謝
http://blog.csdn.net/sailor_8318/archive/2010/09/25/5905988.aspx

與50位技術專家面對面20年技術見證,附贈技術全景圖

總結

以上是生活随笔為你收集整理的【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】PowerPC + Linux2.6.25平台下的SPI驱动架构分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 网红av在线 | 三级在线看中文字幕完整版 | 永久免费AV无码网站韩国毛片 | 久草免费在线播放 | 亚洲高清不卡 | 亚洲精品三 | 欧美熟妇毛茸茸 | 久久久久久亚洲精品中文字幕 | 男女做爰真人视频直播 | 玖玖爱这里只有精品 | 亚洲av永久纯肉无码精品动漫 | 手机av在线网 | 欧美精品免费看 | 亚洲天堂男人网 | 亚洲一区二区三区日韩 | 懂色av一区二区三区在线播放 | 亚洲精品h | 禁漫天堂黄漫画无遮挡观看 | 永久免费的av网站 | 成人做爰66片免费看网站 | www.香蕉视频在线观看 | 琪琪五月天 | 69sex久久精品国产麻豆 | 一区二区福利 | 一区二区三区久久精品 | 黄色的视频网站 | 777精品| 我和单位漂亮少妇激情 | 99黄色片 | 51嘿嘿嘿国产精品伦理 | 亚洲色图偷 | 黄wwwww | 亚洲综合网在线观看 | 欧美日韩人妻精品一区二区三区 | 成年人www | 久热精品在线观看视频 | 日韩久久精品一区二区 | 天天黄色片 | 国产欧美综合一区二区三区 | 亚洲黄色一区 | 在线播放你懂的 | 欧美日韩在线二区 | 深夜视频在线 | 午夜天堂在线观看 | 亚洲国产精品午夜久久久 | 国产精品一区二区欧美 | 中文字幕在线观看不卡 | 国产精品久久久久9999 | 特级大胆西西4444人体 | 日本少妇b | 超碰在线国产97 | 亚洲欧美另类在线视频 | 黄色在线观看www | 2021毛片| 永久免费网站直接看 | 免费麻豆视频 | 97人妻精品一区二区三区视频 | 欧美在线观看视频 | 在线网站黄 | 91亚洲精品久久久久久久久久久久 | 超碰免费在线 | 伊人色播 | 美女被草出白浆 | 色偷偷av一区二区三区 | 日本精品免费视频 | 青青草精品在线视频 | 欧美性福利 | 国模私拍在线观看 | 极品销魂美女一区二区 | 亚洲国内在线 | 日本黄色大片在线观看 | 波多野结衣电影免费观看 | 日韩精品一二区 | 日本欧美国产一区二区三区 | 中文成人在线 | 色老头在线视频 | 男女一起插插插 | 欧美va在线观看 | 岛国大片在线 | 久久一区二区电影 | 人妻少妇久久中文字幕 | 亚洲婷婷综合网 | 五月天国产在线 | 三级伦理片 | 综合精品国产 | 一级黄色美女视频 | 色欲av伊人久久大香线蕉影院 | 亚洲天堂2013| 日韩av手机在线 | 亚洲欧美日本在线观看 | 老妇裸体性猛交视频 | 欧美另类一区 | 99资源| 夜夜操av | 香港三级韩国三级日本三级 | 亚洲黑丝在线 | 久久影视精品 | 中国特级黄色大片 | aaa人片在线|