三:spi设备driver
在板文件中添加spi设备
static struct spi_board_info s3c_spi_devs[] __initdata
[0] = {
.modalias = "spidev", /* device node name */
.mode = SPI_MODE_0, /* CPOL=0, CPHA=0 */
.max_speed_hz = 10000000,
/* Connected to SPI-1 as 1st Slave */
.bus_num = 1,
.irq = IRQ_SPI1,
.chip_select = 0,
.controller_data = &smdk_spi1_csi[SMDK_MMCSPI_CS],
},
}
设备的片选信息
/*
static struct s3c64xx_spi_csinfo smdk_spi0_csi[] = {
[SMDK_MMCSPI_CS] = {
.line = S5PV210_GPB(1),
.set_level = gpio_set_value,
.fb_delay = 0x0,
},
};*/
设置平台信息
if (!gpio_request(S5PV210_GPB(1), "SPICS0")) {
gpio_direction_output(S5PV210_GPB(1), 1);
s3c_gpio_cfgpin(S5PV210_GPB(1), S3C_GPIO_SFN(1));
s3c_gpio_setpull(S5PV210_GPB(1), S3C_GPIO_PULL_UP);
s5pv210_spi_set_info(0, S5PV210_SPI_SRCCLK_PCLK,
ARRAY_SIZE(smdk_spi0_csi));
}
s5pv210_spi_set_info(0, S5PV210_SPI_SRCCLK_PCLK,
ARRAY_SIZE(smdk_spi0_csi));
void __init s5pv210_spi_set_info(int cntrlr, int src_clk_nr, int num_cs)
{
struct s3c64xx_spi_info *pd;
/* Reject invalid configuration */
if (!num_cs || src_clk_nr < 0
|| src_clk_nr > S5PV210_SPI_SRCCLK_SCLK) {
printk(KERN_ERR "%s: Invalid SPI configuration\n", __func__);
return;
}
switch (cntrlr) {
case 0:
pd = &s5pv210_spi0_pdata;
break;
case 1:
pd = &s5pv210_spi1_pdata;
break;
default:
printk(KERN_ERR "%s: Invalid SPI controller(%d)\n",
__func__, cntrlr);
return;
}
pd->num_cs = num_cs;
pd->src_clk_nr = src_clk_nr;
pd->src_clk_name = spi_src_clks[src_clk_nr];
}
pd = &s5pv210_spi0_pdata;
static struct s3c64xx_spi_info s5pv210_spi0_pdata = {
.cfg_gpio = s5pv210_spi_cfg_gpio,
.fifo_lvl_mask = 0x1ff,
.rx_lvl_offset = 15,
.high_speed = 1,
};
将该设备注册到系统中
spi_register_board_info(s3c_spi_devs, ARRAY_SIZE(s3c_spi_devs));
板文件设置完了,就是设备驱动了
驱动结构体
struct spidev_data {
dev_t devt;
spinlock_t spi_lock;
struct spi_device *spi;
struct list_head device_entry;
/* buffer is NULL unless this device is open (users > 0) */
struct mutex buf_lock;
unsigned users;
u8 *buffer;
};
注册驱动
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.
*/
};
static int __init spidev_init(void)
{
int status;
/* Claim our 256 reserved device numbers. Then register a class
* that will key udev/mdev to add/remove /dev nodes. Last, register
* the driver which manages those device numbers.
*/
BUILD_BUG_ON(N_SPI_MINORS > 256);
status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);//注册字符驱动,方便文件io操作
if (status < 0)
return status;
spidev_class = class_create(THIS_MODULE, "spidev");//用与自动创建节点,在驱动探测到后,就会创建节点
if (IS_ERR(spidev_class)) {
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
return PTR_ERR(spidev_class);
}
status = spi_register_driver(&spidev_spi_driver);//注册spi驱动
if (status < 0) {
class_destroy(spidev_class);
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
}
return status;
}
module_init(spidev_init);
当驱动探测到后
static int __devinit spidev_probe(struct spi_device *spi)
{
struct spidev_data *spidev;
int status;
unsigned long minor;
/* Allocate driver data */
spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);//分配spi驱动数据
if (!spidev)
return -ENOMEM;
/* Initialize the driver data *///驱动赋值
spidev->spi = spi;
spin_lock_init(&spidev->spi_lock);
mutex_init(&spidev->buf_lock);
INIT_LIST_HEAD(&spidev->device_entry);//初始化链表
/* If we can allocate a minor number, hook up this device.
* Reusing minors is fine so long as udev or mdev is working.
*/
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);
if (status == 0)
spi_set_drvdata(spi, spidev);//保存驱动数据到spi的私有数据.用于后面的读写操作
else
kfree(spidev);
return status;
}
到这里,,驱动就成功匹配到设备了
驱动的数据写流程:
当驱动打开后,应用层调用写函数
,在驱动中的过程如下
static ssize_t
spidev_write(struct file *filp, const char __user *buf,
size_t count, loff_t *f_pos)
{
struct spidev_data *spidev;
ssize_t status = 0;
unsigned long missing;
/* chipselect only toggles at start or end of operation */
if (count > bufsiz)
return -EMSGSIZE;
spidev = filp->private_data;//从spi私有数据获取驱动信息
mutex_lock(&spidev->buf_lock);
missing = copy_from_user(spidev->buffer, buf, count);//从用户层获取数据
if (missing == 0) {
status = spidev_sync_write(spidev, count);//成功,则调用该函数
} else
status = -EFAULT;
mutex_unlock(&spidev->buf_lock);
return status;
}
static inline ssize_t spidev_sync_write(struct spidev_data *spidev, size_t len) {//spi发送数据所需要的两个结构体,要发送的数据保存在
//struct spi_transfer中 struct spi_transfer t = { .tx_buf = spidev->buffer, .len = len, }; struct spi_message m; spi_message_init(&m); spi_message_add_tail(&t, &m);//spi是一个消息队列来发送的 return spidev_sync(spidev, &m);//再调用此函数 }
static ssize_t spidev_sync(struct spidev_data *spidev, struct spi_message *message) { DECLARE_COMPLETION_ONSTACK(done); int status; message->complete = spidev_complete;//同步 message->context = &done; spin_lock_irq(&spidev->spi_lock); if (spidev->spi == NULL) status = -ESHUTDOWN; else status = spi_async(spidev->spi, message);//调用spi核心层的发送函数 spin_unlock_irq(&spidev->spi_lock); if (status == 0) { wait_for_completion(&done); status = message->status; if (status == 0) status = message->actual_length; } return status; }
int spi_async(struct spi_device *spi, struct spi_message *message) { struct spi_master *master = spi->master;//取出该设备的控制器 /* Half-duplex links include original MicroWire, and ones with * only one data pin like SPI_3WIRE (switches direction) or where * either MOSI or MISO is missing. They can also be caused by * software limitations. */ if ((master->flags & SPI_MASTER_HALF_DUPLEX) || (spi->mode & SPI_3WIRE)) { struct spi_transfer *xfer; unsigned flags = master->flags; list_for_each_entry(xfer, &message->transfers, transfer_list) { if (xfer->rx_buf && xfer->tx_buf) return -EINVAL; if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf) return -EINVAL; if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf) return -EINVAL; } }//找到要发送的信息 message->spi = spi; message->status = -EINPROGRESS; return master->transfer(spi, message);//调用控制器的发送函数发送 }
即:master->transfer = s3c64xx_spi_transfer;
接下来就是工作队列来发送数据static int s3c64xx_spi_transfer(struct spi_device *spi, struct spi_message *msg) { struct s3c64xx_spi_driver_data *sdd; unsigned long flags; sdd = spi_master_get_devdata(spi->master); spin_lock_irqsave(&sdd->lock, flags); printk(KERN_DEBUG"s3c64xx_spi_transfer\n"); if (sdd->state & SUSPND) { spin_unlock_irqrestore(&sdd->lock, flags); return -ESHUTDOWN; } msg->status = -EINPROGRESS; msg->actual_length = 0; list_add_tail(&msg->queue, &sdd->queue);//将消息调价到消息链表尾部 queue_work(sdd->workqueue, &sdd->work);//添加到工作队列 spin_unlock_irqrestore(&sdd->lock, flags); return 0; }
static void s3c64xx_spi_work(struct work_struct *work) { struct s3c64xx_spi_driver_data *sdd = container_of(work, struct s3c64xx_spi_driver_data, work); unsigned long flags; printk(KERN_DEBUG"s3c64xx_spi_work\n"); /* Acquire DMA channels */ while (!acquire_dma(sdd)) msleep(10); spin_lock_irqsave(&sdd->lock, flags); while (!list_empty(&sdd->queue) && !(sdd->state & SUSPND)) { struct spi_message *msg; msg = container_of(sdd->queue.next, struct spi_message, queue); list_del_init(&msg->queue); /* Set Xfer busy flag */ sdd->state |= SPIBUSY; spin_unlock_irqrestore(&sdd->lock, flags); handle_msg(sdd, msg);//发送数据 spin_lock_irqsave(&sdd->lock, flags); sdd->state &= ~SPIBUSY; } spin_unlock_irqrestore(&sdd->lock, flags); /* Free DMA channels */ s3c2410_dma_free(sdd->tx_dmach, &s3c64xx_spi_dma_client); s3c2410_dma_free(sdd->rx_dmach, &s3c64xx_spi_dma_client); } static int s3c64xx_spi_transfer(struct spi_device *spi, struct spi_message *msg) { struct s3c64xx_spi_driver_data *sdd; unsigned long flags; sdd = spi_master_get_devdata(spi->master); spin_lock_irqsave(&sdd->lock, flags); printk(KERN_DEBUG"s3c64xx_spi_transfer\n"); if (sdd->state & SUSPND) { spin_unlock_irqrestore(&sdd->lock, flags); return -ESHUTDOWN; } msg->status = -EINPROGRESS; msg->actual_length = 0; list_add_tail(&msg->queue, &sdd->queue); queue_work(sdd->workqueue, &sdd->work); spin_unlock_irqrestore(&sdd->lock, flags); return 0; }
上面选择dma还是fifo的截图:static void handle_msg(struct s3c64xx_spi_driver_data *sdd, struct spi_message *msg) { struct s3c64xx_spi_info *sci = sdd->cntrlr_info; struct spi_device *spi = msg->spi; struct s3c64xx_spi_csinfo *cs = spi->controller_data; struct spi_transfer *xfer; int status = 0, cs_toggle = 0; u32 speed; u8 bpw; int i; printk(KERN_DEBUG"handle_msg\n"); /* If Master's(controller) state differs from that needed by Slave */ if (sdd->cur_speed != spi->max_speed_hz || sdd->cur_mode != spi->mode || sdd->cur_bpw != spi->bits_per_word) { sdd->cur_bpw = spi->bits_per_word; sdd->cur_speed = spi->max_speed_hz; sdd->cur_mode = spi->mode; s3c64xx_spi_config(sdd);//master的传输方式跟从设备不同时,重新配置 } /* Map all the transfers if needed */ if (s3c64xx_spi_map_mssg(sdd, msg)) { dev_err(&spi->dev, "Xfer: Unable to map message buffers!\n"); status = -ENOMEM; goto out; } /* Configure feedback delay */ writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK); list_for_each_entry(xfer, &msg->transfers, transfer_list) { unsigned long flags; int use_dma; INIT_COMPLETION(sdd->xfer_completion); /* Only BPW and Speed may change across transfers */ bpw = xfer->bits_per_word ? : spi->bits_per_word; speed = xfer->speed_hz ? : spi->max_speed_hz; if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) { sdd->cur_bpw = bpw; sdd->cur_speed = speed; s3c64xx_spi_config(sdd); } /* Polling method for xfers not bigger than FIFO capacity */ if (xfer->len <= ((sci->fifo_lvl_mask >> 1) + 1))//这里可以看芯片手册,主要是判断用dma方式传输还是fifo,下面有截图 use_dma = 0; else use_dma = 1; spin_lock_irqsave(&sdd->lock, flags); /* Pending only which is to be done */ sdd->state &= ~RXBUSY; sdd->state &= ~TXBUSY; char *tx_tmp =(char *)xfer->tx_buf; for(i=0; i<xfer->len;i++) printk(KERN_DEBUG"xfer->len=%d,xfer->tx_buf[%d]=%x\n",xfer->len,i,tx_tmp[i]); enable_datapath(sdd, spi, xfer, use_dma); /* Slave Select */ enable_cs(sdd, spi); /* Start the signals */ S3C64XX_SPI_ACT(sdd); spin_unlock_irqrestore(&sdd->lock, flags); status = wait_for_xfer(sdd, xfer, use_dma);//等待完成 /* Quiese the signals */ S3C64XX_SPI_DEACT(sdd); if (status) { dev_err(&spi->dev, "I/O Error: " "rx-%d tx-%d res:rx-%c tx-%c len-%d\n", xfer->rx_buf ? 1 : 0, xfer->tx_buf ? 1 : 0, (sdd->state & RXBUSY) ? 'f' : 'p', (sdd->state & TXBUSY) ? 'f' : 'p', xfer->len); if (use_dma) { if (xfer->tx_buf != NULL && (sdd->state & TXBUSY)) s3c2410_dma_ctrl(sdd->tx_dmach, S3C2410_DMAOP_FLUSH); if (xfer->rx_buf != NULL && (sdd->state & RXBUSY)) s3c2410_dma_ctrl(sdd->rx_dmach, S3C2410_DMAOP_FLUSH); } goto out; } if (xfer->delay_usecs) udelay(xfer->delay_usecs); if (xfer->cs_change) { /* Hint that the next mssg is gonna be for the same device */ if (list_is_last(&xfer->transfer_list, &msg->transfers)) cs_toggle = 1; else disable_cs(sdd, spi); } msg->actual_length += xfer->len; flush_fifo(sdd); } out: if (!cs_toggle || status) disable_cs(sdd, spi); else sdd->tgl_spi = spi; s3c64xx_spi_unmap_mssg(sdd, msg); msg->status = status; if (msg->complete) msg->complete(msg->context); }
static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, struct spi_device *spi, struct spi_transfer *xfer, int dma_mode) { struct s3c64xx_spi_info *sci = sdd->cntrlr_info; void __iomem *regs = sdd->regs; u32 modecfg, chcfg; printk(KERN_DEBUG"enable_datapath\n"); //配置寄存器 modecfg = readl(regs + S3C64XX_SPI_MODE_CFG); modecfg &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON); chcfg = readl(regs + S3C64XX_SPI_CH_CFG); chcfg &= ~S3C64XX_SPI_CH_TXCH_ON; if (dma_mode) { chcfg &= ~S3C64XX_SPI_CH_RXCH_ON; } else { /* Always shift in data in FIFO, even if xfer is Tx only, * this helps setting PCKT_CNT value for generating clocks * as exactly needed. */ chcfg |= S3C64XX_SPI_CH_RXCH_ON; writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff) | S3C64XX_SPI_PACKET_CNT_EN, regs + S3C64XX_SPI_PACKET_CNT); } if (xfer->tx_buf != NULL) { sdd->state |= TXBUSY; chcfg |= S3C64XX_SPI_CH_TXCH_ON; if (dma_mode) { printk(KERN_DEBUG"DMA_MODE\n"); modecfg |= S3C64XX_SPI_MODE_TXDMA_ON; s3c2410_dma_config(sdd->tx_dmach, 1); s3c2410_dma_enqueue(sdd->tx_dmach, (void *)sdd, xfer->tx_dma, xfer->len); s3c2410_dma_ctrl(sdd->tx_dmach, S3C2410_DMAOP_START); } else { unsigned char *buf = (unsigned char *) xfer->tx_buf; int i = 0; while (i < xfer->len) writeb(buf[i++], regs + S3C64XX_SPI_TX_DATA);//将数据写到发送寄存器 } } if (xfer->rx_buf != NULL) { sdd->state |= RXBUSY; if (sci->high_speed && sdd->cur_speed >= 30000000UL && !(sdd->cur_mode & SPI_CPHA)) chcfg |= S3C64XX_SPI_CH_HS_EN; if (dma_mode) { modecfg |= S3C64XX_SPI_MODE_RXDMA_ON; chcfg |= S3C64XX_SPI_CH_RXCH_ON; writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff) | S3C64XX_SPI_PACKET_CNT_EN, regs + S3C64XX_SPI_PACKET_CNT); s3c2410_dma_config(sdd->rx_dmach, 1); s3c2410_dma_enqueue(sdd->rx_dmach, (void *)sdd, xfer->rx_dma, xfer->len); s3c2410_dma_ctrl(sdd->rx_dmach, S3C2410_DMAOP_START); } } writel(modecfg, regs + S3C64XX_SPI_MODE_CFG); writel(chcfg, regs + S3C64XX_SPI_CH_CFG); }
static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd, struct spi_transfer *xfer, int dma_mode) { struct s3c64xx_spi_info *sci = sdd->cntrlr_info; void __iomem *regs = sdd->regs; unsigned long val; int ms; printk(KERN_DEBUG"wait_for_xfer\n"); /* millisecs to xfer 'len' bytes @ 'cur_speed' */ ms = xfer->len * 8 * 1000 / sdd->cur_speed; ms += 5; /* some tolerance */ if (dma_mode) { val = msecs_to_jiffies(ms) + 10; val = wait_for_completion_timeout(&sdd->xfer_completion, val); } else { u32 status; val = msecs_to_loops(ms); do { status = readl(regs + S3C64XX_SPI_STATUS);读状态寄存器 printk(KERN_DEBUG"status=%d\n",status); } while (RX_FIFO_LVL(status, sci) < xfer->len && --val); } printk(KERN_DEBUG"VAL=%d\n",val); if (!val) return -EIO; if (dma_mode) { u32 status; /* * DmaTx returns after simply writing data in the FIFO, * w/o waiting for real transmission on the bus to finish. * DmaRx returns only after Dma read data from FIFO which * needs bus transmission to finish, so we don't worry if * Xfer involved Rx(with or without Tx). */ if (xfer->rx_buf == NULL) { val = msecs_to_loops(10); status = readl(regs + S3C64XX_SPI_STATUS); while ((TX_FIFO_LVL(status, sci) || !S3C64XX_SPI_ST_TX_DONE(status, sci)) && --val) { cpu_relax(); status = readl(regs + S3C64XX_SPI_STATUS); } if (!val) return -EIO; } } else { unsigned char *buf; int i; /* If it was only Tx */ if (xfer->rx_buf == NULL) { sdd->state &= ~TXBUSY; return 0; } i = 0; buf = xfer->rx_buf; while (i < xfer->len) buf[i++] = readb(regs + S3C64XX_SPI_RX_DATA); sdd->state &= ~RXBUSY; } return 0; }