Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15809
  • 博文数量: 3
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 67
  • 用 户 组: 普通用户
  • 注册时间: 2013-05-16 19:02
个人简介

别让孤独吞噬咯青春

文章分类

全部博文(3)

文章存档

2013年(3)

我的朋友

分类: 嵌入式

2013-07-09 17:33:36

Linux的SPI子系统采用主机驱动和外设驱动分离的思想,首先主机SPI控制器是一种平台设备,因此它以platform的方式注册进内核,外设的信息是以boardinfo形式静态定义的,在创建spi_master时,会根据外设的bus_num和主机的bus_num是否相等,来选择是否将该外设挂接在该SPI主控制器下。先看SPI子系统中几个关键的数据结构:

struct spi_master用来描述一个SPI主控制器

struct spi_master {
	struct device    dev;
	s16    bus_num; /*总线编号*/
	u16    num_chipselect;/*支持的外设数量*/
	u16    dma_alignment;
	int   (*transfer)(struct spi_device *spi, struct spi_message *mesg);/*用于将消息添加到队列*/
	void  (*cleanup)(struct spi_device *spi);
};


struct spi_device用来描述一个SPI从设备

struct spi_device {
	struct device		dev;
	struct spi_master	*master;                 /*从设备所属的SPI主控器*/
	u32			max_speed_hz;   /*最大传输频率*/
	u8			chip_select;    /*片选号,用于区别其他从设备*/
	u8			mode;           /*传输模式*/
/*各个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 */
	u8			bits_per_word; /*每个字的比特数*/
	int			irq;           /*所使用的中断*/
	void			*controller_state;
	void			*controller_data;
	char			modalias[32];  /*设备名,在和从设备驱动匹配时会用到*/

};


struct spi_driver用来描述一个SPI从设备的驱动,它的形式和struct platform_driver是一致的

struct spi_driver {
	int			(*probe)(struct spi_device *spi);
	int			(*remove)(struct spi_device *spi);
	void			(*shutdown)(struct spi_device *spi);
	int			(*suspend)(struct spi_device *spi, pm_message_t mesg);
	int			(*resume)(struct spi_device *spi);
	struct device_driver	driver;
};


SPI子系统初始化的第一步就是将SPI总线注册进内核,并且在/sys下创建一个spi_master的类,以后注册的从设备都将挂接在该总线下

static int __init spi_init(void)
{
	int	status;

	buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
	if (!buf) {
		status = -ENOMEM;
		goto err0;
	}

	status = bus_register(&spi_bus_type);//注册SPI总线
	if (status < 0)
		goto err1;

	status = class_register(&spi_master_class);//注册spi_master类
	if (status < 0)
		goto err2;
	return 0;

err2:
	bus_unregister(&spi_bus_type);
err1:
	kfree(buf);
	buf = NULL;
err0:
	return status;
}


我们来看spi_bus_type的定义

struct bus_type spi_bus_type = {
	.name		= "spi",
	.dev_attrs	= spi_dev_attrs,
	.match		= spi_match_device,
	.uevent		= spi_uevent,
	.suspend	= spi_suspend,
	.resume		= spi_resume,
};

来看挂接在SPI总线下的从设备和从设备驱动是如何匹配的,也就是spi_match_device函数
static const struct spi_device_id *spi_match_id(const struct spi_device_id *id,
                        const struct spi_device *sdev)
{
    while (id->name[0]) {
        if (!strcmp(sdev->modalias, id->name))
            return id;
        id++;
    }
    return NULL;
}

static int spi_match_device(struct device *dev, struct device_driver *drv)
{
    const struct spi_device    *spi = to_spi_device(dev);
    const struct spi_driver    *sdrv = to_spi_driver(drv);

    /* Attempt an OF style match */
    if (of_driver_match_device(dev, drv))
        return 1;
    //如果驱动中定义id_table,查看driver结构体id_table->name与 平台设备结构体dev->modalias 是否相同
    if (sdrv->id_table)
        return !!spi_match_id(sdrv->id_table, spi);
    //如果驱动中没有定义id_table,则对比drv->name 与 dev->modalias是否相同
    return strcmp(spi->modalias, drv->name) == 0;
}


这里可以看到是将struct device_driver中的name字段与struct spi_device中的modalias字段进行匹配

 

这里已经完成了SPI子系统初始化的第一步,也就是注册SPI总线,这一步是和平台无关的,第二步是和平台相关的初始化,下一节再做介绍。

  本节以spidev设备驱动为例,来阐述SPI数据传输的过程。spidev是内核中一个通用的设备驱动,我们注册的从设备都可以使用该驱动,只需在注册 时将从设备的modalias字段设置为"spidev",这样才能和spidev驱动匹配成功。我们要传输的数据有时需要分为一段一段的(比如先发送, 后读取,就需要两个字段),每个字段都被封装成一个transfer,N个transfer可以被添加到message中,作为一个消息包进行传输。当用 户发出传输数据的请求时,message并不会立刻传输到从设备,而是由之前定义的transfer()函数将message放入一个等待队列中,这些 message会以FIFO的方式有workqueue调度进行传输,这样能够避免SPI从设备同一时间对主SPI控制器的竞争。和之前一样,还是习惯先 画一张图来描述数据传输的主要过程。

 

         在使用spidev设备驱动时,需要先初始化spidev. spidev是以字符设备的形式注册进内核的。

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);
	/*将spidev作为字符设备注册*/
	status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
	if (status < 0)
		return status;

	/*创建spidev类*/
	spidev_class = class_create(THIS_MODULE, "spidev");
	if (IS_ERR(spidev_class)) {
		unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
		return PTR_ERR(spidev_class);
	}

	/*注册spidev的driver,可与modalias字段为"spidev"的spi_device匹配*/
	status = spi_register_driver(&spidev_spi);
	if (status < 0) {
		class_destroy(spidev_class);
		unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
	}
	return status;
}


与相应的从设备匹配成功后,则调用spidev中的probe函数

static int spidev_probe(struct spi_device *spi)
{
	struct spidev_data	*spidev;
	int			status;
	unsigned long		minor;

	/* Allocate driver data */
	spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
	if (!spidev)
		return -ENOMEM;

	/* Initialize the driver data */
	spidev->spi = 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);
		/*在spidev_class下创建设备*/
		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);//将minors的相应位置位,表示该位对应的次设备号已被占用
		list_add(&spidev->device_entry, &device_list);//将创建的spidev添加到device_list
	}
	mutex_unlock(&device_list_lock);

	if (status == 0)
		spi_set_drvdata(spi, spidev);
	else
		kfree(spidev);

	return status;
}


然后就可以利用spidev模块提供的接口来实现主从设备之间的数据传输了。我们以spidev_write()函数为例来分析数据传输的过程,实际上spidev_read()和其是差不多的,只是前面的一些步骤不一样,可以参照上图。

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;

	mutex_lock(&spidev->buf_lock);
	//将用户要发送的数据拷贝到spidev->buffer
	missing = copy_from_user(spidev->buffer, buf, count);
	if (missing == 0) {//全部拷贝成功,则调用spidev_sysn_write()
		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)
{
	struct spi_transfer	t = {//设置传输字段
			.tx_buf		= spidev->buffer,
			.len		= len,
		};
	struct spi_message	 m;//创建message

	spi_message_init(&m);
	spi_message_add_tail(&t, &m);//将transfer添加到message中
	return spidev_sync(spidev, &m);
}


我们来看看struct spi_transfer和struct spi_message是如何定义的

struct spi_transfer {
	/* it's ok if tx_buf == rx_buf (right?)
	 * for MicroWire, one buffer must be null
	 * buffers must work with dma_*map_single() calls, unless
	 *   spi_message.is_dma_mapped reports a pre-existing mapping
	 */
	const void	*tx_buf;//发送缓冲区
	void		*rx_buf;//接收缓冲区
	unsigned	len;    //传输数据的长度

	dma_addr_t	tx_dma;
	dma_addr_t	rx_dma;

	unsigned	cs_change:1; //该位如果为1,则表示当该transfer传输完后,改变片选信号
	u8		bits_per_word;//字比特数
	u16		delay_usecs;  //传输后的延时 
	u32		speed_hz;  //指定的时钟

	struct list_head transfer_list;//用于将该transfer链入message
};


 

struct spi_message {
	struct list_head	transfers;//用于链接spi_transfer

	struct spi_device	*spi;      //指向目的从设备

	unsigned		is_dma_mapped:1;

	/* REVISIT:  we might want a flag affecting the behavior of the
	 * last transfer ... allowing things like "read 16 bit length L"
	 * immediately followed by "read L bytes".  Basically imposing
	 * a specific message scheduling algorithm.
	 *
	 * Some controller drivers (message-at-a-time queue processing)
	 * could provide that as their default scheduling algorithm.  But
	 * others (with multi-message pipelines) could need a flag to
	 * tell them about such special cases.
	 */

	/* completion is reported through a callback */
	void			(*complete)(void *context);//用于异步传输完成时调用的回调函数
	void			*context;                  //回调函数的参数
	unsigned		actual_length;            //实际传输的长度
	int			status;

	/* for optional use by whatever driver currently owns the
	 * spi_message ...  between calls to spi_async and then later
	 * complete(), that's the spi_master controller driver.
	 */
	struct list_head	queue; //用于将该message链入bitbang等待队列
	void			*state;
};


继续跟踪源码,进入spidev_sync(),从这一步开始,read和write就完全一样了

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核心层的函数spi_async()
	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;
}


 

static inline int
spi_async(struct spi_device *spi, struct spi_message *message)
{
	message->spi = spi;
	/*调用master的transfer函数将message放入等待队列*/
	return spi->master->transfer(spi, message);
}


 

s3c24xx平台下的transfer函数是在bitbang_start()函数中定义的,为bitbang_transfer()

int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m)
{
	struct spi_bitbang	*bitbang;
	unsigned long		flags;
	int			status = 0;

	m->actual_length = 0;
	m->status = -EINPROGRESS;

	bitbang = spi_master_get_devdata(spi->master);

	spin_lock_irqsave(&bitbang->lock, flags);
	if (!spi->max_speed_hz)
		status = -ENETDOWN;
	else {
		list_add_tail(&m->queue, &bitbang->queue);//将message添加到bitbang的等待队列
		queue_work(bitbang->workqueue, &bitbang->work);//调度运行work
	}
	spin_unlock_irqrestore(&bitbang->lock, flags);

	return status;
}

这里可以看到transfer函数不负责实际的数据传输,而是将message添加到 等待队列中。同样在spi_bitbang_start()中,有这样一个定义INIT_WORK(&bitbang->work, bitbang_work);因此bitbang_work()函数会被调度运行,类似于底半部机制

static void bitbang_work(struct work_struct *work)
{
	struct spi_bitbang	*bitbang =
		container_of(work, struct spi_bitbang, work);//获取bitbang
	unsigned long		flags;

	spin_lock_irqsave(&bitbang->lock, flags);
	bitbang->busy = 1;
	while (!list_empty(&bitbang->queue)) {//等待队列不为空
		struct spi_message	*m;
		struct spi_device	*spi;
		unsigned		nsecs;
		struct spi_transfer	*t = NULL;
		unsigned		tmp;
		unsigned		cs_change;
		int			status;
		int			(*setup_transfer)(struct spi_device *,
						struct spi_transfer *);
        /*取出等待队列中的的第一个message*/
		m = container_of(bitbang->queue.next, struct spi_message,
				queue);
		list_del_init(&m->queue);//将message从队列中删除
		spin_unlock_irqrestore(&bitbang->lock, flags);

		/* FIXME this is made-up ... the correct value is known to
		 * word-at-a-time bitbang code, and presumably chipselect()
		 * should enforce these requirements too?
		 */
		nsecs = 100;

		spi = m->spi;
		tmp = 0;
		cs_change = 1;
		status = 0;
		setup_transfer = NULL;

		/*遍历message中的所有传输字段,逐一进行传输*/
		list_for_each_entry (t, &m->transfers, transfer_list) {

			/* override or restore speed and wordsize */
			if (t->speed_hz || t->bits_per_word) {
				setup_transfer = bitbang->setup_transfer;
				if (!setup_transfer) {
					status = -ENOPROTOOPT;
					break;
				}
			}
			/*调用setup_transfer根据transfer中的信息进行时钟、字比特数的设定*/
			if (setup_transfer) {
				status = setup_transfer(spi, t);
				if (status < 0)
					break;
			}

			/* set up default clock polarity, and activate chip;
			 * this implicitly updates clock and spi modes as
			 * previously recorded for this device via setup().
			 * (and also deselects any other chip that might be
			 * selected ...)
			 */
			if (cs_change) {//使能外设的片选
				bitbang->chipselect(spi, BITBANG_CS_ACTIVE);
				ndelay(nsecs);
			}
			cs_change = t->cs_change;//这里确定进行了这个字段的传输后是否要改变片选状态
			if (!t->tx_buf && !t->rx_buf && t->len) {
				status = -EINVAL;
				break;
			}

			/* transfer data.  the lower level code handles any
			 * new dma mappings it needs. our caller always gave
			 * us dma-safe buffers.
			 */
			if (t->len) {
				/* REVISIT dma API still needs a designated
				 * DMA_ADDR_INVALID; ~0 might be better.
				 */
				if (!m->is_dma_mapped)
					t->rx_dma = t->tx_dma = 0;
				/*调用针对于平台的传输函数txrx_bufs*/
				status = bitbang->txrx_bufs(spi, t);
			}
			if (status > 0)
				m->actual_length += status;
			if (status != t->len) {
				/* always report some kind of error */
				if (status >= 0)
					status = -EREMOTEIO;
				break;
			}
			status = 0;

			/* protocol tweaks before next transfer */
			/*如果要求在传输完一个字段后进行delay,则进行delay*/
			if (t->delay_usecs)
				udelay(t->delay_usecs);

			if (!cs_change)
				continue;
			
			/*最后一个字段传输完毕了,则跳出循环*/
			if (t->transfer_list.next == &m->transfers)
				break;

			/* sometimes a short mid-message deselect of the chip
			 * may be needed to terminate a mode or command
			 */
			ndelay(nsecs);
			bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
			ndelay(nsecs);
		}

		m->status = status;
		m->complete(m->context);

		/* restore speed and wordsize */
		if (setup_transfer)
			setup_transfer(spi, NULL);

		/* normally deactivate chipselect ... unless no error and
		 * cs_change has hinted that the next message will probably
		 * be for this chip too.
		 */
		if (!(status == 0 && cs_change)) {
			ndelay(nsecs);
			bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
			ndelay(nsecs);
		}

		spin_lock_irqsave(&bitbang->lock, flags);
	}
	bitbang->busy = 0;
	spin_unlock_irqrestore(&bitbang->lock, flags);
}


只要bitbang->queue等待队列不为空,就表示相应的SPI主控制器上还有 传输任务没有完成,因此bitbang_work()会被不断地调度执行。 bitbang_work()中的工作主要是两个循环,外循环遍历等待队列中的message,内循环遍历message中的transfer,在 bitbang_work()中,传输总是以transfer为单位的。当选定了一个transfer后,便会调用transfer_txrx()函数, 进行实际的数据传输,显然这个函数是针对于平台的SPI控制器而实现的,在s3c24xx平台中,该函数为s3c24xx_spi_txrx();

static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
{
	struct s3c24xx_spi *hw = to_hw(spi);

	dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
		t->tx_buf, t->rx_buf, t->len);

	hw->tx = t->tx_buf;//获取发送缓冲区
	hw->rx = t->rx_buf;//获取读取缓存区
	hw->len = t->len;  //获取数据长度
	hw->count = 0;

	init_completion(&hw->done);//初始化完成量

	/* send the first byte */
	/*只发送第一个字节,其他的在中断中发送(读取)*/
	writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);

	wait_for_completion(&hw->done);

	return hw->count;
}


 

static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count)
{
	/*如果tx不为空,也就是说当前是从主机向从机发送数据,则直接将tx[count]发送过去,
	  如果tx为空,也就是说当前是从从机向主机发送数据,则向从机写入0*/
	return hw->tx ? hw->tx[count] : 0;
}


负责SPI数据传输的中断函数:

static irqreturn_t s3c24xx_spi_irq(int irq, void *dev)
{
	struct s3c24xx_spi *hw = dev;
	unsigned int spsta = readb(hw->regs + S3C2410_SPSTA);
	unsigned int count = hw->count;

	/*冲突检测*/
	if (spsta & S3C2410_SPSTA_DCOL) {
		dev_dbg(hw->dev, "data-collision\n");
		complete(&hw->done);
		goto irq_done;
	}

	/*设备忙检测*/
	if (!(spsta & S3C2410_SPSTA_READY)) {
		dev_dbg(hw->dev, "spi not ready for tx?\n");
		complete(&hw->done);
		goto irq_done;
	}

	hw->count++;

	if (hw->rx)//读取数据到缓冲区
		hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);

	count++;

	if (count < hw->len)//向从机写入数据
		writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);
	else//count == len,一个字段发送完成,唤醒完成量
		complete(&hw->done);

 irq_done:
	return IRQ_HANDLED;
}
这里可以看到一点,即使tx为空,也就是说用户申请的是从从设备读取数据,也要不断地向从设备写入数据,只不过写入从设备的是无效数据(0),这样做得目的是为了维持SPI总线上的时钟。至此,SPI框架已分析完毕。

阅读(1028) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:硬盘存储原理和内部结构

给主人留下些什么吧!~~