]> Git Repo - J-u-boot.git/blob - drivers/misc/turris_omnia_mcu.c
pinctrl: renesas: Minimize R8A77970 V3M PFC tables
[J-u-boot.git] / drivers / misc / turris_omnia_mcu.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2022 Pali Rohár <[email protected]>
4  * Copyright (C) 2024 Marek Behún <[email protected]>
5  */
6
7 #include <console.h>
8 #include <dm.h>
9 #include <dm/lists.h>
10 #include <i2c.h>
11 #include <rng.h>
12 #include <sysreset.h>
13 #include <turris-omnia-mcu-interface.h>
14 #include <asm/byteorder.h>
15 #include <asm/gpio.h>
16 #include <linux/delay.h>
17 #include <linux/log2.h>
18
19 #define CMD_TRNG_MAX_ENTROPY_LEN        64
20
21 struct turris_omnia_mcu_info {
22         u32 features;
23 };
24
25 static int omnia_gpio_get_function(struct udevice *dev, uint offset)
26 {
27         struct turris_omnia_mcu_info *info = dev_get_priv(dev->parent);
28
29         switch (offset) {
30         /* bank 0 */
31         case 0 ... 15:
32                 switch (offset) {
33                 case ilog2(STS_USB30_PWRON):
34                 case ilog2(STS_USB31_PWRON):
35                 case ilog2(STS_ENABLE_4V5):
36                 case ilog2(STS_BUTTON_MODE):
37                         return GPIOF_OUTPUT;
38                 default:
39                         return GPIOF_INPUT;
40                 }
41
42         /* bank 1 - supported only when FEAT_EXT_CMDS is set */
43         case (16 + 0) ... (16 + 31):
44                 if (!(info->features & FEAT_EXT_CMDS))
45                         return -EINVAL;
46                 return GPIOF_INPUT;
47
48         /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
49         case (16 + 32 + 0) ... (16 + 32 + 15):
50                 if (!(info->features & FEAT_EXT_CMDS))
51                         return -EINVAL;
52                 if (!(info->features & FEAT_PERIPH_MCU))
53                         return -EINVAL;
54                 return GPIOF_OUTPUT;
55
56         default:
57                 return -EINVAL;
58         }
59 }
60
61 static int omnia_gpio_get_value(struct udevice *dev, uint offset)
62 {
63         struct turris_omnia_mcu_info *info = dev_get_priv(dev->parent);
64         u32 val32;
65         u16 val16;
66         int ret;
67
68         switch (offset) {
69         /* bank 0 */
70         case 0 ... 15:
71                 ret = dm_i2c_read(dev->parent, CMD_GET_STATUS_WORD,
72                                   (void *)&val16, sizeof(val16));
73                 if (ret)
74                         return ret;
75
76                 return !!(le16_to_cpu(val16) & BIT(offset));
77
78         /* bank 1 - supported only when FEAT_EXT_CMDS is set */
79         case (16 + 0) ... (16 + 31):
80                 if (!(info->features & FEAT_EXT_CMDS))
81                         return -EINVAL;
82
83                 ret = dm_i2c_read(dev->parent, CMD_GET_EXT_STATUS_DWORD,
84                                   (void *)&val32, sizeof(val32));
85                 if (ret)
86                         return ret;
87
88                 return !!(le32_to_cpu(val32) & BIT(offset - 16));
89
90         /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
91         case (16 + 32 + 0) ... (16 + 32 + 15):
92                 if (!(info->features & FEAT_EXT_CMDS))
93                         return -EINVAL;
94                 if (!(info->features & FEAT_PERIPH_MCU))
95                         return -EINVAL;
96
97                 ret = dm_i2c_read(dev->parent, CMD_GET_EXT_CONTROL_STATUS,
98                                   (void *)&val16, sizeof(val16));
99                 if (ret)
100                         return ret;
101
102                 return !!(le16_to_cpu(val16) & BIT(offset - 16 - 32));
103
104         default:
105                 return -EINVAL;
106         }
107 }
108
109 static int omnia_gpio_set_value(struct udevice *dev, uint offset, int value)
110 {
111         struct turris_omnia_mcu_info *info = dev_get_priv(dev->parent);
112         u16 valmask16[2];
113         u8 valmask8[2];
114
115         switch (offset) {
116         /* bank 0 */
117         case 0 ... 15:
118                 switch (offset) {
119                 case ilog2(STS_USB30_PWRON):
120                         valmask8[1] = CTL_USB30_PWRON;
121                         break;
122                 case ilog2(STS_USB31_PWRON):
123                         valmask8[1] = CTL_USB31_PWRON;
124                         break;
125                 case ilog2(STS_ENABLE_4V5):
126                         valmask8[1] = CTL_ENABLE_4V5;
127                         break;
128                 case ilog2(STS_BUTTON_MODE):
129                         valmask8[1] = CTL_BUTTON_MODE;
130                         break;
131                 default:
132                         return -EINVAL;
133                 }
134
135                 valmask8[0] = value ? valmask8[1] : 0;
136
137                 return dm_i2c_write(dev->parent, CMD_GENERAL_CONTROL, valmask8,
138                                     sizeof(valmask8));
139
140         /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
141         case (16 + 32 + 0) ... (16 + 32 + 15):
142                 if (!(info->features & FEAT_EXT_CMDS))
143                         return -EINVAL;
144                 if (!(info->features & FEAT_PERIPH_MCU))
145                         return -EINVAL;
146
147                 valmask16[1] = cpu_to_le16(BIT(offset - 16 - 32));
148                 valmask16[0] = value ? valmask16[1] : 0;
149
150                 return dm_i2c_write(dev->parent, CMD_EXT_CONTROL,
151                                     (void *)valmask16, sizeof(valmask16));
152
153         default:
154                 return -EINVAL;
155         }
156 }
157
158 static int omnia_gpio_direction_input(struct udevice *dev, uint offset)
159 {
160         int ret;
161
162         ret = omnia_gpio_get_function(dev, offset);
163         if (ret < 0)
164                 return ret;
165         else if (ret != GPIOF_INPUT)
166                 return -EOPNOTSUPP;
167
168         return 0;
169 }
170
171 static int omnia_gpio_direction_output(struct udevice *dev, uint offset, int value)
172 {
173         int ret;
174
175         ret = omnia_gpio_get_function(dev, offset);
176         if (ret < 0)
177                 return ret;
178         else if (ret != GPIOF_OUTPUT)
179                 return -EOPNOTSUPP;
180
181         return omnia_gpio_set_value(dev, offset, value);
182 }
183
184 static int omnia_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
185                                   struct ofnode_phandle_args *args)
186 {
187         uint bank, gpio, flags, offset;
188         int ret;
189
190         if (args->args_count != 3)
191                 return -EINVAL;
192
193         bank = args->args[0];
194         gpio = args->args[1];
195         flags = args->args[2];
196
197         switch (bank) {
198         case 0:
199                 if (gpio >= 16)
200                         return -EINVAL;
201                 offset = gpio;
202                 break;
203         case 1:
204                 if (gpio >= 32)
205                         return -EINVAL;
206                 offset = 16 + gpio;
207                 break;
208         case 2:
209                 if (gpio >= 16)
210                         return -EINVAL;
211                 offset = 16 + 32 + gpio;
212                 break;
213         default:
214                 return -EINVAL;
215         }
216
217         ret = omnia_gpio_get_function(dev, offset);
218         if (ret < 0)
219                 return ret;
220
221         desc->offset = offset;
222         desc->flags = gpio_flags_xlate(flags);
223
224         return 0;
225 }
226
227 static const struct dm_gpio_ops omnia_gpio_ops = {
228         .direction_input        = omnia_gpio_direction_input,
229         .direction_output       = omnia_gpio_direction_output,
230         .get_value              = omnia_gpio_get_value,
231         .set_value              = omnia_gpio_set_value,
232         .get_function           = omnia_gpio_get_function,
233         .xlate                  = omnia_gpio_xlate,
234 };
235
236 static int omnia_gpio_probe(struct udevice *dev)
237 {
238         struct turris_omnia_mcu_info *info = dev_get_priv(dev->parent);
239         struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
240
241         uc_priv->bank_name = "mcu_";
242
243         if ((info->features & FEAT_EXT_CMDS) && (info->features & FEAT_PERIPH_MCU))
244                 uc_priv->gpio_count = 16 + 32 + 16;
245         else if (info->features & FEAT_EXT_CMDS)
246                 uc_priv->gpio_count = 16 + 32;
247         else
248                 uc_priv->gpio_count = 16;
249
250         return 0;
251 }
252
253 U_BOOT_DRIVER(turris_omnia_mcu_gpio) = {
254         .name           = "turris-omnia-mcu-gpio",
255         .id             = UCLASS_GPIO,
256         .ops            = &omnia_gpio_ops,
257         .probe          = omnia_gpio_probe,
258 };
259
260 static int omnia_sysreset_request(struct udevice *dev, enum sysreset_t type)
261 {
262         struct {
263                 u16 magic;
264                 u16 arg;
265                 u32 csum;
266         } __packed args;
267
268         if (type != SYSRESET_POWER_OFF)
269                 return -EPROTONOSUPPORT;
270
271         args.magic = CMD_POWER_OFF_MAGIC;
272         args.arg = CMD_POWER_OFF_POWERON_BUTTON;
273         args.csum = 0xba3b7212;
274
275         return dm_i2c_write(dev->parent, CMD_POWER_OFF, (void *)&args,
276                             sizeof(args));
277 }
278
279 static const struct sysreset_ops omnia_sysreset_ops = {
280         .request        = omnia_sysreset_request,
281 };
282
283 U_BOOT_DRIVER(turris_omnia_mcu_sysreset) = {
284         .name           = "turris-omnia-mcu-sysreset",
285         .id             = UCLASS_SYSRESET,
286         .ops            = &omnia_sysreset_ops,
287 };
288
289 static int omnia_rng_read(struct udevice *dev, void *data, size_t count)
290 {
291         u8 buf[1 + CMD_TRNG_MAX_ENTROPY_LEN];
292         size_t len;
293         int ret;
294
295         while (count) {
296                 ret = dm_i2c_read(dev->parent, CMD_TRNG_COLLECT_ENTROPY, buf,
297                                   sizeof(buf));
298                 if (ret)
299                         return ret;
300
301                 len = min_t(size_t, buf[0],
302                             min_t(size_t, CMD_TRNG_MAX_ENTROPY_LEN, count));
303
304                 if (!len) {
305                         /* wait 500ms (fail if interrupted), then try again */
306                         for (int i = 0; i < 5; ++i) {
307                                 mdelay(100);
308                                 if (ctrlc())
309                                         return -EINTR;
310                         }
311                         continue;
312                 }
313
314                 memcpy(data, &buf[1], len);
315                 data += len;
316                 count -= len;
317         }
318
319         return 0;
320 }
321
322 static const struct dm_rng_ops omnia_rng_ops = {
323         .read           = omnia_rng_read,
324 };
325
326 U_BOOT_DRIVER(turris_omnia_mcu_trng) = {
327         .name           = "turris-omnia-mcu-trng",
328         .id             = UCLASS_RNG,
329         .ops            = &omnia_rng_ops,
330 };
331
332 static int turris_omnia_mcu_bind(struct udevice *dev)
333 {
334         /* bind MCU GPIOs as a child device */
335         return device_bind_driver_to_node(dev, "turris-omnia-mcu-gpio",
336                                           "turris-omnia-mcu-gpio",
337                                           dev_ofnode(dev), NULL);
338 }
339
340 static int turris_omnia_mcu_probe(struct udevice *dev)
341 {
342         struct turris_omnia_mcu_info *info = dev_get_priv(dev);
343         u32 dword;
344         u16 word;
345         int ret;
346
347         ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, (void *)&word, sizeof(word));
348         if (ret < 0) {
349                 printf("Error: turris_omnia_mcu CMD_GET_STATUS_WORD failed: %d\n",
350                        ret);
351                 return ret;
352         }
353
354         if (le16_to_cpu(word) & STS_FEATURES_SUPPORTED) {
355                 /* try read 32-bit features */
356                 ret = dm_i2c_read(dev, CMD_GET_FEATURES, (void *)&dword,
357                                   sizeof(dword));
358                 if (ret < 0) {
359                         /* try read 16-bit features */
360                         ret = dm_i2c_read(dev, CMD_GET_FEATURES, (void *)&word,
361                                           sizeof(word));
362                         if (ret < 0) {
363                                 printf("Error: turris_omnia_mcu CMD_GET_FEATURES failed: %d\n",
364                                        ret);
365                                 return ret;
366                         }
367
368                         info->features = le16_to_cpu(word);
369                 } else {
370                         info->features = le32_to_cpu(dword);
371                         if (info->features & FEAT_FROM_BIT_16_INVALID)
372                                 info->features &= GENMASK(15, 0);
373                 }
374         }
375
376         /* bind sysreset if poweroff is supported */
377         if (info->features & FEAT_POWEROFF_WAKEUP) {
378                 ret = device_bind_driver_to_node(dev,
379                                                  "turris-omnia-mcu-sysreset",
380                                                  "turris-omnia-mcu-sysreset",
381                                                  dev_ofnode(dev), NULL);
382                 if (ret < 0)
383                         return ret;
384         }
385
386         /* bind rng if trng is supported */
387         if (info->features & FEAT_TRNG) {
388                 ret = device_bind_driver_to_node(dev, "turris-omnia-mcu-trng",
389                                                  "turris-omnia-mcu-trng",
390                                                  dev_ofnode(dev), NULL);
391                 if (ret < 0)
392                         return ret;
393         }
394
395         return 0;
396 }
397
398 static const struct udevice_id turris_omnia_mcu_ids[] = {
399         { .compatible = "cznic,turris-omnia-mcu" },
400         { }
401 };
402
403 U_BOOT_DRIVER(turris_omnia_mcu) = {
404         .name           = "turris-omnia-mcu",
405         .id             = UCLASS_MISC,
406         .bind           = turris_omnia_mcu_bind,
407         .probe          = turris_omnia_mcu_probe,
408         .priv_auto      = sizeof(struct turris_omnia_mcu_info),
409         .of_match       = turris_omnia_mcu_ids,
410 };
This page took 0.049696 seconds and 4 git commands to generate.