linux驱动学习笔记(九)SPI

前言

SPI协议是由摩托罗拉公司提出的串行通信协议,是一种高速全双工的通信总线。常见的SPI接口包含四根线,MOSI(主设备输出,从设备输入),MISO(主设备输入,从设备输出)SCLK(时钟,决定通信速率),CS(片选,用于多从机)。所以一根SPI总线也可以挂载多个设备,是用多个片选IO来区分。

时序介绍

1.空闲状态
CS脚拉高时候SPI设备处于空闲状态。
2.起始信号与终止信号
CS脚下降沿时候为起始信号 CS脚上升沿时候为停止信号。
3.数据传输(四种工作模式)
(1)CPOL=0,串行时钟空闲状态为低电平。
(2)CPOL=1,串行时钟空闲状态为高电平。
(3)CPHA=0,串行时钟的第一个跳变沿(上升沿或下降沿)采集数据。
(4)CPHA=1,串行时钟的第二个跳变沿(上升沿或下降沿)采集数据。

I2C数据结构介绍

spi_master

struct spi_master {
	struct device	dev;//对应spi设备
	struct list_head list;
	s16			bus_num;//SPI总线号
	u16			num_chipselect;//片选信号的个数
	u16			dma_alignment;//dma buf的对齐
	/*控制器启动的模式位,被控制器驱动解析*/
	u16			mode_bits;
	/* bitmask of supported bits_per_word for transfers */
	u32			bits_per_word_mask;
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1))
#define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1))

	/* 通信速率 */
	u32			min_speed_hz;
	u32			max_speed_hz;

	/* other constraints relevant to this driver */
	u16			flags;
#define SPI_MASTER_HALF_DUPLEX	BIT(0)		/* 不能全双工*/
#define SPI_MASTER_NO_RX	BIT(1)		/* 不能读*/
#define SPI_MASTER_NO_TX	BIT(2)		/* 不能写*/
#define SPI_MASTER_MUST_RX      BIT(3)		/* 必须有读 */
#define SPI_MASTER_MUST_TX      BIT(4)		/* 必须有写*/

	/* lock and mutex for SPI bus locking */
	spinlock_t		bus_lock_spinlock;
	struct mutex		bus_lock_mutex;

	/* flag indicating that the SPI bus is locked for exclusive use */
	bool			bus_lock_flag;

	int			(*setup)(struct spi_device *spi);//时钟和spi模式的设置函数

	int			(*transfer)(struct spi_device *spi,
						struct spi_message *mesg);//SPI的通信函数指针

	/* called on release() to free memory provided by spi_master */
	void			(*cleanup)(struct spi_device *spi);


	bool			(*can_dma)(struct spi_master *master,
					   struct spi_device *spi,
					   struct spi_transfer *xfer);
	/*内核线程 ,SPI控制器 用到了内核线程*/
	bool				queued;
	struct kthread_worker		kworker;
	struct task_struct		*kworker_task;
	struct kthread_work		pump_messages;
	spinlock_t			queue_lock;
	struct list_head		queue;
	struct spi_message		*cur_msg;
	bool				idling;
	bool				busy;
	bool				running;
	bool				rt;
	bool				auto_runtime_pm;
	bool                            cur_msg_prepared;
	bool				cur_msg_mapped;
	struct completion               xfer_completion;
	size_t				max_dma_len;

	int (*prepare_transfer_hardware)(struct spi_master *master);// 准备传输前硬件初始化
	int (*transfer_one_message)(struct spi_master *master,
				    struct spi_message *mesg);
	int (*unprepare_transfer_hardware)(struct spi_master *master);
	int (*prepare_message)(struct spi_master *master,
			       struct spi_message *message);
	int (*unprepare_message)(struct spi_master *master,
				 struct spi_message *message);

	void (*set_cs)(struct spi_device *spi, bool enable);
	int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
			    struct spi_transfer *transfer);
	void (*handle_err)(struct spi_master *master,
			   struct spi_message *message);

	/* gpio chip select */
	int			*cs_gpios;

	/* DMA channels for use with core dmaengine helpers */
	struct dma_chan		*dma_tx;
	struct dma_chan		*dma_rx;

	/* dummy data for full duplex devices */
	void			*dummy_rx;
	void			*dummy_tx;
};

spi_device

struct spi_device {
	struct device		dev;
	struct spi_master	*master;//设备对应控制器
	u32			max_speed_hz;
	u8			chip_select;//片选
	u8			bits_per_word;
	u16			mode;//模式(下面的#DEFINE)
#define	SPI_CPHA	0x01			/* 时钟相位*/
#define	SPI_CPOL	0x02			/*时钟极性 */
#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			/* CS是不是高位有效 */
#define	SPI_LSB_FIRST	0x08			/* 先传输最低有效位 */
#define	SPI_3WIRE	0x10			/* SI/SO signals shared */
#define	SPI_LOOP	0x20			/* loopback mode */
#define	SPI_NO_CS	0x40			/*只有一个从设备 无片选 */
#define	SPI_READY	0x80			/* slave pulls low to pause */
#define	SPI_TX_DUAL	0x100			/* 两条传输线*/
#define	SPI_TX_QUAD	0x200			/* 四条传输线*/
#define	SPI_RX_DUAL	0x400			/*两根线接收*/
#define	SPI_RX_QUAD	0x800			/*四根线接收 */
	int			irq;
	void			*controller_state;
	void			*controller_data;
	char			modalias[SPI_NAME_SIZE];
	int			cs_gpio;	/* chip select gpio */
};

spi_message

struct spi_message {
	struct list_head	transfers;

	struct spi_device	*spi;

	unsigned		is_dma_mapped:1;//是否使用DMA模式

	/* 传输完成后的部分 */
	void			(*complete)(void *context);
	void			*context;
	unsigned		frame_length;
	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;
	void			*state;
};

spi_transfer

struct spi_transfer {
	
	const void	*tx_buf;//发送缓冲区
	void		*rx_buf;//接收缓冲区
	unsigned	len;//缓冲区字节数
	/*DMA传输情况下 发送接收BUFFER的DMA地址*/
	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;
};

SPI控制器

在drivers/spi/spi-imx.c文件中

match

static struct platform_device_id spi_imx_devtype[] = {
	...
	 {
		.name = "imx21-cspi",
		.driver_data = (kernel_ulong_t) &imx21_cspi_devtype_data,
	}, {
		.name = "imx27-cspi",
		.driver_data = (kernel_ulong_t) &imx27_cspi_devtype_data,
	}, 
	...
};

static const struct of_device_id spi_imx_dt_ids[] = {
	{ .compatible = "fsl,imx1-cspi", .data = &imx1_cspi_devtype_data, },
	{ .compatible = "fsl,imx21-cspi", .data = &imx21_cspi_devtype_data, },
	...
};
MODULE_DEVICE_TABLE(of, spi_imx_dt_ids);
static struct platform_driver spi_imx_driver = {
	.driver = {
		   .name = DRIVER_NAME,
		   .of_match_table = spi_imx_dt_ids,
		   .pm = IMX_SPI_PM,
	},
	.id_table = spi_imx_devtype,
	.probe = spi_imx_probe,
	.remove = spi_imx_remove,
};
module_platform_driver(spi_imx_driver);

匹配适配器成功后运行probe函数

probe

static int spi_imx_probe(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node;
	const struct of_device_id *of_id =
			of_match_device(spi_imx_dt_ids, &pdev->dev);
	struct spi_imx_master *mxc_platform_info =
			dev_get_platdata(&pdev->dev);
	struct spi_master *master;
	struct spi_imx_data *spi_imx;
	struct resource *res;
	int i, ret, num_cs, irq;

	if (!np && !mxc_platform_info) {
		dev_err(&pdev->dev, "can't get the platform data\n");
		return -EINVAL;
	}

	ret = of_property_read_u32(np, "fsl,spi-num-chipselects", &num_cs);
	if (ret < 0) {
		if (mxc_platform_info)
			num_cs = mxc_platform_info->num_chipselect;
		else
			return ret;
	}
	/*申请一个SPI适配器*/
	master = spi_alloc_master(&pdev->dev,
			sizeof(struct spi_imx_data) + sizeof(int) * num_cs);
	if (!master)
		return -ENOMEM;

	platform_set_drvdata(pdev, master);

	master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
	master->bus_num = pdev->id;//总线号
	master->num_chipselect = num_cs;//片选号

	spi_imx = spi_master_get_devdata(master);
	spi_imx->bitbang.master = master;
	/*从设备树获取片选IO*/
	for (i = 0; i < master->num_chipselect; i++) {
		int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
		if (!gpio_is_valid(cs_gpio) && mxc_platform_info)
			cs_gpio = mxc_platform_info->chipselect[i];

		spi_imx->chipselect[i] = cs_gpio;
		if (!gpio_is_valid(cs_gpio))
			continue;

		ret = devm_gpio_request(&pdev->dev, spi_imx->chipselect[i],
					DRIVER_NAME);
		if (ret) {
			dev_err(&pdev->dev, "can't get cs gpios\n");
			goto out_master_put;
		}
	}
	//设置相关通信的函数指针
	spi_imx->bitbang.chipselect = spi_imx_chipselect;
	spi_imx->bitbang.setup_transfer = spi_imx_setupxfer;
	spi_imx->bitbang.txrx_bufs = spi_imx_transfer;
	spi_imx->bitbang.master->setup = spi_imx_setup;
	spi_imx->bitbang.master->cleanup = spi_imx_cleanup;
	spi_imx->bitbang.master->prepare_message = spi_imx_prepare_message;
	spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message;
	spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;

	init_completion(&spi_imx->xfer_done);

	spi_imx->devtype_data = of_id ? of_id->data :
		(struct spi_imx_devtype_data *) pdev->id_entry->driver_data;
	//获取SPI的基地址并虚拟
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	spi_imx->base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(spi_imx->base)) {
		ret = PTR_ERR(spi_imx->base);
		goto out_master_put;
	}
	//获取设备树中配置的中断
	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
		ret = irq;
		goto out_master_put;
	}
	//申请一个中断
	ret = devm_request_irq(&pdev->dev, irq, spi_imx_isr, 0,
			       dev_name(&pdev->dev), spi_imx);
	if (ret) {
		dev_err(&pdev->dev, "can't get irq%d: %d\n", irq, ret);
		goto out_master_put;
	}
	//获取设备树ipgclk
	spi_imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
	if (IS_ERR(spi_imx->clk_ipg)) {
		ret = PTR_ERR(spi_imx->clk_ipg);
		goto out_master_put;
	}
	//获取设备树perclk
	spi_imx->clk_per = devm_clk_get(&pdev->dev, "per");
	if (IS_ERR(spi_imx->clk_per)) {
		ret = PTR_ERR(spi_imx->clk_per);
		goto out_master_put;
	}

	ret = clk_prepare_enable(spi_imx->clk_per);
	if (ret)
		goto out_master_put;

	ret = clk_prepare_enable(spi_imx->clk_ipg);
	if (ret)
		goto out_put_per;
	//设置spi clk
	spi_imx->spi_clk = clk_get_rate(spi_imx->clk_per);
	/*
	 * Only validated on i.mx6 now, can remove the constrain if validated on
	 * other chips.
	 */
	if ((spi_imx->devtype_data == &imx51_ecspi_devtype_data
	    || spi_imx->devtype_data == &imx6ul_ecspi_devtype_data)
	    && spi_imx_sdma_init(&pdev->dev, spi_imx, master, res))
		dev_err(&pdev->dev, "dma setup error,use pio instead\n");

	spi_imx->devtype_data->reset(spi_imx);

	spi_imx->devtype_data->intctrl(spi_imx, 0);

	master->dev.of_node = pdev->dev.of_node;
	ret = spi_bitbang_start(&spi_imx->bitbang);//此函数用于将spi_bitbang的参数进行初始化
	if (ret) {
		dev_err(&pdev->dev, "bitbang start failed with %d\n", ret);
		goto out_clk_put;
	}

	dev_info(&pdev->dev, "probed\n");

	clk_disable_unprepare(spi_imx->clk_ipg);
	clk_disable_unprepare(spi_imx->clk_per);
	return ret;

out_clk_put:
	clk_disable_unprepare(spi_imx->clk_ipg);
out_put_per:
	clk_disable_unprepare(spi_imx->clk_per);
out_master_put:
	spi_master_put(master);

	return ret;
}

spi_bitbang_start

int spi_bitbang_start(struct spi_bitbang *bitbang)
{
	struct spi_master *master = bitbang->master;
	int ret;

	if (!master || !bitbang->chipselect)
		return -EINVAL;

	spin_lock_init(&bitbang->lock);

	if (!master->mode_bits)
		master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;

	if (master->transfer || master->transfer_one_message)
		return -EINVAL;
	/*设置适配器传输消息的函数指针*/
	master->prepare_transfer_hardware = spi_bitbang_prepare_hardware;
	master->unprepare_transfer_hardware = spi_bitbang_unprepare_hardware;
	master->transfer_one_message = spi_bitbang_transfer_one;

	if (!bitbang->txrx_bufs) {
		bitbang->use_dma = 0;
		bitbang->txrx_bufs = spi_bitbang_bufs;
		if (!master->setup) {
			if (!bitbang->setup_transfer)
				bitbang->setup_transfer =
					 spi_bitbang_setup_transfer;
			master->setup = spi_bitbang_setup;
			master->cleanup = spi_bitbang_cleanup;
		}
	}

	/* driver may get busy before register() returns, especially
	 * if someone registered boardinfo for devices
	 */
	ret = spi_register_master(spi_master_get(master));
	if (ret)
		spi_master_put(master);

	return 0;
}
static int spi_bitbang_transfer_one(struct spi_master *master,
				    struct spi_message *m)
{
	struct spi_bitbang	*bitbang;
	unsigned		nsecs;
	struct spi_transfer	*t = NULL;
	unsigned		cs_change;
	int			status;
	int			do_setup = -1;
	struct spi_device	*spi = m->spi;

	bitbang = spi_master_get_devdata(master);

	/* 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;

	cs_change = 1;
	status = 0;

	list_for_each_entry(t, &m->transfers, transfer_list) {

		/* override speed or wordsize? */
		if (t->speed_hz || t->bits_per_word)
			do_setup = 1;

		/* init (-1) or override (1) transfer params */
		if (do_setup != 0) {
			if (bitbang->setup_transfer) {
				status = bitbang->setup_transfer(spi, t);//设置时钟极性
				if (status < 0)
					break;
			}
			if (do_setup == -1)
				do_setup = 0;
		}

		/* 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;
			status = bitbang->txrx_bufs(spi, t);//传输数据 也就是probe设置对应的spi_imx_transfer函数
		}
		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 */
		if (t->delay_usecs)
			udelay(t->delay_usecs);

		if (cs_change &&
		    !list_is_last(&t->transfer_list, &m->transfers)) {
			/* 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);
		}
	}
	...
}

发送流程


spi_message_init();		/* 初始化spi_message */
spi_message_add_tail();/* 将spi_transfer添加到spi_message队列 */
spi_sync();	/* 同步发送 */
	->__spi_sync()
		->__spi_pump_messages()
			-> master->transfer_one_message()
			-> master->transfer_one_message = spi_bitbang_transfer_one;
			-> spi_imx->bitbang.txrx_bufs = spi_imx_transfer;
			-> bitbang->txrx_bufs(spi, t);
			->spi_imx_transfer()
				->spi_imx_pio_transfer()
					->spi_imx_push()
						->spi_imx->tx(spi_imx);//spi_imx_setupxfer中设置好了发送函数以U8来说
						->spi_imx_buf_tx_u8()
						
static void spi_imx_buf_tx_u8(struct spi_imx_data *spi_imx)		
{									
	type val = 0;															
	if (spi_imx->tx_buf) {						
		val = *(type *)spi_imx->tx_buf;				
		spi_imx->tx_buf += sizeof(type);			
	}															
	spi_imx->count -= sizeof(type);											
	writel(val, spi_imx->base + MXC_CSPITXDATA);			
}

函数介绍

/*注册和释放spi驱动*/
int spi_register_driver(struct spi_driver *sdrv);
void spi_unregister_driver(struct spi_driver *sdrv);


static void spi_message_init(struct spi_message *m);//消息初始化
void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)//将spi_transfer添加到spi_message队列
int spi_sync(struct spi_device *spi, struct spi_message *message)//同步发送
int spi_async(struct spi_device *spi, struct spi_message *message)//异步发送
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值