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