设备树学习之(九)SPI设备注册过程

开发板: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>;
                };
        };
};

你可能感兴趣的:(Tiny4412,Device,Tree,DTS之Tiny4412)