Linux驱动分析之SPI控制器

蹉跎莫遣韶光老,人生唯有读书好。这篇文章主要讲述Linux驱动分析之SPI控制器相关的知识,希望能为你提供帮助。


前言之前对SPI驱动的整体架构做了介绍,现在来分析具体的驱动程序。之前说过,SPI驱动分为设备驱动和控制器驱动。先来分析控制器驱动。我们以RockChip的控制器来作为分析。
SPI控制器分析下面的代码分析主要都在注释中,会按照驱动中函数的执行顺序分析。
(1) 装载和卸载函数

//dts匹配表
static const struct of_device_id rockchip_spi_dt_match[] = {
{ .compatible = "rockchip,rv1108-spi", },
{ .compatible = "rockchip,rk3036-spi", },
{ .compatible = "rockchip,rk3066-spi", },
{ .compatible = "rockchip,rk3188-spi", },
{ .compatible = "rockchip,rk3228-spi", },
{ .compatible = "rockchip,rk3288-spi", },
{ .compatible = "rockchip,rk3368-spi", },
{ .compatible = "rockchip,rk3399-spi", },
{ },
};
MODULE_DEVICE_TABLE(of, rockchip_spi_dt_match);


static struct platform_driver rockchip_spi_driver = {
.driver = {
.name= DRIVER_NAME,
.pm = & rockchip_spi_pm,
.of_match_table = of_match_ptr(rockchip_spi_dt_match),
},
.probe = rockchip_spi_probe,
.remove = rockchip_spi_remove,
};
//宏封装了platform_driver_register和platform_driver_unregister
module_platform_driver(rockchip_spi_driver);

module_platform_driver宏定义在 include/linux/platform_device.h, 具体看一下源码:
#define module_platform_driver(__platform_driver) \\
module_driver(__platform_driver, platform_driver_register, \\
platform_driver_unregister)


#define module_driver(__driver, __register, __unregister, ...) \\
static int __init __driver##_init(void) \\
{ \\
return __register(& (__driver) , ##__VA_ARGS__); \\
} \\
module_init(__driver##_init); \\
static void __exit __driver##_exit(void) \\
{ \\
__unregister(& (__driver) , ##__VA_ARGS__); \\
} \\
module_exit(__driver##_exit);

所以其实和我们看到的platform_driver的注册和卸载时一样的,只是进行了封装。
(2) probe()函数
static int rockchip_spi_probe(struct platform_device *pdev)
{
int ret;
struct rockchip_spi *rs;
struct spi_master *master;
struct resource *mem;
u32 rsd_nsecs;
//分配一个spi_master
master = spi_alloc_master(& pdev-> dev, sizeof(struct rockchip_spi));
//保存为driver_data, 方便其他地方获取使用
platform_set_drvdata(pdev, master);
//获取设备数据,就是driver_data
rs = spi_master_get_devdata(master);


//获取IO资源
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
//申请IO资源并进行重映射
rs-> regs = devm_ioremap_resource(& pdev-> dev, mem);


//获取APB时钟(APB时钟)
rs-> apb_pclk = devm_clk_get(& pdev-> dev, "apb_pclk");
//获取spi时钟(APB提供)
rs-> spiclk = devm_clk_get(& pdev-> dev, "spiclk");
//使能APB时钟
ret = clk_prepare_enable(rs-> apb_pclk);
//使能spi时钟
ret = clk_prepare_enable(rs-> spiclk);
//关闭spi控制器(设置SSIENR寄存器的值),查看芯片手册
spi_enable_chip(rs, 0);


rs-> type = SSI_MOTO_SPI; //摩托罗拉SPI协议
rs-> master = master; //spi_master
rs-> dev = & pdev-> dev; //device
rs-> max_freq = clk_get_rate(rs-> spiclk); //最大时钟频率
//接收采样延迟时间
if (!of_property_read_u32(pdev-> dev.of_node, "rx-sample-delay-ns",
& rsd_nsecs))
rs-> rsd_nsecs = rsd_nsecs;
//FIFO大小
rs-> fifo_len = get_fifo_len(rs);


spin_lock_init(& rs-> lock);
//电源管理
pm_runtime_set_active(& pdev-> dev);
pm_runtime_enable(& pdev-> dev);


master-> auto_runtime_pm = true; //自动电源管理
master-> bus_num = pdev-> id; //哪个spi, 比如是SPI1就bus_num=1, SPI2就bus_num=2
master-> mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; //所支持的模式
master-> num_chipselect = ROCKCHIP_SPI_MAX_CS_NUM; //片选最大值+1,spi设备的片选值要小于它
master-> dev.of_node = pdev-> dev.of_node;
master-> bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8); //支持8或16bit
//回调函数
master-> set_cs = rockchip_spi_set_cs; //硬件片选,使用控制器的片选(没使用可以不实现)
master-> prepare_message = rockchip_spi_prepare_message; //设置spi控制器(传输前的准备)
master-> unprepare_message = rockchip_spi_unprepare_message; //释放prepare的资源
master-> transfer_one = rockchip_spi_transfer_one; //传输一个简单的spi_transfer
master-> max_transfer_size = rockchip_spi_max_transfer_size;
master-> handle_err = rockchip_spi_handle_err;
master-> flags = SPI_MASTER_GPIO_SS;
//使用DMA
rs-> dma_tx.ch = dma_request_chan(rs-> dev, "tx");
rs-> dma_rx.ch = dma_request_chan(rs-> dev, "rx");


if (rs-> dma_tx.ch & & rs-> dma_rx.ch) {
//FIFO的地址
rs-> dma_tx.addr = (dma_addr_t)(mem-> start + ROCKCHIP_SPI_TXDR);
rs-> dma_rx.addr = (dma_addr_t)(mem-> start + ROCKCHIP_SPI_RXDR);


master-> can_dma = rockchip_spi_can_dma;
master-> dma_tx = rs-> dma_tx.ch;
master-> dma_rx = rs-> dma_rx.ch;
}
//注册spi_master
ret = devm_spi_register_master(& pdev-> dev, master);


return 0;
//错误处理
//.....
return ret;
}

上面将一些错误判断及Log信息去掉了,只留下关键的部分。
(3) 传输函数 -- rockchip_spi_transfer_one
static int rockchip_spi_transfer_one(
struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *xfer)
{
//获取rockchip_spi
struct rockchip_spi *rs = spi_master_get_devdata(master);
//判断spi当前状态
WARN_ON(readl_relaxed(rs-> regs + ROCKCHIP_SPI_SSIENR) & &
(readl_relaxed(rs-> regs + ROCKCHIP_SPI_SR) & SR_BUSY));


if (!xfer-> tx_buf & & !xfer-> rx_buf) {
dev_err(rs-> dev, "No buffer for transfer\\n");
return -EINVAL;
}


if (xfer-> len > ROCKCHIP_SPI_MAX_TRANLEN) {
dev_err(rs-> dev, "Transfer is too long (%d)\\n", xfer-> len);
return -EINVAL;
}

rs-> speed = xfer-> speed_hz; //传输速率
rs-> bpw = xfer-> bits_per_word; //8bit或16bit
rs-> n_bytes = rs-> bpw > > 3;
//传输的数据
rs-> tx = xfer-> tx_buf;
rs-> tx_end = rs-> tx + xfer-> len;
rs-> rx = xfer-> rx_buf;
rs-> rx_end = rs-> rx + xfer-> len;
rs-> len = xfer-> len;


rs-> tx_sg = xfer-> tx_sg;
rs-> rx_sg = xfer-> rx_sg;


if (rs-> tx & & rs-> rx)
rs-> tmode = CR0_XFM_TR; //发送并接收
else if (rs-> tx)
rs-> tmode = CR0_XFM_TO; //只发送
else if (rs-> rx)
rs-> tmode = CR0_XFM_RO; //只接收


/* we need prepare dma before spi was enabled */
//是否使用DMA
if (master-> can_dma & & master-> can_dma(master, spi, xfer))
rs-> use_dma = true;
else
rs-> use_dma = false;
//配置spi,对寄存器进行配置
rockchip_spi_config(rs);


if (rs-> use_dma)
return rockchip_spi_prepare_dma(rs);
//数据传输
return rockchip_spi_pio_transfer(rs);
}

  • rockchip_spi_pio_transfer
static int rockchip_spi_pio_transfer(struct rockchip_spi *rs)
{
int remain = 0;
//使能SPI
spi_enable_chip(rs, 1);


do {
if (rs-> tx) {
remain = rs-> tx_end - rs-> tx;
rockchip_spi_pio_writer(rs); //发送
}


if (rs-> rx) {
remain = rs-> rx_end - rs-> rx;
rockchip_spi_pio_reader(rs); //读取
}


cpu_relax();
} while (remain);


/* If tx, wait until the FIFO data completely. */
if (rs-> tx)
wait_for_idle(rs);
//关闭SPI
spi_enable_chip(rs, 0);


return 0;
}

  • rockchip_spi_pio_writer
//发送
static void rockchip_spi_pio_writer(struct rockchip_spi *rs)
{
u32 max = tx_max(rs);
u32 txw = 0;


while (max--) {
if (rs-> n_bytes == 1)
txw = *(u8 *)(rs-> tx);
else
txw = *(u16 *)(rs-> tx);
//写到数据发送寄存器
writel_relaxed(txw, rs-> regs + ROCKCHIP_SPI_TXDR);
rs-> tx += rs-> n_bytes;
}
}
//读取
static void rockchip_spi_pio_reader(struct rockchip_spi *rs)
{
u32 max = rx_max(rs);
u32 rxw;


while (max--) {
//读取数据接收寄存器中的数据
rxw = readl_relaxed(rs-> regs + ROCKCHIP_SPI_RXDR);
if (rs-> n_bytes == 1)
*(u8 *)(rs-> rx) = (u8)rxw;
else
*(u16 *)(rs-> rx) = (u16)rxw;
rs-> rx += rs-> n_bytes;
}
}

总结【Linux驱动分析之SPI控制器】每次看到这么多代码分析,很多人肯定都不怎么想看,但是多看几份,你就会发现都是套路。每个Linux版本的结构体可能都会变,但是基本的东西都是不变的。大家可以和之前的SPI驱动架构分析那篇文章一起看。
Linux驱动分析之SPI控制器

文章图片




    推荐阅读