三: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;
}
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);
}
上面选择dma还是fifo的截图:
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;
}