Linux SPI 设备驱动笔记

这几天在看一些SPI设备驱动,不求甚解地去做,就是把事情往更深入的层面去说事…

用片段代码,来说明问题,有时候更容易的理解一些事情.

static int read_sr(struct m25p *flash)
{
	ssize_t retval;
	u8 code = OPCODE_RDSR;
	u8 val;
	/*
		设备号:flash->spi
		要发送的内容:&code
		要发送的长度:1
		要接受的内容:&val
		要接受的长度:1
	*/
	retval = spi_write_then_read(flash->spi, &code, 1, &val, 1);
	if (retval < 0) {
		dev_err(&flash->spi->dev, "error %d reading SR
",
				(int) retval);
		return retval;
	}
	return val;
}

发当然也有后面发0x00,前面发一个字的.

static const struct spi_device_id *__devinit jedec_probe(struct spi_device *spi)
{
	int			tmp;
	u8			code = OPCODE_RDID;
	u8			id[5];
	u32			jedec;
	u16                     ext_jedec;
	struct flash_info	*info;
	/* JEDEC also defines an optional "extended device information"
	 * string for after vendor-specific data, after the three bytes
	 * we use here.  Supporting some chips might require using it.
	 */
	tmp = spi_write_then_read(spi, &code, 1, id, 5);
	if (tmp < 0) {
		DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID
",
			dev_name(&spi->dev), tmp);
		return NULL;
}

另外也有函数,只发不收:

static int write_sr(struct m25p *flash, u8 val)
{
	flash->command[0] = OPCODE_WRSR;
	flash->command[1] = val;
	/*
		设备号:flash->spi
		要发送的内容:flash->command
		要发送的长度:2
	*/
	return spi_write(flash->spi, flash->command, 2);
}

当然,对应还有spi_read呢,当然高级一点,用spi_message_add_tail,把东西塞进去,首先要有这个结构体:

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;
         struct sg_table tx_sg;
         struct sg_table rx_sg;
         unsigned        cs_change:1;
         unsigned        tx_nbits:3;
         unsigned        rx_nbits:3;
         #define SPI_NBITS_SINGLE        0x01 /* 1bit transfer */
         #define SPI_NBITS_DUAL          0x02 /* 2bits transfer */
         #define SPI_NBITS_QUAD          0x04 /* 4bits transfer */
         u8              bits_per_word;
         u16             delay_usecs;
         u32             speed_hz;
         struct list_head transfer_list;
};

然后填要发送的数据,然后发送,如下函数:

static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
	size_t *retlen, u_char *buf)
{
	struct m25p *flash = mtd_to_m25p(mtd);
	struct spi_transfer t[2];
	struct spi_message m;
	DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd
",
			dev_name(&flash->spi->dev), __func__, "from",
			(u32)from, len);
	/* sanity checks */
	if (!len)
		return 0;
	if (from + len > flash->mtd.size)
		return -EINVAL;
	/*
	  用于初始化spi_message这个结构体,结构体填充.
	*/
	spi_message_init(&m);
	memset(t, 0, (sizeof t));
	/* NOTE:
	 * OPCODE_FAST_READ (if available) is faster.
	 * Should add 1 byte DUMMY_BYTE.
	 */
	/*
		填充两次发送序列.
	*/
	t[0].tx_buf = flash->command;
	t[0].len = m25p_cmdsz(flash) + FAST_READ_DUMMY_BYTE;
	spi_message_add_tail(&t[0], &m);
	t[1].rx_buf = buf;
	t[1].len = len;
	spi_message_add_tail(&t[1], &m);
	/* Byte count starts at zero. */
	if (retlen)
		*retlen = 0;
	mutex_lock(&flash->lock);
	/* Wait till previous write/erase is done. */
	if (wait_till_ready(flash)) {
		/* REVISIT status return?? */
		mutex_unlock(&flash->lock);
		return 1;
	}
	/* FIXME switch to OPCODE_FAST_READ.  It's required for higher
	 * clocks; and at this writing, every chip this driver handles
	 * supports that opcode.
	 */
	/* Set up the write data buffer. */
	flash->command[0] = OPCODE_READ;
	m25p_addr2cmd(flash, from, flash->command);
	/*
	  同步API发送SPI数据.
	*/
	spi_sync(flash->spi, &m);
	*retlen = m.actual_length - m25p_cmdsz(flash) - FAST_READ_DUMMY_BYTE;
	mutex_unlock(&flash->lock);
	return 0;
}

如果使用异步方式,就需要修改spi_message结构体,加入一个回调函数.其中tx_buf填写了,肯定就是发送填写的内容,否则发送0x00,然后收到的,肯定就存到rxbuf里头了.但是说实话,用async的应用也不多.但只是阻塞就有点塞CPU了,也不绿色低碳环保.找来找去,只能找到一个2046的程序.但是此函数实在有点庞大…截取部分:

	x++;
	packet->pwrdown = PWRDOWN;
	x->tx_buf = &packet->pwrdown;
	x->len = 1;
	spi_message_add_tail(x, m);
	x++;
	x->rx_buf = &packet->dummy;
	x->len = 2;
	CS_CHANGE(*x);
	spi_message_add_tail(x, m);
	m->complete = ads7846_rx;
	m->context = ts;

如果完成传输,调用函数ads7846_rx,这个函数传入参数应该是void型的,如下~

static void ads7846_rx(void *ads)
{
	struct ads7846		*ts = ads;
	struct ads7846_packet	*packet = ts->packet;
	unsigned		Rt;
	u16			x, y, z1, z2;
	/* ads7846_rx_val() did in-place conversion (including byteswap) from
	 * on-the-wire format as part of debouncing to get stable readings.
	 */
	x = packet->tc.x;
	y = packet->tc.y;
	z1 = packet->tc.z1;
	z2 = packet->tc.z2;

那么,模式等等哪里设置呢,当然是板级设置文件哈,举个栗子:

static struct spi_board_info eb_spi_devices[] = {
	{	/* RTC Dallas DS1306 */
		.modalias	= "rtc-ds1305",
		.chip_select	= 3,
		.mode		= SPI_CS_HIGH | SPI_CPOL | SPI_CPHA,
		.max_speed_hz	= 500000,
		.bus_num	= 0,
		.irq		= AT572D940HF_ID_IRQ1,
		.platform_data	= (void *) &ds1306_data,
	},
#if defined(CONFIG_MTD_AT91_DATAFLASH_CARD)
	{	/* Dataflash card */
		.modalias	= "mtd_dataflash",
		.chip_select	= 0,
		.max_speed_hz	= 15 * 1000 * 1000,
		.bus_num	= 0,
	},
#endif
};

好吧,今天就分析到这儿吧.这个月Windows 10就要发布了,好兴奋的说.

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注