您的位置:首页 > 其它

lpc3250spi控制器驱动部分分析

2012-01-09 16:17 369 查看
本人是初学者,只为备忘。

SPI驱动分SPI控制器驱动和SPI设备驱动

SPI控制器驱动:以下是代码在arch-lpc32xx.c中。LPC3250有两个SSP控制器(可配置成两个SPI控制器)。它将两个控制器注册成平台设备,但两个控制器使用一个驱动.只有id不一样。

#if defined(CONFIG_SPI_LPC32XX)

#ifdefined(CONFIG_MACH_LPC32XX_SSP0_ENABLE)

static struct resource ssp0_resources[] = {

[0]= {

.start = SSP0_BASE,

.end = SSP0_BASE + SZ_4K - 1,

.flags = IORESOURCE_MEM,

},

[1]= {

.start = IRQ_SSP0,

.end = IRQ_SSP0,

.flags = IORESOURCE_IRQ,

},

};

static struct platform_device ssp0_device ={

.name = "spi_lpc32xx",

.id = 0,

.dev = {

.platform_data = &lpc32xx_spi1data,

},

.num_resources = ARRAY_SIZE(ssp0_resources),

.resource = ssp0_resources,

};

#endif

#ifdefined(CONFIG_MACH_LPC32XX_SSP1_ENABLE)

static struct resource ssp1_resources[] = {

[0]= {

.start = SSP1_BASE,

.end = SSP1_BASE + SZ_4K - 1,

.flags = IORESOURCE_MEM,

},

[1]= {

.start = IRQ_SSP1,

.end = IRQ_SSP1,

.flags = IORESOURCE_IRQ,

},

};

static struct platform_device ssp1_device ={

.name = "spi_lpc32xx",

.id = 1,

.dev = {

.platform_data = &lpc32xx_spi2data,

},

.num_resources = ARRAY_SIZE(ssp1_resources),

.resource = ssp1_resources,

};

#endif

#endif

通过以上代码,SPI控制器会在系统初始化的时候,通过platform_device_register将它注册进系统。

以上注册完了SPI控制器的device。下一步注册SPI控制器的driver

static struct platform_driverlpc32xx_spi_driver = {

.probe = lpc32xx_spi_probe,

.remove = __devexit_p(lpc32xx_spi_remove),

.driver = {

.name = "spi_lpc32xx",

.owner = THIS_MODULE,

},

};

在Linux 中,每一个类型的驱动都会有一个相应的结构体来描述,这里的spi_master就是

用来描述SPI 主机控制器驱动的,其主要成员是bus_num,cs,spi 模式和时钟设置用到的

函数,数据传输用到的函数等。

分配、注册和注销SPI 主机驱动结构体的API 由SPI 核心层提供:

struct spi_master * spi_alloc_master(structdevice *host, unsigned size);

int spi_register_master(struct spi_master*master);

void spi_unregister_master(structspi_master *master);

使用到platform 总线API 注册到platform

总线上的控制器设备和驱动,都是common 的部分。specific 的部分,会在platform driver

注册后,在probe 函数里面基于注册的common 部分的资源信息来具体实现。称之为

spi_master 的注册部分。

spi_master结构

1. structspi_master {

2. struct device dev;

3.

4. s16 bus_num;//总线编号,从零开始

5.

6. u16 num_chipselect;//支持的片选的数量。从设备的片选号不能大于这个数

7.

8. /* setup mode and clock, etc (spi driver may call many times) */

9. int (*setup)(struct spi_device *spi);//根据spi设备更新硬件配置。

10.

11. int (*transfer)(struct spi_device *spi,struct spi_message *mesg);//添加消息到队列的方法。这个函数不可睡眠。它的职责是安排发生的传送并且调用注册的回调函数complete()。

12.

13. /* called on release() to free memory provided by spi_master */

14. void (*cleanup)(struct spi_device*spi);//cleanup函数会在spidev_release函数中被调用,spidev_release被登记为spi dev的release函数。

15. };

spi的设备驱动

SPI 设备要挂载到SPI 总线上,这个过程是由SPIcore 提供的一些API 来完成的,

spi_register_driver(),spi_register_device()。

module init 函数中使用spi_register_driver 注册外设驱动,并注册char 设备,导出SPI 操作

API。

1. structspi_driver {

2. int (*probe)(struct spi_device *spi);//和spi匹配成功之后会调用这个方法。因此这个方法需要对设备和私有数据进行初始化。

3. int (*remove)(struct spi_device*spi);//解除spi_device和spi_driver的绑定,释放probe申请的资源。

4. void (*shutdown)(struct spi_device*spi);//关闭

5. int (*suspend)(struct spi_device *spi,pm_message_t mesg);//挂起

6. int (*resume)(struct spi_device*spi);//恢复

7. struct device_driver driver;

8. }

1. structspi_device {

2. struct device dev;

3. struct spi_master *master;//对应的控制器指针

4. u32 max_speed_hz;//spi通信时钟

5. u8 chip_select;//片选号,用来区分同一主控制器上的设备。

6. u8 mode;//各位的定义如下,主要是传输模式、片选极性。

7. #defineSPI_CPHA 0x01 /* clock phase */

8. #defineSPI_CPOL 0x02 /* clock polarity */

9. #defineSPI_MODE_0 (0|0) /* (originalMicroWire) */

10. #defineSPI_MODE_1 (0|SPI_CPHA)

11. #defineSPI_MODE_2 (SPI_CPOL|0)

12. #defineSPI_MODE_3 (SPI_CPOL|SPI_CPHA)

13. #defineSPI_CS_HIGH 0x04 /* chipselect activehigh? */片选电位为高

14. #defineSPI_LSB_FIRST 0x08 /* per-wordbits-on-wire */先输出低比特

15. #defineSPI_3WIRE 0x10 /* SI/SO signals shared*/输入输出共享接口,此时只能做半双工。

16. #defineSPI_LOOP 0x20 /* loopback mode */回写/回显模式

17. u8 bits_per_word;//每个字长的比特数。

18. int irq;//使用到的中断

19. void *controller_state;

20. void *controller_data;

21. char modalias[32];//名字。

22. };

23.

24. ~~~~~~~~~~~~~~~~~~~~~~~

25. 这个结构体描述设备的信息。

26. structspi_board_info {

27. char modalias[32];//设备名

28. const void *platform_data;//平台数据

29. void *controller_data;

30. int irq;//中断

31.

32. /* slower signaling on noisy or low voltage boards */

33. u32 max_speed_hz;//通信时钟

34.

35. u16 bus_num;//总线号

36. u16 chip_select;//片选号

37.

38. u8 mode;//参考spi_device中的成员

39. };

以下是部分spi-lpc32xx.c驱动分析

static int __init lpc32xx_spi_probe(structplatform_device *pdev)

{

structspi_master *master;

structlpc32xxspi *spidat;

structresource *res;

charclkname[16];

intret, irq, i;

/*Get required resources */

res= platform_get_resource(pdev, IORESOURCE_MEM, 0);

irq= platform_get_irq(pdev, 0);

//通过以上语句获得了相应的内存和中断资源。

if((!res) || (irq < 0) | (irq >= NR_IRQS))

{

return-EBUSY;

}

master= spi_alloc_master(&pdev->dev, sizeof(struct lpc32xxspi));

//第二个参数指明要为dev.driver_data分配的空间

if(!master)

{

return-ENODEV;

}

spidat= spi_master_get_devdata(master);

//将spi->master->dev->driver_data 的空间指针传递给spidata

platform_set_drvdata(pdev,master);

//将pdev->dev->driver_data赋为master

//pdev作为spi控制器的common部分,master作为spi控制器的specific部分。

/*Save ID for this device */

spidat->id= pdev->id;

//id指明了使用哪一个控制器

spidat->irq= irq;

spin_lock_init(&spidat->lock);

INIT_WORK(&spidat->work,lpc32xx_work);

//初始化工作队列

INIT_LIST_HEAD(&spidat->queue);

init_waitqueue_head(&spidat->waitq);

//初始化等待队列

spidat->workqueue= create_singlethread_workqueue(master->dev.parent->bus_id);

//创建一个单线程的工作队列

if(!spidat->workqueue)

{

ret= -ENOMEM;

gotoerrout;

}

/*Generate clock name and get clock */

snprintf(clkname,10, "spi%d_ck", spidat->id);

//根据控制器id产生一个时钟名,如spi0_ck

spidat->clk= clk_get(&pdev->dev, clkname);

if(IS_ERR(spidat->clk)) {

ret= -ENODEV;

gotoerrout_qdel;

}

clk_enable(spidat->clk);

/*Save IO resources */

spidat->membase= ioremap(res->start, res->end - res->start + 1);

if(!spidat->membase)

{

ret= -EBUSY;

gotoerrout2;

}

ret= request_irq(spidat->irq, lpc32xx_spi_irq,

IRQF_DISABLED,"spiirq", spidat);

if(ret)

{

ret= -EBUSY;

gotoerrout3;

}

//中断号spidat->irq,这个参数是由pdev传入,要执行的函数指针lpc32xx_spi_irq,

//IRQF_DISABLED,执行中断函数期间,irq被屏蔽

//最后是一个void*类型的指针。

disable_irq(spidat->irq);

master->bus_num= spidat->id;

master->setup= lpc32xx_spi_setup;

master->transfer= lpc32xx_spi_transfer;

/*Is a board specific configuration available? */

spidat->psspcfg= (struct lpc32xx_spi_cfg *) pdev->dev.platform_data;

//platform_data中的数据就是配置数据,但是配置数据放在两个地方,一个在//dev.platform_data中,另一个就是本文件中对struct lpc32xx_spi_cfg的显式初始化。//psspcfg->num_cs代表此时spi控制器中携带的从设备数。

if(spidat->psspcfg == NULL)

{

spidat->psspcfg= &lpc32xx_stdspi_cfg;

}

if(spidat->psspcfg->num_cs < 1)

{

spidat->psspcfg= &lpc32xx_stdspi_cfg;

}

master->num_chipselect= spidat->psspcfg->num_cs;

/*Initialize each chip select and set chip select low */

for(i = 0; i < spidat->psspcfg->num_cs; i++)

{

if(spidat->psspcfg->spi_cs_setup != NULL)

{

spidat->psspcfg->spi_cs_setup(i);

//通过追踪得知,它调用的是static void smartarm3250_spi_cs_setup(int cs)

//此函数仅是将ssel这个引脚置(GPIO_05)为高电平。

}

if(spidat->psspcfg->spi_cs_set != NULL)

{

spidat->psspcfg->spi_cs_set(i,0);

//通过追踪得知,它调用的是static int smartarm3250_spi_cs_set(int cs, int state)

//函数认为cs=1时,设备不存在。state的电平和ssel电平同步。

}

}

/*Initial setup of SPI */

lpc32xx_spi_prep(spidat);

/*Keep the SSP clock off until a transfer is performed to save power */

clk_disable(spidat->clk);

ret= spi_register_master(master);

if(ret)

{

gotoerrout4;

}

return0;

errout4:

free_irq(spidat->irq,pdev);

errout3:

iounmap(spidat->membase);

errout2:

clk_disable(spidat->clk);

clk_put(spidat->clk);

errout_qdel:

destroy_workqueue(spidat->workqueue);

errout:

platform_set_drvdata(pdev,NULL);

spi_master_put(master);

returnret;

}

static void lpc32xx_spi_prep(structlpc32xxspi *spidat)

{

u32tmp;

/*Clear and mask SSP interrupts */

__raw_writel((SSP_ICR_RORIC| SSP_ICR_RTIC), SSP_ICR(spidat->membase));

__raw_writel(0,SSP_IMSC(spidat->membase));

/*Setup default SPI mode */

__raw_writel((SSP_CR0_DSS(16)| SSP_CR0_FRF_SPI | SSP_CR0_CPOL(0) |

SSP_CR0_CPHA(0)| SSP_CR0_SCR(0)), SSP_CR0(spidat->membase));

__raw_writel(SSP_CR1_SSP_ENABLE,SSP_CR1(spidat->membase));

__raw_writel(SSP_CPSR_CPDVSR(2),SSP_CPSR(spidat->membase));

//默认的格式为16位格式,第一种SPI传输模式,主机模式,ssp使能,频率为pclk的2分//频

/*Flush FIFO */

while(__raw_readl(SSP_SR(spidat->membase)) & SSP_SR_RNE)

{

tmp= __raw_readl(SSP_DATA(spidat->membase));

}

//等待接收FIFO为空

/*Controller stays disabled until a transfer occurs */

}

static void lpc32xx_cs_set_state(structspi_device *spi, unsigned int cs,

unsignedint active, unsigned int delay_ns)

{

structlpc32xxspi *spidat = spi_master_get_devdata(spi->master);

intval = (spi->mode & SPI_CS_HIGH) ? active : !active;

if(spidat->psspcfg->spi_cs_set != NULL)

{

spidat->psspcfg->spi_cs_set(cs,val);

}

ndelay(delay_ns);

}

//如果选择了高电平有效,则active和state状态相同,如果没有选择则状态相反。

static int lpc32xx_spi_setup(structspi_device *spi)

{

structlpc32xxspi *spidat = spi_master_get_devdata(spi->master);

unsignedlong flags;

unsignedint bits = spi->bits_per_word;

u32tmp;

if(spi->chip_select > spi->master->num_chipselect)

{

dev_dbg(&spi->dev,

"setup:invalid chipselect %u (%u defined)\n",

spi->chip_select,spi->master->num_chipselect);

return-EINVAL;

}

if(bits == 0)

{

bits= 8;

}

if((bits < 4) || (bits > 16))

{

dev_dbg(&spi->dev,

"setup:invalid bits_per_word %u (8 to 16)\n", bits);

return-EINVAL;

}

if(spi->mode & ~MODEBITS)

{

dev_dbg(&spi->dev,"setup: unsupported mode bits %x\n",

spi->mode& ~MODEBITS);

return-EINVAL;

}

spin_lock_irqsave(&spidat->lock,flags);

clk_enable(spidat->clk);

/*Setup CR0 register */

tmp= SSP_CR0_FRF_SPI;

if(spi->mode & SPI_CPOL)

{

tmp|= SSP_CR0_CPOL(1);

}

if(spi->mode & SPI_CPHA)

{

tmp|= SSP_CR0_CPHA(1);

}

__raw_writel(tmp,SSP_CR0(spidat->membase));

//写入spi控制器的mode

lpc32xx_update_spi_dwidth(spidat,bits);

//写入每帧的位数

lpc32xx_update_spi_clock(spidat,spi->max_speed_hz);

//设置时钟频率

lpc32xx_cs_set_state(spi,spi->chip_select, 0, 0);

//spi->chip_select表示是选中哪一个从机。目前只支持spi->chip_select=0;

//第三个参数为与cs显示的电平关联,最后一个参数是延时。

clk_disable(spidat->clk);

spin_unlock_irqrestore(&spidat->lock,flags);

#if defined (SSP_DEBUG)

dev_dbg(&spi->dev,"SSP (%d) prog rate = %d / "

"actualrate = %d, bits = %d\n", spi->chip_select,

spi->max_speed_hz,spidat->current_speed_hz, spidat->current_bits_wd);

#endif

return0;

}

static irqreturn_t lpc32xx_spi_irq(int irq,void *dev_id)

{

structlpc32xxspi *spidat = dev_id;

/*Disable interrupts for now, do not clear the interrupt states */

__raw_writel(0,SSP_IMSC(spidat->membase));

//禁能中断

wake_up(&spidat->waitq);

//在中断中,唤醒等待队列。

returnIRQ_HANDLED;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: