1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2023-02-25     GuEe-GUI     the first version
9  */
10 
11 #include <rthw.h>
12 #include <rtthread.h>
13 
14 #include <ioremap.h>
15 #include <drivers/ofw_io.h>
16 #include <drivers/syscon.h>
17 #include <drivers/core/dm.h>
18 #include <drivers/platform.h>
19 
20 static RT_DEFINE_SPINLOCK(_syscon_nodes_lock);
21 static rt_list_t _syscon_nodes = RT_LIST_OBJECT_INIT(_syscon_nodes);
22 
rt_syscon_read(struct rt_syscon * syscon,rt_off_t offset,rt_uint32_t * out_val)23 rt_err_t rt_syscon_read(struct rt_syscon *syscon, rt_off_t offset, rt_uint32_t *out_val)
24 {
25     if (offset < syscon->iomem_size)
26     {
27         rt_ubase_t level = rt_spin_lock_irqsave(&syscon->rw_lock);
28 
29         *out_val = HWREG32(syscon->iomem_base + offset);
30 
31         rt_spin_unlock_irqrestore(&syscon->rw_lock, level);
32 
33         return RT_EOK;
34     }
35     else
36     {
37         return -RT_EINVAL;
38     }
39 }
40 
rt_syscon_write(struct rt_syscon * syscon,rt_off_t offset,rt_uint32_t val)41 rt_err_t rt_syscon_write(struct rt_syscon *syscon, rt_off_t offset, rt_uint32_t val)
42 {
43     if (offset < syscon->iomem_size)
44     {
45         rt_ubase_t level = rt_spin_lock_irqsave(&syscon->rw_lock);
46 
47         HWREG32(syscon->iomem_base + offset) = val;
48 
49         rt_spin_unlock_irqrestore(&syscon->rw_lock, level);
50 
51         return RT_EOK;
52     }
53     else
54     {
55         return -RT_EINVAL;
56     }
57 }
58 
rt_syscon_update_bits(struct rt_syscon * syscon,rt_off_t offset,rt_uint32_t mask,rt_uint32_t val)59 rt_err_t rt_syscon_update_bits(struct rt_syscon *syscon, rt_off_t offset, rt_uint32_t mask, rt_uint32_t val)
60 {
61     rt_err_t err;
62     rt_ubase_t level = rt_spin_lock_irqsave(&syscon->rw_lock);
63 
64     if (offset < syscon->iomem_size)
65     {
66         rt_uint32_t old_val = HWREG32(syscon->iomem_base + offset);
67 
68         old_val &= ~mask;
69 
70         HWREG32(syscon->iomem_base + offset) = old_val | val;
71 
72         err = RT_EOK;
73     }
74     else
75     {
76         err = -RT_EINVAL;
77     }
78 
79     rt_spin_unlock_irqrestore(&syscon->rw_lock, level);
80 
81     return err;
82 }
83 
84 static rt_err_t syscon_probe(struct rt_platform_device *pdev);
85 
rt_syscon_find_by_ofw_node(struct rt_ofw_node * np)86 struct rt_syscon *rt_syscon_find_by_ofw_node(struct rt_ofw_node *np)
87 {
88     rt_ubase_t level;
89     struct rt_syscon *syscon = RT_NULL, *syscon_tmp;
90     struct rt_platform_device syscon_pdev;
91 
92     if (!np)
93     {
94         goto _exit;
95     }
96 
97     level = rt_spin_lock_irqsave(&_syscon_nodes_lock);
98 
99     /* ofw_data is not safety */
100     rt_list_for_each_entry(syscon_tmp, &_syscon_nodes, list)
101     {
102         if (syscon_tmp->np == np)
103         {
104             syscon = syscon_tmp;
105             break;
106         }
107     }
108 
109     rt_spin_unlock_irqrestore(&_syscon_nodes_lock, level);
110 
111     if (syscon)
112     {
113         goto _exit;
114     }
115 
116     /* Not found, try probe this node */
117     if (!rt_ofw_node_is_compatible(np, "syscon") &&
118         !rt_ofw_node_is_compatible(np, "simple-mfd"))
119     {
120         goto _exit;
121     }
122 
123     syscon_pdev.parent.ofw_node = np;
124 
125     if (!syscon_probe(&syscon_pdev))
126     {
127         syscon = rt_ofw_data(np);
128     }
129 
130 _exit:
131     return syscon;
132 }
133 
rt_syscon_find_by_ofw_compatible(const char * compatible)134 struct rt_syscon *rt_syscon_find_by_ofw_compatible(const char *compatible)
135 {
136     struct rt_syscon *syscon = RT_NULL;
137     struct rt_ofw_node *syscon_np = rt_ofw_find_node_by_compatible(RT_NULL, compatible);
138 
139     if (syscon_np)
140     {
141         syscon = rt_syscon_find_by_ofw_node(syscon_np);
142 
143         rt_ofw_node_put(syscon_np);
144     }
145 
146     return syscon;
147 }
148 
rt_syscon_find_by_ofw_phandle(struct rt_ofw_node * np,const char * propname)149 struct rt_syscon *rt_syscon_find_by_ofw_phandle(struct rt_ofw_node *np, const char *propname)
150 {
151     struct rt_syscon *syscon = RT_NULL;
152     struct rt_ofw_node *syscon_np = rt_ofw_parse_phandle(np, propname, 0);
153 
154     if (syscon_np)
155     {
156         syscon = rt_syscon_find_by_ofw_node(syscon_np);
157 
158         rt_ofw_node_put(syscon_np);
159     }
160 
161     return syscon;
162 }
163 
syscon_probe(struct rt_platform_device * pdev)164 static rt_err_t syscon_probe(struct rt_platform_device *pdev)
165 {
166     rt_err_t err;
167     rt_ubase_t level;
168     struct rt_ofw_node *np;
169     rt_uint64_t iomem_range[2];
170     struct rt_syscon *syscon = rt_calloc(1, sizeof(*syscon));
171 
172     if (!syscon)
173     {
174         return -RT_ENOMEM;
175     }
176 
177     np = pdev->parent.ofw_node;
178 
179     if ((err = rt_ofw_get_address(np, 0, &iomem_range[0], &iomem_range[1])))
180     {
181         goto _fail;
182     }
183 
184     syscon->iomem_size = (rt_size_t)iomem_range[1];
185     syscon->iomem_base = rt_ioremap((void *)iomem_range[0], syscon->iomem_size);
186 
187     if (!syscon->iomem_base)
188     {
189         goto _fail;
190     }
191 
192     rt_list_init(&syscon->list);
193     level = rt_spin_lock_irqsave(&_syscon_nodes_lock);
194     rt_list_insert_after(&_syscon_nodes, &syscon->list);
195     rt_spin_unlock_irqrestore(&_syscon_nodes_lock, level);
196 
197     rt_spin_lock_init(&syscon->rw_lock);
198 
199     pdev->parent.user_data = syscon;
200 
201     syscon->np = pdev->parent.ofw_node;
202     rt_ofw_data(np) = syscon;
203 
204     return RT_EOK;
205 
206 _fail:
207     rt_free(syscon);
208 
209     return err;
210 }
211 
syscon_remove(struct rt_platform_device * pdev)212 static rt_err_t syscon_remove(struct rt_platform_device *pdev)
213 {
214     struct rt_syscon *syscon = pdev->parent.user_data;
215 
216     rt_iounmap(syscon->iomem_base);
217 
218     rt_free(syscon);
219 
220     return RT_EOK;
221 }
222 
223 static const struct rt_ofw_node_id syscon_ofw_ids[] =
224 {
225     { .compatible = "syscon" },
226     { /* sentinel */ }
227 };
228 
229 static struct rt_platform_driver syscon_driver =
230 {
231     .name = "mfd-syscon",
232     .ids = syscon_ofw_ids,
233 
234     .probe = syscon_probe,
235     .remove = syscon_remove,
236 };
237 
syscon_drv_register(void)238 static int syscon_drv_register(void)
239 {
240     rt_platform_driver_register(&syscon_driver);
241 
242     return 0;
243 }
244 INIT_SUBSYS_EXPORT(syscon_drv_register);
245