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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章