linux spi防火墙,Linux SPI通过设备树文件添加设备

一、设备树文件编写

spi_master: spi_master

{

#address-cells = <1>;

#size-cells = <0>;

compatible = "spi_master";

io_phy_addr = <0x1f000000>;

banks = <0x1110>,<0x1111>,<0x1038>,<0x101E>;

interrupts = ,;

spi0_mode = <1>;

spi1_mode = <3>;

status = "okay";

spi_device: spi_device

{

compatible = "spi_device";

reg = <0x0>;

spi-max-frequency = <10000000>;

status = "okay";

};

};

如上DTS文件片段,SPI Device 节点必须定义在 SPI Master 节点下,其中 compatible 属性和 reg 属性,以上 compatible 属性用于匹配对应的 Driver 程序,reg 属性用于指定使用的 SPI Master 的编号,SPI 相关设备树文件识别见下文讲解。

二、代码流程

匹配设备树文件在SPI子系统中有两个地方:在 spi_register_master() 中匹配和在 device register 时通过内核的通知链(notifier_block)来调用设备树匹配相关程序。

在 spi_register_master() 中匹配:

//driver/spi/spi.c

int spi_register_master(struct spi_master *master)

{

static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1);

struct device *dev = master->dev.parent;

struct boardinfo *bi;

int status = -ENODEV;

int dynamic = 0;

if (!dev)

return -ENODEV;

status = of_spi_register_master(master);

if (status)

return status;

/* even if it's just one always-selected device, there must

* be at least one chipselect

*/

if (master->num_chipselect == 0)

return -EINVAL;

if ((master->bus_num < 0) && master->dev.of_node)

master->bus_num = of_alias_get_id(master->dev.of_node, "spi");

/* convention: dynamically assigned bus IDs count down from the max */

if (master->bus_num < 0) {

/* FIXME switch to an IDR based scheme, something like

* I2C now uses, so we can't run out of "dynamic" IDs

*/

master->bus_num = atomic_dec_return(&dyn_bus_id);

dynamic = 1;

}

INIT_LIST_HEAD(&master->queue);

spin_lock_init(&master->queue_lock);

spin_lock_init(&master->bus_lock_spinlock);

mutex_init(&master->bus_lock_mutex);

mutex_init(&master->io_mutex);

master->bus_lock_flag = 0;

init_completion(&master->xfer_completion);

if (!master->max_dma_len)

master->max_dma_len = INT_MAX;

/* register the device, then userspace will see it.

* registration fails if the bus ID is in use.

*/

dev_set_name(&master->dev, "spi%u", master->bus_num);

status = device_add(&master->dev);

if (status < 0)

goto done;

dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),

dynamic ? " (dynamic)" : "");

/* If we're using a queued driver, start the queue */

if (master->transfer)

dev_info(dev, "master is unqueued, this is deprecated\n");

else {

status = spi_master_initialize_queue(master);

if (status) {

device_del(&master->dev);

goto done;

}

}

/* add statistics */

spin_lock_init(&master->statistics.lock);

mutex_lock(&board_lock);

list_add_tail(&master->list, &spi_master_list);

list_for_each_entry(bi, &board_list, list)

spi_match_master_to_boardinfo(master, &bi->board_info);

mutex_unlock(&board_lock);

/* Register devices from the device tree and ACPI */

of_register_spi_devices(master);  // 设备树匹配操作

acpi_register_spi_devices(master);

done:

return status;

}

//driver/spi/spi.c

static void of_register_spi_devices(struct spi_master *master)

{

struct spi_device *spi;

struct device_node *nc;

if (!master->dev.of_node)

return;

for_each_available_child_of_node(master->dev.of_node, nc) {

if (of_node_test_and_set_flag(nc, OF_POPULATED))

continue;

spi = of_register_spi_device(master, nc);  // 设备树匹配操作

if (IS_ERR(spi)) {

dev_warn(&master->dev, "Failed to create SPI device for %s\n",

nc->full_name);

of_node_clear_flag(nc, OF_POPULATED);

}

}

}

在 device register 时匹配:

//driver/spi/spi.c

static int __init spi_init(void)

{

int status;

buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);

if (!buf) {

status = -ENOMEM;

goto err0;

}

status = bus_register(&spi_bus_type);

if (status < 0)

goto err1;

status = class_register(&spi_master_class);

if (status < 0)

goto err2;

if (IS_ENABLED(CONFIG_OF_DYNAMIC))

WARN_ON(of_reconfig_notifier_register(&spi_of_notifier));

if (IS_ENABLED(CONFIG_ACPI))

WARN_ON(acpi_reconfig_notifier_register(&spi_acpi_notifier));

return 0;

err2:

bus_unregister(&spi_bus_type);

err1:

kfree(buf);

buf = NULL;

err0:

return status;

}

postcore_initcall(spi_init);

在 device register 时,需配置 CONFIG_OF_DYNAMIC 宏以开启动态匹配才能够使用设备树添加设备,该宏在 menuconfig/Device Drivers/Device Tree and Open Firmware support 中开启,如下图:

c296cdf89a07

//driver/spi/spi.c

static struct notifier_block spi_of_notifier = {

.notifier_call = of_spi_notify,

};

static int of_spi_notify(struct notifier_block *nb, unsigned long action,

void *arg)

{

struct of_reconfig_data *rd = arg;

struct spi_master *master;

struct spi_device *spi;

switch (of_reconfig_get_state_change(action, arg)) {

case OF_RECONFIG_CHANGE_ADD:

master = of_find_spi_master_by_node(rd->dn->parent);

if (master == NULL)

return NOTIFY_OK; /* not for us */

if (of_node_test_and_set_flag(rd->dn, OF_POPULATED)) {

put_device(&master->dev);

return NOTIFY_OK;

}

spi = of_register_spi_device(master, rd->dn);  // 设备树匹配操作

put_device(&master->dev);

if (IS_ERR(spi)) {

pr_err("%s: failed to create for '%s'\n",

__func__, rd->dn->full_name);

of_node_clear_flag(rd->dn, OF_POPULATED);

return notifier_from_errno(PTR_ERR(spi));

}

break;

case OF_RECONFIG_CHANGE_REMOVE:

/* already depopulated? */

if (!of_node_check_flag(rd->dn, OF_POPULATED))

return NOTIFY_OK;

/* find our device by node */

spi = of_find_spi_device_by_node(rd->dn);

if (spi == NULL)

return NOTIFY_OK; /* no? not meant for us */

/* unregister takes one ref away */

spi_unregister_device(spi);

/* and put the reference of the find */

put_device(&spi->dev);

break;

}

return NOTIFY_OK;

}

//driver/spi/spi.c

static struct spi_device *

of_register_spi_device(struct spi_master *master, struct device_node *nc)

{

struct spi_device *spi;

int rc;

u32 value;

/* Alloc an spi_device */

spi = spi_alloc_device(master);

if (!spi) {

dev_err(&master->dev, "spi_device alloc error for %s\n",

nc->full_name);

rc = -ENOMEM;

goto err_out;

}

/* Select device driver */

rc = of_modalias_node(nc, spi->modalias,

sizeof(spi->modalias));

if (rc < 0) {

dev_err(&master->dev, "cannot find modalias for %s\n",

nc->full_name);

goto err_out;

}

/* Device address */

rc = of_property_read_u32(nc, "reg", &value);

if (rc) {

dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n",

nc->full_name, rc);

goto err_out;

}

spi->chip_select = value;

/* Mode (clock phase/polarity/etc.) */

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;

}

}

/* Device speed */

rc = of_property_read_u32(nc, "spi-max-frequency", &value);

if (rc) {

dev_err(&master->dev, "%s has no valid 'spi-max-frequency' property (%d)\n",

nc->full_name, rc);

goto err_out;

}

spi->max_speed_hz = value;

/* Store a pointer to the node in the device structure */

of_node_get(nc);

spi->dev.of_node = nc;

/* Register the new device */

rc = spi_add_device(spi);

if (rc) {

dev_err(&master->dev, "spi_device register error %s\n",

nc->full_name);

goto err_of_node_put;

}

return spi;

err_of_node_put:

of_node_put(nc);

err_out:

spi_dev_put(spi);

return ERR_PTR(rc);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值