first
This commit is contained in:
11
rt-thread/components/drivers/core/Kconfig
Normal file
11
rt-thread/components/drivers/core/Kconfig
Normal 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
|
21
rt-thread/components/drivers/core/SConscript
Normal file
21
rt-thread/components/drivers/core/SConscript
Normal 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')
|
520
rt-thread/components/drivers/core/bus.c
Normal file
520
rt-thread/components/drivers/core/bus.c
Normal 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
|
481
rt-thread/components/drivers/core/device.c
Normal file
481
rt-thread/components/drivers/core/device.c
Normal 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 */
|
469
rt-thread/components/drivers/core/dm.c
Normal file
469
rt-thread/components/drivers/core/dm.c
Normal 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;
|
||||
}
|
||||
|
53
rt-thread/components/drivers/core/driver.c
Normal file
53
rt-thread/components/drivers/core/driver.c
Normal 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);
|
165
rt-thread/components/drivers/core/mnt.c
Normal file
165
rt-thread/components/drivers/core/mnt.c
Normal 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);
|
171
rt-thread/components/drivers/core/numa.c
Normal file
171
rt-thread/components/drivers/core/numa.c
Normal 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 */
|
202
rt-thread/components/drivers/core/platform.c
Normal file
202
rt-thread/components/drivers/core/platform.c
Normal 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);
|
256
rt-thread/components/drivers/core/platform_ofw.c
Normal file
256
rt-thread/components/drivers/core/platform_ofw.c
Normal 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;
|
||||
}
|
477
rt-thread/components/drivers/core/power_domain.c
Normal file
477
rt-thread/components/drivers/core/power_domain.c
Normal 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;
|
||||
}
|
Reference in New Issue
Block a user