spi驅動流程學習

</pre><pre code_snippet_id="1593617" snippet_file_name="blog_20160301_2_3014540" name="code" class="cpp">
diff --git a/1980c/base/kernel/linux-3.10/arch/arm/configs/M88_phone_v1_0_defconfig b/1980c/base/kernel/linux-3.10/arch/arm/configs/M88_phone_v1_0_defconfig
index a4e81bb..b15afeb 100755
--- a/1980c/base/kernel/linux-3.10/arch/arm/configs/M88_phone_v1_0_defconfig
+++ b/1980c/base/kernel/linux-3.10/arch/arm/configs/M88_phone_v1_0_defconfig
@@ -380,7 +380,7 @@ CONFIG_MMC_COMIP_IOPOWER=y
 CONFIG_ON2_COMIP=y
 CONFIG_PWM_COMIP=y
 CONFIG_RESET_COMIP=y
-# CONFIG_SPI_COMIP is not set
+CONFIG_SPI_COMIP=y
 # CONFIG_TPZ_COMIP is not set
 # CONFIG_TPZ2_COMIP is not set
 
@@ -1833,6 +1833,7 @@ CONFIG_DEVMEM=y
 # CONFIG_SERIAL_MAX310X is not set
 CONFIG_SERIAL_CORE=y
 CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_SERIAL_SC16IS7X2=y
 # CONFIG_SERIAL_SCCNXP is not set
 # CONFIG_SERIAL_TIMBERDALE is not set
 # CONFIG_SERIAL_ALTERA_JTAGUART is not set
@@ -1905,7 +1906,7 @@ CONFIG_SPI_MASTER=y
 #
 # SPI Protocol Masters
 #
-# CONFIG_SPI_SPIDEV is not set
+CONFIG_SPI_SPIDEV=y
 # CONFIG_SPI_TLE62X0 is not set
 
 #
diff --git a/1980c/base/kernel/linux-3.10/arch/arm/mach-lc186x/board/board-M88.c b/1980c/base/kernel/linux-3.10/arch/arm/mach-lc186x/board/board-M88.c
index 9fc4a01..65e4bdb 100755
--- a/1980c/base/kernel/linux-3.10/arch/arm/mach-lc186x/board/board-M88.c
+++ b/1980c/base/kernel/linux-3.10/arch/arm/mach-lc186x/board/board-M88.c
@@ -28,6 +28,11 @@
 #include <plat/comip-backlight.h>
 #include <plat/comip-thermal.h>
 #include <plat/comip-battery.h>
+#if defined(CONFIG_SPI_COMIP) || defined(CONFIG_SPI_COMIP_MODULE)
+#include <linux/spi/spi.h>
+#include <plat/spi.h>
+#include <mach/devices.h>
+#endif
 #if defined(CONFIG_COMIP_LC1160)
 #include <plat/lc1160.h>
 #include <plat/lc1160-pmic.h>
@@ -61,6 +66,166 @@
 #include <linux/mpu.h>
 #endif
 
+#if defined(CONFIG_SPI_COMIP) || defined(CONFIG_SPI_COMIP_MODULE)
+#include <linux/serial_sc16is7x2.h>
+
+static struct sc16is7x2_platform_data sc16is7x2_pdata = {
+	.uartclk = 115200,
+	.uart_base = 0,
+	.gpio_base = 0,
+	.label = NULL,
+	.names = NULL,
+	.gpio_for_irq =12345,
+	.gpio_for_reset =12345,
+};
+
+//掛在spi0上的從設備數組
+static struct spi_board_info comip_spi_board_info[] = {
+	{//掛在spi0上的第一個從設備
+		.modalias = "sc16is7x2",//從設備的別名,用來和從設備的驅動進行關聯
+		.platform_data = &sc16is7x2_pdata,//如果從設備有需要傳入驅動的私有數據,則通過該屬性傳入
+		.controller_data = NULL,
+		.irq = 0,//此處忽略,在從設備probe中會重新賦值
+		.max_speed_hz = 100000,
+		.bus_num = 0,//用來標識該從設備掛載在哪個spi控制器上
+		.chip_select = 0,//使用第幾個片選信號,一個spi控制器能支持片選總個數由master->num_chipselect 確定。所以此處的值應介於0 ~ master->num_chipselect之間。
+		.mode = SPI_MODE_0,
+	},
+
+//測試spidev.c添加==============================start
+	{//掛在spi0上的第二個從設備,生成的設備節點爲/dev/spidev0.1 (第一個數字表示總線號,第二個數字表示片選號)
+		.modalias = "spidev",
+		.max_speed_hz = 200000,
+		.bus_num = 0, //spi0
+		.chip_select = 1,//這是掛在spi0上的第二個外設,該片選的值必須爲1(另外一個外設的片選爲0,此處的值必須和另外一個的不同),否則probe不到該外設
+		.platform_data = NULL,
+	},
+
+	{//掛在spi1上的第一個從設備,生成的設備節點爲/dev/spidev1.0 (第一個數字表示總線號,第二個數字表示片選號)
+		.modalias = "spidev",
+		.max_speed_hz = 200000,
+		.bus_num = 1, //spi0
+		.chip_select = 0,
+		.platform_data = NULL,
+	},
+
+	{//掛在spi2上的第一個從設備,生成的設備節點爲/dev/spidev2.0 (第一個數字表示總線號,第二個數字表示片選號)
+		.modalias = "spidev",
+		.max_speed_hz = 200000,
+		.bus_num = 2, //spi0
+		.chip_select = 0,
+		.platform_data = NULL,
+	},
+//測試spidev.c添加==============================end
+
+/*
+	{//掛在spi0上的第二個從設備
+		.modalias = "foo1",
+		.max_speed_hz = 200000,
+		.bus_num = 0, //spi0
+		.chip_select = 0,
+		.platform_data = NULL,
+	},
+	{//掛在spi1上的第一個從設備
+		.modalias = "foo2",
+		.max_speed_hz = 150000,
+		.bus_num = 1,//spi1
+		.chip_select = 0,
+		.platform_data = NULL,
+	},
+	{//掛在spi2上的第一個從設備
+		.modalias = "foo3",
+		.max_speed_hz = 150000,
+		.bus_num = 2,//spi2
+		.chip_select = 0,
+		.platform_data = NULL,
+	},
+*/
+};
+
+//添加片選信號的回調函數,這個回調函數因board而異,因此放在board中實現
+//參考最終調用位置ssi->pdata->cs_state(ssi->chip_select, level);實現
+static int spi0_cs_state(int chipselect, int level)
+{
+	/*TODO:
+	每次數據傳輸都會調用這個函數(如果有的話)
+	實際調用時,參數chipselect來自spi_board_info中的chip_select。但是chip_select的含義
+	是由用戶決定的,假設由從設備傳來的chip_select爲0,表示使用GPIO_118作爲
+	片選,爲1表示用GPIO_119作爲片選,傳入2表示使用spi控制器自身支持的
+	一個片選信號,那麼此處的實現可能是如下這樣的:
+	if(0 == chipselect) {
+		ret = gpio_request(GPIO_118, "gpio_name")
+		//do error check of ret
+		gpio_direction_output(GPIO_118, level);
+		gpio_free(GPIO_118);
+		return 1;
+	}
+	if(1 == chipselect) {
+		ret = gpio_request(GPIO_119, "gpio_name")
+		//do error check of ret
+		gpio_direction_output(GPIO_119, level);
+		gpio_free(GPIO_119);
+		return 1;
+	}
+	if(2 == chipselect) {
+		TODO: 需要用某種方式配置控制器的寄存器......
+		一種可能的方法是在這裏返回一個flag,然後驅動
+		基於該返回值去配置好對應寄存器
+		return 1;
+	}
+	*/
+	return 1;
+}
+
+static struct comip_spi_platform_data comip_spi0_info = {
+	.num_chipselect = 2,//該spi控制器(spi0)所能支持的片選總數,有幾個外設就填幾,如果填小了,則多餘的外設不會probe
+	.cs_state = spi0_cs_state,
+};
+
+static struct comip_spi_platform_data comip_spi1_info = {//foo
+	.num_chipselect = 1,
+	.cs_state = NULL,
+};
+
+static struct comip_spi_platform_data comip_spi2_info = {//foo
+	.num_chipselect = 1,
+	.cs_state = NULL,
+};
+static void __init comip_init_spi(void)
+{
+	comip_set_spi0_info(&comip_spi0_info);//註冊spi0設備(控制器),並傳入該設備的私有數據comip_spi0_info
+	comip_set_spi1_info(&comip_spi1_info);//foo
+	comip_set_spi2_info(&comip_spi2_info);//foo
+
+/*註冊掛在spi0上的所有從設備。
+這個函數會先將每個從設備的節點信息逐個掛在全局鏈表board_list中,每掛載一個的
+同時會遍歷一遍spi_master_list,針對每個master節點,通過調用spi_match_master_to_boardinfo,來比對
+master節點的bus_num和當前從設備的bus_num,如果發現二者一致則會註冊該從設備的device
+在從設備的設備註冊過程中會將該從設備和其對應的master綁定,該綁定在spi_new_device中
+的spi_alloc_device調用中實現,實現的方法就是讓從設備的master域指向該master。從設備註冊
+過程會去match對應的從設備驅動,當然還是通過name去找了。找到了我們就看到對應的
+從設備的probe會被執行。
+如果無法匹配到對應的master,則從設備不會被註冊,驅動的probe當然不會被調用。
+*/
+	spi_register_board_info(comip_spi_board_info, ARRAY_SIZE(comip_spi_board_info));
+
+/*
+在spi.c中維護了兩個全局鏈表,如下:
+static LIST_HEAD(board_list);
+static LIST_HEAD(spi_master_list);
+board_list 中會存放所有的spi從設備節點,節點的添加動作發生在spi_register_board_info中。
+spi_master_list中會存放所有的spi控制器節點,即master的節點。這在spi控制器驅動註冊過程中,通過
+調用spi_register_master來進行節點的添加。
+以上可以看出必須先註冊控制器的device,而後註冊控制器的驅動以添加控制器對應的
+master節點到鏈表中。
+
+從以上分析也可以看到,針對spi_board_info實例化的按照不同的spi控制器的分組其實是沒有
+必要的,因爲不管是掛在哪個控制器上的從設備的info都會被添加到同一個鏈表中(全局的board_list),
+然後再去和邋spi_master_list中的每個maser去匹配。
+*/	
+}
+#endif
+
 #ifdef CONFIG_USB_HSIC_USB3503
 #include <linux/platform_data/usb3503.h>
 #endif
@@ -627,6 +792,13 @@ static struct mfp_pin_cfg comip_mfp_cfg[] = {
 	{CODEC_PA_PIN,		MFP_PIN_MODE_GPIO},
 #endif
 
+#if defined(CONFIG_SPI_COMIP) || defined(CONFIG_SPI_COMIP_MODULE)
+	{MFP_PIN_GPIO(157), 	MFP_PIN_MODE_0},
+	{MFP_PIN_GPIO(158), 	MFP_PIN_MODE_0},
+	{MFP_PIN_GPIO(159), 	MFP_PIN_MODE_0},
+	{MFP_PIN_GPIO(160), 	MFP_PIN_MODE_0},
+#endif
+
 #if defined (CONFIG_USB_HSIC_USB3503)
 	{HSIC_RESET_N_PIN,      MFP_PIN_MODE_GPIO},
 	{HSIC_INT_N_PIN,        MFP_PIN_MODE_GPIO},
@@ -811,6 +983,12 @@ static struct mfp_pull_cfg comip_mfp_pull_cfg[] = {
 	{MFP_PIN_GPIO(148), 	MFP_PULL_UP},
 #endif
 
+#if defined(CONFIG_SPI_COMIP) || defined(CONFIG_SPI_COMIP_MODULE)
+	{MFP_PIN_GPIO(157), 	MFP_PULL_UP},
+	{MFP_PIN_GPIO(158), 	MFP_PULL_UP},
+	{MFP_PIN_GPIO(159), 	MFP_PULL_UP},
+	{MFP_PIN_GPIO(160), 	MFP_PULL_UP},
+#endif
 };
 
 #ifdef CONFIG_BAT_ID_BQ2022A
@@ -2195,6 +2373,9 @@ static void __init comip_init_board_early(void)
 	comip_init_mfp();
 	comip_init_gpio_lp();
 	comip_init_i2c();
+#if defined(CONFIG_SPI_COMIP) || defined(CONFIG_SPI_COMIP_MODULE)
+	comip_init_spi();
+#endif
 	comip_init_lcd();
 	comip_init_codec();
 }
diff --git a/1980c/base/kernel/linux-3.10/arch/arm/mach-lc186x/devices.c b/1980c/base/kernel/linux-3.10/arch/arm/mach-lc186x/devices.c
old mode 100644
new mode 100755
index 1304805..2aed2c6
--- a/1980c/base/kernel/linux-3.10/arch/arm/mach-lc186x/devices.c
+++ b/1980c/base/kernel/linux-3.10/arch/arm/mach-lc186x/devices.c
@@ -22,9 +22,9 @@ void __init comip_register_device(struct platform_device *dev, void *data)
 {
 	int ret;
 
-	dev->dev.platform_data = data;
+	dev->dev.platform_data = data;//傳入設備的私有數據以供其驅動提取
 
-	ret = platform_device_register(dev);
+	ret = platform_device_register(dev);//平臺設備註冊的最後一步
 	if (ret)
 		dev_err(&dev->dev, "unable to register device: %d\n", ret);
 }
@@ -314,6 +314,7 @@ void __init comip_set_i2c4_info(struct comip_i2c_platform_data *info)
 	comip_register_device(&comip_device_i2c4, info);
 }
 
+#if defined(CONFIG_SPI_COMIP) || defined(CONFIG_SPI_COMIP_MODULE)
 static struct resource comip_resource_spi0[] = {
 	[0] = {
 		.start = SSI0_BASE,
@@ -337,15 +338,15 @@ static struct resource comip_resource_spi0[] = {
 	},
 };
 
-struct platform_device comip_device_spi0 = {
-	.name = "comip-spi",
-	.id = 0,
-	.resource = comip_resource_spi0,
-	.num_resources = ARRAY_SIZE(comip_resource_spi0),
+struct platform_device comip_device_spi0 = {//spi控制器的device信息(這是一個平臺設備)
+	.name = "comip-spi",//設備名稱,用來匹配驅動
+	.id = 0,//設備id,對應於驅動中的master->bus_num,   用來區別同一個驅動下的不同設備。因爲一個驅動可以對應多個設備。對本例而言,我們只有一個spi驅動,但是卻有三個spi控制器在用它
+	.resource = comip_resource_spi0,//設備資源數組,寄存器映射或中斷號或dma號等等
+	.num_resources = ARRAY_SIZE(comip_resource_spi0),//上述資源的數量(即上述數組寬度)
 };
 
 void __init comip_set_spi0_info(struct comip_spi_platform_data *info)
-{
+{//註冊spi控制器爲platform device,info將作爲該設備的私有數據傳入,以供驅動提取
 	comip_register_device(&comip_device_spi0, info);
 }
 
@@ -418,6 +419,7 @@ void __init comip_set_spi2_info(struct comip_spi_platform_data *info)
 {
 	comip_register_device(&comip_device_spi2, info);
 }
+#endif
 
 static u64 comip_mmc_dma_mask = COMIP_DMA_BIT_MASK;
 static struct resource comip_resource_mmc0[] = {
diff --git a/1980c/base/kernel/linux-3.10/arch/arm/plat-lc/drivers/base/spi/comip-spi.c b/1980c/base/kernel/linux-3.10/arch/arm/plat-lc/drivers/base/spi/comip-spi.c
old mode 100644
new mode 100755
index 68debfb..8f4a2fc
--- a/1980c/base/kernel/linux-3.10/arch/arm/plat-lc/drivers/base/spi/comip-spi.c
+++ b/1980c/base/kernel/linux-3.10/arch/arm/plat-lc/drivers/base/spi/comip-spi.c
@@ -763,9 +763,9 @@ static void comip_spi_cleanup(struct spi_device *spi)
 static int __init comip_spi_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
-	struct comip_spi_platform_data *pdata = dev->platform_data;
-	struct spi_master *master;
-	struct comip_ssi *ssi;
+	struct comip_spi_platform_data *pdata = dev->platform_data;//由該設備傳入的針對該spi控制器的私有數據,每個控制器都可以通過自己的設備屬性集傳入與衆不同的私有數據信息
+	struct spi_master *master;//針對spi框架層的master屬性集
+	struct comip_ssi *ssi;//該具體的spi控制器的屬性集
 	struct resource *r;
 	int dma_tx_channel;
 	int dma_rx_channel;
@@ -798,21 +798,22 @@ static int __init comip_spi_probe(struct platform_device *pdev)
 
 	/* Allocate master with space for drv_data and null dma buffer. */
 	master = spi_alloc_master(dev, sizeof(struct comip_ssi));
+	//注意這個實現在給master分配的空間中會追加上該具體的spi控制器的屬性集信息(即追加了sizeof(*ssi))
 	if (!master) {
 		dev_err(&pdev->dev, "cannot alloc spi_master\n");
 		ret = -ENOMEM;
 		goto out;
 	}
 
-	master->bus_num = pdev->id;
-	master->num_chipselect = pdata->num_chipselect;
+	master->bus_num = pdev->id;//bus_num就是設備的id
+	master->num_chipselect = pdata->num_chipselect;//片選總數
 	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP;
 	master->cleanup = comip_spi_cleanup;
 	master->setup = comip_spi_setup;
 	master->transfer = comip_spi_transfer;
 
-	ssi = spi_master_get_devdata(master);
-	ssi->master = master;
+	ssi = spi_master_get_devdata(master);//因爲上面給master分配的空間是追加了ssi的空間的,所以這裏可以通過master得到ssi
+	ssi->master = master;//以下開始設置ssi的屬性集參數
 	ssi->pdata = pdata;
 	ssi->pdev = pdev;
 	ssi->res = r;
@@ -830,7 +831,7 @@ static int __init comip_spi_probe(struct platform_device *pdev)
 	ssi->base = ioremap(r->start, r->end - r->start + 1);
 	if (!ssi->base) {
 		ret = -ENOMEM;
-		dev_err(&pdev->dev, "cannot remap io 0x%08x\n", r->start);
+		dev_err(&pdev->dev, "cannot remap io 0x%08x\n", (unsigned int)r->start);
 		goto out_ioremap;
 	}
 
diff --git a/1980c/base/kernel/linux-3.10/drivers/spi/spidev.c b/1980c/base/kernel/linux-3.10/drivers/spi/spidev.c
old mode 100644
new mode 100755
index 911e9e0..0459719
--- a/1980c/base/kernel/linux-3.10/drivers/spi/spidev.c
+++ b/1980c/base/kernel/linux-3.10/drivers/spi/spidev.c
@@ -39,6 +39,22 @@
 
 #include <asm/uaccess.h>
 
+/**********************************/
+#define ENTER \
+do{ printk(KERN_INFO "[SPIDEV_DBG][%04d][%s]\n", __LINE__, __func__); }while(0)
+
+#define PRINT_DBG(format,x...) \
+do{ printk(KERN_INFO "[SPIDEV_DBG][%04d] " format, __LINE__, ## x); }while(0)
+
+#define PRINT_INFO(format,x...) \
+do{ printk(KERN_INFO "[SPIDEV_INFO][%04d] " format, __LINE__, ## x); }while(0)
+
+#define PRINT_WARN(format,x...) \
+do{ printk(KERN_INFO "[SPIDEV_WARN][%04d] " format, __LINE__, ## x); }while(0)
+
+#define PRINT_ERR(format,x...) \
+do{ printk(KERN_ERR "[SPIDEV_ERR][%04d][%s] " format, __LINE__, __func__, ## x); }while(0)
+/**********************************/
 
 /*
  * This supports access to SPI devices using normal userspace I/O calls.
@@ -579,6 +595,7 @@ static int spidev_probe(struct spi_device *spi)
 	int			status;
 	unsigned long		minor;
 
+	ENTER;
 	/* Allocate driver data */
 	spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
 	if (!spidev)
@@ -677,6 +694,8 @@ static int __init spidev_init(void)
 	 * that will key udev/mdev to add/remove /dev nodes.  Last, register
 	 * the driver which manages those device numbers.
 	 */
+
+	ENTER;
 	BUILD_BUG_ON(N_SPI_MINORS > 256);
 	status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
 	if (status < 0)


我的參考代碼百度網盤路徑:work\DTT\spi-work-flow.rar




發佈了307 篇原創文章 · 獲贊 52 · 訪問量 98萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章