开发板为sbc3530,芯片为ti的omap3530
Mscpi主设备。/arch/arm/match-omap2/devices.c
static struct omap2_mcspi_platform_configomap2_mcspi1_config = {
.num_cs = 4,
};
static struct resourceomap2_mcspi1_resources[] = {
{
.start = OMAP2_MCSPI1_BASE,
.end = OMAP2_MCSPI1_BASE + 0xff,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device omap2_mcspi1= {
.name = "omap2_mcspi",
.id = 1,
.num_resources = ARRAY_SIZE(omap2_mcspi1_resources),
.resource = omap2_mcspi1_resources,
.dev = {
.platform_data= &omap2_mcspi1_config,
},
};
static struct omap2_mcspi_platform_configomap2_mcspi2_config = {
.num_cs = 2,
};
static struct resourceomap2_mcspi2_resources[] = {
{
.start = OMAP2_MCSPI2_BASE,
.end = OMAP2_MCSPI2_BASE + 0xff,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device omap2_mcspi2= {
.name = "omap2_mcspi",
.id = 2,
.num_resources = ARRAY_SIZE(omap2_mcspi2_resources),
.resource = omap2_mcspi2_resources,
.dev = {
.platform_data= &omap2_mcspi2_config,
},
};
static struct omap2_mcspi_platform_configomap2_mcspi3_config = {
.num_cs = 2,
};
static struct resourceomap2_mcspi3_resources[] = {
{
.start = OMAP2_MCSPI3_BASE,
.end = OMAP2_MCSPI3_BASE + 0xff,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device omap2_mcspi3= {
.name = "omap2_mcspi",
.id = 3,
.num_resources = ARRAY_SIZE(omap2_mcspi3_resources),
.resource = omap2_mcspi3_resources,
.dev = {
.platform_data= &omap2_mcspi3_config,
},
};
static struct omap2_mcspi_platform_configomap2_mcspi4_config = {
.num_cs = 1,
};
static struct resourceomap2_mcspi4_resources[] = {
{
.start = OMAP2_MCSPI4_BASE,
.end = OMAP2_MCSPI4_BASE + 0xff,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device omap2_mcspi4= {
.name = "omap2_mcspi",
.id = 4,
.num_resources = ARRAY_SIZE(omap2_mcspi4_resources),
.resource = omap2_mcspi4_resources,
.dev = {
.platform_data= &omap2_mcspi4_config,
},
};
注册mcspi设备。
static void omap_init_mcspi(void)
{
if(cpu_is_omap44xx())
omap4_mcspi_fixup();
platform_device_register(&omap2_mcspi1);
platform_device_register(&omap2_mcspi2);
if(cpu_is_omap2430() || cpu_is_omap343x() || cpu_is_omap44xx())
omap2_mcspi3_init();
if(cpu_is_omap343x() || cpu_is_omap44xx())
omap2_mcspi4_init();
}
./drive/spi/omap2-mcspi.c
mcSpi驱动
static struct platform_driveromap2_mcspi_driver = {
.driver= {
.name= "omap2_mcspi",
.owner= THIS_MODULE,
},
.remove= __exit_p(omap2_mcspi_remove),
};
注册mcSpi驱动
static int __init omap2_mcspi_init(void)
{
omap2_mcspi_wq= create_singlethread_workqueue(
omap2_mcspi_driver.driver.name);
if(omap2_mcspi_wq == NULL)
return-1;
returnplatform_driver_probe(&omap2_mcspi_driver, omap2_mcspi_probe);
}
//运行mcspi的probe函数
static int __init omap2_mcspi_probe(structplatform_device *pdev)
{
structspi_master *master;
structomap2_mcspi *mcspi;
structresource *r;
int status = 0, i;
constu8 *rxdma_id, *txdma_id;
unsigned num_chipselect;
switch(pdev->id) {
case1:
rxdma_id= spi1_rxdma_id;
txdma_id= spi1_txdma_id;
num_chipselect= 4;
break;
case2:
rxdma_id= spi2_rxdma_id;
txdma_id= spi2_txdma_id;
num_chipselect= 2;
break;
#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) \
||defined(CONFIG_ARCH_OMAP4)
case3:
rxdma_id= spi3_rxdma_id;
txdma_id= spi3_txdma_id;
num_chipselect= 2;
break;
#endif
#if defined(CONFIG_ARCH_OMAP3) ||defined(CONFIG_ARCH_OMAP4)
case4:
rxdma_id= spi4_rxdma_id;
txdma_id= spi4_txdma_id;
num_chipselect= 1;
break;
#endif
default:
return-EINVAL;
}
master= spi_alloc_master(&pdev->dev, sizeof *mcspi);//分master 和mcspi的空间
if(master == NULL) {
dev_dbg(&pdev->dev,"master allocation failed\n");
return-ENOMEM;
}
/*the spi->mode bits understood by this driver: */
master->mode_bits= SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
if(pdev->id != -1)
master->bus_num= pdev->id;
master->setup= omap2_mcspi_setup;
master->transfer= omap2_mcspi_transfer;
master->cleanup= omap2_mcspi_cleanup;
master->num_chipselect= num_chipselect;
dev_set_drvdata(&pdev->dev,master);
mcspi= spi_master_get_devdata(master);//由master地址得到mcspi地址
mcspi->master= master;
r= platform_get_resource(pdev, IORESOURCE_MEM, 0);
if(r == NULL) {
status= -ENODEV;
gotoerr1;
}
if(!request_mem_region(r->start, (r->end - r->start) + 1,
dev_name(&pdev->dev))){
status= -EBUSY;
gotoerr1;
}
mcspi->phys= r->start; //spi控制寄存器起始的物理地址
mcspi->base= ioremap(r->start, r->end - r->start + 1);// spi控制寄存器起始的虚拟地址
if(!mcspi->base) {
dev_dbg(&pdev->dev,"can't ioremap MCSPI\n");
status= -ENOMEM;
gotoerr1aa;
}
INIT_WORK(&mcspi->work,omap2_mcspi_work);//初始化工作队列,传输的主要函数
spin_lock_init(&mcspi->lock);
INIT_LIST_HEAD(&mcspi->msg_queue); //初始化传输数据队列,
INIT_LIST_HEAD(&omap2_mcspi_ctx[master->bus_num- 1].cs);
mcspi->ick= clk_get(&pdev->dev, "ick"); //时钟
if(IS_ERR(mcspi->ick)) {
dev_dbg(&pdev->dev,"can't get mcspi_ick\n");
status= PTR_ERR(mcspi->ick);
gotoerr1a;
}
mcspi->fck= clk_get(&pdev->dev, "fck");//时钟
if(IS_ERR(mcspi->fck)) {
dev_dbg(&pdev->dev,"can't get mcspi_fck\n");
status= PTR_ERR(mcspi->fck);
gotoerr2;
}
mcspi->dma_channels= kcalloc(master->num_chipselect,
sizeof(structomap2_mcspi_dma),
GFP_KERNEL);
if(mcspi->dma_channels == NULL)
gotoerr3;
for(i = 0; i < num_chipselect; i++) {
mcspi->dma_channels[i].dma_rx_channel= -1;
mcspi->dma_channels[i].dma_rx_sync_dev= rxdma_id[i];
mcspi->dma_channels[i].dma_tx_channel= -1;
mcspi->dma_channels[i].dma_tx_sync_dev= txdma_id[i];
}
if(omap2_mcspi_reset(mcspi) < 0)
gotoerr4;
status= spi_register_master(master);
if(status < 0)
gotoerr4;
returnstatus;
err4:
kfree(mcspi->dma_channels);
err3:
clk_put(mcspi->fck);
err2:
clk_put(mcspi->ick);
err1a:
iounmap(mcspi->base);
err1aa:
release_mem_region(r->start,(r->end - r->start) + 1);
err1:
spi_master_put(master);
returnstatus;
}
struct spi_master *spi_alloc_master(structdevice *dev, unsigned size)
{
structspi_master *master;
if(!dev)
returnNULL;
master= kzalloc(size + sizeof *master, GFP_KERNEL);
if(!master)
returnNULL;
device_initialize(&master->dev);
master->dev.class= &spi_master_class;
master->dev.parent= get_device(dev);
spi_master_set_devdata(master,&master[1]);
returnmaster;
}
int spi_register_master(struct spi_master*master)
{
staticatomic_t dyn_bus_id = ATOMIC_INIT((1<<15)- 1);
structdevice *dev =master->dev.parent;
int status = -ENODEV;
int dynamic = 0;
if(!dev)
return-ENODEV;
/*even if it's just one always-selected device, there must
* be at least one chipselect
*/
if(master->num_chipselect == 0)
return-EINVAL;
/*convention: dynamically assigned bus IDscount down from the max */
if(master->bus_num < 0) {
/*FIXME switch to an IDR based scheme, something like
* I2Cnow uses, so we can't run out of "dynamic" IDs
*/
master->bus_num= atomic_dec_return(&dyn_bus_id);
dynamic= 1;
}
/*register the device, then userspace will see it.
* registration fails if the bus ID is in use.
*/
dev_set_name(&master->dev,"spi%u", master->bus_num);
status= device_add(&master->dev);
if(status < 0)
gotodone;
dev_dbg(dev,"registered master %s%s\n", dev_name(&master->dev),
dynamic? " (dynamic)" : "");
/*populate children from any spi device tables */
scan_boardinfo(master);
status= 0;
done:
returnstatus;
}
注册spi drive.
static void scan_boardinfo(structspi_master *master)
{
structboardinfo *bi;
mutex_lock(&board_lock);
list_for_each_entry(bi,&board_list, list) {
structspi_board_info *chip =bi->board_info;
unsigned n;
for(n = bi->n_board_info; n > 0; n--, chip++) {
if(chip->bus_num != master->bus_num)
continue;
/*NOTE: this relies on spi_new_device to
* issue diagnostics when given bogus inputs
*/
(void)spi_new_device(master, chip);
printk(KERN_ALERT"chip%s master is %d \n",chip->modalias,master->bus_num);//yuanye
}
}
mutex_unlock(&board_lock);
}
struct spi_device *spi_new_device(structspi_master *master,
struct spi_board_info *chip)
{
structspi_device *proxy;
int status;
/*NOTE: caller did any chip->bus_numchecks necessary.
*
* Also, unless we change the return valueconvention to use
* error-or-pointer (not NULL-or-pointer),troubleshootability
* suggests syslogged diagnostics are best here(ugh).
*/
proxy= spi_alloc_device(master);
if(!proxy)
returnNULL;
WARN_ON(strlen(chip->modalias)>= sizeof(proxy->modalias));
proxy->chip_select= chip->chip_select;
proxy->max_speed_hz= chip->max_speed_hz;
proxy->mode= chip->mode;
proxy->irq= chip->irq;
strlcpy(proxy->modalias,chip->modalias, sizeof(proxy->modalias));
proxy->dev.platform_data= (void *) chip->platform_data;
proxy->controller_data= chip->controller_data;
proxy->controller_state= NULL;
status= spi_add_device(proxy);
if(status < 0) {
spi_dev_put(proxy);
return NULL;
}
returnproxy;
}
int spi_add_device(struct spi_device *spi)
{
staticDEFINE_MUTEX(spi_add_lock);
structdevice *dev = spi->master->dev.parent;
intstatus;
/*Chipselects are numbered 0..max; validate. */
if(spi->chip_select >= spi->master->num_chipselect) {
dev_err(dev,"cs%d >= max %d\n",
spi->chip_select,
spi->master->num_chipselect);
return-EINVAL;
}
/*Set the bus ID string */
dev_set_name(&spi->dev,"%s.%u", dev_name(&spi->master->dev),
spi->chip_select);
/*We need to make sure there's no other device with this
* chipselect **BEFORE** we call setup(), elsewe'll trash
* its configuration. Lock against concurrent add() calls.
*/
mutex_lock(&spi_add_lock);
if(bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev))
!=NULL) {
dev_err(dev,"chipselect %d already in use\n",
spi->chip_select);
status= -EBUSY;
gotodone;
}
/*Drivers may modify this initial i/o setup, but will
* normally rely on the device beingsetup. Devices
* using SPI_CS_HIGH can't coexist wellotherwise...
*/
status= spi_setup(spi);
if(status < 0) {
dev_err(dev,"can't %s %s, status %d\n",
"setup",dev_name(&spi->dev), status);
gotodone;
}
/*Device may be bound to an active driver when this returns */
status= device_add(&spi->dev);
if(status < 0)
dev_err(dev,"can't %s %s, status %d\n",
"add",dev_name(&spi->dev), status);
else
dev_dbg(dev,"registered child %s\n", dev_name(&spi->dev));
done:
mutex_unlock(&spi_add_lock);
returnstatus;
}
int spi_setup(struct spi_device *spi)
{
unsigned bad_bits;
int status;
/*help drivers fail *cleanly* when they need options
* that aren't supported with their currentmaster
*/
bad_bits= spi->mode & ~spi->master->mode_bits;
if(bad_bits) {
dev_dbg(&spi->dev,"setup: unsupported mode bits %x\n",
bad_bits);
return-EINVAL;
}
if(!spi->bits_per_word)
spi->bits_per_word= 8;
status= spi->master->setup(spi);
dev_dbg(&spi->dev,"setup mode %d, %s%s%s%s"
"%ubits/w, %u Hz max --> %d\n",
(int)(spi->mode & (SPI_CPOL | SPI_CPHA)),
(spi->mode& SPI_CS_HIGH) ? "cs_high, " : "",
(spi->mode& SPI_LSB_FIRST) ? "lsb, " : "",
(spi->mode& SPI_3WIRE) ? "3wire, " : "",
(spi->mode& SPI_LOOP) ? "loopback, " : "",
spi->bits_per_word,spi->max_speed_hz,
status);
returnstatus;
} static int omap2_mcspi_setup(structspi_device *spi)
{
int ret;
structomap2_mcspi *mcspi;
structomap2_mcspi_dma *mcspi_dma;
structomap2_mcspi_cs *cs =spi->controller_state;
if(spi->bits_per_word < 4 || spi->bits_per_word > 32) {
dev_dbg(&spi->dev,"setup: unsupported %d bit words\n",
spi->bits_per_word);
return-EINVAL;
}
mcspi= spi_master_get_devdata(spi->master);
mcspi_dma= &mcspi->dma_channels[spi->chip_select];
if(!cs) {
cs= kzalloc(sizeof *cs, GFP_KERNEL);
if(!cs)
return-ENOMEM;
cs->base= mcspi->base + spi->chip_select * 0x14;
cs->phys= mcspi->phys + spi->chip_select * 0x14;
cs->chconf0= 0;
spi->controller_state= cs;
/*Link this to context save list */
list_add_tail(&cs->node,
&omap2_mcspi_ctx[mcspi->master->bus_num- 1].cs);
}
if(mcspi_dma->dma_rx_channel == -1
||mcspi_dma->dma_tx_channel == -1) {
ret= omap2_mcspi_request_dma(spi);
if(ret < 0)
returnret;
}
if(omap2_mcspi_enable_clocks(mcspi))
return-ENODEV;
ret= omap2_mcspi_setup_transfer(spi, NULL);
omap2_mcspi_disable_clocks(mcspi);
returnret;
}
对static intomap2_mcspi_request_dma(struct spi_device *spi)进行分析。
static int omap2_mcspi_request_dma(structspi_device *spi)
{
structspi_master *master = spi->master;
structomap2_mcspi *mcspi;
structomap2_mcspi_dma *mcspi_dma;
mcspi= spi_master_get_devdata(master);
mcspi_dma= mcspi->dma_channels + spi->chip_select;
printk(KERN_ALERT"mcspi_dma->dma_rx_sync_devis %d\n",mcspi_dma->dma_rx_sync_dev);
printk(KERN_ALERT"mcspi_dma->dma_tx_sync_devis %d\n",mcspi_dma->dma_tx_sync_dev) ;
if(omap_request_dma(mcspi_dma->dma_rx_sync_dev, "McSPI RX",
omap2_mcspi_dma_rx_callback,spi,
&mcspi_dma->dma_rx_channel)){
dev_err(&spi->dev,"no RX DMA channel for McSPI\n");
return-EAGAIN;
}
if(omap_request_dma(mcspi_dma->dma_tx_sync_dev, "McSPI TX",
omap2_mcspi_dma_tx_callback,spi,
&mcspi_dma->dma_tx_channel)){
omap_free_dma(mcspi_dma->dma_rx_channel);
mcspi_dma->dma_rx_channel= -1;
dev_err(&spi->dev,"no TX DMA channel for McSPI\n");
return-EAGAIN;
}
init_completion(&mcspi_dma->dma_rx_completion);
init_completion(&mcspi_dma->dma_tx_completion);
return0;
}
主要分析omap_request_dma()函数。
int omap_request_dma(int dev_id, const char*dev_name,
void (*callback)(int lch, u16 ch_status,void *data),
void *data, int *dma_ch_out)
{
intch, free_ch = -1;
unsignedlong flags;
structomap_dma_lch *chan;
spin_lock_irqsave(&dma_chan_lock,flags);
for(ch = 0; ch < dma_chan_count; ch++) {
if(free_ch == -1 && dma_chan[ch].dev_id == -1) {//寻找空闲的DMA chanel
free_ch= ch;
if(dev_id == 0)
break;
}
}
if(free_ch == -1) {
spin_unlock_irqrestore(&dma_chan_lock,flags);
return-EBUSY;
}
chan= dma_chan + free_ch;
chan->dev_id= dev_id;
if(cpu_class_is_omap1())
clear_lch_regs(free_ch);
if(cpu_class_is_omap2())
omap_clear_dma(free_ch);//初始化所找到的chanel所对应的寄存器
spin_unlock_irqrestore(&dma_chan_lock,flags);
chan->dev_name= dev_name;
chan->callback= callback;
chan->data= data;
chan->flags= 0;
#ifndef CONFIG_ARCH_OMAP1
if(cpu_class_is_omap2()) {
chan->chain_id= -1;
chan->next_linked_ch= -1;
}
#endif
chan->enabled_irqs= OMAP_DMA_DROP_IRQ | OMAP_DMA_BLOCK_IRQ;
if(cpu_class_is_omap1())
chan->enabled_irqs|= OMAP1_DMA_TOUT_IRQ;
elseif (cpu_class_is_omap2())
chan->enabled_irqs|= OMAP2_DMA_MISALIGNED_ERR_IRQ |
OMAP2_DMA_TRANS_ERR_IRQ;
if(cpu_is_omap16xx()) {
/*If the sync device is set, configure it dynamically. */
if(dev_id != 0) {
set_gdma_dev(free_ch+ 1, dev_id);
dev_id= free_ch + 1;
}
/*
* Disable the 1510 compatibility mode and setthe sync device
* id.
*/
dma_write(dev_id| (1 << 10), CCR(free_ch));
}else if (cpu_is_omap7xx() || cpu_is_omap15xx()) {
dma_write(dev_id,CCR(free_ch));
}
if(cpu_class_is_omap2()) {
omap2_enable_irq_lch(free_ch);//使能free_ch所对应信道号上的中断
omap_enable_channel_irq(free_ch);//free_ch 所对应的chanel状态复位
/*Clear the CSR register and IRQ status register */
dma_write(OMAP2_DMA_CSR_CLEAR_MASK,CSR(free_ch));
dma_write(1<< free_ch, IRQSTATUS_L0);
}
*dma_ch_out= free_ch;
return0;
}注册流程:
omap2_mcspi_probe()-->spi_register_master()-->scan_boardinfo()-->spi_new_device()-->spi_add_device()-->spi_setup()-->omap2_mcspi_setup()
驱动注册完成后,主要的数据结构:
structspi_master *master{
master ->dev.class =&spi_master_class;
master ->dev.parent =get_device(dev);//platform设备的dev
master->dev.p-> driver_data-> mcspi;
master->bus_num = pdev->id;//此处为3,为spi3模块
master->setup = omap2_mcspi_setup;
master->transfer = omap2_mcspi_transfer;
master->cleanup = omap2_mcspi_cleanup;
master->num_chipselect =num_chipselect;//此处为2
}
structomap2_mcspi *mcspi{
mcspi->master = master;
mcspi->phys = r->start;//控制寄存器初始物理地址
mcspi->base = ioremap(r->start,r->end - r->start + 1);//虚拟地址
mcspi->ick = clk_get(&pdev->dev,"ick");
mcspi->fck = clk_get(&pdev->dev,"fck");
mcspi->dma_channels[0].dma_rx_channel= -1;
mcspi->dma_channels[0].dma_rx_sync_dev= rxdma_id[0];
//OMAP24XX_DMA_SPI3_RX0 16 DMA请求源序号+1
mcspi->dma_channels[0].dma_tx_channel= -1;// /为申请到的DMA信号号
mcspi->dma_channels[0].dma_tx_sync_dev= txdma_id[0];
// OMAP24XX_DMA_SPI3_TX0 值15 DMA 请求源序号+1
mcspi->dma_channels[1].dma_rx_sync_dev= rxdma_id[1];
//OMAP24XX_DMA_SPI3_RX1 24 DMA请求源序号+1
mcspi->dma_channels[1].dma_tx_channel= -1;//为申请到的DMA信号号
mcspi->dma_channels[0].dma_tx_sync_dev= txdma_id[1];
// OMAP24XX_DMA_SPI3_TX1 值23 DMA 请求源序号+1
}
structspi_device *proxy{
proxy ->master = master;
proxy ->dev.parent =master->dev.parent;;
proxy ->dev.bus = &spi_bus_type;
proxy ->dev.release = spidev_release; proxy->chip_select= 0;
proxy->max_speed_hz = 1500000
proxy->mode = 0
proxy->irq =180
sproxy->modalias=” spidev“;
proxy->dev.platform_data =NULL
proxy->controller_data = NULL
proxy->controller_state = NULL;
proxy ->bits_per_word = 8;
cs = kzalloc(sizeof *cs, GFP_KERNEL);
cs->base = mcspi->base;
cs->phys = mcspi->phys;
cs->chconf0 = 0;
proxy ->controller_state = cs;
}
//注册的spi设备
struct spi_board_infoomap3stalker_spi_board_info[] = {
[0]= {
.modalias = “spidev”,
.bus_num = 1,
.chip_select = 0,
.max_speed_hz = 1500000,
.irq =OMAP_GPIO_IRQ(OMAP3_STALKER_TS_GPIO),//irq=180
},
};
//注册的spi驱动
static struct spi_driver spidev_spi = {
.driver= {
.name= "spidev",
.owner= THIS_MODULE,
},
.probe= spidev_probe,
.remove= __devexit_p(spidev_remove),
/*NOTE: suspend/resume methods are notnecessary here.
* We don't do anything except pass the requeststo/from
* the underlying controller. The refrigerator handles
* most issues; the controller driver handlesthe rest.
*/
};
//分析一下SPI写的驱动
首先调用
static ssize_t
spidev_write(struct file *filp, const char__user *buf,
size_tcount, loff_t *f_pos)
{
structspidev_data *spidev;
ssize_t status = 0;
unsignedlong missing;
/*chipselect only toggles at start or end of operation */
if(count > bufsiz)
return-EMSGSIZE;
spidev= filp->private_data;
mutex_lock(&spidev->buf_lock);
missing= copy_from_user(spidev->buffer, buf, count);//把数据从用户空间拷到内核空间
if(missing == 0) {
status= spidev_sync_write(spidev, count);
}else
status = -EFAULT;
mutex_unlock(&spidev->buf_lock);
returnstatus;
}
接着调用
static inline ssize_t
spidev_sync_write(struct spidev_data*spidev, size_t len)
{
structspi_transfer t = {
.tx_buf = spidev->buffer,
.len = len,
};
structspi_message m;
spi_message_init(&m);//初始化m为0,初始化m->transfers链表
spi_message_add_tail(&t,&m);// 将t->transfer_list加入m->transfers
returnspidev_sync(spidev, &m);
}
继续调用
static ssize_t
spidev_sync(struct spidev_data *spidev,struct spi_message *message)
{
DECLARE_COMPLETION_ONSTACK(done);//声明并初始化struct completion done
intstatus;
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);
spin_unlock_irq(&spidev->spi_lock);
if(status == 0) {
wait_for_completion(&done);
status= message->status;
if(status == 0)
status= message->actual_length;
}
returnstatus;
}
接着分析
int spi_async(struct spi_device *spi,struct spi_message *message)
{
structspi_master *master = spi->master;
/*Half-duplex links include original MicroWire, and ones with
* only one data pin like SPI_3WIRE (switchesdirection) or where
* either MOSI or MISO is missing. They can also be caused by
* software limitations.
*/
if((master->flags & SPI_MASTER_HALF_DUPLEX)
||(spi->mode & SPI_3WIRE)) {
structspi_transfer *xfer;
unsignedflags = master->flags;
list_for_each_entry(xfer,&message->transfers, transfer_list) {
if(xfer->rx_buf && xfer->tx_buf)
return-EINVAL;
if((flags & SPI_MASTER_NO_TX) && xfer->tx_buf)
return-EINVAL;
if((flags & SPI_MASTER_NO_RX) && xfer->rx_buf)
return-EINVAL;
}
}
message->spi= spi;
message->status= -EINPROGRESS;
returnmaster->transfer(spi, message);
}
调用master->transfer(spi,message),由前面分析,master->transfer = omap2_mcspi_transfer;
static int omap2_mcspi_transfer(structspi_device *spi, struct spi_message *m)
{
structomap2_mcspi *mcspi;
unsignedlong flags;
structspi_transfer *t;
m->actual_length= 0;
m->status= 0;
/*reject invalid messages and transfers */
if(list_empty(&m->transfers) || !m->complete)
return-EINVAL;
list_for_each_entry(t,&m->transfers, transfer_list) {
constvoid *tx_buf = t->tx_buf;
void *rx_buf = t->rx_buf;
unsigned len = t->len;
if(t->speed_hz > OMAP2_MCSPI_MAX_FREQ
||(len && !(rx_buf || tx_buf))
||(t->bits_per_word &&
( t->bits_per_word < 4
||t->bits_per_word > 32))) {
dev_dbg(&spi->dev,"transfer: %d Hz, %d %s%s, %d bpw\n",
t->speed_hz,
len,
tx_buf? "tx" : "",
rx_buf? "rx" : "",
t->bits_per_word);
return-EINVAL;
}
if(t->speed_hz && t->speed_hz <OMAP2_MCSPI_MAX_FREQ/(1<<16)) {
dev_dbg(&spi->dev,"%d Hz max exceeds %d\n",
t->speed_hz,
OMAP2_MCSPI_MAX_FREQ/(1<<16));
return-EINVAL;
}
if(m->is_dma_mapped || len < DMA_MIN_BYTES)
continue;
/*Do DMA mapping "early" for better error reporting and
* dcache use. Note that if dma_unmap_single() ever starts
* to do real work on ARM, we'd need to cleanup mappings
* for previous transfers on *ALL* exits ofthis loop...
*/
if(tx_buf != NULL) {
//dma内存映射,用于DMA传输
t->tx_dma= dma_map_single(&spi->dev, (void *) tx_buf,
len,DMA_TO_DEVICE);
if(dma_mapping_error(&spi->dev, t->tx_dma)) {
dev_dbg(&spi->dev,"dma %cX %d bytes error\n",
'T',len);
return-EINVAL;
}
}
if(rx_buf != NULL) {
t->rx_dma= dma_map_single(&spi->dev, rx_buf, t->len,
DMA_FROM_DEVICE);
if(dma_mapping_error(&spi->dev, t->rx_dma)) {
dev_dbg(&spi->dev,"dma %cX %d bytes error\n",
'R',len);
if(tx_buf != NULL)
dma_unmap_single(NULL,t->tx_dma,
len,DMA_TO_DEVICE);
return-EINVAL;
}
}
}
mcspi= spi_master_get_devdata(spi->master);
spin_lock_irqsave(&mcspi->lock,flags);
list_add_tail(&m->queue,&mcspi->msg_queue);//加入mcspi->msg_queue链表
queue_work(omap2_mcspi_wq,&mcspi->work);//调度工作队列
spin_unlock_irqrestore(&mcspi->lock,flags);
return0;
}
由前面分析,INIT_WORK(&mcspi->work,omap2_mcspi_work),将执行omap2_mcspi_work
static void omap2_mcspi_work(structwork_struct *work)
{
structomap2_mcspi *mcspi;
mcspi= container_of(work, struct omap2_mcspi, work);
spin_lock_irq(&mcspi->lock);
if(omap2_mcspi_enable_clocks(mcspi))
gotoout;
/*We only enable one channel at a time -- the one whose message is
* at the head of the queue -- although thiscontroller would gladly
* arbitrate among multiple channels. This corresponds to "single
* channel" master mode. As a side effect, we need to manage the
* chipselect with the FORCE bit ... CS !=channel enable.
*/
while(!list_empty(&mcspi->msg_queue)) {// mcspi->msg_queue显然不为空
structspi_message *m;
structspi_device *spi;
structspi_transfer *t = NULL;
int cs_active = 0;
structomap2_mcspi_cs *cs;
int par_override = 0;
int status = 0;
u32 chconf;
m= container_of(mcspi->msg_queue.next, struct spi_message,
queue);
list_del_init(&m->queue);
spin_unlock_irq(&mcspi->lock);
spi= m->spi;
cs= spi->controller_state;
omap2_mcspi_set_enable(spi,1);
list_for_each_entry(t,&m->transfers, transfer_list) {
if(t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
status= -EINVAL;
break;
}
if(par_override || t->speed_hz || t->bits_per_word) {
par_override= 1;
status= omap2_mcspi_setup_transfer(spi, t);
if(status < 0)
break;
if(!t->speed_hz && !t->bits_per_word)
par_override= 0;
}
if(!cs_active) {
omap2_mcspi_force_cs(spi,1);
cs_active= 1;
}
chconf= mcspi_cached_chconf0(spi);
chconf&= ~OMAP2_MCSPI_CHCONF_TRM_MASK;
if(t->tx_buf == NULL)
chconf|= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY;
elseif (t->rx_buf == NULL)
chconf|= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY;
mcspi_write_chconf0(spi,chconf);
if(t->len) {
unsigned count;
/*RX_ONLY mode needs dummy data in TX reg */
if(t->tx_buf == NULL)
__raw_writel(0,cs->base
+OMAP2_MCSPI_TX0);
if(m->is_dma_mapped || t->len >= DMA_MIN_BYTES)
count= omap2_mcspi_txrx_dma(spi, t);
else
count= omap2_mcspi_txrx_pio(spi, t);
m->actual_length+= count;
if(count != t->len) {
status= -EIO;
break;
}
}
if(t->delay_usecs)
udelay(t->delay_usecs);
/*ignore the "leave it on after last xfer" hint */
if(t->cs_change) {
omap2_mcspi_force_cs(spi,0);
cs_active= 0;
}
}
/*Restore defaults if they were overriden */
if(par_override) {
par_override= 0;
status= omap2_mcspi_setup_transfer(spi, NULL);
}
if(cs_active)
omap2_mcspi_force_cs(spi,0);
omap2_mcspi_set_enable(spi,0);
m->status= status;
m->complete(m->context);
spin_lock_irq(&mcspi->lock);
}
omap2_mcspi_disable_clocks(mcspi);
out:
spin_unlock_irq(&mcspi->lock);
}
省略分析中间设置寄存器的过程,分析关键的函数:if (m->is_dma_mapped || t->len >= DMA_MIN_BYTES)
count= omap2_mcspi_txrx_dma(spi, t);
else
count= omap2_mcspi_txrx_pio(spi, t);
omap2_mcspi_txrx_pio(spi, t)函数很简单,只分析count =omap2_mcspi_txrx_dma(spi, t)
起定义如下:
static unsigned
omap2_mcspi_txrx_dma(struct spi_device*spi, struct spi_transfer *xfer)
{
structomap2_mcspi *mcspi;
structomap2_mcspi_cs *cs =spi->controller_state;
structomap2_mcspi_dma *mcspi_dma;
unsignedint count, c;
unsignedlong base, tx_reg, rx_reg;
int word_len, data_type,element_count;
u8 * rx;
constu8 * tx;
mcspi= spi_master_get_devdata(spi->master);
mcspi_dma= &mcspi->dma_channels[spi->chip_select];
count= xfer->len;
c= count;
word_len= cs->word_len;
base= cs->phys;
tx_reg= base + OMAP2_MCSPI_TX0;
rx_reg= base + OMAP2_MCSPI_RX0;
rx= xfer->rx_buf;
tx= xfer->tx_buf;
if(word_len <= 8) {
data_type= OMAP_DMA_DATA_TYPE_S8;
element_count= count;
}else if (word_len <= 16) {
data_type= OMAP_DMA_DATA_TYPE_S16;
element_count= count >> 1;
}else /* word_len <= 32 */ {
data_type= OMAP_DMA_DATA_TYPE_S32;
element_count= count >> 2;
}
if(tx != NULL) {
//设置DMA传输参数,具体可以参照芯片资料的sDMA模块
omap_set_dma_transfer_params(mcspi_dma->dma_tx_channel,
data_type,element_count, 1,
OMAP_DMA_SYNC_ELEMENT,
mcspi_dma->dma_tx_sync_dev,0);
omap_set_dma_dest_params(mcspi_dma->dma_tx_channel,0,
OMAP_DMA_AMODE_CONSTANT,
tx_reg,0, 0);
omap_set_dma_src_params(mcspi_dma->dma_tx_channel,0,
OMAP_DMA_AMODE_POST_INC,
xfer->tx_dma,0, 0);
}
if(rx != NULL) {
omap_set_dma_transfer_params(mcspi_dma->dma_rx_channel,
data_type,element_count - 1, 1,
OMAP_DMA_SYNC_ELEMENT,
mcspi_dma->dma_rx_sync_dev,1);
omap_set_dma_src_params(mcspi_dma->dma_rx_channel,0,
OMAP_DMA_AMODE_CONSTANT,
rx_reg,0, 0);
omap_set_dma_dest_params(mcspi_dma->dma_rx_channel,0,
OMAP_DMA_AMODE_POST_INC,
xfer->rx_dma,0, 0);
}
if(tx != NULL) {
omap_start_dma(mcspi_dma->dma_tx_channel);
omap2_mcspi_set_dma_req(spi,0, 1);
}
if(rx != NULL) {
omap_start_dma(mcspi_dma->dma_rx_channel);
omap2_mcspi_set_dma_req(spi,1, 1);
}
if(tx != NULL) {
wait_for_completion(&mcspi_dma->dma_tx_completion);//等待传输完成
dma_unmap_single(NULL,xfer->tx_dma, count, DMA_TO_DEVICE);//释放申请的DMA内存
}
if(rx != NULL) {
wait_for_completion(&mcspi_dma->dma_rx_completion);
dma_unmap_single(NULL,xfer->rx_dma, count, DMA_FROM_DEVICE);
omap2_mcspi_set_enable(spi,0);
if(likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
&OMAP2_MCSPI_CHSTAT_RXS)) {
u32w;
w= mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0);
if(word_len <= 8)
((u8*)xfer->rx_buf)[element_count - 1] = w;
elseif (word_len <= 16)
((u16*)xfer->rx_buf)[element_count - 1] = w;
else/* word_len <= 32 */
((u32*)xfer->rx_buf)[element_count - 1] = w;
}else {
dev_err(&spi->dev,"DMA RX last word empty");
count-= (word_len <= 8) ? 1 :
(word_len <= 16) ? 2 :
/* word_len <= 32 */ 4;
}
omap2_mcspi_set_enable(spi,1);
}
returncount;
}
主要分析,设置好DMA参数后,进行DMA传输:
if (tx != NULL) {
omap_start_dma(mcspi_dma->dma_tx_channel);//使能对应chanel上的中断
omap2_mcspi_set_dma_req(spi,0, 1);
}
if (tx != NULL) {
wait_for_completion(&mcspi_dma->dma_tx_completion);//等待传输完成
dma_unmap_single(NULL,xfer->tx_dma, count, DMA_TO_DEVICE);//释放申请的DMA内存
wait_for_completion(&mcspi_dma->dma_tx_completion);//等待传输完成,此时进程重新调度,进入等待队列。下面中断处理函数唤醒等待队列,此时传输已经完成
static void omap2_mcspi_dma_tx_callback(intlch, u16 ch_status, void *data)
{
structspi_device *spi = data;
structomap2_mcspi *mcspi;
structomap2_mcspi_dma *mcspi_dma;
mcspi= spi_master_get_devdata(spi->master);
mcspi_dma= &(mcspi->dma_channels[spi->chip_select]);
complete(&mcspi_dma->dma_tx_completion);
/*We must disable the DMA TX request */
omap2_mcspi_set_dma_req(spi,0, 0);
}
简单测试程序:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
int main(int argc ,char **argv)
{
int fbfd=0;
unsigned int i,num;
for(i=0;i<100;i++)
buf[i]=0x5a;
fbfd = open("/dev/spidev3.0",O_RDWR);
if(!fbfd)
{
printf("openfail\n");
exit(1);
}
while(1)
{
num=write(fbfd,buf,100);
printf("thenum is %d \n",num);
sleep(1);
}
close(fbfd);
return 0;
}