SPI总线驱动分析:
基本数据结构:
链表queue : 为spi controller 提供 任务队列。 每个任务是一个message。
链表transfer_list :message 中存放任务具体内容的队列, 它的内容在应用中填充
struct spi_device {
struct device dev;
struct spi_master *master;
u32 max_speed_hz;
u8 chip_select;
u8 mode;
u8 bits_per_word;
int irq;
void *controller_state;
void *controller_data;
char modalias[32];
/*
* likely need more hooks for more protocol options affecting how
* the controller talks to each chip, like:
* - memory packing (12 bit samples into low bits, others zeroed)
* - priority
* - drop chipselect after each word
* - chipselect delays
* - ...
*/
};
任务的数据结构:
struct spi_message {
struct list_head transfers;
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;
void *state;
};
/
API:
spi_message_init---------->创建message(生成“transfer”链表)
spi_message_add----------->将 t 嵌入 message(m)中的链表“transfer”
spi_sync------------------>调用 pnx_spi_transfer 将 message 嵌入到 链表“queue”中,并执行message。
example of how to use spi bus:
spi_write(struct spi_device *spi, const u8 *buf, size_t len)
{
struct spi_transfer t = {
.tx_buf = buf,
.len = len,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
return spi_sync(spi, &m);
}
///
controller functions:
pnx67xx_spi_transfer 负责 将 message 挂入 queue。
pnx67xx_next_message 执行message传输。
pnx67xx_spi_next_xfer 执行transfer传输
static void __init pnx67xx_spi_setup_master(struct platform_device *pdev,
struct spi_master *master)
{
master->bus_num = pdev->id;
master->num_chipselect = PNX_SPI_CHIP_SELECTS;
master->setup = pnx67xx_spi_setup;
master->transfer = pnx67xx_spi_transfer;
master->cleanup = pnx67xx_spi_cleanup;
platform_set_drvdata(pdev, master);
}
pnx67xx_spi_transfer(struct spi_device *spi, struct spi_message *msg)
{ 1.检查消息链表是否为空
2.检查链表中的消息是否有异常
3.将新消息插入链表
4。如果当前不处于传输状态,则发起传输 pnx67xx_spi_next_message(pnx_spi)
}
//---------transmit msges----------------
pnx67xx_spi_next_message(struct pnx67xx_spi *pnx_spi)
{ 1.取出消息:list_entry(pnx_spi->queue.next, struct spi_message, queue);
2.select chip if it's not still active
3.消息传输 :pnx67xx_spi_next_xfer(pnx_spi, msg);
}
//-------transmit for singal msg-------------
pnx67xx_spi_next_xfer(pnx_spi, msg);
{
1.pnx67xx_do_poll_transfer();// 小于 dma buffer
2.pnx67xx_do_dma_transfer();//大于等于dma buffer-----intr--->pnx67xx_spi_transfer_done()
--spi_write_stat-->pnx67xx_spi_transfer_done()
}
pnx67xx_spi_transfer_done(struct pnx67xx_spi *pnx_spi)
{
1.stop dma ,update actual length
2.如果msg传输完成,pnx67xx_spi_msg_done()
3.如果msg没有传完,pnx67xx_spi_next_xfer()
}
pnx67xx_spi_msg_done()
{
1.摘除msg
2.如果msg链表为空,进入节电模式,否则执行下一个msg pnx67xx_spi_next_message(pnx_spi)
}
//------------------------------------------------------------
经典:
/* compute max packetsize for polling 20us */
pollmax = clock * 20;
pollmax = pollmax / 1000000;
cs->pollsize_thread = pollmax / 8;
/* polling in it is allowed up to 5 us */
cs->pollsize_it = pollmax / 32;
polling 一次 pollsize 需要 20 us.
当使用cpu传输时,如说所传时间大于20us,则使用中断同步,否则使用轮寻机制。
20us 可能等于完成一次中断响应所需要的时间。
基本数据结构:
链表queue : 为spi controller 提供 任务队列。 每个任务是一个message。
链表transfer_list :message 中存放任务具体内容的队列, 它的内容在应用中填充
struct spi_device {
struct device dev;
struct spi_master *master;
u32 max_speed_hz;
u8 chip_select;
u8 mode;
u8 bits_per_word;
int irq;
void *controller_state;
void *controller_data;
char modalias[32];
/*
* likely need more hooks for more protocol options affecting how
* the controller talks to each chip, like:
* - memory packing (12 bit samples into low bits, others zeroed)
* - priority
* - drop chipselect after each word
* - chipselect delays
* - ...
*/
};
任务的数据结构:
struct spi_message {
struct list_head transfers;
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;
void *state;
};
/
API:
spi_message_init---------->创建message(生成“transfer”链表)
spi_message_add----------->将 t 嵌入 message(m)中的链表“transfer”
spi_sync------------------>调用 pnx_spi_transfer 将 message 嵌入到 链表“queue”中,并执行message。
example of how to use spi bus:
spi_write(struct spi_device *spi, const u8 *buf, size_t len)
{
struct spi_transfer t = {
.tx_buf = buf,
.len = len,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
return spi_sync(spi, &m);
}
///
controller functions:
pnx67xx_spi_transfer 负责 将 message 挂入 queue。
pnx67xx_next_message 执行message传输。
pnx67xx_spi_next_xfer 执行transfer传输
static void __init pnx67xx_spi_setup_master(struct platform_device *pdev,
struct spi_master *master)
{
master->bus_num = pdev->id;
master->num_chipselect = PNX_SPI_CHIP_SELECTS;
master->setup = pnx67xx_spi_setup;
master->transfer = pnx67xx_spi_transfer;
master->cleanup = pnx67xx_spi_cleanup;
platform_set_drvdata(pdev, master);
}
pnx67xx_spi_transfer(struct spi_device *spi, struct spi_message *msg)
{ 1.检查消息链表是否为空
2.检查链表中的消息是否有异常
3.将新消息插入链表
4。如果当前不处于传输状态,则发起传输 pnx67xx_spi_next_message(pnx_spi)
}
//---------transmit msges----------------
pnx67xx_spi_next_message(struct pnx67xx_spi *pnx_spi)
{ 1.取出消息:list_entry(pnx_spi->queue.next, struct spi_message, queue);
2.select chip if it's not still active
3.消息传输 :pnx67xx_spi_next_xfer(pnx_spi, msg);
}
//-------transmit for singal msg-------------
pnx67xx_spi_next_xfer(pnx_spi, msg);
{
1.pnx67xx_do_poll_transfer();// 小于 dma buffer
2.pnx67xx_do_dma_transfer();//大于等于dma buffer-----intr--->pnx67xx_spi_transfer_done()
--spi_write_stat-->pnx67xx_spi_transfer_done()
}
pnx67xx_spi_transfer_done(struct pnx67xx_spi *pnx_spi)
{
1.stop dma ,update actual length
2.如果msg传输完成,pnx67xx_spi_msg_done()
3.如果msg没有传完,pnx67xx_spi_next_xfer()
}
pnx67xx_spi_msg_done()
{
1.摘除msg
2.如果msg链表为空,进入节电模式,否则执行下一个msg pnx67xx_spi_next_message(pnx_spi)
}
//------------------------------------------------------------
经典:
/* compute max packetsize for polling 20us */
pollmax = clock * 20;
pollmax = pollmax / 1000000;
cs->pollsize_thread = pollmax / 8;
/* polling in it is allowed up to 5 us */
cs->pollsize_it = pollmax / 32;
polling 一次 pollsize 需要 20 us.
当使用cpu传输时,如说所传时间大于20us,则使用中断同步,否则使用轮寻机制。
20us 可能等于完成一次中断响应所需要的时间。