diff --git a/components/drivers/Kconfig b/components/drivers/Kconfig index 8b69868205..ab7e1c956c 100755 --- a/components/drivers/Kconfig +++ b/components/drivers/Kconfig @@ -22,6 +22,7 @@ rsource "graphic/Kconfig" rsource "hwcrypto/Kconfig" rsource "wlan/Kconfig" rsource "virtio/Kconfig" +rsource "mfd/Kconfig" rsource "ofw/Kconfig" rsource "pci/Kconfig" rsource "pic/Kconfig" diff --git a/components/drivers/include/drivers/syscon.h b/components/drivers/include/drivers/syscon.h new file mode 100755 index 0000000000..15bb98b6a2 --- /dev/null +++ b/components/drivers/include/drivers/syscon.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#ifndef __SYSCON_H__ +#define __SYSCON_H__ + +#include + +struct rt_syscon +{ + rt_list_t list; + + struct rt_ofw_node *np; + + void *iomem_base; + rt_size_t iomem_size; + struct rt_spinlock rw_lock; +}; + +rt_err_t rt_syscon_read(struct rt_syscon *syscon, rt_off_t offset, rt_uint32_t *out_val); +rt_err_t rt_syscon_write(struct rt_syscon *syscon, rt_off_t offset, rt_uint32_t val); +rt_err_t rt_syscon_update_bits(struct rt_syscon *syscon, rt_off_t offset, rt_uint32_t mask, rt_uint32_t val); + +struct rt_syscon *rt_syscon_find_by_ofw_node(struct rt_ofw_node *np); +struct rt_syscon *rt_syscon_find_by_ofw_compatible(const char *compatible); +struct rt_syscon *rt_syscon_find_by_ofw_phandle(struct rt_ofw_node *np, const char *propname); + +#endif /* __SYSCON_H__ */ diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h index a9a8515b7f..2a30d8f0d5 100644 --- a/components/drivers/include/rtdevice.h +++ b/components/drivers/include/rtdevice.h @@ -56,6 +56,10 @@ extern "C" { #ifdef RT_USING_PIC #include "drivers/pic.h" #endif + +#ifdef RT_MFD_SYSCON +#include "drivers/syscon.h" +#endif #endif /* RT_USING_DM */ #ifdef RT_USING_RTC diff --git a/components/drivers/mfd/Kconfig b/components/drivers/mfd/Kconfig new file mode 100644 index 0000000000..0b12ca42ba --- /dev/null +++ b/components/drivers/mfd/Kconfig @@ -0,0 +1,10 @@ +menuconfig RT_USING_MFD + bool "Using Multifunction device drivers" + depends on RT_USING_DM + default n + +config RT_MFD_SYSCON + bool "System Controller Register R/W" + depends on RT_USING_MFD + depends on RT_USING_OFW + default y diff --git a/components/drivers/mfd/SConscript b/components/drivers/mfd/SConscript new file mode 100755 index 0000000000..f952f68987 --- /dev/null +++ b/components/drivers/mfd/SConscript @@ -0,0 +1,17 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_MFD']): + Return('group') + +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] +src = [] + +if GetDepend(['RT_MFD_SYSCON']): + src += ['mfd-syscon.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/mfd/mfd-syscon.c b/components/drivers/mfd/mfd-syscon.c new file mode 100644 index 0000000000..1778906383 --- /dev/null +++ b/components/drivers/mfd/mfd-syscon.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include + +#include +#include +#include +#include +#include + +static struct rt_spinlock _syscon_nodes_lock = { 0 }; +static rt_list_t _syscon_nodes = RT_LIST_OBJECT_INIT(_syscon_nodes); + +rt_err_t rt_syscon_read(struct rt_syscon *syscon, rt_off_t offset, rt_uint32_t *out_val) +{ + if (offset < syscon->iomem_size) + { + rt_ubase_t level = rt_spin_lock_irqsave(&syscon->rw_lock); + + *out_val = HWREG32(syscon->iomem_base + offset); + + rt_spin_unlock_irqrestore(&syscon->rw_lock, level); + + return RT_EOK; + } + else + { + return -RT_EINVAL; + } +} + +rt_err_t rt_syscon_write(struct rt_syscon *syscon, rt_off_t offset, rt_uint32_t val) +{ + if (offset < syscon->iomem_size) + { + rt_ubase_t level = rt_spin_lock_irqsave(&syscon->rw_lock); + + HWREG32(syscon->iomem_base + offset) = val; + + rt_spin_unlock_irqrestore(&syscon->rw_lock, level); + + return RT_EOK; + } + else + { + return -RT_EINVAL; + } +} + +rt_err_t rt_syscon_update_bits(struct rt_syscon *syscon, rt_off_t offset, rt_uint32_t mask, rt_uint32_t val) +{ + rt_err_t err; + rt_ubase_t level = rt_spin_lock_irqsave(&syscon->rw_lock); + + if (offset < syscon->iomem_size) + { + rt_uint32_t old_val = HWREG32(syscon->iomem_base + offset); + + old_val &= ~mask; + + HWREG32(syscon->iomem_base + offset) = old_val | val; + + err = RT_EOK; + } + else + { + err = -RT_EINVAL; + } + + rt_spin_unlock_irqrestore(&syscon->rw_lock, level); + + return err; +} + +static rt_err_t syscon_probe(struct rt_platform_device *pdev); + +struct rt_syscon *rt_syscon_find_by_ofw_node(struct rt_ofw_node *np) +{ + rt_ubase_t level; + struct rt_syscon *syscon = RT_NULL, *syscon_tmp; + struct rt_platform_device syscon_pdev; + + if (!np) + { + goto _exit; + } + + level = rt_spin_lock_irqsave(&_syscon_nodes_lock); + + /* ofw_data is not safety */ + rt_list_for_each_entry(syscon_tmp, &_syscon_nodes, list) + { + if (syscon_tmp->np == np) + { + syscon = syscon_tmp; + break; + } + } + + rt_spin_unlock_irqrestore(&_syscon_nodes_lock, level); + + if (syscon) + { + goto _exit; + } + + /* Not found, try probe this node */ + if (!rt_ofw_node_is_compatible(np, "syscon") && + !rt_ofw_node_is_compatible(np, "simple-mfd")) + { + goto _exit; + } + + syscon_pdev.parent.ofw_node = np; + + if (!syscon_probe(&syscon_pdev)) + { + syscon = rt_ofw_data(np); + } + +_exit: + return syscon; +} + +struct rt_syscon *rt_syscon_find_by_ofw_compatible(const char *compatible) +{ + struct rt_syscon *syscon = RT_NULL; + struct rt_ofw_node *syscon_np = rt_ofw_find_node_by_compatible(RT_NULL, compatible); + + if (syscon_np) + { + syscon = rt_syscon_find_by_ofw_node(syscon_np); + + rt_ofw_node_put(syscon_np); + } + + return syscon; +} + +struct rt_syscon *rt_syscon_find_by_ofw_phandle(struct rt_ofw_node *np, const char *propname) +{ + struct rt_syscon *syscon = RT_NULL; + struct rt_ofw_node *syscon_np = rt_ofw_parse_phandle(np, propname, 0); + + if (syscon_np) + { + syscon = rt_syscon_find_by_ofw_node(syscon_np); + + rt_ofw_node_put(syscon_np); + } + + return syscon; +} + +static rt_err_t syscon_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_ubase_t level; + struct rt_ofw_node *np; + rt_uint64_t iomem_range[2]; + struct rt_syscon *syscon = rt_calloc(1, sizeof(*syscon)); + + if (!syscon) + { + return -RT_ENOMEM; + } + + np = pdev->parent.ofw_node; + + if ((err = rt_ofw_get_address(np, 0, &iomem_range[0], &iomem_range[1]))) + { + goto _fail; + } + + syscon->iomem_size = (rt_size_t)iomem_range[1]; + syscon->iomem_base = rt_ioremap((void *)iomem_range[0], syscon->iomem_size); + + if (!syscon->iomem_base) + { + goto _fail; + } + + rt_list_init(&syscon->list); + level = rt_spin_lock_irqsave(&_syscon_nodes_lock); + rt_list_insert_after(&_syscon_nodes, &syscon->list); + rt_spin_unlock_irqrestore(&_syscon_nodes_lock, level); + + rt_spin_lock_init(&syscon->rw_lock); + + pdev->parent.user_data = syscon; + + syscon->np = pdev->parent.ofw_node; + rt_ofw_data(np) = syscon; + + return RT_EOK; + +_fail: + rt_free(syscon); + + return err; +} + +static rt_err_t syscon_remove(struct rt_platform_device *pdev) +{ + struct rt_syscon *syscon = pdev->parent.user_data; + + rt_iounmap(syscon->iomem_base); + + rt_free(syscon); + + return RT_EOK; +} + +static const struct rt_ofw_node_id syscon_ofw_ids[] = +{ + { .compatible = "syscon" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver syscon_driver = +{ + .name = "mfd-syscon", + .ids = syscon_ofw_ids, + + .probe = syscon_probe, + .remove = syscon_remove, +}; + +static int syscon_drv_register(void) +{ + rt_platform_driver_register(&syscon_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(syscon_drv_register);