61 OrangePi Linux内核里的spi控制器驱动
来源:互联网 发布:linux ntpdate 脚本 编辑:程序博客网 时间:2024/05/29 07:01
在全志H3里有2个spi控制器, 每个控制器可有4个片选信号:
//////////////////////////
script.bin里的spi控制器,spi设备相关设置:
[spi0]spi_used = 1spi_cs_bitmap = 1spi_mosi = port:PC00<3><default><default><default>spi_miso = port:PC01<3><default><default><default>spi_sclk = port:PC02<3><default><default><default>spi_cs0 = port:PC03<3><1><default><default>[spi1]spi_used = 0spi_cs_bitmap = 1spi_cs0 = port:PA13<2><1><default><default>spi_sclk = port:PA14<2><default><default><default>spi_mosi = port:PA15<2><default><default><default>spi_miso = port:PA16<2><default><default><default>[spi_devices]spi_dev_num = 1[spi_board0]modalias = "spidev"max_speed_hz = 33000000bus_num = 0chip_select = 0mode = 0full_duplex = 1manual_cs = 0
/////////////////在linux内核源码里spi控制器驱动//////
make menuconfig ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-Device Drivers ---> [*] SPI support ---> <*> SUNXI SPI Controller
控制器驱动源码在”drivers/spi/spi-sunxi.c”
控制器的驱动都是平台驱动,由平台设备来描述具体控制器的硬件资源。这spi控件器驱动与i2c控制器驱动一样,平台设备与平台驱动都在spi-sunxi.c文件里了。用起来方便,但不是很规范.
spi-sunxi.c的主过程分析:
module_init(sunxi_spi_init);static struct platform_driver sunxi_spi_driver = { .probe = sunxi_spi_probe, .remove = sunxi_spi_remove, .driver = { .name = SUNXI_SPI_DEV_NAME, //名字为"spi" .owner = THIS_MODULE, .pm = SUNXI_SPI_DEV_PM_OPS, },};static int __init sunxi_spi_init(void){ int i, ret = 0; sunxi_spi_device_scan(); //初始化控制器的平台设备,并提供控制器的配置寄存器地址,中断号等硬件资源 sunxi_spi_chan_cfg(sunxi_spi_pdata); //用全局设量spi_used_mask记录script.bin里"spi_used=1"的控制器 ret = sunxi_spi_register_spidev(); //注册script.bin里描述的spi设备 ...#ifdef CONFIG_EVB_PLATFORM //此宏成立 for (i=0; i<SUNXI_SPI_NUM; i++) // SUNXI_SPI_NUM的值为2#else i = CONFIG_SPI_CHAN_NUM; /* In FPGA, only one channel is available. */#endif { if (sunxi_spi_chan_is_enable(i)) { SPI_DBG("Sunxi SPI init channel %d \n", i); ret = platform_device_register(&sunxi_spi_device[i]); //注册script.bin里"spi_used=1"的控制器的平台设备 ... sunxi_spi_sysfs(&sunxi_spi_device[i]); //并在"/sys"目录下的控制器的平台设备目录下创建出"info", "status"属性文件。 } } if (spi_used_mask) return platform_driver_register(&sunxi_spi_driver); //如有控制器的平台设备注册,则也注册控制器的平台驱动 ...}
///////////////////////////细看初始化函数里调用到的函数////////////////////////////
static int spi_used_mask = 0; //全局变量用于记录script.bin里哪个控制器是"spi_used=1"的//下面三个全局数组表示每个spi控制器使用的平台设备对象,资源,平台数据等 static struct resource sunxi_spi_resources[SUNXI_SPI_NUM * SUNXI_SPI_RES_NUM];static struct sunxi_spi_platform_data sunxi_spi_pdata[SUNXI_SPI_NUM];static struct platform_device sunxi_spi_device[SUNXI_SPI_NUM];//初始化控制器的平台设备,并提供控制器的配置寄存器地址,中断号等硬件资源static void __init sunxi_spi_device_scan(void){ int i; memset(sunxi_spi_device, 0, sizeof(sunxi_spi_device)); memset(sunxi_spi_pdata, 0, sizeof(sunxi_spi_pdata)); memset(sunxi_spi_resources, 0, sizeof(sunxi_spi_resources)); for (i=0; i<SUNXI_SPI_NUM; i++) { sunxi_spi_resources[i * SUNXI_SPI_RES_NUM].start = SUNXI_SPI_MEM_START(i); ... sunxi_spi_resources[i * SUNXI_SPI_RES_NUM + 1].start = SUNXI_SPI_IRQ(i); ... sunxi_spi_pdata[i].cs_bitmap = SUNXI_CS_BITMAP(i); // 值为1 sunxi_spi_pdata[i].cs_num = SUNXI_CS_NUM(i); // 值为1 sunxi_spi_device[i].name = SUNXI_SPI_DEV_NAME; //"spi" sunxi_spi_device[i].id = i; //平台设备的ID,也就驱动好的控制器编号 ... //下面准备控制器的平台设备的资源,平台数据 sunxi_spi_device[i].resource = &sunxi_spi_resources[i * SUNXI_SPI_RES_NUM]; sunxi_spi_device[i].num_resources = SUNXI_SPI_RES_NUM; sunxi_spi_device[i].dev.platform_data = &sunxi_spi_pdata[i]; }}//用全局设量spi_used_mask记录script.bin里"spi_used=1"的控制器static void sunxi_spi_chan_cfg(struct sunxi_spi_platform_data *pdata){ int i; script_item_u item = {0}; script_item_value_type_e type = 0; char spi_para[16] = {0}; for (i=0; i<SUNXI_SPI_NUM; i++) { sprintf(spi_para, SUNXI_SPI_DEV_NAME"%d", i); type = script_get_item(spi_para, "spi_used", &item); ... if (item.val) spi_used_mask |= SUNXI_SPI_CHAN_MASK(i); ... }}
////////////////就像i2c设备驱动模型一样,spi设备驱动模型里用spi_board_info来描述spi设备,然后用spi_register_board_info函数来注册spi_board_info设备信息. 最后在控制器驱动对象注册时再创建出相应的spi_device设备对象struct spi_board_info { char modalias[SPI_NAME_SIZE]; //设备名字 const void *platform_data; //spi_device.dev.platform_data void *controller_data; // spi_device.controller_data, 这里通常指定一个片选的操作函数及IO口. 在控制器驱动里会调用到 int irq; // spi_device.irq u32 max_speed_hz; // spi_device.max_speed_hz; u16 bus_num; //标明此SPI设备是挂载到哪个SPI控制器上 u16 chip_select; //一个控制器可以挂载多个设备,这里指定此设备使用的是控制器上的第几个片选, 使片选数组里的第几个片选 u8 mode; //工作时序方式 };//根据script.bin里描述的spi设备,创建出对应的spi_board_info对象,并注册static int __init sunxi_spi_register_spidev(void){ script_item_u spi_dev_num, temp_info; script_item_value_type_e type; int i, spidev_num; char spi_board_name[32] = {0}; struct spi_board_info* board; type = script_get_item("spi_devices", "spi_dev_num", &spi_dev_num); //获取script.bin里的"spi_devices"的键值 ... spidev_num = spi_dev_num.val; //表示在script.bin里描述spi设备的个数, 在script.bin里每个设备的主键是"[spi_board%d]" ... spi_boards = (struct spi_board_info*)kzalloc(sizeof(struct spi_board_info) * spidev_num, GFP_KERNEL); //创建出所需的spi_board_info对象空间. for (i=0; i<spidev_num; i++) { board = &spi_boards[i]; sprintf(spi_board_name, "spi_board%d", i); type = script_get_item(spi_board_name, "modalias", &temp_info); //获取"spi_board0"主键下的"modalias"子键的值 ... sprintf(board->modalias, "%s", temp_info.str); //把获取出的名字设为spi_board_info对象的名字 type = script_get_item(spi_board_name, "max_speed_hz", &temp_info); //设备的最大工作时钟 ... board->max_speed_hz = temp_info.val; type = script_get_item(spi_board_name, "bus_num", &temp_info); //表示此设备是连接在哪个编号的控制器 ... board->bus_num = temp_info.val; type = script_get_item(spi_board_name, "chip_select", &temp_info);//获取设备的片选线 ... board->chip_select = temp_info.val; type = script_get_item(spi_board_name, "mode", &temp_info); //获取设备的时序工作方式 ... board->mode = temp_info.val; SPI_INF("%-16d %-16s %-16d %-8d %-4d %-4d\n", i, board->modalias, board->max_speed_hz, board->bus_num, board->chip_select, board->mode); } if (spi_register_board_info(spi_boards, spidev_num)) { //最后注册所有的spi_board_info信息 ... } return 0;}
///////////////////////////////////////////////////////////////////////
/////////// 在linux内核里,spi控制器驱动好后用struct spi_master的一个对象来描述.struct spi_master { struct device dev; //基于device成员扩展 ... s16 bus_num; //控制器对象的编号,由平台设备提供 ... u16 num_chipselect; ... u16 mode_bits; //工作时序方式 ...}; 在spi-sunxi.c里,封装了结构体struct sunxi_spi用于记录每个控制器对象在驱动里的数据struct sunxi_spi { struct platform_device *pdev; //指定控制器的平台设备对象地址 struct spi_master *master; //指定驱动好后创建出来的spi_master对象地址 ... enum spi_mode_type mode_type; //工作时序方式 unsigned int irq; //中断号 char dev_name[48]; //名字为"spi.%d" ...};控制器的平台驱动的probe函数static int __devinit sunxi_spi_probe(struct platform_device *pdev){ struct resource *mem_res; struct sunxi_spi *sspi; struct sunxi_spi_platform_data *pdata; struct spi_master *master; int ret = 0, err = 0, irq; int cs_bitmap = 0; ... pdata = pdev->dev.platform_data; //获取平台设备提供的资源 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ... irq = platform_get_irq(pdev, 0); //创建出spi_master对象的空间和struct sunxi_spi对象的空间, 并dev_set_drvdata(&spi_master.dev, sunxi_spi); master = spi_alloc_master(&pdev->dev, sizeof(struct sunxi_spi)); ... platform_set_drvdata(pdev, master); sspi = spi_master_get_devdata(master); // dev_get_drvdata(...) memset(sspi, 0, sizeof(struct sunxi_spi)); sspi->master = master; sspi->irq = irq; .. sspi->cs_control = sunxi_spi_cs_control; sspi->cs_bitmap = pdata->cs_bitmap; /* cs0-0x1; cs1-0x2; cs0&cs1-0x3. */ sspi->busy = SPI_FREE; sspi->mode_type = MODE_TYPE_NULL; //初始化spi_master对象 master->bus_num = pdev->id; master->setup = sunxi_spi_setup; master->cleanup = sunxi_spi_cleanup; master->transfer = sunxi_spi_transfer; master->num_chipselect = pdata->cs_num; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH| SPI_LSB_FIRST; ... cs_bitmap = sunxi_spi_get_cfg_csbitmap(pdev->id); ... snprintf(sspi->dev_name, sizeof(sspi->dev_name), SUNXI_SPI_DEV_NAME"%d", pdev->id); err = request_irq(sspi->irq, sunxi_spi_handler, IRQF_DISABLED, sspi->dev_name, sspi); ... sspi->base_addr = ioremap(mem_res->start, resource_size(mem_res)); .. sspi->base_addr_phy = mem_res->start; sspi->workqueue = create_singlethread_workqueue(dev_name(master->dev.parent)); //创建一个基于线程的工作队列,用于spi控制器的数据传输工作任务 ... sspi->pdev = pdev; pdev->dev.init_name = sspi->dev_name; ret = sunxi_spi_hw_init(sspi, pdata); //根据参数配置控制器的寄存器 ... spin_lock_init(&sspi->lock); init_completion(&sspi->done); INIT_WORK(&sspi->work, sunxi_spi_work); //初始化工作任务,当工作任务得到调用时,sunxi_spi_work函数就会触发调用 INIT_LIST_HEAD(&sspi->queue); if (spi_register_master(master)) { //注册spi_master对象 ... } ...}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
spi控制器驱动好不会在/sys目录下创建spi_master对象的子目录,只可以通过”/sys/bus/platform/drivers”目录下查看平台驱动目录下有哪些匹配上的平台设备.
阅读全文
0 0
- 61 OrangePi Linux内核里的spi控制器驱动
- 54 OrangePi linux内核里的i2c控制器驱动
- 62 linux内核里基于GPIO口的SPI控制器驱动
- 63 linux内核的SPI设备驱动模型及应用程序调用SPI控制器的方法
- 二、Linux spi 控制器驱动
- Linux SPI 子系统驱动笔记之Linux spi设备驱动与SPI控制器驱动的匹配问题
- 55 linux内核里基于GPIO口的I2C控制器驱动
- 14 orangepi 内核里控制IO口
- 23 H5的spi控制器驱动
- 全志 H5 orangepi SPI显示屏驱动测试
- spi控制器驱动模型
- 64 linux spi设备驱动之mcp2515(can控制器)驱动
- 12 orangepi Linux设备驱动前言
- linux内核SPI总线驱动分析
- linux内核SPI总线驱动分析
- linux内核SPI总线驱动分析思路
- Linux内核中SPI总线驱动分析
- Linux内核---48.spi驱动修改
- 搜索旋转排序数组II
- ztree 节点单选多选按钮不显示
- 3.2Struts2的搭建和基础知识
- opencv中遍历每一个像素点进行处理
- Spring实现AOP的4种方式
- 61 OrangePi Linux内核里的spi控制器驱动
- android的surfaceflinger原理讲解
- 两个fragment之间跳转listview数据丢失
- 【JavaSE练习】面向对象_模拟银行账户
- 程序员也喜欢高达模型
- 大图轮播效果
- FreeRtos在RH850 D1L芯片上移植
- windows下使用thrift编译+示例+详解解决坑
- 合法IP&窗口最大值数组&打印素数&递归逆序一个栈&栈排序另一个栈&MyString&寻找数组中出现的重复字符&数组中的重复数字&逆序打印链表&空格替换成%20