This commit is contained in:
2024-08-05 20:57:09 +08:00
commit 46d9ee7795
3020 changed files with 1725767 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
config RT_USING_DM
bool "Enable device driver model with device tree"
default n
help
Enable device driver model with device tree (FDT). It will use more memory
to parse and support device tree feature.
config RT_USING_DEV_BUS
bool "Using Device Bus device drivers"
default y if RT_USING_SMART
default n

View File

@@ -0,0 +1,21 @@
from building import *
cwd = GetCurrentDir()
src = ['device.c']
CPPPATH = [cwd + '/../include']
if GetDepend(['RT_USING_DEV_BUS']) or GetDepend(['RT_USING_DM']):
src = src + ['bus.c']
if GetDepend(['RT_USING_DM']):
src = src + ['dm.c', 'driver.c', 'numa.c', 'platform.c', 'power_domain.c']
if GetDepend(['RT_USING_DFS']):
src += ['mnt.c'];
if GetDepend(['RT_USING_OFW']):
src += ['platform_ofw.c']
group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_DEVICE'], CPPPATH = CPPPATH)
Return('group')

View File

@@ -0,0 +1,520 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-10-13 flybreak the first version
* 2023-04-12 ErikChan support rt_bus
*/
#include <rtthread.h>
#include <string.h>
#include <stdlib.h>
#define DBG_TAG "dev_bus"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#ifdef RT_USING_DEV_BUS
#if defined(RT_USING_POSIX_DEVIO)
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <dfs_file.h>
static int bus_fops_open(struct dfs_file *fd)
{
LOG_D("bus fops open");
return 0;
}
static int bus_fops_close(struct dfs_file *fd)
{
LOG_D("bus fops close");
return 0;
}
static const struct dfs_file_ops bus_fops =
{
bus_fops_open,
bus_fops_close,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
};
#endif
rt_device_t rt_device_bus_create(char *name, int attach_size)
{
rt_err_t result = RT_EOK;
rt_device_t dev = rt_device_create(RT_Device_Class_Bus, 0);
result = rt_device_register(dev, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE);
if (result < 0)
{
rt_kprintf("dev bus [%s] register failed!, ret=%d\n", name, result);
return RT_NULL;
}
#if defined(RT_USING_POSIX_DEVIO)
dev->fops = &bus_fops;
#endif
LOG_D("bus create");
return dev;
}
rt_err_t rt_device_bus_destroy(rt_device_t dev)
{
rt_device_unregister(dev);
dev->parent.type = RT_Object_Class_Device;
rt_device_destroy(dev);
LOG_D("bus destroy");
return RT_EOK;
}
#endif
#ifdef RT_USING_DM
#include <drivers/core/bus.h>
static struct rt_spinlock bus_lock = {};
static rt_list_t bus_nodes = RT_LIST_OBJECT_INIT(bus_nodes);
static void _dm_bus_lock(struct rt_spinlock *spinlock)
{
rt_hw_spin_lock(&spinlock->lock);
}
static void _dm_bus_unlock(struct rt_spinlock *spinlock)
{
rt_hw_spin_unlock(&spinlock->lock);
}
/**
* @brief This function loop the dev_list of the bus, and call fn in each loop
*
* @param bus the target bus
*
* @param data the data push when call fn
*
* @param fn the function callback in each loop
*
* @return the error code, RT_EOK on added successfully.
*/
rt_err_t rt_bus_for_each_dev(rt_bus_t bus, void *data, int (*fn)(rt_device_t dev, void *))
{
rt_device_t dev;
rt_err_t err = -RT_EEMPTY;
rt_list_t *dev_list;
struct rt_spinlock *dev_lock;
RT_ASSERT(bus != RT_NULL);
dev_list = &bus->dev_list;
dev_lock = &bus->dev_lock;
_dm_bus_lock(dev_lock);
dev = rt_list_entry(dev_list->next, struct rt_device, node);
_dm_bus_unlock(dev_lock);
while (&dev->node != dev_list)
{
if (!fn(dev, data))
{
err = RT_EOK;
break;
}
_dm_bus_lock(dev_lock);
dev = rt_list_entry(dev->node.next, struct rt_device, node);
_dm_bus_unlock(dev_lock);
}
return err;
}
/**
* @brief This function loop the drv_list of the bus, and call fn in each loop
*
* @param bus the target bus
*
* @param data the data push when call fn
*
* @param fn the function callback in each loop
*
* @return the error code, RT_EOK on added successfully.
*/
rt_err_t rt_bus_for_each_drv(rt_bus_t bus, void *data, int (*fn)(rt_driver_t drv, void *))
{
rt_driver_t drv;
rt_err_t err = -RT_EEMPTY;
rt_list_t *drv_list;
struct rt_spinlock *drv_lock;
RT_ASSERT(bus != RT_NULL);
drv_list = &bus->drv_list;
drv_lock = &bus->drv_lock;
_dm_bus_lock(drv_lock);
drv = rt_list_entry(drv_list->next, struct rt_driver, node);
_dm_bus_unlock(drv_lock);
while (&drv->node != drv_list)
{
if (!fn(drv, data))
{
err = RT_EOK;
break;
}
_dm_bus_lock(drv_lock);
drv = rt_list_entry(drv->node.next, struct rt_driver, node);
_dm_bus_unlock(drv_lock);
}
return err;
}
static rt_err_t bus_probe(rt_driver_t drv, rt_device_t dev)
{
rt_bus_t bus = drv->bus;
rt_err_t err = -RT_EEMPTY;
if (!bus)
{
bus = dev->bus;
}
if (!dev->drv && bus->match(drv, dev))
{
dev->drv = drv;
err = bus->probe(dev);
if (err)
{
dev->drv = RT_NULL;
}
}
return err;
}
static int bus_probe_driver(rt_device_t dev, void *drv_ptr)
{
bus_probe(drv_ptr, dev);
/*
* The driver is shared by multiple devices,
* so we always return the '1' to enumerate all devices.
*/
return 1;
}
static int bus_probe_device(rt_driver_t drv, void *dev_ptr)
{
rt_err_t err;
err = bus_probe(drv, dev_ptr);
if (!err)
{
rt_bus_t bus = drv->bus;
_dm_bus_lock(&bus->drv_lock);
++drv->ref_count;
_dm_bus_unlock(&bus->drv_lock);
}
return err;
}
/**
* @brief This function add a driver to the drv_list of a specific bus
*
* @param bus the bus to add
*
* @param drv the driver to be added
*
* @return the error code, RT_EOK on added successfully.
*/
rt_err_t rt_bus_add_driver(rt_bus_t bus, rt_driver_t drv)
{
RT_ASSERT(bus != RT_NULL);
RT_ASSERT(drv != RT_NULL);
drv->bus = bus;
rt_list_init(&drv->node);
_dm_bus_lock(&bus->drv_lock);
rt_list_insert_before(&bus->drv_list, &drv->node);
_dm_bus_unlock(&bus->drv_lock);
rt_bus_for_each_dev(bus, drv, bus_probe_driver);
return RT_EOK;
}
/**
* @brief This function add a device to the dev_list of a specific bus
*
* @param bus the bus to add
*
* @param dev the device to be added
*
* @return the error code, RT_EOK on added successfully.
*/
rt_err_t rt_bus_add_device(rt_bus_t bus, rt_device_t dev)
{
RT_ASSERT(bus != RT_NULL);
RT_ASSERT(dev != RT_NULL);
dev->bus = bus;
rt_list_init(&dev->node);
_dm_bus_lock(&bus->dev_lock);
rt_list_insert_before(&bus->dev_list, &dev->node);
_dm_bus_unlock(&bus->dev_lock);
rt_bus_for_each_drv(bus, dev, bus_probe_device);
return RT_EOK;
}
/**
* @brief This function remove a driver from bus
*
* @param drv the driver to be removed
*
* @return the error code, RT_EOK on added successfully.
*/
rt_err_t rt_bus_remove_driver(rt_driver_t drv)
{
rt_err_t err;
rt_bus_t bus;
RT_ASSERT(drv != RT_NULL);
RT_ASSERT(drv->bus != RT_NULL);
bus = drv->bus;
LOG_D("Bus(%s) remove driver %s", bus->name, drv->parent.name);
_dm_bus_lock(&bus->drv_lock);
if (drv->ref_count)
{
err = -RT_EBUSY;
}
else
{
rt_list_remove(&drv->node);
err = RT_EOK;
}
_dm_bus_unlock(&bus->drv_lock);
return err;
}
/**
* @brief This function remove a device from bus
*
* @param dev the device to be removed
*
* @return the error code, RT_EOK on added successfully.
*/
rt_err_t rt_bus_remove_device(rt_device_t dev)
{
rt_bus_t bus;
rt_driver_t drv;
rt_err_t err = RT_EOK;
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(dev->bus != RT_NULL);
bus = dev->bus;
drv = dev->drv;
LOG_D("Bus(%s) remove device %s", bus->name, dev->parent.name);
_dm_bus_lock(&bus->dev_lock);
rt_list_remove(&dev->node);
_dm_bus_unlock(&bus->dev_lock);
if (dev->bus->remove)
{
err = dev->bus->remove(dev);
}
else if (drv)
{
if (drv->shutdown)
{
err = drv->shutdown(dev);
}
/* device and driver are in the same bus */
_dm_bus_lock(&bus->drv_lock);
--drv->ref_count;
_dm_bus_unlock(&bus->drv_lock);
}
return err;
}
struct bus_shutdown_info
{
rt_bus_t bus;
rt_err_t err;
};
static int device_shutdown(rt_device_t dev, void *info_ptr)
{
rt_bus_t bus;
rt_err_t err = RT_EOK;
struct bus_shutdown_info *info = info_ptr;
bus = info->bus;
if (bus->shutdown)
{
LOG_D("Device(%s) shutdown", dev->parent.name);
err = bus->shutdown(dev);
LOG_D(" Result: %s", rt_strerror(err));
}
else if (dev->drv && dev->drv->shutdown)
{
LOG_D("Device(%s) shutdown", dev->parent.name);
err = dev->drv->shutdown(dev);
LOG_D(" Result: %s", rt_strerror(err));
}
if (err)
{
/* Only get the last one while system not crash */
info->err = err;
}
/* Go on, we want to ask all devices to shutdown */
return 1;
}
/**
* @brief This function call all buses' shutdown
*
* @return the error code, RT_EOK on shutdown successfully.
*/
rt_err_t rt_bus_shutdown(void)
{
rt_bus_t bus = RT_NULL;
struct bus_shutdown_info info =
{
.err = RT_EOK,
};
_dm_bus_lock(&bus_lock);
rt_list_for_each_entry(bus, &bus_nodes, list)
{
info.bus = bus;
rt_bus_for_each_dev(bus, &info, device_shutdown);
}
_dm_bus_unlock(&bus_lock);
return info.err;
}
/**
* @brief This function find a bus by name
* @param bus the name to be finded
*
* @return the bus finded by name.
*/
rt_bus_t rt_bus_find_by_name(const char *name)
{
rt_bus_t bus = RT_NULL;
RT_ASSERT(name != RT_NULL);
_dm_bus_lock(&bus_lock);
rt_list_for_each_entry(bus, &bus_nodes, list)
{
if (!rt_strncmp(bus->name, name, RT_NAME_MAX))
{
break;
}
}
_dm_bus_unlock(&bus_lock);
return bus;
}
/**
* @brief This function transfer dev_list and drv_list to the other bus
*
* @param new_bus the bus to transfer
*
* @param dev the target device
*
* @return the error code, RT_EOK on added successfully.
*/
rt_err_t rt_bus_reload_driver_device(rt_bus_t new_bus, rt_device_t dev)
{
rt_bus_t old_bus;
RT_ASSERT(new_bus != RT_NULL);
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(dev->bus != RT_NULL);
RT_ASSERT(dev->bus != new_bus);
old_bus = dev->bus;
_dm_bus_lock(&old_bus->dev_lock);
rt_list_remove(&dev->node);
_dm_bus_unlock(&old_bus->dev_lock);
return rt_bus_add_device(new_bus, dev);
}
/**
* @brief This function register a bus
* @param bus the bus to be registered
*
* @return the error code, RT_EOK on registeration successfully.
*/
rt_err_t rt_bus_register(rt_bus_t bus)
{
RT_ASSERT(bus != RT_NULL);
rt_list_init(&bus->list);
rt_list_init(&bus->dev_list);
rt_list_init(&bus->drv_list);
rt_spin_lock_init(&bus->dev_lock);
rt_spin_lock_init(&bus->drv_lock);
_dm_bus_lock(&bus_lock);
rt_list_insert_before(&bus_nodes, &bus->list);
_dm_bus_unlock(&bus_lock);
return RT_EOK;
}
#endif

View File

@@ -0,0 +1,481 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2007-01-21 Bernard the first version
* 2010-05-04 Bernard add rt_device_init implementation
* 2012-10-20 Bernard add device check in register function,
* provided by Rob <rdent@iinet.net.au>
* 2012-12-25 Bernard return RT_EOK if the device interface not exist.
* 2013-07-09 Grissiom add ref_count support
* 2016-04-02 Bernard fix the open_flag initialization issue.
* 2021-03-19 Meco Man remove rt_device_init_all()
*/
#include <rtthread.h>
#define DBG_TAG "kernel.device"
#ifdef RT_DEBUG_DEVICE
#define DBG_LVL DBG_LOG
#else
#define DBG_LVL DBG_WARNING
#endif /* defined (RT_DEBUG_DEVICE) */
#include <rtdbg.h>
#ifdef RT_USING_POSIX_DEVIO
#include <rtdevice.h> /* for wqueue_init */
#endif /* RT_USING_POSIX_DEVIO */
#ifdef RT_USING_DFS_V2
#include <devfs.h>
#endif /* RT_USING_DFS_V2 */
#ifdef RT_USING_DEVICE
#ifdef RT_USING_DEVICE_OPS
#define device_init (dev->ops ? dev->ops->init : RT_NULL)
#define device_open (dev->ops ? dev->ops->open : RT_NULL)
#define device_close (dev->ops ? dev->ops->close : RT_NULL)
#define device_read (dev->ops ? dev->ops->read : RT_NULL)
#define device_write (dev->ops ? dev->ops->write : RT_NULL)
#define device_control (dev->ops ? dev->ops->control : RT_NULL)
#else
#define device_init (dev->init)
#define device_open (dev->open)
#define device_close (dev->close)
#define device_read (dev->read)
#define device_write (dev->write)
#define device_control (dev->control)
#endif /* RT_USING_DEVICE_OPS */
/**
* @brief This function registers a device driver with a specified name.
*
* @param dev is the pointer of device driver structure.
*
* @param name is the device driver's name.
*
* @param flags is the capabilities flag of device.
*
* @return the error code, RT_EOK on initialization successfully.
*/
rt_err_t rt_device_register(rt_device_t dev,
const char *name,
rt_uint16_t flags)
{
if (dev == RT_NULL)
return -RT_ERROR;
if (rt_device_find(name) != RT_NULL)
return -RT_ERROR;
rt_object_init(&(dev->parent), RT_Object_Class_Device, name);
dev->flag = flags;
dev->ref_count = 0;
dev->open_flag = 0;
#ifdef RT_USING_POSIX_DEVIO
dev->fops = RT_NULL;
rt_wqueue_init(&(dev->wait_queue));
#endif /* RT_USING_POSIX_DEVIO */
#ifdef RT_USING_DFS_V2
dfs_devfs_device_add(dev);
#endif /* RT_USING_DFS_V2 */
return RT_EOK;
}
RTM_EXPORT(rt_device_register);
/**
* @brief This function removes a previously registered device driver.
*
* @param dev is the pointer of device driver structure.
*
* @return the error code, RT_EOK on successfully.
*/
rt_err_t rt_device_unregister(rt_device_t dev)
{
/* parameter check */
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
RT_ASSERT(rt_object_is_systemobject(&dev->parent));
rt_object_detach(&(dev->parent));
return RT_EOK;
}
RTM_EXPORT(rt_device_unregister);
/**
* @brief This function finds a device driver by specified name.
*
* @param name is the device driver's name.
*
* @return the registered device driver on successful, or RT_NULL on failure.
*/
rt_device_t rt_device_find(const char *name)
{
return (rt_device_t)rt_object_find(name, RT_Object_Class_Device);
}
RTM_EXPORT(rt_device_find);
#ifdef RT_USING_HEAP
/**
* @brief This function creates a device object with user data size.
*
* @param type is the type of the device object.
*
* @param attach_size is the size of user data.
*
* @return the allocated device object, or RT_NULL when failed.
*/
rt_device_t rt_device_create(int type, int attach_size)
{
int size;
rt_device_t device;
size = RT_ALIGN(sizeof(struct rt_device), RT_ALIGN_SIZE);
attach_size = RT_ALIGN(attach_size, RT_ALIGN_SIZE);
/* use the total size */
size += attach_size;
device = (rt_device_t)rt_malloc(size);
if (device)
{
rt_memset(device, 0x0, sizeof(struct rt_device));
device->type = (enum rt_device_class_type)type;
}
return device;
}
RTM_EXPORT(rt_device_create);
/**
* @brief This function destroy the specific device object.
*
* @param dev is a specific device object.
*/
void rt_device_destroy(rt_device_t dev)
{
/* parameter check */
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
RT_ASSERT(rt_object_is_systemobject(&dev->parent) == RT_FALSE);
rt_object_detach(&(dev->parent));
/* release this device object */
rt_free(dev);
}
RTM_EXPORT(rt_device_destroy);
#endif /* RT_USING_HEAP */
/**
* @brief This function will initialize the specified device.
*
* @param dev is the pointer of device driver structure.
*
* @return the result, RT_EOK on successfully.
*/
rt_err_t rt_device_init(rt_device_t dev)
{
rt_err_t result = RT_EOK;
RT_ASSERT(dev != RT_NULL);
/* get device_init handler */
if (device_init != RT_NULL)
{
if (!(dev->flag & RT_DEVICE_FLAG_ACTIVATED))
{
result = device_init(dev);
if (result != RT_EOK)
{
LOG_E("To initialize device:%s failed. The error code is %d",
dev->parent.name, result);
}
else
{
dev->flag |= RT_DEVICE_FLAG_ACTIVATED;
}
}
}
return result;
}
/**
* @brief This function will open a device.
*
* @param dev is the pointer of device driver structure.
*
* @param oflag is the flags for device open.
*
* @return the result, RT_EOK on successfully.
*/
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag)
{
rt_err_t result = RT_EOK;
/* parameter check */
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
/* if device is not initialized, initialize it. */
if (!(dev->flag & RT_DEVICE_FLAG_ACTIVATED))
{
if (device_init != RT_NULL)
{
result = device_init(dev);
if (result != RT_EOK)
{
LOG_E("To initialize device:%s failed. The error code is %d",
dev->parent.name, result);
return result;
}
}
dev->flag |= RT_DEVICE_FLAG_ACTIVATED;
}
/* device is a stand alone device and opened */
if ((dev->flag & RT_DEVICE_FLAG_STANDALONE) &&
(dev->open_flag & RT_DEVICE_OFLAG_OPEN))
{
return -RT_EBUSY;
}
/* device is not opened or opened by other oflag, call device_open interface */
if (!(dev->open_flag & RT_DEVICE_OFLAG_OPEN) ||
((dev->open_flag & RT_DEVICE_OFLAG_MASK) != (oflag & RT_DEVICE_OFLAG_MASK)))
{
if (device_open != RT_NULL)
{
result = device_open(dev, oflag);
}
else
{
/* set open flag */
dev->open_flag = (oflag & RT_DEVICE_OFLAG_MASK);
}
}
/* set open flag */
if (result == RT_EOK || result == -RT_ENOSYS)
{
dev->open_flag |= RT_DEVICE_OFLAG_OPEN;
dev->ref_count++;
/* don't let bad things happen silently. If you are bitten by this assert,
* please set the ref_count to a bigger type. */
RT_ASSERT(dev->ref_count != 0);
}
return result;
}
RTM_EXPORT(rt_device_open);
/**
* @brief This function will close a device.
*
* @param dev is the pointer of device driver structure.
*
* @return the result, RT_EOK on successfully.
*/
rt_err_t rt_device_close(rt_device_t dev)
{
rt_err_t result = RT_EOK;
/* parameter check */
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
if (dev->ref_count == 0)
return -RT_ERROR;
dev->ref_count--;
if (dev->ref_count != 0)
return RT_EOK;
/* call device_close interface */
if (device_close != RT_NULL)
{
result = device_close(dev);
}
/* set open flag */
if (result == RT_EOK || result == -RT_ENOSYS)
dev->open_flag = RT_DEVICE_OFLAG_CLOSE;
return result;
}
RTM_EXPORT(rt_device_close);
/**
* @brief This function will read some data from a device.
*
* @param dev is the pointer of device driver structure.
*
* @param pos is the position when reading.
*
* @param buffer is a data buffer to save the read data.
*
* @param size is the size of buffer.
*
* @return the actually read size on successful, otherwise 0 will be returned.
*
* @note the unit of size/pos is a block for block device.
*/
rt_ssize_t rt_device_read(rt_device_t dev,
rt_off_t pos,
void *buffer,
rt_size_t size)
{
/* parameter check */
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
if (dev->ref_count == 0)
{
rt_set_errno(-RT_ERROR);
return 0;
}
/* call device_read interface */
if (device_read != RT_NULL)
{
return device_read(dev, pos, buffer, size);
}
/* set error code */
rt_set_errno(-RT_ENOSYS);
return 0;
}
RTM_EXPORT(rt_device_read);
/**
* @brief This function will write some data to a device.
*
* @param dev is the pointer of device driver structure.
*
* @param pos is the position when writing.
*
* @param buffer is the data buffer to be written to device.
*
* @param size is the size of buffer.
*
* @return the actually written size on successful, otherwise 0 will be returned.
*
* @note the unit of size/pos is a block for block device.
*/
rt_ssize_t rt_device_write(rt_device_t dev,
rt_off_t pos,
const void *buffer,
rt_size_t size)
{
/* parameter check */
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
if (dev->ref_count == 0)
{
rt_set_errno(-RT_ERROR);
return 0;
}
/* call device_write interface */
if (device_write != RT_NULL)
{
return device_write(dev, pos, buffer, size);
}
/* set error code */
rt_set_errno(-RT_ENOSYS);
return 0;
}
RTM_EXPORT(rt_device_write);
/**
* @brief This function will perform a variety of control functions on devices.
*
* @param dev is the pointer of device driver structure.
*
* @param cmd is the command sent to device.
*
* @param arg is the argument of command.
*
* @return the result, -RT_ENOSYS for failed.
*/
rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg)
{
/* parameter check */
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
/* call device_write interface */
if (device_control != RT_NULL)
{
return device_control(dev, cmd, arg);
}
return -RT_ENOSYS;
}
RTM_EXPORT(rt_device_control);
/**
* @brief This function will set the reception indication callback function. This callback function
* is invoked when this device receives data.
*
* @param dev is the pointer of device driver structure.
*
* @param rx_ind is the indication callback function.
*
* @return RT_EOK
*/
rt_err_t rt_device_set_rx_indicate(rt_device_t dev,
rt_err_t (*rx_ind)(rt_device_t dev,
rt_size_t size))
{
/* parameter check */
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
dev->rx_indicate = rx_ind;
return RT_EOK;
}
RTM_EXPORT(rt_device_set_rx_indicate);
/**
* @brief This function will set a callback function. The callback function
* will be called when device has written data to physical hardware.
*
* @param dev is the pointer of device driver structure.
*
* @param tx_done is the indication callback function.
*
* @return RT_EOK
*/
rt_err_t rt_device_set_tx_complete(rt_device_t dev,
rt_err_t (*tx_done)(rt_device_t dev,
void *buffer))
{
/* parameter check */
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
dev->tx_complete = tx_done;
return RT_EOK;
}
RTM_EXPORT(rt_device_set_tx_complete);
#endif /* RT_USING_DEVICE */

View File

@@ -0,0 +1,469 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-04-20 ErikChan the first version
*/
#include <rtthread.h>
#ifdef RT_USING_OFW
#include <drivers/ofw_io.h>
#include <drivers/ofw_irq.h>
#endif
#include <drivers/core/dm.h>
#ifdef RT_USING_SMP
static int rti_secondary_cpu_start(void)
{
return 0;
}
INIT_EXPORT(rti_secondary_cpu_start, "6.end");
static int rti_secondary_cpu_end(void)
{
return 0;
}
INIT_EXPORT(rti_secondary_cpu_end, "7.end");
void rt_dm_secondary_cpu_init(void)
{
#ifdef RT_DEBUGING_AUTO_INIT
int result;
const struct rt_init_desc *desc;
rt_kprintf("do secondary cpu initialization.\n");
for (desc = &__rt_init_desc_rti_secondary_cpu_start; desc < &__rt_init_desc_rti_secondary_cpu_end; ++desc)
{
rt_kprintf("initialize %s", desc->fn_name);
result = desc->fn();
rt_kprintf(":%d done\n", result);
}
#else
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_secondary_cpu_start; fn_ptr < &__rt_init_rti_secondary_cpu_end; ++fn_ptr)
{
(*fn_ptr)();
}
#endif /* RT_DEBUGING_AUTO_INIT */
}
#endif /* RT_USING_SMP */
struct prefix_track
{
rt_list_t list;
int uid;
const char *prefix;
};
static struct rt_spinlock _prefix_nodes_lock = { 0 };
static rt_list_t _prefix_nodes = RT_LIST_OBJECT_INIT(_prefix_nodes);
int rt_dm_dev_set_name_auto(rt_device_t dev, const char *prefix)
{
int uid = -1;
struct prefix_track *pt = RT_NULL;
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(prefix != RT_NULL);
RT_DEBUG_NOT_IN_INTERRUPT;
rt_spin_lock(&_prefix_nodes_lock);
rt_list_for_each_entry(pt, &_prefix_nodes, list)
{
/* caller always input constants string, check ptr is faster */
if (pt->prefix == prefix || !rt_strcmp(pt->prefix, prefix))
{
uid = ++pt->uid;
break;
}
}
rt_spin_unlock(&_prefix_nodes_lock);
if (uid < 0)
{
pt = rt_malloc(sizeof(*pt));
if (!pt)
{
return -RT_ENOMEM;
}
rt_list_init(&pt->list);
pt->uid = uid = 0;
pt->prefix = prefix;
rt_spin_lock(&_prefix_nodes_lock);
rt_list_insert_before(&_prefix_nodes, &pt->list);
rt_spin_unlock(&_prefix_nodes_lock);
}
return rt_dm_dev_set_name(dev, "%s%u", prefix, uid);
}
int rt_dm_dev_get_name_id(rt_device_t dev)
{
int id = 0, len;
const char *name;
RT_ASSERT(dev != RT_NULL);
name = rt_dm_dev_get_name(dev);
len = rt_strlen(name) - 1;
name += len;
while (len --> 0)
{
if (*name < '0' || *name > '9')
{
while (*(++name))
{
id *= 10;
id += *name - '0';
}
break;
}
--name;
}
return id;
}
int rt_dm_dev_set_name(rt_device_t dev, const char *format, ...)
{
int n;
va_list arg_ptr;
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(format != RT_NULL);
va_start(arg_ptr, format);
n = rt_vsnprintf(dev->parent.name, RT_NAME_MAX, format, arg_ptr);
va_end(arg_ptr);
return n;
}
const char *rt_dm_dev_get_name(rt_device_t dev)
{
RT_ASSERT(dev != RT_NULL);
return dev->parent.name;
}
#ifdef RT_USING_OFW
#define ofw_api_call(name, ...) rt_ofw_##name(__VA_ARGS__)
#define ofw_api_call_ptr(name, ...) ofw_api_call(name, __VA_ARGS__)
#else
#define ofw_api_call(name, ...) (-RT_ENOSYS)
#define ofw_api_call_ptr(name, ...) RT_NULL
#endif
int rt_dm_dev_get_address_count(rt_device_t dev)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(get_address_count, dev->ofw_node);
}
#endif
return -RT_ENOSYS;
}
rt_err_t rt_dm_dev_get_address(rt_device_t dev, int index,
rt_uint64_t *out_address, rt_uint64_t *out_size)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(get_address, dev->ofw_node, index,
out_address, out_size);
}
#endif
return -RT_ENOSYS;
}
rt_err_t rt_dm_dev_get_address_by_name(rt_device_t dev, const char *name,
rt_uint64_t *out_address, rt_uint64_t *out_size)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(get_address_by_name, dev->ofw_node, name,
out_address, out_size);
}
#endif
return -RT_ENOSYS;
}
int rt_dm_dev_get_address_array(rt_device_t dev, int nr, rt_uint64_t *out_regs)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(get_address_array, dev->ofw_node, nr, out_regs);
}
#endif
return -RT_ENOSYS;
}
void *rt_dm_dev_iomap(rt_device_t dev, int index)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call_ptr(iomap, dev->ofw_node, index);
}
#endif
return RT_NULL;
}
void *rt_dm_dev_iomap_by_name(rt_device_t dev, const char *name)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call_ptr(iomap_by_name, dev->ofw_node, name);
}
#endif
return RT_NULL;
}
int rt_dm_dev_get_irq_count(rt_device_t dev)
{
RT_ASSERT(dev != RT_NULL);
#if defined(RT_USING_OFW) && defined(RT_USING_PIC)
if (dev->ofw_node)
{
return ofw_api_call(get_irq_count, dev->ofw_node);
}
#endif
return -RT_ENOSYS;
}
int rt_dm_dev_get_irq(rt_device_t dev, int index)
{
RT_ASSERT(dev != RT_NULL);
#if defined(RT_USING_OFW) && defined(RT_USING_PIC)
if (dev->ofw_node)
{
return ofw_api_call(get_irq, dev->ofw_node, index);
}
#endif
return -RT_ENOSYS;
}
int rt_dm_dev_get_irq_by_name(rt_device_t dev, const char *name)
{
RT_ASSERT(dev != RT_NULL);
#if defined(RT_USING_OFW) && defined(RT_USING_PIC)
if (dev->ofw_node)
{
return ofw_api_call(get_irq_by_name, dev->ofw_node, name);
}
#endif
return -RT_ENOSYS;
}
void rt_dm_dev_bind_fwdata(rt_device_t dev, void *fw_np, void *data)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (!dev->ofw_node && fw_np)
{
dev->ofw_node = fw_np;
rt_ofw_data(fw_np) = data;
}
if (dev->ofw_node == RT_NULL)
{
rt_kprintf("[%s:%s] line=%d ofw_node is NULL\r\n", __FILE__, __func__, __LINE__);
return;
}
rt_ofw_data(dev->ofw_node) = data;
#endif
}
void rt_dm_dev_unbind_fwdata(rt_device_t dev, void *fw_np)
{
RT_ASSERT(dev!= RT_NULL);
#ifdef RT_USING_OFW
void *dev_fw_np = RT_NULL;
if (!dev->ofw_node && fw_np)
{
dev_fw_np = fw_np;
rt_ofw_data(fw_np) = RT_NULL;
}
if (dev_fw_np == RT_NULL)
{
rt_kprintf("[%s:%s] line=%d dev_fw_np is NULL\r\n", __FILE__, __func__, __LINE__);
return;
}
rt_ofw_data(dev_fw_np) = RT_NULL;
#endif
}
int rt_dm_dev_prop_read_u8_array_index(rt_device_t dev, const char *propname,
int index, int nr, rt_uint8_t *out_values)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_UISNG_OFW
if (dev->ofw_node)
{
return ofw_api_call(prop_read_u8_array_index, dev->ofw_node, propname,
index, nr, out_value);
}
#endif
return -RT_ENOSYS;
}
int rt_dm_dev_prop_read_u16_array_index(rt_device_t dev, const char *propname,
int index, int nr, rt_uint16_t *out_values)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(prop_read_u16_array_index, dev->ofw_node, propname,
index, nr, out_values);
}
#endif
return -RT_ENOSYS;
}
int rt_dm_dev_prop_read_u32_array_index(rt_device_t dev, const char *propname,
int index, int nr, rt_uint32_t *out_values)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(prop_read_u32_array_index, dev->ofw_node, propname,
index, nr, out_values);
}
#endif
return -RT_ENOSYS;
}
int rt_dm_dev_prop_read_u64_array_index(rt_device_t dev, const char *propname,
int index, int nr, rt_uint64_t *out_values)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(prop_read_u64_array_index, dev->ofw_node, propname,
index, nr, out_values);
}
#endif
return -RT_ENOSYS;
}
int rt_dm_dev_prop_read_string_array_index(rt_device_t dev, const char *propname,
int index, int nr, const char **out_strings)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(prop_read_string_array_index, dev->ofw_node, propname,
index, nr, out_strings);
}
#endif
return -RT_ENOSYS;
}
int rt_dm_dev_prop_count_of_size(rt_device_t dev, const char *propname, int size)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(prop_count_of_size, dev->ofw_node, propname, size);
}
#endif
return -RT_ENOSYS;
}
int rt_dm_dev_prop_index_of_string(rt_device_t dev, const char *propname, const char *string)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(prop_index_of_string, dev->ofw_node, propname, string);
}
#endif
return -RT_ENOSYS;
}
rt_bool_t rt_dm_dev_prop_read_bool(rt_device_t dev, const char *propname)
{
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
return ofw_api_call(prop_read_bool, dev->ofw_node, propname);
}
#endif
return RT_FALSE;
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <rtthread.h>
#include <drivers/core/bus.h>
#if defined(RT_USING_POSIX_DEVIO)
#include <rtdevice.h> /* for wqueue_init */
#endif
/**
* This function attach a driver to bus
*
* @param drv the driver to be attached
*/
rt_err_t rt_driver_register(rt_driver_t drv)
{
rt_err_t ret;
struct rt_bus *bus = RT_NULL;
RT_ASSERT(drv != RT_NULL);
if (drv->bus)
{
bus = drv->bus;
ret = rt_bus_add_driver(bus, drv);
}
else
{
ret = -RT_EINVAL;
}
return ret;
}
RTM_EXPORT(rt_driver_register);
/**
* This function remove driver from system.
*
* @param drv the driver to be removed
*/
rt_err_t rt_driver_unregister(rt_driver_t drv)
{
rt_err_t ret;
ret = rt_bus_remove_driver(drv);
return ret;
}
RTM_EXPORT(rt_driver_unregister);

View File

@@ -0,0 +1,165 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-21 GuEe-GUI first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#define DBG_TAG "rtdm.mnt"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include <stdlib.h>
#include <dfs_fs.h>
#ifdef RT_USING_FINSH
#include <msh.h>
#endif
#include <ioremap.h>
#include <mm_memblock.h>
#ifdef RT_USING_OFW
#define bootargs_select rt_ofw_bootargs_select
#else
#error Platform have not kernel parameters select interfaces!
#endif
static int rootfs_mnt_init(void)
{
rt_err_t err = -RT_ERROR;
void *fsdata = RT_NULL;
const char *cromfs_type = "crom";
const char *dev = bootargs_select("root=", 0);
const char *fstype = bootargs_select("rootfstype=", 0);
const char *rw = bootargs_select("rw", 0);
if (!dev || !fstype)
{
const char *name = "initrd";
rt_uint64_t initrd_start = 0, initrd_end = 0;
struct rt_mmblk_reg *iter = RT_NULL;
rt_slist_for_each_entry(iter, &(rt_memblock_get_reserved()->reg_list), node)
{
if (rt_strcmp(iter->memreg.name, name) == 0)
{
initrd_start = iter->memreg.start;
initrd_end = iter->memreg.end;
break;
}
}
if (initrd_start && initrd_end)
{
size_t initrd_size = initrd_end - initrd_start;
if ((fsdata = rt_ioremap_cached((void *)initrd_start, initrd_size)))
{
fstype = cromfs_type;
}
}
}
if (fstype != cromfs_type && dev)
{
rt_tick_t timeout = 0;
const char *rootwait, *rootdelay = RT_NULL;
rootwait = bootargs_select("rootwait", 0);
/* Maybe it is undefined or 'rootwaitABC' */
if (!rootwait || *rootwait)
{
rootdelay = bootargs_select("rootdelay=", 0);
if (rootdelay)
{
timeout = rt_tick_from_millisecond(atoi(rootdelay));
}
rootwait = RT_NULL;
}
/*
* Delays in boot flow is a terrible behavior in RTOS, but the RT-Thread
* SDIO framework init the devices in a task that we need to wait for
* SDIO devices to init complete...
*
* WHAT THE F*CK PROBLEMS WILL HAPPENED?
*
* Your main PE, applications, services that depend on the root FS and
* the multi cores setup, init will delay, too...
*
* So, you can try to link this function to `INIT_APP_EXPORT` even later
* and remove the delays if you want to optimize the boot time and mount
* the FS auto.
*/
for (; rootdelay || rootwait; --timeout)
{
if (!rootwait && timeout == 0)
{
LOG_E("Wait for /dev/%s init time out", dev);
/*
* We don't return at once because the device driver may init OK
* when we break from this point, might as well give it another
* try.
*/
break;
}
if (rt_device_find(dev))
{
break;
}
rt_thread_mdelay(1);
}
}
if (fstype)
{
if (!(err = dfs_mount(dev, "/", fstype, rw ? 0 : ~0, fsdata)))
{
LOG_I("Mount root %s%s type=%s %s",
(dev && *dev) ? "on /dev/" : "",
(dev && *dev) ? dev : "\b",
fstype, "done");
}
else
{
LOG_W("Mount root %s%s type=%s %s",
(dev && *dev) ? "on /dev/" : "",
(dev && *dev) ? dev : "\b",
fstype, "fail");
if (fstype == cromfs_type)
{
rt_iounmap(fsdata);
}
}
}
return 0;
}
INIT_ENV_EXPORT(rootfs_mnt_init);
static int fstab_mnt_init(void)
{
mkdir("/mnt", 0755);
#ifdef RT_USING_FINSH
/* Try mount by table */
msh_exec_script("fstab.sh", 16);
#endif
LOG_I("File system initialization done");
return 0;
}
INIT_FS_EXPORT(fstab_mnt_init);

View File

@@ -0,0 +1,171 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-09-24 GuEe-GUI the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#define DBG_TAG "rtdm.numa"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include <drivers/pic.h>
struct numa_memory
{
rt_list_t list;
int nid;
rt_uint64_t start;
rt_uint64_t end;
union
{
void *ofw_node;
};
};
static rt_bool_t numa_enabled = RT_FALSE;
static int cpu_numa_map[RT_CPUS_NR] rt_section(".bss.noclean.numa");
static rt_list_t numa_memory_nodes rt_section(".bss.noclean.numa");
int rt_numa_cpu_id(int cpuid)
{
if (!numa_enabled)
{
return -RT_ENOSYS;
}
return cpuid < RT_ARRAY_SIZE(cpu_numa_map) ? cpu_numa_map[cpuid] : -RT_EINVAL;
}
int rt_numa_device_id(struct rt_device *dev)
{
rt_uint32_t nid = (rt_uint32_t)-RT_ENOSYS;
if (!numa_enabled)
{
return nid;
}
return rt_dm_dev_prop_read_u32(dev, "numa-node-id", &nid) ? : (int)nid;
}
rt_err_t rt_numa_memory_affinity(rt_uint64_t phy_addr, rt_bitmap_t *out_affinity)
{
struct numa_memory *nm;
if (!out_affinity)
{
return -RT_EINVAL;
}
if (!numa_enabled)
{
/* Default to CPU#0 */
RT_IRQ_AFFINITY_SET(out_affinity, 0);
return RT_EOK;
}
rt_memset(out_affinity, 0, sizeof(*out_affinity) * RT_BITMAP_LEN(RT_CPUS_NR));
rt_list_for_each_entry(nm, &numa_memory_nodes, list)
{
if (phy_addr >= nm->start && phy_addr < nm->end)
{
for (int i = 0; i < RT_ARRAY_SIZE(cpu_numa_map); ++i)
{
if (cpu_numa_map[i] == nm->nid)
{
RT_IRQ_AFFINITY_SET(out_affinity, i);
}
}
return RT_EOK;
}
}
return -RT_EEMPTY;
}
#ifdef RT_USING_OFW
static int numa_ofw_init(void)
{
int i = 0;
rt_uint32_t nid;
const char *numa_conf;
struct rt_ofw_node *np = RT_NULL;
numa_conf = rt_ofw_bootargs_select("numa=", 0);
if (!numa_conf || rt_strcmp(numa_conf, "on"))
{
return (int)RT_EOK;
}
numa_enabled = RT_TRUE;
for (int i = 0; i < RT_ARRAY_SIZE(cpu_numa_map); ++i)
{
cpu_numa_map[i] = -RT_ENOSYS;
}
rt_list_init(&numa_memory_nodes);
rt_ofw_foreach_cpu_node(np)
{
rt_ofw_prop_read_u32(np, "numa-node-id", (rt_uint32_t *)&cpu_numa_map[i]);
if (++i >= RT_CPUS_NR)
{
break;
}
}
rt_ofw_foreach_node_by_type(np, "memory")
{
if (!rt_ofw_prop_read_u32(np, "numa-node-id", &nid))
{
int mem_nr = rt_ofw_get_address_count(np);
for (i = 0; i < mem_nr; ++i)
{
rt_uint64_t addr, size;
struct numa_memory *nm;
if (rt_ofw_get_address(np, i, &addr, &size))
{
continue;
}
nm = rt_malloc(sizeof(*nm));
if (!nm)
{
LOG_E("No memory to record NUMA[%d] memory[%p, %p] info",
nid, addr, addr + size);
return (int)-RT_ENOMEM;
}
nm->start = addr;
nm->end = addr + size;
nm->ofw_node = np;
rt_list_init(&nm->list);
rt_list_insert_before(&numa_memory_nodes, &nm->list);
}
}
}
return 0;
}
INIT_CORE_EXPORT(numa_ofw_init);
#endif /* RT_USING_OFW */

View File

@@ -0,0 +1,202 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-04-12 ErikChan the first version
* 2023-10-13 zmshahaha distinguish ofw and none-ofw situation
*/
#include <rtthread.h>
#define DBG_TAG "rtdm.pltaform"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include <drivers/platform.h>
#include <drivers/core/bus.h>
#include <drivers/core/dm.h>
#include <drivers/core/power_domain.h>
static struct rt_bus platform_bus;
/**
* @brief This function create a platform device.
*
* @param name is name of the platform device.
*
* @return a new platform device.
*/
struct rt_platform_device *rt_platform_device_alloc(const char *name)
{
struct rt_platform_device *pdev = rt_calloc(1, sizeof(*pdev));
if (!pdev)
{
return RT_NULL;
}
pdev->parent.bus = &platform_bus;
pdev->name = name;
return pdev;
}
/**
* @brief This function register a rt_driver to platform bus.
*
* @return the error code, RT_EOK on successfully.
*/
rt_err_t rt_platform_driver_register(struct rt_platform_driver *pdrv)
{
RT_ASSERT(pdrv != RT_NULL);
pdrv->parent.bus = &platform_bus;
#if RT_NAME_MAX > 0
rt_strcpy(pdrv->parent.parent.name, pdrv->name);
#else
pdrv->parent.parent.name = pdrv->name;
#endif
return rt_driver_register(&pdrv->parent);
}
/**
* @brief This function register a rt_device to platform bus.
*
* @return the error code, RT_EOK on successfully.
*/
rt_err_t rt_platform_device_register(struct rt_platform_device *pdev)
{
RT_ASSERT(pdev != RT_NULL);
return rt_bus_add_device(&platform_bus, &pdev->parent);
}
static rt_bool_t platform_match(rt_driver_t drv, rt_device_t dev)
{
struct rt_platform_driver *pdrv = rt_container_of(drv, struct rt_platform_driver, parent);
struct rt_platform_device *pdev = rt_container_of(dev, struct rt_platform_device, parent);
#ifdef RT_USING_OFW
struct rt_ofw_node *np = dev->ofw_node;
/* 1、match with ofw node */
if (np)
{
pdev->id = rt_ofw_node_match(np, pdrv->ids);
if (pdev->id)
{
return RT_TRUE;
}
}
#endif
/* 2、match with name */
if (pdev->name && pdrv->name)
{
if (pdev->name == pdrv->name)
{
return RT_TRUE;
}
else
{
return !rt_strcmp(pdrv->name, pdev->name);
}
}
return RT_FALSE;
}
static rt_err_t platform_probe(rt_device_t dev)
{
rt_err_t err;
struct rt_platform_driver *pdrv = rt_container_of(dev->drv, struct rt_platform_driver, parent);
struct rt_platform_device *pdev = rt_container_of(dev, struct rt_platform_device, parent);
#ifdef RT_USING_OFW
struct rt_ofw_node *np = dev->ofw_node;
#endif
err = rt_dm_power_domain_attach(dev, RT_TRUE);
if (err && err != -RT_EEMPTY)
{
LOG_E("Attach power domain error = %s in device %s", pdev->name, rt_strerror(err));
return err;
}
err = pdrv->probe(pdev);
if (!err)
{
#ifdef RT_USING_OFW
if (np)
{
rt_ofw_node_set_flag(np, RT_OFW_F_READLY);
}
#endif
}
else
{
if (err == -RT_ENOMEM)
{
LOG_W("System not memory in driver %s", pdrv->name);
}
rt_dm_power_domain_detach(dev, RT_TRUE);
}
return err;
}
static rt_err_t platform_remove(rt_device_t dev)
{
struct rt_platform_driver *pdrv = rt_container_of(dev->drv, struct rt_platform_driver, parent);
struct rt_platform_device *pdev = rt_container_of(dev, struct rt_platform_device, parent);
if (pdrv && pdrv->remove)
{
pdrv->remove(pdev);
}
rt_dm_power_domain_detach(dev, RT_TRUE);
rt_platform_ofw_free(pdev);
return RT_EOK;
}
static rt_err_t platform_shutdown(rt_device_t dev)
{
struct rt_platform_driver *pdrv = rt_container_of(dev->drv, struct rt_platform_driver, parent);
struct rt_platform_device *pdev = rt_container_of(dev, struct rt_platform_device, parent);
if (pdrv && pdrv->shutdown)
{
pdrv->shutdown(pdev);
}
rt_dm_power_domain_detach(dev, RT_TRUE);
rt_platform_ofw_free(pdev);
return RT_EOK;
}
static struct rt_bus platform_bus =
{
.name = "platform",
.match = platform_match,
.probe = platform_probe,
.remove = platform_remove,
.shutdown = platform_shutdown,
};
static int platform_bus_init(void)
{
rt_bus_register(&platform_bus);
return 0;
}
INIT_CORE_EXPORT(platform_bus_init);

View File

@@ -0,0 +1,256 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-06-04 GuEe-GUI the first version
*/
#include <rtthread.h>
#define DBG_TAG "drv.platform"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include <drivers/ofw_io.h>
#include <drivers/ofw_fdt.h>
#include <drivers/platform.h>
#include <drivers/core/dm.h>
#include "../ofw/ofw_internal.h"
static const struct rt_ofw_node_id platform_ofw_ids[] =
{
{ .compatible = "simple-bus", },
#ifdef RT_USING_MFD
{ .compatible = "simple-mfd", },
#endif
#ifdef RT_USING_ISA
{ .compatible = "isa", },
#endif
#ifdef RT_USING_AMBA_BUS
/*
* Maybe ARM has replaced it with compatible: "arm,primecell" and will not
* used anymore in the future.
*/
{ .compatible = "arm,amba-bus", },
#endif
{ /* sentinel */ }
};
static void ofw_device_rename(struct rt_device *dev)
{
rt_uint32_t mask;
rt_uint64_t addr;
const char *dev_name = dev->parent.name;
struct rt_ofw_node *np = dev->ofw_node;
#if RT_NAME_MAX > 0
if (dev_name[0] == '\0')
{
dev_name = RT_NULL;
}
#endif
while (np->parent)
{
if (!rt_ofw_get_address(np, 0, &addr, RT_NULL))
{
const char *node_name = rt_fdt_node_name(np->full_name);
rt_size_t tag_len = strchrnul(node_name, '@') - node_name;
if (!rt_ofw_prop_read_u32(np, "mask", &mask))
{
rt_dm_dev_set_name(dev, dev_name ? "%lx.%x.%.*s:%s" : "%lx.%x.%.*s",
addr, __rt_ffs(mask) - 1, tag_len, node_name, dev_name);
}
else
{
rt_dm_dev_set_name(dev, dev_name ? "%lx.%.*s:%s" : "%lx.%.*s",
addr, tag_len, node_name, dev_name);
}
return;
}
rt_dm_dev_set_name(dev, dev_name ? "%s:%s" : "%s",
rt_fdt_node_name(np->full_name), dev_name);
np = np->parent;
}
}
static struct rt_platform_device *alloc_ofw_platform_device(struct rt_ofw_node *np)
{
struct rt_platform_device *pdev = rt_platform_device_alloc("");
if (pdev)
{
/* inc reference of dt-node */
rt_ofw_node_get(np);
rt_ofw_node_set_flag(np, RT_OFW_F_PLATFORM);
pdev->parent.ofw_node = np;
ofw_device_rename(&pdev->parent);
}
else
{
LOG_E("Alloc device fail for %s", rt_ofw_node_full_name(np));
}
return pdev;
}
static rt_err_t platform_ofw_device_probe_once(struct rt_ofw_node *parent_np)
{
rt_err_t err = RT_EOK;
struct rt_ofw_node *np;
struct rt_platform_device *pdev;
rt_ofw_foreach_available_child_node(parent_np, np)
{
const char *name;
struct rt_ofw_node_id *id;
struct rt_ofw_prop *compat_prop = RT_NULL;
LOG_D("%s found in %s", np->full_name, parent_np->full_name);
/* Is system node or have driver */
if (rt_ofw_node_test_flag(np, RT_OFW_F_SYSTEM) ||
rt_ofw_node_test_flag(np, RT_OFW_F_READLY))
{
continue;
}
compat_prop = rt_ofw_get_prop(np, "compatible", RT_NULL);
name = rt_ofw_node_name(np);
/* Not have name and compatible */
if (!compat_prop && (name == (const char *)"<NULL>" || !rt_strcmp(name, "<NULL>")))
{
continue;
}
id = rt_ofw_prop_match(compat_prop, platform_ofw_ids);
if (id && np->child)
{
/* scan next level */
err = platform_ofw_device_probe_once(np);
if (err)
{
rt_ofw_node_put(np);
LOG_E("%s bus probe fail", np->full_name);
break;
}
}
pdev = alloc_ofw_platform_device(np);
if (!pdev)
{
rt_ofw_node_put(np);
err = -RT_ENOMEM;
break;
}
pdev->dev_id = ofw_alias_node_id(np);
LOG_D("%s register to bus", np->full_name);
rt_platform_device_register(pdev);
}
return err;
}
rt_err_t rt_platform_ofw_device_probe_child(struct rt_ofw_node *np)
{
rt_err_t err;
struct rt_ofw_node *parent = rt_ofw_get_parent(np);
if (parent && rt_strcmp(parent->name, "/") &&
rt_ofw_get_prop(np, "compatible", RT_NULL) &&
!rt_ofw_node_test_flag(np, RT_OFW_F_PLATFORM))
{
struct rt_platform_device *pdev = alloc_ofw_platform_device(np);
if (pdev)
{
err = rt_platform_device_register(pdev);
}
else
{
err = -RT_ENOMEM;
}
}
else
{
err = -RT_EINVAL;
}
rt_ofw_node_put(parent);
return err;
}
static int platform_ofw_device_probe(void)
{
rt_err_t err = RT_EOK;
struct rt_ofw_node *node;
if (ofw_node_root)
{
err = platform_ofw_device_probe_once(ofw_node_root);
rt_ofw_node_put(ofw_node_root);
if ((node = rt_ofw_find_node_by_path("/firmware")))
{
platform_ofw_device_probe_once(node);
rt_ofw_node_put(node);
}
if ((node = rt_ofw_get_child_by_compatible(ofw_node_chosen, "simple-framebuffer")))
{
platform_ofw_device_probe_once(node);
rt_ofw_node_put(node);
}
}
else
{
err = -RT_ENOSYS;
}
return (int)err;
}
INIT_PLATFORM_EXPORT(platform_ofw_device_probe);
rt_err_t rt_platform_ofw_free(struct rt_platform_device *pdev)
{
rt_err_t err = RT_EOK;
if (pdev)
{
struct rt_ofw_node *np = pdev->parent.ofw_node;
if (np)
{
rt_ofw_node_clear_flag(np, RT_OFW_F_PLATFORM);
rt_ofw_node_put(np);
pdev->parent.ofw_node = RT_NULL;
}
}
else
{
err = -RT_EINVAL;
}
return err;
}

View File

@@ -0,0 +1,477 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-09-24 GuEe-GUI the first version
*/
#include <rtdevice.h>
#define DBG_TAG "rtdm.power_domain"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include <drivers/ofw.h>
void rt_dm_power_domain_proxy_default_name(struct rt_dm_power_domain_proxy *proxy)
{
#if RT_NAME_MAX > 0
rt_strncpy(proxy->parent.name, RT_POWER_DOMAIN_OBJ_NAME, RT_NAME_MAX);
#else
proxy->parent.name = RT_POWER_DOMAIN_OBJ_NAME;
#endif
}
void rt_dm_power_domain_proxy_ofw_bind(struct rt_dm_power_domain_proxy *proxy,
struct rt_ofw_node *np)
{
if (!proxy || !proxy->ofw_parse || !np)
{
return;
}
rt_dm_power_domain_proxy_default_name(proxy);
rt_ofw_data(np) = proxy;
}
static void dm_power_domain_init(struct rt_dm_power_domain *domain)
{
#if RT_NAME_MAX > 0
rt_strncpy(domain->parent.name, RT_POWER_DOMAIN_OBJ_NAME, RT_NAME_MAX);
#else
domain->parent.name = RT_POWER_DOMAIN_OBJ_NAME;
#endif
domain->parent_domain = RT_NULL;
rt_list_init(&domain->list);
rt_list_init(&domain->child_nodes);
rt_list_init(&domain->unit_nodes);
rt_ref_init(&domain->ref);
rt_spin_lock_init(&domain->lock);
}
static rt_bool_t dm_power_domain_is_free(struct rt_dm_power_domain *domain)
{
return rt_ref_read(&domain->ref) == 1 && !rt_list_isempty(&domain->child_nodes);
}
rt_err_t rt_dm_power_domain_register(struct rt_dm_power_domain *domain)
{
if (!domain)
{
return -RT_EINVAL;
}
dm_power_domain_init(domain);
return RT_EOK;
}
rt_err_t rt_dm_power_domain_unregister(struct rt_dm_power_domain *domain)
{
rt_err_t err = RT_EOK;
if (!domain)
{
return -RT_EINVAL;
}
if (!dm_power_domain_is_free(domain))
{
return -RT_EBUSY;
}
if (domain->parent_domain)
{
err = rt_dm_power_domain_unregister_child(domain->parent_domain, domain);
}
return err;
}
rt_err_t rt_dm_power_domain_register_child(struct rt_dm_power_domain *domain,
struct rt_dm_power_domain *child_domain)
{
if (!domain || !child_domain)
{
return -RT_EINVAL;
}
dm_power_domain_init(child_domain);
child_domain->parent_domain = domain;
return RT_EOK;
}
rt_err_t rt_dm_power_domain_unregister_child(struct rt_dm_power_domain *domain,
struct rt_dm_power_domain *child_domain)
{
rt_err_t err = RT_EOK;
if (!domain || !child_domain)
{
return -RT_EINVAL;
}
rt_hw_spin_lock(&domain->lock.lock);
if (dm_power_domain_is_free(domain))
{
rt_list_remove(&child_domain->list);
}
else
{
err = -RT_EBUSY;
}
rt_hw_spin_unlock(&domain->lock.lock);
return err;
}
rt_err_t rt_dm_power_domain_power_on(struct rt_dm_power_domain *domain)
{
rt_err_t err = RT_EOK;
struct rt_dm_power_domain *child_domain;
if (!domain)
{
return -RT_EINVAL;
}
rt_hw_spin_lock(&domain->lock.lock);
if (rt_ref_read(&domain->ref) == 1)
{
err = domain->power_on(domain);
}
if (!err)
{
struct rt_dm_power_domain *fail_domain = RT_NULL;
rt_list_for_each_entry(child_domain, &domain->child_nodes, list)
{
err = rt_dm_power_domain_power_on(child_domain);
if (err)
{
fail_domain = child_domain;
break;
}
}
if (fail_domain)
{
rt_list_for_each_entry(child_domain, &domain->child_nodes, list)
{
if (child_domain == fail_domain)
{
break;
}
rt_dm_power_domain_power_off(child_domain);
}
}
}
rt_hw_spin_unlock(&domain->lock.lock);
if (!err)
{
rt_ref_get(&domain->ref);
}
return err;
}
static void dm_power_domain_release(struct rt_ref *r)
{
struct rt_dm_power_domain *domain = rt_container_of(r, struct rt_dm_power_domain, ref);
if (domain->dev)
{
LOG_E("%s power domain is release", rt_dm_dev_get_name(domain->dev));
}
RT_ASSERT(0);
}
rt_err_t rt_dm_power_domain_power_off(struct rt_dm_power_domain *domain)
{
rt_err_t err;
struct rt_dm_power_domain *child_domain;
if (!domain)
{
return -RT_EINVAL;
}
rt_ref_put(&domain->ref, dm_power_domain_release);
rt_hw_spin_lock(&domain->lock.lock);
if (rt_ref_read(&domain->ref) == 1)
{
err = domain->power_off(domain);
}
else
{
err = -RT_EBUSY;
}
if (!err)
{
struct rt_dm_power_domain *fail_domain = RT_NULL;
rt_list_for_each_entry(child_domain, &domain->child_nodes, list)
{
err = rt_dm_power_domain_power_off(child_domain);
if (err)
{
fail_domain = child_domain;
break;
}
}
if (fail_domain)
{
rt_list_for_each_entry(child_domain, &domain->child_nodes, list)
{
if (child_domain == fail_domain)
{
break;
}
rt_dm_power_domain_power_on(child_domain);
}
}
}
rt_hw_spin_unlock(&domain->lock.lock);
if (err)
{
rt_ref_get(&domain->ref);
}
return err;
}
#ifdef RT_USING_OFW
static struct rt_dm_power_domain *ofw_find_power_domain(struct rt_device *dev,
int index, struct rt_ofw_cell_args *args)
{
struct rt_object *obj;
struct rt_dm_power_domain_proxy *proxy;
struct rt_dm_power_domain *domain = RT_NULL;
struct rt_ofw_node *np = dev->ofw_node, *power_domain_np;
if (!rt_ofw_parse_phandle_cells(np, "power-domains", "#power-domain-cells",
index, args))
{
power_domain_np = args->data;
if (power_domain_np && (obj = rt_ofw_data(power_domain_np)))
{
if (!rt_strcmp(obj->name, RT_POWER_DOMAIN_OBJ_NAME))
{
proxy = rt_container_of(obj, struct rt_dm_power_domain_proxy, parent);
domain = proxy->ofw_parse(proxy, args);
}
else if (!rt_strcmp(obj->name, RT_POWER_DOMAIN_OBJ_NAME))
{
domain = rt_container_of(obj, struct rt_dm_power_domain, parent);
}
else if ((obj = rt_ofw_parse_object(power_domain_np,
RT_POWER_DOMAIN_PROXY_OBJ_NAME, "#power-domain-cells")))
{
proxy = rt_container_of(obj, struct rt_dm_power_domain_proxy, parent);
domain = proxy->ofw_parse(proxy, args);
}
else if ((obj = rt_ofw_parse_object(power_domain_np,
RT_POWER_DOMAIN_OBJ_NAME, "#power-domain-cells")))
{
domain = rt_container_of(obj, struct rt_dm_power_domain, parent);
}
rt_ofw_node_put(power_domain_np);
}
}
return domain;
}
#else
rt_inline struct rt_dm_power_domain *ofw_find_power_domain(struct rt_device *dev,
int index, struct rt_ofw_cell_args *args)
{
return RT_NULL;
}
#endif /* RT_USING_OFW */
struct rt_dm_power_domain *rt_dm_power_domain_get_by_index(struct rt_device *dev,
int index)
{
struct rt_ofw_cell_args args;
struct rt_dm_power_domain *domain;
if (!dev || index < 0)
{
return RT_NULL;
}
if ((domain = ofw_find_power_domain(dev, index, &args)))
{
goto _end;
}
_end:
return domain;
}
struct rt_dm_power_domain *rt_dm_power_domain_get_by_name(struct rt_device *dev,
const char *name)
{
int index;
if (!dev || !name)
{
return RT_NULL;
}
if ((index = rt_dm_dev_prop_index_of_string(dev, "power-domain-names", name)) < 0)
{
LOG_E("%s find power domain %s not found", rt_dm_dev_get_name(dev));
return RT_NULL;
}
return rt_dm_power_domain_get_by_index(dev, index);
}
rt_err_t rt_dm_power_domain_put(struct rt_dm_power_domain *domain)
{
if (!domain)
{
return -RT_EINVAL;
}
return RT_EOK;
}
rt_err_t rt_dm_power_domain_attach(struct rt_device *dev, rt_bool_t on)
{
int id = -1;
rt_err_t err = RT_EOK;
struct rt_ofw_cell_args args;
struct rt_dm_power_domain *domain;
struct rt_dm_power_domain_unit *unit;
if (!dev)
{
return -RT_EINVAL;
}
/* We only attach the first one, get domains self if there are multiple domains */
if ((domain = ofw_find_power_domain(dev, 0, &args)))
{
id = args.args[0];
}
if (!domain)
{
return -RT_EEMPTY;
}
unit = rt_malloc(sizeof(*unit));
if (!unit)
{
return -RT_ENOMEM;
}
rt_list_init(&unit->list);
unit->id = id;
unit->domain = domain;
dev->power_domain_unit = unit;
rt_hw_spin_lock(&domain->lock.lock);
if (domain->attach_dev)
{
err = domain->attach_dev(domain, dev);
}
if (!err)
{
rt_list_insert_before(&domain->unit_nodes, &unit->list);
}
rt_hw_spin_unlock(&domain->lock.lock);
if (err)
{
dev->power_domain_unit = RT_NULL;
rt_free(unit);
return err;
}
if (on)
{
err = rt_dm_power_domain_power_on(domain);
}
return err;
}
rt_err_t rt_dm_power_domain_detach(struct rt_device *dev, rt_bool_t off)
{
rt_err_t err = RT_EOK;
struct rt_dm_power_domain *domain;
struct rt_dm_power_domain_unit *unit;
if (!dev || !dev->power_domain_unit)
{
return -RT_EINVAL;
}
unit = dev->power_domain_unit;
domain = unit->domain;
rt_hw_spin_lock(&domain->lock.lock);
if (domain->detach_dev)
{
err = domain->detach_dev(domain, dev);
}
if (!err)
{
rt_list_remove(&unit->list);
}
rt_hw_spin_unlock(&domain->lock.lock);
if (err)
{
return err;
}
rt_free(unit);
dev->power_domain_unit = RT_NULL;
if (off)
{
err = rt_dm_power_domain_power_off(domain);
}
return err;
}