-
一、SPI总线概述
-
SPI是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口,是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便。
-
SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要4根线,事实上3根也可以。也是所有基于SPI的设备共有的,它们是SDI(数据输入),SDO(数据输出),SCLK(时钟),CS(片选)。
-
MOSI(SDO):主器件数据输出,从器件数据输入。
-
MISO(SDI):主器件数据输入,从器件数据输出。
-
SCLK :时钟信号,由主器件产生。
-
CS:从器件使能信号,由主器件控制。
-
其中CS是控制芯片是否被选中的,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),对此芯片的操作才有效,这就允许在同一总线上连接多个SPI设备成为可能。需要注意的是,在具体的应用中,当一条SPI总线上连接有多个设备时,SPI本身的CS有可能被其他的GPIO脚代替,即每个设备的CS脚被连接到处理器端不同的GPIO,通过操作不同的GPIO口来控制具体的需要操作的SPI设备,减少各个SPI设备间的干扰。
-
SPI是串行通讯协议,也就是说数据是一位一位从MSB或者LSB开始传输的,这就是SCK时钟线存在的原因,由SCK提供时钟脉冲,MISO、MOSI则基于此脉冲完成数据传输。 SPI支持4-32bits的串行数据传输,支持MSB和LSB,每次数据传输时当从设备的大小端发生变化时需要重新设置SPI Master的大小端。
-
-
二、Linux SPI驱动总体架构
-
在2.6的linux内核中,SPI的驱动架构可以分为如下三个层次:SPI 核心层、SPI控制器驱动层和SPI设备驱动层。Linux 中SPI驱动代码位于drivers/spi目录。
-
1.SPI核心层
-
SPI核心层是Linux的SPI核心部分,提供了核心数据结构的定义、SPI控制器驱动和设备驱动的注册、注销管理等API。其为硬件平台无关层,向下屏蔽了物理总线控制器的差异,定义了统一的访问策略和接口;其向上提供了统一的接口,以便SPI设备驱动通过总线控制器进行数据收发。
-
Linux中,SPI核心层的代码位于driver/spi/spi.c。
-
-
2.SPI控制器驱动层
-
SPI控制器驱动层,每种处理器平台都有自己的控制器驱动,属于平台移植相关层。它的职责是为系统中每条SPI总线实现相应的读写方法。在物理上,每个SPI控制器可以连接若干个SPI从设备。
-
在系统开机时,SPI控制器驱动被首先装载。一个控制器驱动用于支持一条特定的SPI总线的读写。一个控制器驱动可以用数据结构struct spi_master来描述。
-
-
3.SPI设备驱动层
-
SPI设备驱动层为用户接口层,其为用户提供了通过SPI总线访问具体设备的接口。
-
SPI设备驱动层可以用两个模块来描述,struct spi_driver和struct spi_device。
-
Driver是为device服务的,spi_driver注册时会扫描SPI bus上的设备,进行驱动和设备的绑定,probe函数用于驱动和设备匹配时被调用。从上面的结构体注释中我们可以知道,SPI的通信是通过消息队列机制,而不是像I2C那样通过与从设备进行对话的方式。
-
-
三、spi子系统主要数据结构
-
1.在Linux中,使用spi_master结构来描述一个SPI主机控制器的驱动。
-
struct spi_master {
-
struct device dev;
-
s16 bus_num; //为该控制器对应的SPI总线号
-
u16 num_chipselect; //控制器支持的片选数量,即能支持多少个spi设备,从设备的片选号不能大于这个数量
-
u16 dma_alignment;
-
int (*setup)(struct
spi_device *spi);//函数是设置SPI总线的模式,时钟等的初始化函数,
针对设备设置SPI的工作时钟及数据传输模式等。在spi_add_device函数中调用
-
int (*transfer)(struct
spi_device *spi,struct spi_message *mesg);//实现SPI总线读写方法的函数。实现数据的双向传输,可能会睡眠
-
void (*cleanup)(struct
spi_device *spi); //注销时候调用
-
};
-
//分配,注册和注销的SPI主机的API由SPI核心提供:
-
struct spi_master *spi_alloc_master(struct device *host, unsigned
size);
-
int spi_register_master(struct spi_master *master);
-
void spi_unregister_master(struct spi_master *master);
-
-
2.在Linux中用spi_driver来描述一个SPI外设驱动。
-
struct spi_driver {
-
const struct spi_device_id *id_table;//匹配的设备表
-
int (*probe)(struct
spi_device *spi);
-
int (*remove)(struct
spi_device *spi);
-
void (*shutdown)(struct
spi_device *spi);
-
int (*suspend)(struct
spi_device *spi, pm_message_t mesg);
-
int (*resume)(struct
spi_device *spi);
-
struct device_driver driver;
-
};
-
-
3.Linux用spi_device来描述一个SPI外设设备。
-
struct spi_device {
-
struct device dev;
-
struct spi_master *master;//对应的控制器指针
-
u32 max_speed_hz;//spi通信的时钟
-
u8 chip_select; //片选,用于区分同一总线上的不同设备
-
u8 mode;
-
#define SPI_CPHA 0x01 /* clock phase */
-
#define SPI_CPOL 0x02 /* clock polarity */
-
#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 /* chipselect active high? */
-
#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
-
#define SPI_3WIRE 0x10 /* SI/SO signals shared */
-
#define SPI_LOOP 0x20 /* loopback mode */
-
#define SPI_NO_CS 0x40 /* 1 dev/bus, no
chipselect */
-
#define SPI_READY 0x80 /* slave pulls low to pause */
-
u8 bits_per_word;//每个字长的比特数
-
int irq;//使用的中断号
-
void *controller_state;
-
void *controller_data;
-
char modalias[SPI_NAME_SIZE];//名字
-
};
-
看这三个结构的关系,这里spi_device与spi_master是同一个父设备,这是在spi_new_device函数中设定的,一般这个设备是一个物理设备。
-
-
4.为了解决多个不同的SPI设备共享SPI控制器而带来的访问冲突,spi_bitbang使用内核提供的工作队列(workqueue)。 workqueue是Linux内核中定义的一种回调处理方式。采用这种方式需要传输数据时,不直接完成数据的传输,而是将要传输的工作分装成相应的消息 (spi_message),发送给对应的workqueue,由与workqueue关联的内核守护线程(daemon)负责具体的执行。由于
workqueue会将收到的消息按时间先后顺序排列,这样就是对设备的访问严格串行化,解决了冲突。
-
struct spi_bitbang {
-
struct workqueue_struct *workqueue;//工作队列头
-
struct work_struct work;//每一次传输都传递下来一个spi_message,都向工作队列头添加一个
-
workspinlock_t lock;
-
struct list_head queue;//挂接spi_message,如果上一次的spi_message还没有处理完,接下来的spi_message就挂接在queue上等待处理
-
u8 busy;//忙碌标志
-
u8 use_dma;
-
u8 flags;
-
struct spi_master *master;//指向spi控制器
-
int (*setup_transfer)(struct
spi_device *spi,struct spi_transfer *t);//设置传输模式
-
void (*chipselect)(struct
spi_device *spi, int is_on);//片选
-
#define BITBANG_CS_ACTIVE 1 /* normally nCS, active low */
-
#define BITBANG_CS_INACTIVE 0
-
int (*txrx_bufs)(struct
spi_device *spi, struct spi_transfer *t);//传输函数
-
u32 (*txrx_word[4])(struct
spi_device *spi,unsigned nsecs,u32 word, u8
bits);
-
};
-
-
5.下面来看看spi_message:
-
struct spi_message {
-
struct list_head transfers; //此次消息的传输队列,一个消息可以包含多个传输段
-
struct spi_device *spi; //传输的目的设备
-
unsigned is_dma_mapped:1; //如果为真,此次调用提供dma和cpu虚拟地址
-
void (*complete)(void *context); //异步调用完成后的回调函数
-
void *context; //回调函数的参数
-
unsigned actual_length; //此次传输的实际长度
-
int status; //执行的结果,成功被置0,否则是一个负的错误码
-
struct list_head queue;
-
void *state;
-
};
-
在有消息需要传递的时候,会将spi_transfer通过自己的transfer_list字段挂到spi_message的transfers链表头上。spi_message用来原子的执行spi_transfer表示的一串数组传输请求。这个传输队列是原子的,这意味着在这个消息完成之前不会有其 他消息占用总线。消息的执行总是按照FIFO的顺序。
-
-
6.下面看一看spi_transfer:
-
struct spi_transfer {
-
const void *tx_buf; //要写入设备的数据(必须是dma_safe),或者为NULL
-
void *rx_buf; //要读取的数据缓冲(必须是dma_safe),或者为NULL
-
unsigned len; //tx和rx的大小(字节数),这里不是指它的和,而是各自的长度,他们总是相等的
-
dma_addr_t tx_dma; //如果spi_message.is_dma_mapped是真,这个是tx的dma地址
-
dma_addr_t rx_dma; //如果spi_message.is_dma_mapped是真,这个是rx的dma地址
-
unsigned cs_change:1; //影响此次传输之后的片选,指示本次tranfer结束之后是否要重新片选并调用setup改变设置,这个标志可以较少系统开销u8
-
bits_per_word; //每个字长的比特数,如果是0,使用默认值
-
u16 delay_usecs; //此次传输结束和片选改变之间的延时,之后就会启动另一个传输或者结束整个消息
-
u32 speed_hz; //通信时钟。如果是0,使用默认值
-
struct list_head transfer_list; //用来连接的双向链表节点
-
};
-
-
7.spi控制器驱动的私有数据
-
struct davinci_spi {
-
struct spi_bitbang bitbang;
-
struct clk *clk;//时钟
-
u8 version;
-
resource_size_t pbase;//spi寄存器物理地址
-
void __iomem *base;//spi寄存器虚拟地址
-
u32 irq;//spi中断号
-
struct completion done;
-
-
const void *tx;//发送buffer
-
void *rx;//读buffer
-
#define SPI_TMP_BUFSZ (SMP_CACHE_BYTES + 1)
-
u8 rx_tmp_buf[SPI_TMP_BUFSZ];
-
int rcount;//读数据个数
-
int wcount;//写数据个数
-
struct davinci_spi_dma dma;
-
struct davinci_spi_platform_data *pdata;
-
-
void (*get_rx)(u32
rx_data, struct davinci_spi *);//读数据函数
-
u32 (*get_tx)(struct
davinci_spi *);//发送数据函数
-
u8 bytes_per_word[SPI_MAX_CHIPSELECT];
-
u32 speed;
-
u32 cs_num;
-
bool in_use;
-
-
#ifdef CONFIG_CPU_FREQ
-
struct notifier_block freq_transition;
-
#endif
-
};
-
-
-
四、spi驱动初始化
-
1.spi总线注册
-
//spi总线结构
-
struct bus_type spi_bus_type = {
-
.name = "spi",
-
.dev_attrs = spi_dev_attrs,
-
.match = spi_match_device,
-
.uevent = spi_uevent,
-
.suspend = spi_suspend,
-
.resume = spi_resume,
-
};
-
-
//spi_master类,会在sysfs文件系统创建该类
-
static struct class spi_master_class = {
-
.name = "spi_master",
-
.owner = THIS_MODULE,
-
.dev_release = spi_master_release,
-
};
-
-
static int __init spi_init(void)
-
{
-
int status;
-
-
buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
-
if (!buf) {
-
status = -ENOMEM;
-
goto err0;
-
}
-
//分别对应sysfs文件系统中的sys/bus/下的spi目录和sys/class/下的spi_master目录。
-
status = bus_register(&spi_bus_type);//注册spi总线
-
if (status < 0)
-
goto err1;
-
-
status = class_register(&spi_master_class);//创建spi_master的类
-
if (status < 0)
-
goto err2;
-
return 0;
-
-
err2:
-
bus_unregister(&spi_bus_type);
-
err1:
-
kfree(buf);
-
buf = NULL;
-
err0:
-
return status;
-
}
-
-
2.spi控制器管脚的复用配置
-
const short da850_spi0_pins[] __initdata = {
-
DA850_SPI0_CLK,DA850_SPI0_ENA,DA850_SPI0_SOMI,DA850_SPI0_SIMO,
-
-1
-
};
-
-
static __init void da850_evm_init(void)
-
{
-
//......
-
ret = davinci_cfg_reg_list(da850_spi0_pins);//复用管脚配置
-
if (ret)
-
pr_warning("da850_evm_init: spi0 mux setup failed: %d\n", ret);
-
//......
-
}
-
-
3.注册spi的platform device设备
-
static struct spi_board_info da850evm_spi_info[] = {
-
{
-
.modalias = "ads7846",//设备名,与spi_driver中定义的名字相同
-
.bus_num = 0,
-
.chip_select = 0,
-
.max_speed_hz = 1500000,
-
.mode = SPI_MODE_0,
-
.irq = 128,//GPIO1[11]
-
.platform_data = &ads7846_config,
-
},
-
};
-
-
static void __init da850evm_init_spi0(struct spi_board_info *info, unsigned len)
-
{
-
int ret;
-
-
//注册spi设备的信息到board_list链表中,待spi master注册好后扫描这些板级设备再进行注册
-
ret = spi_register_board_info(info, len);
-
if (ret)
-
pr_warning("failed to register board info : %d\n", ret);
-
-
//注册spi0的platform device
-
ret = da8xx_register_spi(0, &da850evm_spi0_pdata);
-
if (ret)
-
pr_warning("failed to register spi 1 device : %d\n", ret);
-
-
if (!(system_rev & 0x100))
-
register_mtd_user(&spi_notifier);
-
}
-
-
int __init spi_register_board_info(struct spi_board_info const *info, unsigned
n)
-
{
-
struct boardinfo *bi;
-
int i;
-
-
bi = kzalloc(n * sizeof(*bi), GFP_KERNEL);//分配一个boardinfo结构
-
if (!bi)
-
return -ENOMEM;
-
-
for (i = 0; i < n; i++, bi++, info++) {
-
struct spi_master *master;
-
memcpy(&bi->board_info, info, sizeof(*info));//填充boardinfo结构
-
mutex_lock(&board_lock);
-
list_add_tail(&bi->list, &board_list);//挂载到全局链表board_list中
-
-
//遍历spi_master_list链表,查看是否有spi设备在此spi控制器上,现在因为spi master尚未注册,不会注册板级设备。
-
//待spi控制器注册后,还会扫描链表进行匹配,来注册板级设备
-
list_for_each_entry(master, &spi_master_list, list)
-
spi_match_master_to_boardinfo(master, &bi->board_info);
-
mutex_unlock(&board_lock);
-
}
-
return 0;
-
}
-
-
static struct resource da8xx_spi0_resources[] = {
-
[0] = {
-
.start = 0x01c41000,
-
.end = 0x01c41fff,
-
.flags = IORESOURCE_MEM,
-
},
-
[1] = {
-
.start = IRQ_DA8XX_SPINT0,
-
.end = IRQ_DA8XX_SPINT0,
-
.flags = IORESOURCE_IRQ,
-
},
-
[2] = {
-
.start = EDMA_CTLR_CHAN(0, 14),
-
.end = EDMA_CTLR_CHAN(0, 14),
-
.flags = IORESOURCE_DMA,
-
},
-
[3] = {
-
.start = EDMA_CTLR_CHAN(0, 15),
-
.end = EDMA_CTLR_CHAN(0, 15),
-
.flags = IORESOURCE_DMA,
-
},
-
[4] = {
-
.start = 0,
-
.end = 0,
-
.flags = IORESOURCE_DMA,
-
},
-
};
-
-
static struct platform_device da8xx_spi_device[] = {
-
[0] = {
-
.name = "spi_davinci",
-
.id = 0,
-
.num_resources = ARRAY_SIZE(da8xx_spi0_resources),
-
.resource = da8xx_spi0_resources,
-
},
-
[1] = {
-
.name = "spi_davinci",
-
.id = 1,
-
.num_resources = ARRAY_SIZE(da8xx_spi1_resources),
-
.resource = da8xx_spi1_resources,
-
},
-
};
-
-
int __init da8xx_register_spi(int instance,struct
davinci_spi_platform_data *pdata)
-
{
-
struct platform_device *pdev;
-
-
if (instance == 0)
-
pdev = &da8xx_spi_device[0];
-
else if (instance == 1)
-
pdev = &da8xx_spi_device[1];
-
else
-
return -EINVAL;
-
-
pdev->dev.platform_data = pdata;
-
-
return platform_device_register(pdev);//注册platform
device设备
-
}
-
-
4.注册platform driver,会注册spi控制器spi_master
-
-
static struct platform_driver davinci_spi_driver = {
-
.driver = {
-
.name = "spi_davinci",
-
.owner = THIS_MODULE,
-
},
-
.remove = __exit_p(davinci_spi_remove),
-
.suspend = davinci_spi_suspend,
-
.resume = davinci_spi_resume,
-
};
-
-
static int __init davinci_spi_init(void)
-
{
-
return platform_driver_probe(&davinci_spi_driver, davinci_spi_probe);
-
}
-
-
//platform driver与platform device匹配成功后,调用davinci_spi_probe
-
static int davinci_spi_probe(struct platform_device *pdev)
-
{
-
struct spi_master *master;
-
struct davinci_spi *dspi;
-
struct davinci_spi_platform_data *pdata;
-
struct resource *r, *mem;
-
resource_size_t dma_rx_chan = SPI_NO_RESOURCE;
-
resource_size_t dma_tx_chan = SPI_NO_RESOURCE;
-
resource_size_t dma_eventq = SPI_NO_RESOURCE;
-
int i = 0, ret = 0;
-
u32 spipc0;
-
-
pdata = pdev->dev.platform_data;
-
if (pdata == NULL) {
-
ret = -ENODEV;
-
goto err;
-
}
-
-
//分配一个spi控制器的spi_master结构,分配struct spi_master + struct
davinci_spi大小的数据,把davinci_spi设为spi_master的私有数据
-
master = spi_alloc_master(&pdev->dev, sizeof(struct
davinci_spi));
-
if (master == NULL) {
-
ret = -ENOMEM;
-
goto err;
-
}
-
-
//把spi_master设置为platform_device结构中dev的私有数据
-
dev_set_drvdata(&pdev->dev, master);
-
-
//从master中获得私有数据davinci_spi
-
dspi = spi_master_get_devdata(master);
-
if (dspi == NULL) {
-
ret = -ENOENT;
-
goto free_master;
-
}
-
-
//从platform device的私有数据中获得spi控制器的寄存器物理地址资源
-
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
if (r == NULL) {
-
ret = -ENOENT;
-
goto free_master;
-
}
-
dspi->pbase = r->start;//spi寄存器起始物理地址
-
dspi->pdata = pdata;
-
-
//为spi控制器的物理地址申请内存空间
-
mem = request_mem_region(r->start, resource_size(r), pdev->name);
-
if (mem == NULL) {
-
ret = -EBUSY;
-
goto free_master;
-
}
-
-
//将spi控制器的寄存器物理地址映射到内核空间
-
dspi->base = ioremap(r->start, resource_size(r));
-
if (dspi->base == NULL) {
-
ret = -ENOMEM;
-
goto release_region;
-
}
-
-
//从platform device的私有数据中获得spi控制器的中断号
-
dspi->irq = platform_get_irq(pdev, 0);
-
if (dspi->irq <= 0) {
-
ret = -EINVAL;
-
goto unmap_io;
-
}
-
-
//申请中断号
-
ret = request_irq(dspi->irq, davinci_spi_irq, 0, dev_name(&pdev->dev),dspi);
-
if (ret)
-
goto unmap_io;
-
-
//spi master计数加1,且
-
dspi->bitbang.master = spi_master_get(master);
-
if (dspi->bitbang.master == NULL) {
-
ret = -ENODEV;
-
goto irq_free;
-
}
-
-
//获得spi时钟
-
dspi->clk = clk_get(&pdev->dev, NULL);
-
if (IS_ERR(dspi->clk)) {
-
ret = -ENODEV;
-
goto put_master;
-
}
-
clk_enable(dspi->clk);//时钟使能
-
-
master->bus_num = pdev->id;//spi控制器所在的spi总线号
-
master->num_chipselect = pdata->num_chipselect;//spi控制器的片选数量
-
master->setup = davinci_spi_setup;//spi控制器的setup函数
-
-
//spi_bitbang专门负责数据的传输*
-
dspi->bitbang.chipselect = davinci_spi_chipselect;//片选选择函数
-
dspi->bitbang.setup_transfer = davinci_spi_setup_transfer;//建立传输的函数
-
-
dspi->version = pdata->version;//版本号SPI_VERSION_2
-
//设置一些标志位
-
dspi->bitbang.flags = SPI_NO_CS | SPI_LSB_FIRST | SPI_LOOP;
-
if (dspi->version == SPI_VERSION_2)
-
dspi->bitbang.flags |= SPI_READY;
-
-
//DMA操作的一些设置
-
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
-
if (r)
-
dma_rx_chan = r->start;
-
r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
-
if (r)
-
dma_tx_chan = r->start;
-
r = platform_get_resource(pdev, IORESOURCE_DMA, 2);
-
if (r)
-
dma_eventq = r->start;
-
-
//数据传输函数
-
dspi->bitbang.txrx_bufs = davinci_spi_bufs;
-
//没有使用DMA
-
if (dma_rx_chan != SPI_NO_RESOURCE &&dma_tx_chan != SPI_NO_RESOURCE &&dma_eventq != SPI_NO_RESOURCE) {
-
dspi->dma.rx_channel = dma_rx_chan;
-
dspi->dma.tx_channel = dma_tx_chan;
-
dspi->dma.eventq = dma_eventq;
-
-
ret = davinci_spi_request_dma(dspi);
-
if (ret)
-
goto free_clk;
-
-
dev_info(&pdev->dev, "DMA:
supported\n");
-
dev_info(&pdev->dev, "DMA:
RX channel: %d, TX channel: %d, event queue: %d\n", dma_rx_chan, dma_tx_chan,dma_eventq);
-
}
-
-
//设置spi的读写函数
-
dspi->get_rx = davinci_spi_rx_buf_u8;
-
dspi->get_tx = davinci_spi_tx_buf_u8;
-
-
init_completion(&dspi->done);
-
ret = davinci_spi_cpufreq_register(dspi);//cpu变频相关操作
-
if (ret) {
-
pr_info("davinci SPI contorller driver failed to register cpufreq\n");
-
goto free_dma;
-
}
-
-
iowrite32(0, dspi->base + SPIGCR0);//reset
spi控制器
-
udelay(100);
-
iowrite32(1, dspi->base + SPIGCR0);//跳出spi控制器reset状态
-
-
//初始化设置寄存器,包括对SPIMOSI,SPIMISO,SPICLK引脚的设置
-
spipc0 = SPIPC0_DIFUN_MASK | SPIPC0_DOFUN_MASK | SPIPC0_CLKFUN_MASK;
-
iowrite32(spipc0, dspi->base + SPIPC0);
-
-
//GPIO作为片选管脚的初始化
-
if (pdata->chip_sel) {
-
for (i = 0; i < pdata->num_chipselect; i++) {
-
if (pdata->chip_sel[i] != SPI_INTERN_CS)
-
gpio_direction_output(pdata->chip_sel[i], 1);
-
}
-
}
-
-
if (pdata->intr_line)
-
iowrite32(SPI_INTLVL_1, dspi->base + SPILVL);//设置spi中断的等级
-
else
-
iowrite32(SPI_INTLVL_0, dspi->base + SPILVL);
-
-
iowrite32(CS_DEFAULT, dspi->base + SPIDEF);//设置片选寄存器,写入默认值0xFF
-
-
/* master mode default */
-
set_io_bits(dspi->base + SPIGCR1, SPIGCR1_CLKMOD_MASK);//设置为master模式
-
set_io_bits(dspi->base + SPIGCR1, SPIGCR1_MASTER_MASK);//设置为master模式
-
set_io_bits(dspi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK);//设置为power-down模式
-
-
//设置bitbang传输
-
ret = spi_bitbang_start(&dspi->bitbang);
-
if (ret)
-
goto free_dma;
-
-
dev_info(&pdev->dev, "Controller
at 0x%p\n", dspi->base);
-
-
return ret;
-
-
free_dma:
-
edma_free_channel(dspi->dma.tx_channel);
-
edma_free_channel(dspi->dma.rx_channel);
-
edma_free_slot(dspi->dma.dummy_param_slot);
-
free_clk:
-
clk_disable(dspi->clk);
-
clk_put(dspi->clk);
-
put_master:
-
spi_master_put(master);
-
irq_free:
-
free_irq(dspi->irq, dspi);
-
unmap_io:
-
iounmap(dspi->base);
-
release_region:
-
release_mem_region(dspi->pbase, resource_size(r));
-
free_master:
-
kfree(master);
-
err:
-
return ret;
-
}
-
-
int spi_bitbang_start(struct spi_bitbang *bitbang)
-
{
-
int status;
-
-
if (!bitbang->master || !bitbang->chipselect)
-
return -EINVAL;
-
-
/*初始化一个工作队列,后面再create_singlethread_workqueue,等到有数据要传输的时候,在spi_bitbang_transfer函数中通过调用queue_work(bitbang->workqueue, &bitbang->work),把work扔进workqueue中调度运行,这是内核的一贯做法*/
-
//动态创建一个work_struct结构,它的处理函数是bitbang_work
-
INIT_WORK(&bitbang->work, bitbang_work);
-
spin_lock_init(&bitbang->lock);//初始化自旋锁和链表头
-
INIT_LIST_HEAD(&bitbang->queue);//初始化链表头
-
-
if (!bitbang->master->mode_bits)
-
bitbang->master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;
-
-
if (!bitbang->master->transfer)
-
bitbang->master->transfer = spi_bitbang_transfer;//spi数据的传输就是通过调用这个方法来实现的
-
-
//数据传输过程:spi_bitbang_transfer-->......-->davinci_spi_bufs
-
if (!bitbang->txrx_bufs) {//前边已经赋值davinci_spi_bufs
-
bitbang->use_dma = 0;
-
bitbang->txrx_bufs = spi_bitbang_bufs;
-
if (!bitbang->master->setup) {
-
if (!bitbang->setup_transfer)
-
bitbang->setup_transfer =
-
spi_bitbang_setup_transfer;
-
bitbang->master->setup = spi_bitbang_setup;
-
bitbang->master->cleanup = spi_bitbang_cleanup;
-
}
-
} else if (!bitbang->master->setup)
-
return -EINVAL;
-
-
bitbang->busy = 0;
-
//调用create_singlethread_workqueue创建单个工作线程
-
bitbang->workqueue = create_singlethread_workqueue(dev_name(bitbang->master->dev.parent));
-
if (bitbang->workqueue == NULL) {
-
status = -EBUSY;
-
goto err1;
-
}
-
-
//注册spi主机控制器
-
status = spi_register_master(bitbang->master);
-
if (status < 0)
-
goto err2;
-
-
return status;
-
-
err2:
-
destroy_workqueue(bitbang->workqueue);
-
err1:
-
return status;
-
}
-
-
int spi_register_master(struct spi_master *master)
-
{
-
static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
-
struct device *dev = master->dev.parent;
-
struct boardinfo *bi;
-
int status = -ENODEV;
-
int dynamic = 0;
-
-
if (!dev)
-
return -ENODEV;
-
-
//spi控制器的片选数量不能为0
-
if (master->num_chipselect == 0)
-
return -EINVAL;
-
-
if (master->bus_num < 0) {//spi控制器总线号不能为0
-
master->bus_num = atomic_dec_return(&dyn_bus_id);
-
dynamic = 1;
-
}
-
-
spin_lock_init(&master->bus_lock_spinlock);
-
mutex_init(&master->bus_lock_mutex);
-
master->bus_lock_flag = 0;
-
-
//设置spi控制器的名称,即在sysfs文件系统中显示的名称
-
dev_set_name(&master->dev, "spi%u", master->bus_num);
-
-
//设备添加到内核,会在sysfs文件系统下创建属性文件,这也是sys/class/spi_master下产生spi0,spi1的原因
-
status = device_add(&master->dev);
-
if (status < 0)
-
goto done;
-
dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),dynamic ? "
(dynamic)" : "");
-
-
mutex_lock(&board_lock);
-
list_add_tail(&master->list, &spi_master_list);//将spi控制器的master挂载到全局链表spi_master_list
-
-
/*在spi controller的driver注册的时候不但注册这个主机控制器的驱动,还要遍历这个主机控制器的总线上的spi_device,
-
将总线上的 spi_device全部注册进内核。当注册进内核并且spi_driver已经注册的时候,如果总线match成功,
-
则会调用spi_driver的 probe函数,这个将在后边进行分析。
-
*/
-
list_for_each_entry(bi, &board_list, list)//遍历每个boardinfo管理的spi_board_info,如果设备的总线号与控制器的总线好相等,则创建新设备
-
spi_match_master_to_boardinfo(master, &bi->board_info);
-
mutex_unlock(&board_lock);
-
-
status = 0;
-
/* Register devices from the device tree */
-
of_register_spi_devices(master);
-
done:
-
return status;
-
}
-
-
static void spi_match_master_to_boardinfo(struct spi_master *master,struct
spi_board_info *bi)
-
{
-
struct spi_device *dev;
-
-
if (master->bus_num != bi->bus_num)//要在同一个spi总线上
-
return;
-
-
//向spi控制器添加spi device
-
dev = spi_new_device(master, bi);
-
if (!dev)
-
dev_err(master->dev.parent, "can't
create new device for %s\n",bi->modalias);
-
}
-
-
struct spi_device *spi_new_device(struct spi_master *master,struct
spi_board_info *chip)
-
{
-
struct spi_device *proxy;
-
int status;
-
-
//分配spi_device,并初始化spi->dev的一些字段
-
proxy = spi_alloc_device(master);
-
if (!proxy)
-
return NULL;
-
-
WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
-
//初始化spi_device的各个字段
-
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));//spi设备的名字
-
proxy->dev.platform_data = (void *) chip->platform_data;
-
proxy->controller_data = chip->controller_data;
-
proxy->controller_state = NULL;
-
-
//添加spi设备
-
status = spi_add_device(proxy);
-
if (status < 0) {
-
spi_dev_put(proxy);
-
return NULL;
-
}
-
-
return proxy;
-
}
-
-
int spi_add_device(struct spi_device *spi)
-
{
-
static DEFINE_MUTEX(spi_add_lock);
-
struct device *dev = spi->master->dev.parent;
-
struct device *d;
-
int status;
-
-
//spi设备的片选号不能超过spi控制器的片选数量
-
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;
-
}
-
-
//这里设置是spi_device在Linux设备驱动模型中的name,也就是spi0.0
-
dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),spi->chip_select);
-
mutex_lock(&spi_add_lock);
-
-
//如果总线上挂的设备已经有这个名字,则设置状态忙碌,并退出
-
d = bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev));
-
if (d != NULL) {
-
dev_err(dev, "chipselect %d already in use\n",spi->chip_select);
-
put_device(d);
-
status = -EBUSY;
-
goto done;
-
}
-
-
//对spi_device进行设置
-
status = spi_setup(spi);
-
if (status < 0) {
-
dev_err(dev, "can't setup %s, status %d\n",dev_name(&spi->dev), status);
-
goto done;
-
}
-
-
//添加到内核,在sysfs文件系统下创建相应的属性文件(设备文件dev文件不会在这里创建,下边注册spi device驱动时会创建dev文件)
-
status = device_add(&spi->dev);
-
if (status < 0)
-
dev_err(dev, "can't add %s, status %d\n",dev_name(&spi->dev), status);
-
else
-
dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev));
-
-
done:
-
mutex_unlock(&spi_add_lock);
-
return status;
-
}
-
-
int spi_setup(struct spi_device *spi)
-
{
-
unsigned bad_bits;
-
int status;
-
-
bad_bits = spi->mode & ~spi->master->mode_bits;
-
if (bad_bits) {
-
dev_err(&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);//调用davinci_spi_setup
-
-
dev_dbg(&spi->dev, "setup
mode %d, %s%s%s%s"
-
"%u bits/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);
-
-
return status;
-
}
-
-
static int davinci_spi_setup(struct spi_device *spi)
-
{
-
int retval = 0;
-
struct davinci_spi *dspi;
-
struct davinci_spi_platform_data *pdata;
-
-
//从spi master的私有数据中取出davinci_spi
-
dspi = spi_master_get_devdata(spi->master);
-
pdata = dspi->pdata;
-
-
//设置了每字长的位数,发送速度
-
if (!spi->bits_per_word)
-
spi->bits_per_word = 8;
-
-
//设置spi片选管脚
-
if (!(spi->mode & SPI_NO_CS)) {
-
if ((pdata->chip_sel == NULL) ||(pdata->chip_sel[spi->chip_select] == SPI_INTERN_CS))
-
set_io_bits(dspi->base + SPIPC0, 1 << spi->chip_select);
-
}
-
-
//使能spi控制器
-
if (spi->mode & SPI_READY)
-
set_io_bits(dspi->base + SPIPC0, SPIPC0_SPIENA_MASK);
-
-
//是否使能内部loop-back测试模式
-
if (spi->mode & SPI_LOOP)
-
set_io_bits(dspi->base + SPIGCR1, SPIGCR1_LOOPBACK_MASK);
-
else
-
clear_io_bits(dspi->base + SPIGCR1, SPIGCR1_LOOPBACK_MASK);
-
-
return retval;
-
}
-
-
5.spi device的驱动注册,并创建设备节点
-
static struct spi_driver ads7846_driver = {
-
.driver = {
-
.name = "ads7846",
-
.bus = &spi_bus_type,
-
.owner = THIS_MODULE,
-
},
-
.probe = ads7846_probe,
-
.remove = __devexit_p(ads7846_remove),
-
.suspend = ads7846_suspend,
-
.resume = ads7846_resume,
-
};
-
-
static int __init ads7846_init(void)
-
{
-
return spi_register_driver(&ads7846_driver);
-
}
-
- //驱动与设备匹配成功后,会调用ads7846_probe函数,此函数会创建spi设备的字符设备,设备节点,这里创建的是/dev/input/event0
davinci平台的spi驱动框架
最新推荐文章于 2023-12-27 19:09:24 发布