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