1. 程式人生 > >裝置樹學習之(九)SPI設備註冊過程

裝置樹學習之(九)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 /[email protected]13920000/[email protected]0                 
[    0.984339] spi_master spi0: Failed to create SPI device for /[email protected]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>;
        [email protected]0 {
                compatible = "tiny4412,spi_flash";
                spi-max-frequency = <10000000>;
                reg = <0>;
                controller-data {
                        samsung,spi-feedback-delay = <0>;
                };
        };
};