4
0
mirror of https://github.com/RT-Thread/rt-thread.git synced 2025-01-19 10:03:31 +08:00

[DM/SPI] Support DM mode in SPI

Signed-off-by: GuEe-GUI <2991707448@qq.com>
This commit is contained in:
GuEe-GUI 2024-11-27 11:08:08 +08:00 committed by Rbb666
parent 49d18ec0a9
commit 6f68ca7c71
7 changed files with 519 additions and 0 deletions

View File

@ -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
*

View File

@ -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)

View File

@ -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 */

View File

@ -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);

View File

@ -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;
}

View File

@ -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 */

View File

@ -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__ */