瀏覽代碼

[DM/FEATURE] Support MFD syscon

MFD (Multifunction device) with System Controller Register Read/Write.

Signed-off-by: GuEe-GUI <2991707448@qq.com>
GuEe-GUI 6 月之前
父節點
當前提交
10cac76d3b

+ 1 - 0
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"

+ 35 - 0
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 <drivers/ofw.h>
+
+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__ */

+ 4 - 0
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

+ 10 - 0
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

+ 17 - 0
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')

+ 244 - 0
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 <rthw.h>
+#include <rtthread.h>
+
+#include <ioremap.h>
+#include <drivers/ofw_io.h>
+#include <drivers/syscon.h>
+#include <drivers/core/dm.h>
+#include <drivers/platform.h>
+
+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);