【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】PowerPC + Linux2.6.25平台下的SPI驱动架构分析
PowerPC + Linux2.6.25平臺下的SPI驅(qū)動架構(gòu)分析
?
Sailor_forever? sailing_9806#163.com
(本原創(chuàng)文章發(fā)表于Sailor_forever 的個人blog,未經(jīng)本人許可,不得用于商業(yè)用途。任何個人、媒體、其他網(wǎng)站不得私自抄襲;網(wǎng)絡(luò)媒體轉(zhuǎn)載請注明出處,增加原文鏈接,否則屬于侵權(quán)行為。如 有任何問題,請留言或者發(fā)郵件給sailing_9806#163.com)
http://blog.csdn.net/sailor_8318/archive/2010/10/31/5977733.aspx
?
?
【摘要】本文以PowerPC+Linux 2.6.25 平臺為例,詳細(xì)分析了SPI總線的驅(qū)動架構(gòu)。首先介紹了SPI的總體架構(gòu),從用戶的角度將其分為三個層面,不同的開發(fā)者只需要關(guān)注相應(yīng)的層面即可。然后分析了主要數(shù)據(jù)結(jié)構(gòu)及其之間的相互關(guān)系,接著分析了不同層的具體實現(xiàn),最后以一款SPI接口的時鐘芯片為例講述了如何在用戶空間訪問SPI驅(qū)動。對于ARM + Linux平臺,只有平臺依賴層即總線控制器驅(qū)動有差異。
【關(guān)鍵字】PowerPC, SPI, Master, Slave, spidev
目錄
1??? SPI概述??? 3
2??? SPI總體架構(gòu)??? 3
2.1??? 硬件抽象層??? 3
2.2??? 平臺依賴層??? 3
2.3??? 用戶接口層??? 3
3??? 主要的數(shù)據(jù)結(jié)構(gòu)??? 4
3.1??? Spi_master??? 4
3.2??? SPI_driver??? 5
3.3??? Spi_device??? 6
4??? 平臺依賴層-總線控制器驅(qū)動??? 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??? 驅(qū)動注冊??? 19
5.4??? 數(shù)據(jù)傳輸??? 19
6??? 用戶接口層-SPI設(shè)備驅(qū)動??? 21
6.1??? 統(tǒng)一的設(shè)備模型??? 21
6.1.1??? 關(guān)鍵數(shù)據(jù)結(jié)構(gòu)??? 21
6.1.2??? 初始化??? 22
6.1.3??? Open及release??? 24
6.1.4??? 數(shù)據(jù)收發(fā)??? 25
6.2??? 特定的設(shè)備驅(qū)動??? 36
6.2.1??? 關(guān)鍵數(shù)據(jù)結(jié)構(gòu)??? 37
6.2.2??? 初始化??? 38
6.2.3??? 數(shù)據(jù)收發(fā)??? 42
7??? 驅(qū)動訪問示例??? 42
7.1.1??? 寫操作??? 43
7.1.2??? 讀操作??? 43
8??? 參考鳴謝??? 44
1??? SPI概述
SPI的通信原理很簡單,它以主從方式工作,這種模式通常有一個主設(shè)備和一個或多個從設(shè)備,需要至少4根線,事實上3根也可以(單向傳輸時或者硬件復(fù)用兩根數(shù)據(jù)線),也是所有基于SPI的設(shè)備共有的,它們是MISO、MOSI、SCK、CS。
(1)SDO – 主設(shè)備數(shù)據(jù)輸出,從設(shè)備數(shù)據(jù)輸入
(2)MISO– 主設(shè)備數(shù)據(jù)輸入,從設(shè)備數(shù)據(jù)輸出
(3)SCK – 時鐘信號,由主設(shè)備產(chǎn)生
(4)CS – 從設(shè)備使能信號,由主設(shè)備控制
其中CS是控制芯片是否被選中的,也就是說只有片選信號為預(yù)先規(guī)定的使能信號時(高電位或低電位),對此芯片的操作才有效,這就允許在同一總線上連接多個SPI設(shè)備成為可能。接下來就負(fù)責(zé)通訊的3根線了,通訊是通過數(shù)據(jù)交換完成的。SPI是串行通訊協(xié)議,也就是說數(shù)據(jù)是一位一位從MSB或者LSB開始傳輸?shù)?#xff0c;這就是SCK時鐘線存在的原因,由SCK提供時鐘脈沖,MISO、MOSI則基于此脈沖完成數(shù)據(jù)傳輸。 SPI支持4-32bits的串行數(shù)據(jù)傳輸,支持MSB和LSB,每次數(shù)據(jù)傳輸時當(dāng)從設(shè)備的大小端發(fā)生變化時需要重新設(shè)置SPI Master的大小端。
2??? SPI總體架構(gòu)
在2.6的Linux內(nèi)核中,SPI的驅(qū)動架構(gòu)分為如下三個層次:硬件抽象層、平臺依賴層和用戶接口層。
2.1??? 硬件抽象層
SPI-bitbang.c和SPI.c為其主體框架代碼,提供了核心數(shù)據(jù)結(jié)構(gòu)的定義、SPI控制器驅(qū)動和設(shè)備驅(qū)動的注冊、注銷管理等API。其為硬件平臺無關(guān)層,向下屏蔽了物理總線控制器的差異,定義了統(tǒng)一的訪問策略和接口;其向上提供了統(tǒng)一的接口,以便SPI設(shè)備驅(qū)動通過總線控制器進(jìn)行數(shù)據(jù)收發(fā)。
2.2??? 平臺依賴層
SPI總線Master就是一條SPI總線的控制器(所謂控制是相對于本CPU來說的),在物理上連接若干SPI從設(shè)備。在Linux驅(qū)動中,每種處理器平臺有自己的控制器驅(qū)動,屬于平臺移植相關(guān)層。PowerPC平臺來說,其是spi_mpc83xx.c。其按照核心層定義的接口實現(xiàn)了spi_master。
2.3??? 用戶接口層
設(shè)備驅(qū)動層為用戶接口層,其為用戶提供了通過SPI總線訪問具體設(shè)備的接口。
3??? 主要的數(shù)據(jù)結(jié)構(gòu)
3.1??? Spi_master
spi_master是對某一條SPI總線的抽象,是特定總線的相關(guān)屬性的集合。
/**
?* 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服務(wù)的,SPI_driver注冊時會掃描SPI bus上的設(shè)備,進(jìn)行驅(qū)動和設(shè)備的綁定。
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對應(yīng)著SPI總線上某個特定的slave。每個slave都有特定的大小端、速率及傳輸位寬,各個slave相互之間無干擾。
4??? 平臺依賴層-總線控制器驅(qū)動
總線控制器驅(qū)動,本質(zhì)上就是實現(xiàn)了具體的總線傳輸算法并向核心層注冊了控制器。主要分為三個層面,platform device,platform driver及與SPI core的接口層。
Linux內(nèi)核的所有SPI控制器驅(qū)動程序都在driver/SPI/下面,MPC8xxx驅(qū)動是spi_mpc83xx.c。
4.1??? platform device
2.6內(nèi)核中硬件資源的注冊都采用了platform device的機制。對于PowerPC來說,其硬件資源是通過DTS來描述的。
spi@7000 {
??? cell-index = <0>;
??? compatible = "fsl,spi";
??? reg = <0x7000 0x1000>;
??? interrupts = <16 0x8>;
??? interrupt-parent = <&ipic>;
??? mode = "cpu";
};
中斷號、中斷觸發(fā)電平、寄存器的基地址及范圍等信息會在設(shè)備樹中描述了,此后只需利用platform_get_resource等標(biāo)準(zhǔn)接口自動獲取即可,實現(xiàn)了驅(qū)動和資源的分離。Cell-index標(biāo)識了總線編號,也就是SPI master的編號。
隨后在系統(tǒng)啟動階段會解析DTB文件,將相關(guān)資源注冊到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注冊進(jìn)系統(tǒng)SPI設(shè)備列表中
}
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) { //根據(jù)compatible屬性"fsl,spi"查找相關(guān)device node節(jié)點
??? ??? int ret;
??? ??? unsigned int j;
??? ??? const void *prop;
??? ??? struct resource res[2];
??? ??? struct platform_device *pdev;
??? ??? struct fsl_spi_platform_data pdata = { //板級相關(guān)的SPI片選實現(xiàn)函數(shù)
??? ??? ??? .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++) { //根據(jù)板級移植相關(guān)的board info指定的bus num進(jìn)行匹配
??? ??? ??? 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,后續(xù)的platform driver將以mpc83xx_spi為匹配因子
??? ??? if (!pdev)
??? ??? ??? goto err;
??? ??? ret = platform_device_add_data(pdev, &pdata, sizeof(pdata)); //將pdata等特定的屬性添加到platform device中,以供相應(yīng)的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相關(guān)的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上的所有設(shè)備,匹配因子是mpc83xx_spi,匹配成功后調(diào)用mpc83xx_spi_probe將設(shè)備和驅(qū)動綁定起來,具體過程參加《詳解Linux2.6內(nèi)核中基于platform機制的驅(qū)動模型》,隨后向系統(tǒng)注冊一個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; // 將特定的片選實現(xiàn)函數(shù)保存到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中獲得相應(yīng)的寄存器和中斷等信息
??? 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都要實現(xiàn)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的具體實現(xiàn)待續(xù)。
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);
關(guān)于被subsys_initcall修飾的spi_init何時初始化,請參考下文《詳解Linux2.6內(nèi)核中基于platform機制的驅(qū)動模型》
http://blog.csdn.net/sailor_8318/archive/2010/01/29/5267698.aspx
此時platform bus及platform device已經(jīng)注冊完畢,因此SPI控制器的相關(guān)資源已經(jīng)就緒。
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已經(jīng)在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的總線號和相應(yīng)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中特定的屬性,板級移植需要關(guān)注
??? 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;?? //此設(shè)備所依附的總線為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??? 驅(qū)動注冊
/**
?* 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采用內(nèi)核通用的驅(qū)動模型,其流程和platform driver的注冊過程類似,請請參考下文《詳解Linux2.6內(nèi)核中基于platform機制的驅(qū)動模型》
http://blog.csdn.net/sailor_8318/archive/2010/01/29/5267698.aspx
5.4??? 數(shù)據(jù)傳輸
/**
?* 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);
同步接口,待數(shù)據(jù)收發(fā)完畢后才返回,只能在非中斷上下文中使用。
/**
?* 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);
}
異步接口,根據(jù)spi device找到其所在的spi master,用master所特定的transfer函數(shù)實現(xiàn)數(shù)據(jù)收發(fā)。
核心層提供的通用數(shù)據(jù)收發(fā)接口,屏蔽了底層差異。
6??? 用戶接口層-SPI設(shè)備驅(qū)動
6.1??? 統(tǒng)一的設(shè)備模型
此驅(qū)動模型是針對SPI Slave的,只有注冊board info時modalias是spidev的才能由此驅(qū)動訪問。訪問各個slave的基本框架是一致的,具體的差異將由從設(shè)備號來體現(xiàn)。
6.1.1??? 關(guān)鍵數(shù)據(jù)結(jié)構(gòu)
static struct spi_driver spidev_spi = {
??? .driver = {
??? ??? .name =??? ??? "spidev",
??? ??? .owner =??? THIS_MODULE,
??? },
??? .probe =??? spidev_probe,
??? .remove =??? __devexit_p(spidev_remove),
};
定義了一個標(biāo)準(zhǔn)的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對應(yīng)一個struct spidev_data, 以device_entry為節(jié)點連接在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,
};
任意一個需要和用戶空間通信的驅(qū)動必備的數(shù)據(jù)結(jié)構(gòu),其定義了具體的讀寫操作方法。
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);
首先注冊了一個字符型設(shè)備驅(qū)動,然后注冊spi_driver,其將在SPI總線上查找對應(yīng)的設(shè)備,根據(jù)關(guān)鍵字spidev進(jìn)行匹配,匹配成功后將調(diào)用spi_driver的probe方法,即spidev_probe,將驅(qū)動和每一個salve綁定起來,并建立對應(yīng)的dev設(shè)備節(jié)點,同時維護了一個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的從設(shè)備號是從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和動態(tài)從設(shè)備號為主從設(shè)備號注冊設(shè)備節(jié)點,如果系統(tǒng)有udev或者是hotplug,那么就會在/dev下自動創(chuàng)建相關(guān)的設(shè)備節(jié)點了,其名稱命名方式為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) {? // 根據(jù)設(shè)備節(jié)點號進(jìn)行匹配查找對應(yīng)的spidev_data節(jié)點從而找到相應(yīng)的從設(shè)備
??? ??? ??? status = 0;
??? ??? ??? break;
??? ??? }
??? }
??? if (status == 0) {
??? ??? if (!spidev->buffer) {
??? ??? ??? spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);? // 為每個slave分配了一段緩沖區(qū)
??? ??? ??? 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;?? //面向?qū)ο蟮牡湫蛻?yīng)用,將特定數(shù)據(jù)保存在filp->private_data中,其他函數(shù)可以直接從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操作是用戶空間程序和內(nèi)核驅(qū)動交換的第一步,最終返回給用戶空間的就是struct file結(jié)構(gòu)體。對于SPI 驅(qū)動來說,用戶空間所獲得的就是spidev這個關(guān)鍵信息。
6.1.4??? 數(shù)據(jù)收發(fā)
對于SPI驅(qū)動來說,數(shù)據(jù)收發(fā)有兩種途徑,read/write或者ioctl方式。但無論哪種方式,最終都是構(gòu)造一個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;? // 消息所發(fā)往的對象
??? unsigned??? ??? is_dma_mapped:1;
??? /* completion is reported through a callback */
??? void??? ??? ??? (*complete)(void *context);? //消息發(fā)送完畢后的回調(diào)函數(shù)
??? 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驅(qū)動統(tǒng)一組織成鏈表,順序處理,以此防止各個spi slave同時訪問總線而導(dǎo)致沖突
??? void??? ??? ??? *state;
};
spi_message 包含了一系列spi_transfer,在所有的spi_transfer發(fā)送完畢前,SPI總線一直被占據(jù),其間不會出現(xiàn)總線沖突,因此一個spi_message是一個原子系列,這種特性非常利于復(fù)雜的SPI通信協(xié)議,如先發(fā)送命令字然后才能讀取數(shù)據(jù)。
/*
?* 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是以某種特性如速率、延時及字長連續(xù)傳輸?shù)淖钚挝弧R驗镾PI是全雙工總線,只要總線上有數(shù)據(jù)傳輸,則MOSI和MISO上同時有數(shù)據(jù)采樣,對于SPI 控制器來說,其收發(fā)緩沖區(qū)中都有數(shù)據(jù),但是對于用戶來說,其可以靈活的選擇tx_buf和rx_buf的設(shè)置。當(dāng)發(fā)送數(shù)據(jù)時,tx_buf必須設(shè)置為待發(fā)送的數(shù)據(jù),rx_buf可以為null或者指向無用的緩沖區(qū),即不關(guān)心MISO的數(shù)據(jù)。而當(dāng)接收數(shù)據(jù)時,rx_buf必須指向接收緩沖區(qū),而tx_buf可以為Null,則MOSI上為全0,或者tx_buf指向不會引起從設(shè)備異常相應(yīng)的數(shù)據(jù)。Len為待發(fā)送或者接收的數(shù)據(jù)長度。當(dāng)一系列spi_transfer構(gòu)成一個spi_message時,cs_change的設(shè)置非常講究。當(dāng)cs_change為0即不變時,則可以連續(xù)對同一個設(shè)備進(jìn)行數(shù)據(jù)收發(fā);當(dāng)cs_change為1時通常用來停止command的傳輸而隨后進(jìn)行數(shù)據(jù)接收。因此cs_change可以利用SPI總線構(gòu)造復(fù)雜的通信協(xié)議。
對于SPI總線協(xié)議來說,傳輸單位可以是4-32之間的任意bits,但對于SPI控制器來說,bits_per_word只能是8/16/32,即需要取整,待收發(fā)的數(shù)據(jù)在內(nèi)存里都是以主機字節(jié)序右對齊存儲,SPI控制器會自動根據(jù)傳輸單位及大小端進(jìn)行轉(zhuǎn)換。
對于read/write方式來說,如果不采用board info中的默認(rèn)設(shè)置,則必須先通過ioctl方式設(shè)置該從設(shè)備特定的大小端、bits per word及速率等特性,后續(xù)數(shù)據(jù)傳輸時將一直采用此設(shè)置。
read/write與用戶空間的接口是buf和count,分別為收發(fā)的相應(yīng)數(shù)據(jù)指針和數(shù)據(jù)長度。
/* 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個數(shù)據(jù)發(fā)送期間,該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);
}
自動構(gòu)建一個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); // 將待發(fā)送的數(shù)據(jù)從用戶空間拷貝至內(nèi)核空間
??? 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方式,默認(rèn)情況下就是進(jìn)行數(shù)據(jù)收發(fā)。
IO ctrl方式與用戶空間的接口為spi_ioc_transfer,其數(shù)據(jù)結(jié)構(gòu)如下:
/**
?* 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可以動態(tài)的改變各種傳輸特性,并且可以將多個spi_ioc_transfer組織成一個message連續(xù)傳輸,比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;? // 找到對應(yīng)的struct spi_device
??? switch (cmd) {
??? /* read requests */? // 讀取該slave的相關(guān)屬性
??? 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;? //更新相關(guān)屬性
??? ??? ??? retval = spi_setup(spi);? //使相關(guān)屬性生效,在此不設(shè)置也可以,因為每次傳輸時都會重新設(shè)置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;
??? ??? // 將用戶指定的傳輸屬性保存起來,構(gòu)成內(nèi)核 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) {? //當(dāng)用戶設(shè)置了rx buf時才將數(shù)據(jù)拷貝到用戶空間
??? ??? ??? 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??? 特定的設(shè)備驅(qū)動
原則上所有的SPI從設(shè)備都可以通過上述統(tǒng)一的設(shè)備模型spidev來訪問,但SPI 總線上傳輸?shù)氖峭该鞯臄?shù)據(jù),而對于某些SPI從設(shè)備,其數(shù)據(jù)傳輸需要遵循一定的格式,如果用spidev來訪問,則用戶需要組織好所有的spi_transfer,對一般用戶來說可能比較復(fù)雜。為了提供更加user friendly的接口,可以對特定SPI從設(shè)備的訪問進(jìn)行再次封裝。
如SPI接口的MMC卡,其數(shù)據(jù)的轉(zhuǎn)換較為復(fù)雜,為了減少用戶空間的轉(zhuǎn)換,提供了專用的MMC驅(qū)動供用戶程序訪問,其向上屏蔽了具體MMC芯片的差異。
下面以drivers/mmc/host/mmc_spi.c為例來進(jìn)行講述。
6.2.1??? 關(guān)鍵數(shù)據(jù)結(jié)構(gòu)
struct mmc_spi_host {
??? struct mmc_host??? ??? *mmc;
??? struct spi_device??? *spi;? // MMC卡對應(yīng)的SPI 設(shè)備
??? 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卡協(xié)議對應(yīng)的各種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卡設(shè)備。
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,但其本質(zhì)上是spi_driver,和SPI 設(shè)備匹配的關(guān)鍵字是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);
}
調(diào)用spi_register_driver向SPI總線上注冊mmc_spi_driver,根據(jù)mmc_spi進(jìn)行匹配,匹配成功后將自動調(diào)用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,最終的數(shù)據(jù)訪問是由其他子系統(tǒng)發(fā)起的。
6.2.3??? 數(shù)據(jù)收發(fā)
TBD
7??? 驅(qū)動訪問示例
下面以一款TI 的SPI接口的時鐘芯片62002為例來講述用戶空間如何訪問SPI設(shè)備。
62002支持32bits、LSB訪問模式。內(nèi)部寄存器為28bits,其他4bits為地址位。
?
支持的讀寫命令如下:
?
Xxxx為待寫的數(shù)據(jù),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;? // 待發(fā)送的數(shù)據(jù)Dout由28bits的data field和4位地址域構(gòu)成。
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &write);
7.1.2??? 讀操作
62002的讀操作較復(fù)雜,需要先發(fā)送寫命令指定待讀的內(nèi)部寄存器地址,然后再發(fā)起總線的讀操作。時序如下:
?
SPI Master發(fā)送讀命令后,抬起SPI片選,62002解碼讀命令中的寄存器地址。當(dāng)SPI Master再次通過片選選中62002時,62002在MISO上發(fā)出待讀的數(shù)據(jù)。
因此需要在總線上連續(xù)進(jìn)行兩次數(shù)據(jù)傳輸,即兩個spi_ioc_transfer 。
struct spi_ioc_transfer? read[2]? = {
??? {
??? ??? ??? .tx_buf = (int)dout,
??? ??? ??? .rx_buf = NULL,? //發(fā)送讀命令時,忽略MISO
??? ??? ??? .len = sizeof(long),
??? ??? ??? .delay_usecs = 0,
??? ??? ??? .speed_hz = 500000,
??? ??? ??? .bits_per_word = 32,
??? ??? ??? .cs_change = 1,
??? },
??? {
??? ??? ??? .tx_buf = NULL,? //讀取數(shù)據(jù)時,忽略MOSI,總線上默然發(fā)送的是0
??? ??? ??? .rx_buf = (int)din,
??? ??? ??? .len = sizeof(long),
??? ??? ??? .delay_usecs = 0,
??? ??? ??? .speed_hz = 500000,
??? ??? ??? .bits_per_word = 32,
??? ??? ??? .cs_change = 1,
??? }
}
第一個spi_ioc_transfer發(fā)送讀命令,第二個spi_ioc_transfer讀取數(shù)據(jù)。
ret = ioctl(fd, SPI_IOC_MESSAGE(2), read);
兩個spi_ioc_transfer將構(gòu)成一個spi_message,其在內(nèi)核中是一個原子序列,將連續(xù)占用總線直至兩個spi_ioc_transfer發(fā)送完畢。
第一個spi_ioc_transfer的cs_change一定要置為1,即spi_transfer發(fā)送完畢后片選要發(fā)生變化,由低到高,這樣才滿足62002的時序。
8??? 參考鳴謝
http://blog.csdn.net/sailor_8318/archive/2010/09/25/5905988.aspx
總結(jié)
以上是生活随笔為你收集整理的【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】PowerPC + Linux2.6.25平台下的SPI驱动架构分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux下的压缩文件剖析
- 下一篇: 【嵌入式Linux学习七步曲之第五篇 L