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

来源:互联网 发布:reveal.js echarts 编辑:程序博客网 时间:2024/06/06 07:29

转载自:http://blog.csdn.net/lizuobin2/article/details/54565166

开发板: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);
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4
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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
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;  }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

启动报错:

[    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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5
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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

最开始,就是因为没有指定 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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14