Quellcode durchsuchen

[DM/SPI] Support DM mode in SPI

Signed-off-by: GuEe-GUI <2991707448@qq.com>
GuEe-GUI vor 5 Monaten
Ursprung
Commit
6f68ca7c71

+ 63 - 0
components/drivers/include/drivers/dev_spi.h

@@ -16,6 +16,8 @@
 #include <stdlib.h>
 #include <rtthread.h>
 #include <drivers/dev_pin.h>
+#include <drivers/core/driver.h>
+
 /**
  * @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
  *

+ 3 - 0
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)

+ 71 - 0
components/drivers/spi/dev_spi.c

@@ -10,6 +10,14 @@
 #include <rtthread.h>
 #include "drivers/dev_spi.h"
 
+#define DBG_TAG     "spi.dev"
+#define DBG_LVL     DBG_INFO
+#include <rtdbg.h>
+
+#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 */

+ 203 - 0
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 <rtdbg.h>
+
+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, &reg_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);

+ 44 - 0
components/drivers/spi/dev_spi_core.c

@@ -19,6 +19,10 @@
 #define DBG_LVL    DBG_INFO
 #include <rtdbg.h>
 
+#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;
 }
 

+ 106 - 0
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 <rtdbg.h>
+
+#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 */

+ 29 - 0
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 <rthw.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#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__ */