用A20的芯片做一个项目,rfid和单片机都用spi通讯,挂在同样个spi控制器上,A20的每个spi控制器刚好支持最多两个从设备,但是好像平台的代码有问题还是别的原因,只有rfid可以通讯,单片机的spi始终没有反应,不得已改用gpio模拟,幸好内核有现成的模拟gpio代码,把它配置起来用即可。
不过我以前没有用过,所以花了半天时间分析里面的代码结构才写好驱动代码。
重要的结构体分析:
struct spi_master { struct device dev; struct list_head list; /* 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;//如果芯片带有spi 控制器,一般片选脚集成在控制器中了,如A10每个spi控制器支持两个片选;如果是模拟的控制器,不受限制,可以多个 /* some SPI controllers pose alignment requirements on DMAable * buffers; let protocol drivers know about these requirements. */ u16 dma_alignment; /* spi_device.mode flags understood by this controller driver */ u16 mode_bits; //该控制器支持的传输模式,比如A10的支持SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST,设备驱动中的传输模式必须在该范围 /* other constraints relevant to this driver */ u16 flags; #define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */ #define SPI_MASTER_NO_RX BIT(1) /* can't do buffer read */ #define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */ /* lock and mutex for SPI bus locking */ spinlock_t bus_lock_spinlock; struct mutex bus_lock_mutex; /* flag indicating that the SPI bus is locked for exclusive use */ bool bus_lock_flag; /* Setup mode and clock, etc (spi driver may call many times). * * IMPORTANT: this may be called when transfers to another * device are active. DO NOT UPDATE SHARED REGISTERS in ways * which could break those transfers. */ 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); /* * These hooks are for drivers that want to use the generic * master transfer queueing mechanism. If these are used, the * transfer() function above must NOT be specified by the driver. * Over time we expect SPI drivers to be phased over to this API. */ bool queued; struct kthread_worker kworker; struct task_struct *kworker_task; struct kthread_work pump_messages; spinlock_t queue_lock; struct list_head queue; struct spi_message *cur_msg; bool busy; bool running; bool rt; int (*prepare_transfer_hardware)(struct spi_master *master); int (*transfer_one_message)(struct spi_master *master, struct spi_message *mesg); int (*unprepare_transfer_hardware)(struct spi_master *master); };mode_bits值会在spi.c中做判断:
bad_bits = spi->mode & ~spi->master->mode_bits; if (bad_bits) { dev_err(&spi->dev, "setup: unsupported mode bits %x\n", bad_bits); return -EINVAL; }
struct spi_board_info { /* the device name and module name are coupled, like platform_bus; * "modalias" is normally the driver name. * * platform_data goes to spi_device.dev.platform_data, * controller_data goes to spi_device.controller_data, * irq is copied too */ char modalias[SPI_NAME_SIZE]; //设备名字,和驱动中要对应 const void *platform_data; void *controller_data;//在模拟的控制器中,如果有cs要控制,则为cs对应的gpio int irq; /* slower signaling on noisy or low voltage boards */ u32 max_speed_hz;//最大传输速率 /* bus_num is board specific and matches the bus_num of some * spi_master that will probably be registered later. * * chip_select reflects how this chip is wired to that master; * it's less than num_chipselect. */ u16 bus_num; //挂载那个控制器上,和master中的对应 u16 chip_select; //选择那个引脚作为cs控制脚 /* mode becomes spi_device.mode, and is essential for chips * where the default of SPI_CS_HIGH = 0 is wrong. */ u8 mode; //传输模式, /* ... may need additional spi_device chip config data here. * avoid stuff protocol drivers can set; but include stuff * needed to behave without being bound to a driver: * - quirks like clock rate mattering when not selected */ };
static void spi_gpio_chipselect(struct spi_device *spi, int is_active) { unsigned long cs = (unsigned long) spi->controller_data; /* set initial clock polarity */ if (is_active) setsck(spi, spi->mode & SPI_CPOL); if (cs != SPI_GPIO_NO_CHIPSELECT) { /* SPI is normally active-low */ gpio_set_value(cs, (spi->mode & SPI_CS_HIGH) ? is_active : !is_active); } }
struct spi_device { struct device dev;//每个spi设备对应一个,和device_drvier匹配 struct spi_master *master; //对应的控制器,根据bus_num匹配 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 */ #define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */ #define SPI_READY 0x80 /* slave pulls low to pause */ u8 bits_per_word; int irq; void *controller_state; void *controller_data; char modalias[SPI_NAME_SIZE]; }spi_board_info信息注册时候创建一个spi_device,代码入下:
struct spi_device *spi_new_device(struct spi_master *master, struct spi_board_info *chip) { struct spi_device *proxy; int status; /* NOTE: caller did any chip->bus_num checks necessary. * * Also, unless we change the return value convention to use * error-or-pointer (not NULL-or-pointer), troubleshootability * suggests syslogged diagnostics are best here (ugh). */ proxy = spi_alloc_device(master); if (!proxy) return NULL; WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias)); proxy->chip_select = chip->chip_select; proxy->max_speed_hz = chip->max_speed_hz; proxy->mode = chip->mode; proxy->irq = chip->irq; strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias)); proxy->dev.platform_data = (void *) chip->platform_data; proxy->controller_data = chip->controller_data; proxy->controller_state = NULL; status = spi_add_device(proxy); if (status < 0) { spi_dev_put(proxy); return NULL; } return proxy; } EXPORT_SYMBOL_GPL(spi_new_device);
分析到这里已经心中有数了,先把平台的spi gpio配置成普通的输入/输出功能:
[spi2_para] spi_used = 0 spi_cs_bitmap = 3 ;--- spi2 mapping0 --- spi_cs0 = port:PC19<1><default><default><1> spi_cs1 = port:PB13<1><default><default><1> spi_sclk = port:PC20<1><default><default><default> spi_mosi = port:PC21<1><default><default><default> spi_miso = port:PC22<0><default><default><default> spi_enable = port:PB05<1><default><default><1>
[spi_board1] manual_cs = port:PI07<1><default><default><1>
因为rfid部分代码已经写好了,不想去动它,所以这里把平台注册的所有代码放到了spi keyboard中来,不过这不妨碍代码运行。代码如下:
/* * linux/drivers/misc/spi_keyboard_input.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/input.h> #include <linux/init.h> #include <linux/errno.h> #include <linux/delay.h> #include <linux/miscdevice.h> #include <linux/platform_device.h> #include <asm/uaccess.h> #include <linux/device.h> #include <linux/interrupt.h> #include <linux/mutex.h> #include <linux/fs.h> #include <asm/uaccess.h> #include <mach/gpio.h> #include <linux/gpio.h> #include <mach/a20_config.h> #include <linux/timer.h> #include <linux/sched.h> #include <linux/version.h> #include <linux/semaphore.h> #include <mach/sys_config.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/spi/spi_gpio.h> #include <linux/spi/spi_bitbang.h> #define DEVICE_NAME "spikeyboard-input" #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) static DECLARE_MUTEX(sem_rw);//init as 1 #else DEFINE_SEMAPHORE(sem_rw); #endif struct spi_device *keyboard_spi; static struct input_dev * input; struct workqueue_struct *spikeyboard_wq; struct work_struct spikeyboard_work; static volatile int flags = 1; static char command[3] = {0}; //Set the keycode from 1 - 16 static int spikeyboard_keycode[] = {KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_Q, KEY_ESC, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, KEY_TAB}; #define MAX_BUTTON_CNT (sizeof(spikeyboard_keycode)/sizeof(spikeyboard_keycode[0])) static void report_keyvalue(unsigned char buffer) { if (buffer == 0) buffer = 16; input_report_key(input, buffer, 1); input_sync(input); input_report_key(input, buffer, 0); input_sync(input); } static void spikeyboard_loop_work(struct work_struct *work) { unsigned char buf_rx[1], buf_tx[1]; int ret; buf_tx[0] = 0xaa; while (flags) { down(&sem_rw); buf_rx[0] = -1; ret = spi_write_then_read(keyboard_spi, buf_tx, 1, buf_rx, 1); up(&sem_rw); msleep(20); if ((ret == 0) && (buf_rx[0] != 0xff)) printk("ret:%x\n", buf_rx[0]); report_keyvalue(buf_rx[0]); } } static ssize_t spi_keyboard_input_write(struct file *filp, char *buffer,int len) { int i, ret = 0; struct spi_transfer st[4]; struct spi_message msg; char buf_rx[1]; if (len != sizeof(command)) { printk("command format err\n"); return -EFAULT; } if (copy_from_user(command, buffer, len)) { printk("%s, copy form user err\n", __func__); return -EFAULT; } down(&sem_rw); spi_message_init(&msg); memset(st, 0, sizeof(st)); for (i = 0; i < sizeof(command); i++) { st[i].tx_buf = &command[i]; st[i].len = 1; spi_message_add_tail(&st[i], &msg); } st[3].rx_buf = buf_rx; st[3].len = 1; spi_message_add_tail(&st[3], &msg); spi_sync(keyboard_spi, &msg); up(&sem_rw); msleep(20); switch (buf_rx[0]) { case 0x88 : printk("spi slave respone the command\n"); break; case 0x99 : printk("spi slave not respone the commnad\n"); ret = -EBUSY; break; default : printk("spi send command err\n"); ret = -EBUSY; } return ret; } static int spi_keyboard_input_open(struct inode *inode, struct file *filp) { return 0; } static int spi_keyboard_input_release(struct inode *inode, struct file *filp) { return 0; } static int spi_keyboard_probe(struct spi_device *spi) { keyboard_spi = spi; printk("sclu %s, %d\n", __func__, __LINE__); spikeyboard_wq = create_singlethread_workqueue("spi_keyboard_work"); INIT_WORK(&spikeyboard_work, spikeyboard_loop_work); queue_work(spikeyboard_wq, &spikeyboard_work); return 0; } static int spi_keyboard_remove(struct spi_device *spi) { return 0; } static struct file_operations spi_keyboard_dev_fops = { .owner =THIS_MODULE, .open =spi_keyboard_input_open, .write =spi_keyboard_input_write, .release =spi_keyboard_input_release, }; static struct miscdevice misc = { .minor = MISC_DYNAMIC_MINOR, .name = "spikeyboard-input", .fops = &spi_keyboard_dev_fops, }; static struct spi_driver spi_keyboard_driver = { .probe = spi_keyboard_probe, .remove = spi_keyboard_remove, .driver = { .name = "spi_keyboard", }, }; static struct spi_board_info rfid_rc522 = { .modalias = "rfid_rc522", //.platform_data = &at25df641_info, .mode = SPI_MODE_0, .irq = 0, .max_speed_hz = 12 * 1000 * 1000, .bus_num = 10, .chip_select = 0, //模拟gpio用不到该成员,但是挂在模拟设备上的device还是不能重复该编号,因为spi core会判断重复的话就注册失败 }; static struct spi_board_info spi_keyboard = { .modalias = "spi_keyboard", //.platform_data = &at25df641_info, .mode = SPI_MODE_0, .irq = 0, .max_speed_hz = 12 * 1000 * 1000, .bus_num = 10, .chip_select = 1, //模拟gpio用不到该成员 }; static int spi_gpio_config(char *main_para, char *sub_para){ int ret; script_item_u list[1]; /* 获取gpio list */ ret = script_get_item(main_para, sub_para, list); if (SCIRPT_ITEM_VALUE_TYPE_PIO != ret) { printk("%s: %s get gpio list failed\n", __func__, sub_para); return 0; } return list[0].gpio.gpio; } static struct spi_gpio_platform_data spi_gpio_data = { .num_chipselect = 2, //模拟gpio可以为多个,如果是spi控制器,配合chip_select一起使用 }; static int init_spi_gpio(void) { char *p_main = "spi2_para"; char *p_sclk = "spi_sclk"; char *p_miso = "spi_miso"; char *p_mosi = "spi_mosi"; struct spi_gpio_platform_data *p = &spi_gpio_data; p->sck = spi_gpio_config(p_main, p_sclk); p->miso = spi_gpio_config(p_main, p_miso); p->mosi = spi_gpio_config(p_main, p_mosi); rfid_rc522.controller_data = (void *)spi_gpio_config(p_main, "spi_cs0"); spi_keyboard.controller_data = (void *)spi_gpio_config("spi_board1", "manual_cs"); if (!p->sck || !p->miso || !p->mosi) return -1; else return 0; } static struct platform_device spi_gpio_device = { .name = "spi_gpio", .id = 10,//spi device的编号,设备的bus_num要和这个对应。spi-core也会自动分配编号 .dev.platform_data = &spi_gpio_data, }; static int __init spi_keyboard_init(void) { int ret; int i; input = input_allocate_device(); if(!input) return -ENOMEM; set_bit(EV_KEY, input->evbit); for(i = 0; i < MAX_BUTTON_CNT; i++) set_bit(spikeyboard_keycode[i], input->keybit); input->name = "spikeyboard-input"; input->phys = "spikeyboard-input/input0"; input->id.bustype = BUS_HOST; input->id.vendor = 0x0001; input->id.product = 0x0001; input->id.version = 0x0100; input->keycode = spikeyboard_keycode; if(input_register_device(input) != 0) { printk("spi_keyboard-input input register device fail!!\n"); input_free_device(input); return -ENODEV; } ret = misc_register(&misc); printk(DEVICE_NAME "\tinitialized\n"); init_spi_gpio(); platform_device_register(&spi_gpio_device); if (spi_register_board_info(&rfid_rc522, 1)) printk("register rfid rc522 board info err\n"); else printk("register rfid rc522 board info success\n"); if (spi_register_board_info(&spi_keyboard, 1)) printk("register spi_keyboard board info err\n"); else printk("register spi_keyboard board info success\n"); #if 1 ret = spi_register_driver(&spi_keyboard_driver); if(ret < 0){ printk("register %s fail\n", __FUNCTION__); return ret; } #endif return ret; } static void __exit spi_keyboard_exit(void) { flags = 0; flush_work_sync(&spikeyboard_work); destroy_workqueue(spikeyboard_wq); input_unregister_device(input); spi_unregister_driver(&spi_keyboard_driver); misc_deregister(&misc); platform_device_unregister(&spi_gpio_device); } module_init(spi_keyboard_init); module_exit(spi_keyboard_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("spi_keyboard for A20");主要是添加了下面几行代码:
init_spi_gpio(); platform_device_register(&spi_gpio_device); if (spi_register_board_info(&rfid_rc522, 1)) printk("register rfid rc522 board info err\n"); else printk("register rfid rc522 board info success\n"); if (spi_register_board_info(&spi_keyboard, 1)) printk("register spi_keyboard board info err\n"); else printk("register spi_keyboard board info success\n"); ret = spi_register_driver(&spi_keyboard_driver); if(ret < 0){ printk("register %s fail\n", __FUNCTION__); return ret; }
和i2c一样,spi子系统也提供了spidev.c驱动让用户空间可以直接操作spi设备;不过有点不同的是,spidev.c中spi_driver的名字固定为"spidev",所以spi_board_info信息中的名字也要更改成“spidev”才能注册并使用spidev.c提供的方法。下面列出重点部分代码:
static struct spi_driver spidev_spi_driver = { .driver = { .name = "spidev", .owner = THIS_MODULE, }, .probe = spidev_probe, .remove = __devexit_p(spidev_remove), /* NOTE: suspend/resume methods are not necessary here. * We don't do anything except pass the requests to/from * the underlying controller. The refrigerator handles * most issues; the controller driver handles the rest. */ };
status = spi_register_driver(&spidev_spi_driver);
mutex_lock(&device_list_lock); minor = find_first_zero_bit(minors, N_SPI_MINORS); if (minor < N_SPI_MINORS) { struct device *dev; spidev->devt = MKDEV(SPIDEV_MAJOR, minor); dev = device_create(spidev_class, &spi->dev, spidev->devt, spidev, "spidev%d.%d", spi->master->bus_num, spi->chip_select); status = IS_ERR(dev) ? PTR_ERR(dev) : 0; } else { dev_dbg(&spi->dev, "no minor number available!\n"); status = -ENODEV; } if (status == 0) { set_bit(minor, minors); list_add(&spidev->device_entry, &device_list); } mutex_unlock(&device_list_lock);
一般如果用到spidev.c来提供用户空间的操作函数的话,我们在spi_keyboard.c驱动中就用spi_register_board_info注册好信息即可,无需spi_register_driver注册了,这样就把所有的驱动操作方法搬到用户空间来完成。读写可以用标准的read、write来完成,如果要实现像驱动中的spi_write_then_read,则需要用到ioctl,具体如下。
把驱动中的spi结构体和io 命令复制一份到用户空间,先定义好读写结构:
char tx_buffer[128] = {0x0}, rx_buffer[128] = {0x0}; struct spi_ioc_transfer spi_t[2] = { { .tx_buf = tx_buffer, //.rx_buf = rx_buffer, .len = 2, //.speed_hz = 10 * 1000 * 1000, //.delay_usecs = 10, //.bits_per_word = 1, //.cs_change = 0, //.pad = 0, }, { //.tx_buf = tx_buffer, .rx_buf = rx_buffer, .len = 15, .delay_usecs = 10, } };
ret = ioctl(fd, SPI_IOC_MESSAGE(2), spi_t); if (ret <= 0) { LOGE("ioctl ret = %d, err = %s", ret, strerror(errno)); }
即可实现等同驱动的spi_write_then_read一次性读写操作,ret返回读写总长度,如上述ret返回15 + 2 = 17。读到的的数据返回在rx_buffer中。
如果要一次多次读写,增加数组spi_t的长度和内容即可。