diff --git a/components/drivers/include/drivers/dev_spi.h b/components/drivers/include/drivers/dev_spi.h index 45e8e8544f..ad23453d5b 100644 --- a/components/drivers/include/drivers/dev_spi.h +++ b/components/drivers/include/drivers/dev_spi.h @@ -16,6 +16,8 @@ #include #include #include +#include + /** * @addtogroup Drivers RTTHREAD Driver * @defgroup SPI SPI @@ -151,7 +153,12 @@ struct rt_spi_configuration { rt_uint8_t mode; rt_uint8_t data_width; +#ifdef RT_USING_DM + rt_uint8_t data_width_tx; + rt_uint8_t data_width_rx; +#else rt_uint16_t reserved; +#endif rt_uint32_t max_hz; }; @@ -167,6 +174,12 @@ struct rt_spi_bus rt_uint8_t mode; const struct rt_spi_ops *ops; +#ifdef RT_USING_DM + rt_base_t *pins; + rt_bool_t slave; + int num_chipselect; +#endif /* RT_USING_DM */ + struct rt_mutex lock; struct rt_spi_device *owner; }; @@ -180,6 +193,20 @@ struct rt_spi_ops rt_ssize_t (*xfer)(struct rt_spi_device *device, struct rt_spi_message *message); }; +#ifdef RT_USING_DM +/** + * @brief SPI delay info + */ +struct rt_spi_delay +{ +#define RT_SPI_DELAY_UNIT_USECS 0 +#define RT_SPI_DELAY_UNIT_NSECS 1 +#define RT_SPI_DELAY_UNIT_SCK 2 + rt_uint16_t value; + rt_uint8_t unit; +}; +#endif /* RT_USING_DM */ + /** * @brief SPI Virtual BUS, one device must connected to a virtual BUS */ @@ -188,6 +215,17 @@ struct rt_spi_device struct rt_device parent; struct rt_spi_bus *bus; +#ifdef RT_USING_DM + const char *name; + const struct rt_spi_device_id *id; + const struct rt_ofw_node_id *ofw_id; + + rt_uint8_t chip_select; + struct rt_spi_delay cs_setup; + struct rt_spi_delay cs_hold; + struct rt_spi_delay cs_inactive; +#endif + struct rt_spi_configuration config; rt_base_t cs_pin; void *user_data; @@ -252,6 +290,31 @@ struct rt_qspi_device #define SPI_DEVICE(dev) ((struct rt_spi_device *)(dev)) +#ifdef RT_USING_DM +struct rt_spi_device_id +{ + char name[20]; + void *data; +}; + +struct rt_spi_driver +{ + struct rt_driver parent; + + const struct rt_spi_device_id *ids; + const struct rt_ofw_node_id *ofw_ids; + + rt_err_t (*probe)(struct rt_spi_device *device); + rt_err_t (*remove)(struct rt_spi_device *device); + rt_err_t (*shutdown)(struct rt_spi_device *device); +}; + +rt_err_t rt_spi_driver_register(struct rt_spi_driver *driver); +rt_err_t rt_spi_device_register(struct rt_spi_device *device); + +#define RT_SPI_DRIVER_EXPORT(driver) RT_DRIVER_EXPORT(driver, spi, BUILIN) +#endif /* RT_USING_DM */ + /** * @brief register a SPI bus * diff --git a/components/drivers/spi/SConscript b/components/drivers/spi/SConscript index 874db5b7e9..6897dab6d3 100644 --- a/components/drivers/spi/SConscript +++ b/components/drivers/spi/SConscript @@ -35,6 +35,9 @@ if GetDepend('RT_USING_SFUD'): elif rtconfig.PLATFORM in ['armcc']: LOCAL_CFLAGS += ' --c99' +if GetDepend('RT_USING_DM'): + src += ['dev_spi_dm.c', 'dev_spi_bus.c'] + src += src_device group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_SPI'], CPPPATH = CPPPATH, LOCAL_CFLAGS = LOCAL_CFLAGS) diff --git a/components/drivers/spi/dev_spi.c b/components/drivers/spi/dev_spi.c index b40ffd90db..3f55fd0eb0 100644 --- a/components/drivers/spi/dev_spi.c +++ b/components/drivers/spi/dev_spi.c @@ -10,6 +10,14 @@ #include #include "drivers/dev_spi.h" +#define DBG_TAG "spi.dev" +#define DBG_LVL DBG_INFO +#include + +#ifdef RT_USING_DM +#include "dev_spi_dm.h" +#endif + /* SPI bus device interface, compatible with RT-Thread 0.3.x/1.0.x */ static rt_ssize_t _spi_bus_device_read(rt_device_t dev, rt_off_t pos, @@ -155,3 +163,66 @@ rt_err_t rt_spidev_device_init(struct rt_spi_device *dev, const char *name) /* register to device manager */ return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR); } + +#ifdef RT_USING_DM +static rt_err_t spidev_probe(struct rt_spi_device *spi_dev) +{ + const char *bus_name; + struct rt_device *dev = &spi_dev->parent; + + if (spi_dev->parent.ofw_node) + { + if (rt_dm_dev_prop_index_of_string(dev, "compatible", "spidev") >= 0) + { + LOG_E("spidev is not supported in OFW"); + + return -RT_EINVAL; + } + } + + bus_name = rt_dm_dev_get_name(&spi_dev->bus->parent); + rt_dm_dev_set_name(dev, "%s_%d", bus_name, spi_dev->chip_select); + + return RT_EOK; +} + +static const struct rt_spi_device_id spidev_ids[] = +{ + { .name = "dh2228fv" }, + { .name = "ltc2488" }, + { .name = "sx1301" }, + { .name = "bk4" }, + { .name = "dhcom-board" }, + { .name = "m53cpld" }, + { .name = "spi-petra" }, + { .name = "spi-authenta" }, + { .name = "em3581" }, + { .name = "si3210" }, + { /* sentinel */ }, +}; + +static const struct rt_ofw_node_id spidev_ofw_ids[] = +{ + { .compatible = "cisco,spi-petra" }, + { .compatible = "dh,dhcom-board" }, + { .compatible = "lineartechnology,ltc2488" }, + { .compatible = "lwn,bk4" }, + { .compatible = "menlo,m53cpld" }, + { .compatible = "micron,spi-authenta" }, + { .compatible = "rohm,dh2228fv" }, + { .compatible = "semtech,sx1301" }, + { .compatible = "silabs,em3581" }, + { .compatible = "silabs,si3210" }, + { .compatible = "rockchip,spidev" }, + { /* sentinel */ }, +}; + +static struct rt_spi_driver spidev_driver = +{ + .ids = spidev_ids, + .ofw_ids = spidev_ofw_ids, + + .probe = spidev_probe, +}; +RT_SPI_DRIVER_EXPORT(spidev_driver); +#endif /* RT_USING_DM */ diff --git a/components/drivers/spi/dev_spi_bus.c b/components/drivers/spi/dev_spi_bus.c new file mode 100644 index 0000000000..34aa298aea --- /dev/null +++ b/components/drivers/spi/dev_spi_bus.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#include "dev_spi_dm.h" + +#define DBG_TAG "spi.bus" +#define DBG_LVL DBG_INFO +#include + +extern rt_err_t rt_spidev_device_init(struct rt_spi_device *dev, const char *name); + +static struct rt_bus spi_bus; + +void spi_bus_scan_devices(struct rt_spi_bus *bus) +{ +#ifdef RT_USING_OFW + if (bus->parent.ofw_node) + { + struct rt_ofw_node *np = bus->parent.ofw_node, *spi_dev_np; + + rt_ofw_foreach_available_child_node(np, spi_dev_np) + { + rt_uint64_t reg_offset; + struct rt_spi_device *spi_dev; + + if (!rt_ofw_prop_read_bool(spi_dev_np, "compatible")) + { + continue; + } + + spi_dev = rt_calloc(1, sizeof(*spi_dev)); + + if (!spi_dev) + { + rt_ofw_node_put(spi_dev_np); + LOG_E("Not memory to create spi device: %s", + rt_ofw_node_full_name(spi_dev_np)); + + return; + } + + rt_ofw_get_address(spi_dev_np, 0, ®_offset, RT_NULL); + + spi_dev->parent.ofw_node = spi_dev_np; + spi_dev->parent.type = RT_Device_Class_Unknown; + spi_dev->name = rt_ofw_node_name(spi_dev_np); + spi_dev->bus = bus; + + rt_dm_dev_set_name(&spi_dev->parent, rt_ofw_node_full_name(spi_dev_np)); + + if (spi_device_ofw_parse(spi_dev)) + { + continue; + } + + rt_spi_device_register(spi_dev); + } + } +#endif /* RT_USING_OFW */ +} + +rt_err_t rt_spi_driver_register(struct rt_spi_driver *driver) +{ + RT_ASSERT(driver != RT_NULL); + + driver->parent.bus = &spi_bus; + + return rt_driver_register(&driver->parent); +} + +rt_err_t rt_spi_device_register(struct rt_spi_device *device) +{ + RT_ASSERT(device != RT_NULL); + + return rt_bus_add_device(&spi_bus, &device->parent); +} + +static rt_bool_t spi_match(rt_driver_t drv, rt_device_t dev) +{ + const struct rt_spi_device_id *id; + struct rt_spi_driver *driver = rt_container_of(drv, struct rt_spi_driver, parent); + struct rt_spi_device *device = rt_container_of(dev, struct rt_spi_device, parent); + + if ((id = driver->ids)) + { + for (; id->name[0]; ++id) + { + if (!rt_strcmp(id->name, device->name)) + { + device->id = id; + device->ofw_id = RT_NULL; + + return RT_TRUE; + } + } + } + +#ifdef RT_USING_OFW + device->ofw_id = rt_ofw_node_match(device->parent.ofw_node, driver->ofw_ids); + + if (device->ofw_id) + { + device->id = RT_NULL; + + return RT_TRUE; + } +#endif + + return RT_FALSE; +} + +static rt_err_t spi_probe(rt_device_t dev) +{ + rt_err_t err; + struct rt_spi_bus *bus; + struct rt_spi_driver *driver = rt_container_of(dev->drv, struct rt_spi_driver, parent); + struct rt_spi_device *device = rt_container_of(dev, struct rt_spi_device, parent); + + if (!device->bus) + { + return -RT_EINVAL; + } + + err = driver->probe(device); + + if (err) + { + return err; + } + + bus = device->bus; + + if (bus->pins) + { + device->cs_pin = bus->pins[device->chip_select]; + + rt_pin_mode(device->cs_pin, PIN_MODE_OUTPUT); + } + else + { + device->cs_pin = PIN_NONE; + } + + /* Driver not register SPI device to system */ + if (device->parent.type == RT_Device_Class_Unknown) + { + rt_spidev_device_init(device, rt_dm_dev_get_name(&device->parent)); + } + + return err; +} + +static rt_err_t spi_remove(rt_device_t dev) +{ + struct rt_spi_driver *driver = rt_container_of(dev->drv, struct rt_spi_driver, parent); + struct rt_spi_device *device = rt_container_of(dev, struct rt_spi_device, parent); + + if (driver && driver->remove) + { + driver->remove(device); + } + rt_free(device); + + return RT_EOK; +} + +static rt_err_t spi_shutdown(rt_device_t dev) +{ + struct rt_spi_driver *driver = rt_container_of(dev->drv, struct rt_spi_driver, parent); + struct rt_spi_device *device = rt_container_of(dev, struct rt_spi_device, parent); + + if (driver && driver->shutdown) + { + driver->shutdown(device); + } + rt_free(device); + + return RT_EOK; +} + +static struct rt_bus spi_bus = +{ + .name = "spi", + .match = spi_match, + .probe = spi_probe, + .remove = spi_remove, + .shutdown = spi_shutdown, +}; + +static int spi_bus_init(void) +{ + rt_bus_register(&spi_bus); + + return 0; +} +INIT_CORE_EXPORT(spi_bus_init); diff --git a/components/drivers/spi/dev_spi_core.c b/components/drivers/spi/dev_spi_core.c index 648195544d..d84aaac055 100644 --- a/components/drivers/spi/dev_spi_core.c +++ b/components/drivers/spi/dev_spi_core.c @@ -19,6 +19,10 @@ #define DBG_LVL DBG_INFO #include +#ifdef RT_USING_DM +#include "dev_spi_dm.h" +#endif + extern rt_err_t rt_spi_bus_device_init(struct rt_spi_bus *bus, const char *name); extern rt_err_t rt_spidev_device_init(struct rt_spi_device *dev, const char *name); @@ -41,6 +45,46 @@ rt_err_t rt_spi_bus_register(struct rt_spi_bus *bus, /* set bus mode */ bus->mode = RT_SPI_BUS_MODE_SPI; +#ifdef RT_USING_DM + if (!bus->slave) + { + int pin_count = rt_pin_get_named_pin_count(&bus->parent, "cs"); + + if (pin_count > 0) + { + pin_count = rt_max_t(int, pin_count, bus->num_chipselect); + bus->pins = rt_malloc(sizeof(bus->pins[0]) * pin_count); + + if (!bus->pins) + { + rt_device_unregister(&bus->parent); + return -RT_ENOMEM; + } + + for (int i = 0; i < pin_count; ++i) + { + bus->pins[i] = rt_pin_get_named_pin(&bus->parent, "cs", i, + RT_NULL, RT_NULL); + } + } + else if (pin_count == 0) + { + bus->pins = RT_NULL; + } + else + { + result = pin_count; + + LOG_E("CS PIN find error = %s", rt_strerror(result)); + + rt_device_unregister(&bus->parent); + return result; + } + } + + spi_bus_scan_devices(bus); +#endif + return RT_EOK; } diff --git a/components/drivers/spi/dev_spi_dm.c b/components/drivers/spi/dev_spi_dm.c new file mode 100644 index 0000000000..063615ce62 --- /dev/null +++ b/components/drivers/spi/dev_spi_dm.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-12-06 GuEe-GUI first version + */ + +#include "dev_spi_dm.h" + +#define DBG_TAG "spi.dm" +#define DBG_LVL DBG_INFO +#include + +#ifdef RT_USING_OFW +static void ofw_parse_delay(struct rt_ofw_node *np, struct rt_spi_delay *delay, + const char *prop) +{ + rt_uint32_t value; + + if (!rt_ofw_prop_read_u32(np, prop, &value)) + { + if (value > RT_UINT16_MAX) + { + delay->value = RT_DIV_ROUND_UP(value, 1000); + delay->unit = RT_SPI_DELAY_UNIT_USECS; + } + else + { + delay->value = value; + delay->unit = RT_SPI_DELAY_UNIT_NSECS; + } + } +} + +rt_err_t spi_device_ofw_parse(struct rt_spi_device *spi_dev) +{ + rt_err_t err; + rt_uint32_t value; + struct rt_spi_bus *spi_bus = spi_dev->bus; + struct rt_ofw_node *np = spi_dev->parent.ofw_node; + struct rt_spi_configuration *conf = &spi_dev->config; + + if (rt_ofw_prop_read_bool(np, "spi-cpha")) + { + conf->mode |= RT_SPI_CPHA; + } + if (rt_ofw_prop_read_bool(np, "spi-cpol")) + { + conf->mode |= RT_SPI_CPOL; + } + if (rt_ofw_prop_read_bool(np, "spi-3wire")) + { + conf->mode |= RT_SPI_3WIRE; + } + if (rt_ofw_prop_read_bool(np, "spi-lsb-first")) + { + conf->mode |= RT_SPI_LSB; + } + if (rt_ofw_prop_read_bool(np, "spi-cs-high")) + { + conf->mode |= RT_SPI_CS_HIGH; + } + + value = 1; + rt_ofw_prop_read_u32(np, "spi-tx-bus-width", &value); + conf->data_width_tx = value; + + value = 1; + rt_ofw_prop_read_u32(np, "spi-rx-bus-width", &value); + conf->data_width_rx = value; + + if (spi_bus->slave) + { + if (!rt_ofw_node_tag_equ(np, "slave")) + { + LOG_E("Invalid SPI device = %s", rt_ofw_node_full_name(np)); + + return -RT_EINVAL; + } + + return RT_EOK; + } + + if ((err = rt_ofw_prop_read_u32(np, "reg", &value))) + { + LOG_E("Find 'reg' failed"); + + return err; + } + spi_dev->chip_select = value; + + if (!rt_ofw_prop_read_u32(np, "spi-max-frequency", &value)) + { + conf->max_hz = value; + } + + ofw_parse_delay(np, &spi_dev->cs_setup, "spi-cs-setup-delay-ns"); + ofw_parse_delay(np, &spi_dev->cs_hold, "spi-cs-hold-delay-ns"); + ofw_parse_delay(np, &spi_dev->cs_inactive, "spi-cs-inactive-delay-ns"); + + return RT_EOK; +} +#endif /* RT_USING_OFW */ diff --git a/components/drivers/spi/dev_spi_dm.h b/components/drivers/spi/dev_spi_dm.h new file mode 100644 index 0000000000..c124295788 --- /dev/null +++ b/components/drivers/spi/dev_spi_dm.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#ifndef __DEV_SPI_DM_H__ +#define __DEV_SPI_DM_H__ + +#include +#include +#include + +#ifdef RT_USING_OFW +rt_err_t spi_device_ofw_parse(struct rt_spi_device *spi_dev); +#else +rt_inline rt_err_t spi_device_ofw_parse(struct rt_spi_device *spi_dev) +{ + return RT_EOK; +} +#endif /* RT_USING_OFW */ + +void spi_bus_scan_devices(struct rt_spi_bus *bus); + +#endif /* __DEV_SPI_DM_H__ */