资讯详情

linux spi驱动分析

linux spi驱动分析 altas200模块

  • 准备
  • 匹配设备树节点和驱动
  • spi控制器驱动分析(source/dev_plat/spi/spi-hisi.c)
  • spi设备驱动分析(source/kernel/linux-4.19/drivers/spi/spidev.c)
  • spi测试例程

准备

??华为官方使用的源代码包ascend200AI加速模块的SDK,下载地址为:点击跳转 使用的固件和驱动版本为:1.0.9.alpha 压缩包名称为:A200-3000-sdk_20.2.0.zip ??将A200-3000-sdk_20.2.0.zip解压后可见Ascend310-source-minirc.tar.gz这个压缩包里有压缩包ascend200AI加速模块的linux核源代码包、设备树及驱动文件等。

匹配设备树节点和驱动

??这里不做太多赘述,可以自己百度。 ??其spi位于设备树节点source/dtb/hi1910-fpga-spi.dtsi内容如下:

  alg_clk: alg_clk {         compatible =  "fixed-clock";    #clock-cells = <0>;    clock-frequency =  <200000000>;  };                spi_0: spi@130980000{           #address-cells = <1>;      #size-cells = <0>;                        compatible =  "hisi-spi";                  reg =  <0x1 0x30980000 0  0x10000>,<0x1 0x30900000 0  0x1000>;                  interrupts =  <0  322  4>;                  clocks =  <&alg_clk>;                  clock-names =  "spi_clk";                  num-cs =  <2>;                                id  =  <0>;                  status =  "ok";          };          spi_1: spi@130990000 {           #address-cells = <1>;      #size-cells = <0>;                  compatible =  "hisi-spi";                    reg =  <0x1 0x30990000 0  0x10000>,<0x1 0x30900000 0  0x1000>;                  interrupts =  <0  323  4>;	        clocks = <&alg_clk>;        	clock-names = "spi_clk";	        num-cs = <2>;                id = <1>;        	status = "disable";	};				clocks {
     		clk25m: clk@1 {
     			compatible = "fixed-clock";			reg=<1>;			#clock-cells = <0>;			clock-frequency = <25000000>;			clock-output-names = "clk25m";		};	};

  spi_0和spi_1节点的compatible 属性会和spi控制器的驱动匹配。(位于source/drivers/dev_plat/spi/spi-hisi.c)

STATIC const struct of_device_id hisi_spi_dt_ids[] = {
         {
      .compatible = "hisi-spi" },    {
      /* sentinel */ }};
STATIC struct platform_driver hisi_spi_driver = {
         .driver     = {
             .name   = "hisi_spi",        .pm = HISI_SPI_PM_OPS,        .of_match_table = of_match_ptr(hisi_spi_dt_ids),#ifdef CONFIG_ACPI        .acpi_match_table = ACPI_PTR(hisi_spi_acpi_ids),#endif    },    .probe      = hisi_spi_probe,    .remove     = hisi_spi_remove,};

  内核会先解析设备树节点,然后驱动程序注册的时候of_match_ptr中的of_device_id会和设备树节点进行匹配。

spi控制器驱动分析(source/dev_plat/spi/spi-hisi.c)

  spi_0和spi_1的设备树节点都会匹配上spi控制器驱动,然后执行他的probe函数,由于spi_1节点的状态为disable,所以只有spi_0节点会执行一次probe函数

    int ret;    int irq;    u32 id = 0;    u32 cs_num = 0;    u32 rate = 0;    struct resource *res = NULL;    struct resource *res_iomux = NULL;    struct hisi_spi *hs = NULL;    struct spi_master *master = NULL;

  初始化变量

	ASSERT_RET((pdev != NULL), -EINVAL);	dev_info(&pdev->dev, "hi_spi_probe enter..\n");    dev_info(&pdev->dev, "hi:dev name %s, id %d, auto %d, num res %u\n", pdev->name, pdev->id, pdev->id_auto,        pdev->num_resources);

  ASSERT_RET断言,判断platform_device *pdev结构体是否为空。 dev_info和printk类似,在内核启动时打印消息

ret = hisi_spi_get_dts(pdev, &cs_num, &id, &rate);    if (ret != 0) {
             dev_err(&pdev->dev, "hisi_spi_get_dts fail\n");        return -EINVAL;    }

  hisi_spi_get_dts用于获取片选和时钟等信息

STATIC int hisi_spi_get_dts(struct platform_device *pdev, u32 *cs_num, u32 *id, u32 *rate){
         int ret;#ifndef CONFIG_ACPI    struct clk *clk = NULL;#endif    if ((pdev == NULL) || (cs_num == NULL) || (id == NULL) || (rate == NULL)) {
             return -EINVAL;    }#ifdef CONFIG_ACPI    ret = device_property_read_u32(&pdev->dev, "num-cs", cs_num);    if (ret < 0) {
             dev_err(&pdev->dev, "get num_cs error: %d \n", ret);        return -EINVAL;    }    ret = device_property_read_u32(&pdev->dev, "id", id);    if (ret < 0) {
             dev_err(&pdev->dev, "get id error: %d \n", ret);        return -EINVAL;    }    ret = device_property_read_u32(&pdev->dev, "spi_clk", rate);    if (ret < 0) {
             dev_err(&pdev->dev, "get clk error : %d\n", ret);        return -EINVAL;    }#else    ret = of_property_read_u32(pdev->dev.of_node, "num-cs", cs_num);    if (ret != 0) {
             dev_err(&pdev->dev, "of_property_read_u32 get num-cs fail\n");        return -EINVAL;    }    ret = of_property_read_u32(pdev->dev.of_node, "id", id);    if (ret != 0) {
             dev_err(&pdev->dev, "of_property_read_u32 get id fail\n");        return -EINVAL;    }    clk = clk_get(&pdev->dev, "spi_clk");    if (IS_ERR(clk)) {
             return PTR_ERR(clk);    }    *rate = clk_get_rate(clk);    clk_put(clk);#endif    return 0;}

  这个ACPI好像和电源管理有关,内核中全局搜索CONFIG_ACPI也未找到定义,所里这里先不管他。   这个hisi_spi_get_dts会获取设备书中spi节点信息   片选数量cs_num=2   spi节点id=0   spi时钟速率rate = 200000000   接着获取设备树描述中的mem资源

/* get base addr */    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);    if (unlikely(res == NULL)) {
             dev_err(&pdev->dev, "invalid resource\n");        return -EINVAL;    }

  这里要先说一下platform_get_resource这个函数,内核函数定义如下:

struct resource *platform_get_resource(struct platform_device *dev,				       unsigned int type, unsigned int num){
     	int i;	for (i = 0; i < dev->num_resources; i++) {
     		struct resource *r = &dev->resource[i];		if (type == resource_type(r) && num-- == 0)			return r;	}	return NULL;}

  从第一份资源开始匹配,type用来匹配资源类型num–==0表示你想获得的是第多少份资源   spi0设备树节点:

	reg = <0x1 0x30980000 0 0x10000>, <0x1 0x30900000 0 0x1000>;    interrupts = <0 322 4>;

  则spi0的pdev结构体的资源有三个,两个为IORESOURCE_MEM,一个为IORESOURCE_IRQ。这里调用:

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

  获取的是<0x1 0x30980000 0 0x10000>他表示spi控制器的内存起始地址为0x30980000 ,大小为0x10000。然后获取irq资源:

    irq = platform_get_irq(pdev, 0);    if (irq < 0) {
             dev_err(&pdev->dev, "platform_get_irq error\n");        return -ENODEV;    }    dev_info(&pdev->dev, "hi: get irq %d\n", irq);

  中断代码中我没有使用过,所以不管他了,接下来是分配spi_master结构体的内存:

master = spi_alloc_master(&pdev->dev, sizeof(*hs));    if (master == NULL) {
             dev_err(&pdev->dev, "spi_alloc_master error.\n");        return -ENOMEM;    }

  跳入spi_alloc_master里面:

static inline struct spi_controller *spi_alloc_master(struct device *host,						      unsigned int size){
     	return __spi_alloc_controller(host, size, false);}
struct spi_controller *__spi_alloc_controller(struct device *dev,					      unsigned int size, bool slave){
     	struct spi_controller	*ctlr;	if (!dev)		return NULL;	ctlr = kzalloc(size + sizeof(*ctlr), GFP_KERNEL);	if (!ctlr)		return NULL;	device_initialize(&ctlr->dev);	ctlr->bus_num = -1;	ctlr->num_chipselect = 1;	ctlr->slave = slave;	if (IS_ENABLED(CONFIG_SPI_SLAVE) && slave)		ctlr->dev.class = &spi_slave_class;	else		ctlr->dev.class = &spi_master_class;	ctlr->dev.parent = dev;	pm_suspend_ignore_children(&ctlr->dev, true);	spi_controller_set_devdata(ctlr, &ctlr[1]);	return ctlr;}EXPORT_SYMBOL_GPL(__spi_alloc_controller);

  重点关注这两行:

ctlr = kzalloc(size + sizeof(*ctlr), GFP_KERNEL);spi_controller_set_devdata

标签: uhz液位变送器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

 锐单商城 - 一站式电子元器件采购平台  

 深圳锐单电子有限公司