设备树学习之(九)SPI设备注册过程
来源:互联网 发布:福利软件 编辑:程序博客网 时间:2024/05/19 10:41
开发板:tiny4412SDK + S702 + 4GB Flash
要移植的内核版本:Linux-4.4.0 (支持device tree)
u-boot版本:友善之臂自带的 U-Boot 2010.12
busybox版本:busybox 1.25
目标:
同 i2c 一样,分析 spi 设备的注册过程,其实是一模一样的。
int spi_register_master(struct spi_master *master) //注册控制器驱动 of_register_spi_devices(master); for_each_available_child_of_node(master->dev.of_node, nc) //控制器节点的子节点 of_register_spi_device(master, nc);
static struct spi_device * of_register_spi_device(struct spi_master *master, struct device_node *nc){ struct spi_device *spi; int rc; u32 value; /* 分配一个 spi device ,从属于 master*/ spi = spi_alloc_device(master); /* 获取 compatibel 属性,具体看后边 */ rc = of_modalias_node(nc, spi->modalias, sizeof(spi->modalias)); /* 获取 reg 属性作为片选编号 */ rc = of_property_read_u32(nc, "reg", &value); spi->chip_select = value; /* mode 设置 */ if (of_find_property(nc, "spi-cpha", NULL)) spi->mode |= SPI_CPHA; if (of_find_property(nc, "spi-cpol", NULL)) spi->mode |= SPI_CPOL; if (of_find_property(nc, "spi-cs-high", NULL)) spi->mode |= SPI_CS_HIGH; if (of_find_property(nc, "spi-3wire", NULL)) spi->mode |= SPI_3WIRE; if (of_find_property(nc, "spi-lsb-first", NULL)) spi->mode |= SPI_LSB_FIRST; /* Device DUAL/QUAD mode */ if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) { switch (value) { case 1: break; case 2: spi->mode |= SPI_TX_DUAL; break; case 4: spi->mode |= SPI_TX_QUAD; break; default: dev_warn(&master->dev, "spi-tx-bus-width %d not supported\n", value); break; } } if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) { switch (value) { case 1: break; case 2: spi->mode |= SPI_RX_DUAL; break; case 4: spi->mode |= SPI_RX_QUAD; break; default: dev_warn(&master->dev, "spi-rx-bus-width %d not supported\n", value); break; } } /* 获取最大速度 */ rc = of_property_read_u32(nc, "spi-max-frequency", &value); spi->max_speed_hz = value; /* Store a pointer to the node in the device structure */ of_node_get(nc); spi->dev.of_node = nc; /* 注册 spi device */ rc = spi_add_device(spi); return spi;}
int of_modalias_node(struct device_node *node, char *modalias, int len){ const char *compatible, *p; int cplen; compatible = of_get_property(node, "compatible", &cplen); if (!compatible || strlen(compatible) > cplen) return -ENODEV; p = strchr(compatible, ','); strlcpy(modalias, p ? p + 1 : compatible, len);//如果compatibel属性中有“,”则取“,”之后的内容,否则取全部,它作为匹配依据 return 0;}
static int spi_match_device(struct device *dev, struct device_driver *drv) { const struct spi_device *spi = to_spi_device(dev); const struct spi_driver *sdrv = to_spi_driver(drv); if (sdrv->id_table) return !!spi_match_id(sdrv->id_table, spi); return strcmp(spi->modalias, drv->name) == 0; }
static const struct spi_device_id *spi_match_id(const struct spi_device_id *id, const struct spi_device *sdev){ while (id->name[0]) { if (!strcmp(sdev->modalias, id->name)) return id; id++; } return NULL;}
启动报错:
[ 0.962673] spi spi0.0: child node 'controller-data' not found [ 0.968455] spi spi0.0: No CS for SPI(0) [ 0.972360] s3c64xx-spi 13920000.spi: can't setup spi0.0, status -19 [ 0.978693] spi_device register error /spi@13920000/spidev@0 [ 0.984339] spi_master spi0: Failed to create SPI device for /spi@13920000/s0
int spi_add_device(struct spi_device *spi){ static DEFINE_MUTEX(spi_add_lock); struct spi_master *master = spi->master; struct device *dev = master->dev.parent; int status; /* Chipselects are numbered 0..max; validate. */ if (spi->chip_select >= master->num_chipselect) /* Set the bus ID string */ spi_dev_set_name(spi); mutex_lock(&spi_add_lock); status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check); if (master->cs_gpios) spi->cs_gpio = master->cs_gpios[spi->chip_select]; status = spi_setup(spi); if (status < 0) { dev_err(dev, "can't setup %s, status %d\n", dev_name(&spi->dev), status); goto done; } /* Device may be bound to an active driver when this returns */ 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;}EXPORT_SYMBOL_GPL(spi_add_device);
static int s3c64xx_spi_setup(struct spi_device *spi){ struct s3c64xx_spi_csinfo *cs = spi->controller_data; struct s3c64xx_spi_driver_data *sdd; struct s3c64xx_spi_info *sci; int err; sdd = spi_master_get_devdata(spi->master); if (spi->dev.of_node) { cs = s3c64xx_get_slave_ctrldata(spi); spi->controller_data = cs; } else if (cs) { /* On non-DT platforms the SPI core will set spi->cs_gpio * to -ENOENT. The GPIO pin used to drive the chip select * is defined by using platform data so spi->cs_gpio value * has to be override to have the proper GPIO pin number. */ spi->cs_gpio = cs->line; } if (IS_ERR_OR_NULL(cs)) { dev_err(&spi->dev, "No CS for SPI(%d)\n", spi->chip_select); return -ENODEV; } if (!spi_get_ctldata(spi)) { if (gpio_is_valid(spi->cs_gpio)) { err = gpio_request_one(spi->cs_gpio, GPIOF_OUT_INIT_HIGH, dev_name(&spi->dev)); if (err) { dev_err(&spi->dev, "Failed to get /CS gpio [%d]: %d\n", spi->cs_gpio, err); goto err_gpio_req; } } spi_set_ctldata(spi, cs); } sci = sdd->cntrlr_info; pm_runtime_get_sync(&sdd->pdev->dev); /* Check if we can provide the requested rate */ if (!sdd->port_conf->clk_from_cmu) { u32 psr, speed; /* Max possible */ speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1); if (spi->max_speed_hz > speed) spi->max_speed_hz = speed; psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1; psr &= S3C64XX_SPI_PSR_MASK; if (psr == S3C64XX_SPI_PSR_MASK) psr--; speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); if (spi->max_speed_hz < speed) { if (psr+1 < S3C64XX_SPI_PSR_MASK) { psr++; } else { err = -EINVAL; goto setup_exit; } } speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); if (spi->max_speed_hz >= speed) { spi->max_speed_hz = speed; } else { dev_err(&spi->dev, "Can't set %dHz transfer speed\n", spi->max_speed_hz); err = -EINVAL; goto setup_exit; } } pm_runtime_mark_last_busy(&sdd->pdev->dev); pm_runtime_put_autosuspend(&sdd->pdev->dev); if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL); return 0;setup_exit: pm_runtime_mark_last_busy(&sdd->pdev->dev); pm_runtime_put_autosuspend(&sdd->pdev->dev); /* setup() returns with device de-selected */ if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL); if (gpio_is_valid(spi->cs_gpio)) gpio_free(spi->cs_gpio); spi_set_ctldata(spi, NULL);err_gpio_req: if (spi->dev.of_node) kfree(cs); return err;}
static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata( struct spi_device *spi){ struct s3c64xx_spi_csinfo *cs; struct device_node *slave_np, *data_np = NULL; u32 fb_delay = 0; slave_np = spi->dev.of_node; if (!slave_np) { dev_err(&spi->dev, "device node not found\n"); return ERR_PTR(-EINVAL); } data_np = of_get_child_by_name(slave_np, "controller-data"); if (!data_np) { dev_err(&spi->dev, "child node 'controller-data' not found\n"); return ERR_PTR(-EINVAL); } cs = kzalloc(sizeof(*cs), GFP_KERNEL); if (!cs) { of_node_put(data_np); return ERR_PTR(-ENOMEM); } of_property_read_u32(data_np, "samsung,spi-feedback-delay", &fb_delay); cs->fb_delay = fb_delay; of_node_put(data_np); return cs;}
最开始,就是因为没有指定 controller-data 导致设备无法匹配到驱动程序,查了半天。下面给出后面要写的 spi flash 的设备树:
&spi_0 { status = "okay"; cs-gpios = <&gpb 1 GPIO_ACTIVE_HIGH>; spi_flash@0 { compatible = "tiny4412,spi_flash"; spi-max-frequency = <10000000>; reg = <0>; controller-data { samsung,spi-feedback-delay = <0>; }; };};
1 0
- 设备树学习之(九)SPI设备注册过程
- 设备树学习之(九)SPI设备注册过程
- 设备树学习之(九)SPI设备注册过程
- SPI设备注册过程
- 设备树学习之(七)I2C设备的注册过程分析
- 设备树学习之(七)I2C设备的注册过程分析
- 设备树学习之(七)I2C设备的注册过程分析
- 设备树学习之(十)spi flash
- 设备树学习之(十)spi flash
- 设备树学习之(十)spi flash
- i2c、spi设备展开过程
- 设备驱动注册过程
- SPI驱动之SPI设备驱动程序
- SPI驱动之SPI设备驱动程序
- 24 设备树里描述spi设备
- I2C总线设备注册过程
- Android学习笔记(九)蓝牙设备
- Android学习笔记(九)蓝牙设备
- 扩频通信
- LeetCode 137. Single Number II
- JAVA面试重点总结
- 一个合法的表达式由()包围,()可以嵌套和连接,如(())()也是合法表达式;现在有 6 对(),它们可以组成的合法表达式的个数为_132___
- Android AsyncTask源码简要分析
- 设备树学习之(九)SPI设备注册过程
- php5.4+ CI框架中无法写日志
- git搭建局域网服务器
- 如何用github+hexo搭建个人博客
- Redis String、List、Set、Hash、ZSet常用命令
- HDU3555 Bomb 数位DP经典题
- vue 动画过渡
- 算法提高 输入输出格式练习
- 在Eclipse中显示.project和.classpath和.setting目录