因為我是做單片機上來的,所以,在看代碼的時候,總會去想,這個代碼是怎么和硬件扯上關系的。比如,我現在用的CPU是 Atmel的9G20. 我想用的是它的SPI1。
這個SPI 對應的引腳分別是 :
PB0 -> SPI1_MISO
PB1 -> SPI1_MOSI
PB2 -> SPI1_SPCK
PB3 -> SPI1_NPCS0
不管什么系統,它在使用前一定會初始化硬件設備,所以我覺得先找到內核初始化板子的函數才有一點頭緒。 后來到網上查詢,知道與硬件相關的基本上都在 arch 目錄下面。
因為我使用的是 sam9g20。 arm 架構。 所以我進入的目錄 便是 arch/arm/mach-at91 .

進去之后發現有兩個 c 文件是關於9g20的,因為在配置內核時,選擇的是 mmc。 所以我們使用的應該是 Board-sam9g20ek-2slot-mmc.c這個文件。
於是打開它。在這個文件里面能看到很多被 ‘ __init ’ 修飾的函數,大概猜想他們就是初始化板子硬件的函數。在文件的后面有一段代碼:
MACHINE_START(AT91SAM9G20EK_2MMC, "Atmel AT91SAM9G20-EK 2 MMC Slot Mod")
/* Maintainer: Rob Emanuele */
.phys_io = AT91_BASE_SYS,
.io_pg_offst = (AT91_VA_BASE_SYS >> 18) & 0xfffc,
.boot_params = AT91_SDRAM_BASE + 0x100,
.timer = &at91sam926x_timer,
.map_io = ek_map_io,
.init_irq = ek_init_irq,
.init_machine = ek_board_init,
MACHINE_END
於是我覺得這個文件就是初始化相關硬件/設備的。 隨后找到 ek_board_init 函數。 在里面發現了 at91_add_device_spi(ek_spi_devices, ARRAY_SIZE(ek_spi_devices));
函數的調用。於是追蹤這個函數,在目錄下查找 at91_add_device_spi 字符:

發現與 9G20 相關的都是調用它,沒有它的原型。於是打開Makefile, 看看,與9G20 相關的文件還有哪些:

從圖中可以看到,與9G20相關的還有兩個 9260的文件,他們公用一些代碼。於是直接打開 xxx9260_devices.c. 果不其然,找到了at91_add_device_spi 函數的原型。
我截出一部分代碼:
/* Configure SPI bus(es) */
if (enable_spi0) {
at91_set_A_periph(AT91_PIN_PA0, 0); /* SPI0_MISO */
at91_set_A_periph(AT91_PIN_PA1, 0); /* SPI0_MOSI */
at91_set_A_periph(AT91_PIN_PA2, 0); /* SPI1_SPCK */
at91_clock_associate("spi0_clk", &at91sam9260_spi0_device.dev, "spi_clk");
platform_device_register(&at91sam9260_spi0_device);
}
if (enable_spi1) {
at91_set_A_periph(AT91_PIN_PB0, 0); /* SPI1_MISO */
at91_set_A_periph(AT91_PIN_PB1, 0); /* SPI1_MOSI */
at91_set_A_periph(AT91_PIN_PB2, 0); /* SPI1_SPCK */
at91_clock_associate("spi1_clk", &at91sam9260_spi1_device.dev, "spi_clk");
platform_device_register(&at91sam9260_spi1_device);
}
看到這里,心里一陣高興,終於看到這幾個引腳了。 他們就是我上面列出來的幾個與SPI1相關的引腳。 從代碼上來看,要初始化SPI 是需要條件的。就是 enable_spi1 要不等於0. 再慢慢看代碼,知道與傳入這個函數的參數有關。於是再掉頭回來看調用這個函數的地方。 返回到 Board-sam9g20ek-2slot-mmc.c , 發現傳入的參數是 ek_spi_devices 和 ARRAY_SIZE(ek_spi_devices)。 從字面上看,后者應該表示前者的尺寸。 老辦法,找到ek_spi_devices 數組就在本文件內。
/*
* SPI devices.
*/
static struct spi_board_info ek_spi_devices[] = {
#if !(defined(CONFIG_MMC_ATMELMCI) || defined(CONFIG_MMC_AT91))
{ /* DataFlash chip */
.modalias = "mtd_dataflash",
.chip_select = 1,
.max_speed_hz = 15 * 1000 * 1000,
.bus_num = 0,
},
#if defined(CONFIG_MTD_AT91_DATAFLASH_CARD)
{ /* DataFlash card */
.modalias = "mtd_dataflash",
.chip_select = 0,
.max_speed_hz = 15 * 1000 * 1000,
.bus_num = 0,
},
#endif
#endif
};
看起來還是很簡單,后來網上查了一下,得出了進一步的信息:
struct spi_board_info { char modalias[SPI_NAME_SIZE]; const void * platform_data; void * controller_data; int irq; u32 max_speed_hz; u16 bus_num; u16 chip_select; u8 mode; };
Members
Initializes spi_device.modalias; identifies the driver.
platform_dataInitializes spi_device.platform_data; the particular data stored there is driver-specific.
controller_dataInitializes spi_device.controller_data; some controllers need hints about hardware setup, e.g. for DMA.
irqInitializes spi_device.irq; depends on how the board is wired.
max_speed_hzInitializes spi_device.max_speed_hz; based on limits from the chip datasheet and board-specific signal quality issues.
bus_numIdentifies which spi_master parents the spi_device; unused by spi_new_device, and otherwise depends on board wiring.
chip_selectInitializes spi_device.chip_select; depends on how the board is wired.
modeInitializes spi_device.mode; based on the chip datasheet, board wiring (some devices support both 3WIRE and standard modes), and possibly presence of an inverter in the chipselect path.
Description
When adding new SPI devices to the device tree, these structures serve as a partial device template. They hold information which can't always be determined by drivers. Information that probe can establish (such as the default transfer wordsize) is not included here.
These structures are used in two places. Their primary role is to be stored in tables of board-specific device descriptors, which are declared early in board initialization and then used (much later) to populate a controller's device tree after the that controller's driver initializes. A secondary (and atypical) role is as a parameter to spi_new_device call, which happens after those controller drivers are active in some dynamic board configuration models.
於是自己仿照着寫了一個:
* SPI devices.
*/
static struct spi_board_info ek_spi_devices[] = {
{
.modalias = "HCMS-29xx",
.chip_select = 0, // choice PB3
.max_speed_hz = 1*1000*1000,
.bus_num = 1, // SPI1
},
};
后面要做的就是驅動了。這個網上有很多資料,大家看看就可以了,我主要說明一下,我們在SPI的驅動里需要自己實現probe函數。 因為在內核將我們的驅動和剛剛我們
申請的SPI 設備匹配成功后就需要調用這個函數。 寫驅動時,大家要注意了,我們需要申明一個名為 spi_driver 的結構體。下面是我申請的結構體:
static struct spi_driver hcms29xx_spi_driver = {
.driver = {
.name = "HCMS-29xx",
.owner = THIS_MODULE,
},
.probe = hcms29xx_spi_probe,
.remove = hcms29xx_spi_remove
};
注意里面有一個 .name 的成員。 它是設備和驅動匹配的關鍵。 想想我們在前面初始化SPI設備后,它是怎么和我們寫的驅動掛上勾的呢? 就是它! 這個結構體里面的.name.
就是靠他和前面設備里 那個 modalias 成員。 所以我們在給他們賦值時。他們的取值要相等,這樣才能匹配成功。
剩下的就是寫驅動和調試驅動了,這些就不必說了吧,會C語言的基本上都會,
