前言
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)//异步发送