linux spi 设备驱动简析 二(基于s5pv210)

三: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;
}










  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值