设备树学习之(九)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
原创粉丝点击