blob: 77a0424d61cc60dc64937e3e5a496ebee089d829 [file] [log] [blame]
Pali Roháreb96c0f2022-07-28 13:06:24 +02001// SPDX-License-Identifier: GPL-2.0+
Marek Behúnea51ee52024-04-04 09:51:03 +02002/*
3 * Copyright (C) 2022 Pali Rohár <pali@kernel.org>
4 * Copyright (C) 2024 Marek Behún <kabel@kernel.org>
5 */
Pali Roháreb96c0f2022-07-28 13:06:24 +02006
7#include <common.h>
8#include <dm.h>
Marek Behúnea51ee52024-04-04 09:51:03 +02009#include <dm/lists.h>
Pali Roháreb96c0f2022-07-28 13:06:24 +020010#include <i2c.h>
Marek Behúnea51ee52024-04-04 09:51:03 +020011#include <sysreset.h>
Marek Behúnbb42a5b2024-04-04 09:50:51 +020012#include <turris-omnia-mcu-interface.h>
Marek Behúnd38a2be2024-04-04 09:51:01 +020013#include <asm/byteorder.h>
Pali Roháreb96c0f2022-07-28 13:06:24 +020014#include <asm/gpio.h>
15#include <linux/log2.h>
16
Pali Roháreb96c0f2022-07-28 13:06:24 +020017struct turris_omnia_mcu_info {
Marek Behúnc0132152024-04-04 09:51:02 +020018 u32 features;
Pali Roháreb96c0f2022-07-28 13:06:24 +020019};
20
Marek Behúnea51ee52024-04-04 09:51:03 +020021static int omnia_gpio_get_function(struct udevice *dev, uint offset)
Pali Roháreb96c0f2022-07-28 13:06:24 +020022{
Marek Behúnea51ee52024-04-04 09:51:03 +020023 struct turris_omnia_mcu_info *info = dev_get_priv(dev->parent);
Pali Roháreb96c0f2022-07-28 13:06:24 +020024
25 switch (offset) {
26 /* bank 0 */
27 case 0 ... 15:
28 switch (offset) {
29 case ilog2(STS_USB30_PWRON):
30 case ilog2(STS_USB31_PWRON):
31 case ilog2(STS_ENABLE_4V5):
32 case ilog2(STS_BUTTON_MODE):
33 return GPIOF_OUTPUT;
34 default:
35 return GPIOF_INPUT;
36 }
37
38 /* bank 1 - supported only when FEAT_EXT_CMDS is set */
39 case (16 + 0) ... (16 + 31):
40 if (!(info->features & FEAT_EXT_CMDS))
41 return -EINVAL;
42 return GPIOF_INPUT;
43
Pali Rohár265f9892022-09-22 13:25:13 +020044 /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
Pali Roháreb96c0f2022-07-28 13:06:24 +020045 case (16 + 32 + 0) ... (16 + 32 + 15):
46 if (!(info->features & FEAT_EXT_CMDS))
47 return -EINVAL;
Pali Rohár265f9892022-09-22 13:25:13 +020048 if (!(info->features & FEAT_PERIPH_MCU))
49 return -EINVAL;
Pali Roháreb96c0f2022-07-28 13:06:24 +020050 return GPIOF_OUTPUT;
51
52 default:
53 return -EINVAL;
54 }
55}
56
Marek Behúnea51ee52024-04-04 09:51:03 +020057static int omnia_gpio_get_value(struct udevice *dev, uint offset)
Pali Roháreb96c0f2022-07-28 13:06:24 +020058{
Marek Behúnea51ee52024-04-04 09:51:03 +020059 struct turris_omnia_mcu_info *info = dev_get_priv(dev->parent);
Marek Behúnd38a2be2024-04-04 09:51:01 +020060 u32 val32;
61 u16 val16;
Pali Roháreb96c0f2022-07-28 13:06:24 +020062 int ret;
63
64 switch (offset) {
65 /* bank 0 */
66 case 0 ... 15:
Marek Behúnea51ee52024-04-04 09:51:03 +020067 ret = dm_i2c_read(dev->parent, CMD_GET_STATUS_WORD,
68 (void *)&val16, sizeof(val16));
Pali Roháreb96c0f2022-07-28 13:06:24 +020069 if (ret)
70 return ret;
Marek Behúnd38a2be2024-04-04 09:51:01 +020071
72 return !!(le16_to_cpu(val16) & BIT(offset));
Pali Roháreb96c0f2022-07-28 13:06:24 +020073
74 /* bank 1 - supported only when FEAT_EXT_CMDS is set */
75 case (16 + 0) ... (16 + 31):
76 if (!(info->features & FEAT_EXT_CMDS))
77 return -EINVAL;
Marek Behúnd38a2be2024-04-04 09:51:01 +020078
Marek Behúnea51ee52024-04-04 09:51:03 +020079 ret = dm_i2c_read(dev->parent, CMD_GET_EXT_STATUS_DWORD,
80 (void *)&val32, sizeof(val32));
Pali Roháreb96c0f2022-07-28 13:06:24 +020081 if (ret)
82 return ret;
Marek Behúnd38a2be2024-04-04 09:51:01 +020083
84 return !!(le32_to_cpu(val32) & BIT(offset - 16));
Pali Roháreb96c0f2022-07-28 13:06:24 +020085
Pali Rohár265f9892022-09-22 13:25:13 +020086 /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
Pali Roháreb96c0f2022-07-28 13:06:24 +020087 case (16 + 32 + 0) ... (16 + 32 + 15):
88 if (!(info->features & FEAT_EXT_CMDS))
89 return -EINVAL;
Pali Rohár265f9892022-09-22 13:25:13 +020090 if (!(info->features & FEAT_PERIPH_MCU))
91 return -EINVAL;
Marek Behúnd38a2be2024-04-04 09:51:01 +020092
Marek Behúnea51ee52024-04-04 09:51:03 +020093 ret = dm_i2c_read(dev->parent, CMD_GET_EXT_CONTROL_STATUS,
Marek Behúnd38a2be2024-04-04 09:51:01 +020094 (void *)&val16, sizeof(val16));
Pali Roháreb96c0f2022-07-28 13:06:24 +020095 if (ret)
96 return ret;
Marek Behúnd38a2be2024-04-04 09:51:01 +020097
98 return !!(le16_to_cpu(val16) & BIT(offset - 16 - 32));
Pali Roháreb96c0f2022-07-28 13:06:24 +020099
100 default:
101 return -EINVAL;
102 }
103}
104
Marek Behúnea51ee52024-04-04 09:51:03 +0200105static int omnia_gpio_set_value(struct udevice *dev, uint offset, int value)
Pali Roháreb96c0f2022-07-28 13:06:24 +0200106{
Marek Behúnea51ee52024-04-04 09:51:03 +0200107 struct turris_omnia_mcu_info *info = dev_get_priv(dev->parent);
Marek Behúnd38a2be2024-04-04 09:51:01 +0200108 u16 valmask16[2];
109 u8 valmask8[2];
Pali Roháreb96c0f2022-07-28 13:06:24 +0200110
111 switch (offset) {
112 /* bank 0 */
Pali Rohára93fa242022-08-01 12:11:13 +0200113 case 0 ... 15:
114 switch (offset) {
115 case ilog2(STS_USB30_PWRON):
Marek Behúnd38a2be2024-04-04 09:51:01 +0200116 valmask8[1] = CTL_USB30_PWRON;
Pali Rohára93fa242022-08-01 12:11:13 +0200117 break;
118 case ilog2(STS_USB31_PWRON):
Marek Behúnd38a2be2024-04-04 09:51:01 +0200119 valmask8[1] = CTL_USB31_PWRON;
Pali Rohára93fa242022-08-01 12:11:13 +0200120 break;
121 case ilog2(STS_ENABLE_4V5):
Marek Behúnd38a2be2024-04-04 09:51:01 +0200122 valmask8[1] = CTL_ENABLE_4V5;
Pali Rohára93fa242022-08-01 12:11:13 +0200123 break;
124 case ilog2(STS_BUTTON_MODE):
Marek Behúnd38a2be2024-04-04 09:51:01 +0200125 valmask8[1] = CTL_BUTTON_MODE;
Pali Rohára93fa242022-08-01 12:11:13 +0200126 break;
127 default:
128 return -EINVAL;
129 }
Marek Behúnd38a2be2024-04-04 09:51:01 +0200130
131 valmask8[0] = value ? valmask8[1] : 0;
132
Marek Behúnea51ee52024-04-04 09:51:03 +0200133 return dm_i2c_write(dev->parent, CMD_GENERAL_CONTROL, valmask8,
Marek Behúnd38a2be2024-04-04 09:51:01 +0200134 sizeof(valmask8));
Pali Roháreb96c0f2022-07-28 13:06:24 +0200135
Pali Rohár265f9892022-09-22 13:25:13 +0200136 /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
Pali Roháreb96c0f2022-07-28 13:06:24 +0200137 case (16 + 32 + 0) ... (16 + 32 + 15):
138 if (!(info->features & FEAT_EXT_CMDS))
139 return -EINVAL;
Pali Rohár265f9892022-09-22 13:25:13 +0200140 if (!(info->features & FEAT_PERIPH_MCU))
141 return -EINVAL;
Marek Behúnd38a2be2024-04-04 09:51:01 +0200142
143 valmask16[1] = cpu_to_le16(BIT(offset - 16 - 32));
144 valmask16[0] = value ? valmask16[1] : 0;
145
Marek Behúnea51ee52024-04-04 09:51:03 +0200146 return dm_i2c_write(dev->parent, CMD_EXT_CONTROL,
147 (void *)valmask16, sizeof(valmask16));
Pali Roháreb96c0f2022-07-28 13:06:24 +0200148
149 default:
150 return -EINVAL;
151 }
Pali Roháreb96c0f2022-07-28 13:06:24 +0200152}
153
Marek Behúnea51ee52024-04-04 09:51:03 +0200154static int omnia_gpio_direction_input(struct udevice *dev, uint offset)
Pali Roháreb96c0f2022-07-28 13:06:24 +0200155{
156 int ret;
157
Marek Behúnea51ee52024-04-04 09:51:03 +0200158 ret = omnia_gpio_get_function(dev, offset);
Pali Roháreb96c0f2022-07-28 13:06:24 +0200159 if (ret < 0)
160 return ret;
161 else if (ret != GPIOF_INPUT)
162 return -EOPNOTSUPP;
163
164 return 0;
165}
166
Marek Behúnea51ee52024-04-04 09:51:03 +0200167static int omnia_gpio_direction_output(struct udevice *dev, uint offset, int value)
Pali Roháreb96c0f2022-07-28 13:06:24 +0200168{
169 int ret;
170
Marek Behúnea51ee52024-04-04 09:51:03 +0200171 ret = omnia_gpio_get_function(dev, offset);
Pali Roháreb96c0f2022-07-28 13:06:24 +0200172 if (ret < 0)
173 return ret;
174 else if (ret != GPIOF_OUTPUT)
175 return -EOPNOTSUPP;
176
Marek Behúnea51ee52024-04-04 09:51:03 +0200177 return omnia_gpio_set_value(dev, offset, value);
Pali Roháreb96c0f2022-07-28 13:06:24 +0200178}
179
Marek Behúnea51ee52024-04-04 09:51:03 +0200180static int omnia_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
Pali Roháreb96c0f2022-07-28 13:06:24 +0200181 struct ofnode_phandle_args *args)
182{
183 uint bank, gpio, flags, offset;
184 int ret;
185
186 if (args->args_count != 3)
187 return -EINVAL;
188
189 bank = args->args[0];
190 gpio = args->args[1];
191 flags = args->args[2];
192
193 switch (bank) {
194 case 0:
195 if (gpio >= 16)
196 return -EINVAL;
197 offset = gpio;
198 break;
199 case 1:
200 if (gpio >= 32)
201 return -EINVAL;
202 offset = 16 + gpio;
203 break;
204 case 2:
205 if (gpio >= 16)
206 return -EINVAL;
207 offset = 16 + 32 + gpio;
208 break;
209 default:
210 return -EINVAL;
211 }
212
Marek Behúnea51ee52024-04-04 09:51:03 +0200213 ret = omnia_gpio_get_function(dev, offset);
Pali Roháreb96c0f2022-07-28 13:06:24 +0200214 if (ret < 0)
215 return ret;
216
217 desc->offset = offset;
218 desc->flags = gpio_flags_xlate(flags);
219
220 return 0;
221}
222
Marek Behúnea51ee52024-04-04 09:51:03 +0200223static const struct dm_gpio_ops omnia_gpio_ops = {
224 .direction_input = omnia_gpio_direction_input,
225 .direction_output = omnia_gpio_direction_output,
226 .get_value = omnia_gpio_get_value,
227 .set_value = omnia_gpio_set_value,
228 .get_function = omnia_gpio_get_function,
229 .xlate = omnia_gpio_xlate,
Pali Roháreb96c0f2022-07-28 13:06:24 +0200230};
231
Marek Behúnea51ee52024-04-04 09:51:03 +0200232static int omnia_gpio_probe(struct udevice *dev)
Pali Roháreb96c0f2022-07-28 13:06:24 +0200233{
Marek Behúnea51ee52024-04-04 09:51:03 +0200234 struct turris_omnia_mcu_info *info = dev_get_priv(dev->parent);
Pali Roháreb96c0f2022-07-28 13:06:24 +0200235 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
Marek Behúnea51ee52024-04-04 09:51:03 +0200236
237 uc_priv->bank_name = "mcu_";
238
239 if ((info->features & FEAT_EXT_CMDS) && (info->features & FEAT_PERIPH_MCU))
240 uc_priv->gpio_count = 16 + 32 + 16;
241 else if (info->features & FEAT_EXT_CMDS)
242 uc_priv->gpio_count = 16 + 32;
243 else
244 uc_priv->gpio_count = 16;
245
246 return 0;
247}
248
249U_BOOT_DRIVER(turris_omnia_mcu_gpio) = {
250 .name = "turris-omnia-mcu-gpio",
251 .id = UCLASS_GPIO,
252 .ops = &omnia_gpio_ops,
253 .probe = omnia_gpio_probe,
254};
255
256static int omnia_sysreset_request(struct udevice *dev, enum sysreset_t type)
257{
258 struct {
259 u16 magic;
260 u16 arg;
261 u32 csum;
262 } __packed args;
263
264 if (type != SYSRESET_POWER_OFF)
265 return -EPROTONOSUPPORT;
266
267 args.magic = CMD_POWER_OFF_MAGIC;
268 args.arg = CMD_POWER_OFF_POWERON_BUTTON;
269 args.csum = 0xba3b7212;
270
271 return dm_i2c_write(dev->parent, CMD_POWER_OFF, (void *)&args,
272 sizeof(args));
273}
274
275static const struct sysreset_ops omnia_sysreset_ops = {
276 .request = omnia_sysreset_request,
277};
278
279U_BOOT_DRIVER(turris_omnia_mcu_sysreset) = {
280 .name = "turris-omnia-mcu-sysreset",
281 .id = UCLASS_SYSRESET,
282 .ops = &omnia_sysreset_ops,
283};
284
285static int turris_omnia_mcu_bind(struct udevice *dev)
286{
287 /* bind MCU GPIOs as a child device */
288 return device_bind_driver_to_node(dev, "turris-omnia-mcu-gpio",
289 "turris-omnia-mcu-gpio",
290 dev_ofnode(dev), NULL);
291}
292
293static int turris_omnia_mcu_probe(struct udevice *dev)
294{
295 struct turris_omnia_mcu_info *info = dev_get_priv(dev);
Marek Behúnc0132152024-04-04 09:51:02 +0200296 u32 dword;
297 u16 word;
Pali Roháreb96c0f2022-07-28 13:06:24 +0200298 int ret;
299
Marek Behúnc0132152024-04-04 09:51:02 +0200300 ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, (void *)&word, sizeof(word));
Marek Behúnd38a2be2024-04-04 09:51:01 +0200301 if (ret < 0) {
302 printf("Error: turris_omnia_mcu CMD_GET_STATUS_WORD failed: %d\n",
303 ret);
Pali Roháreb96c0f2022-07-28 13:06:24 +0200304 return ret;
305 }
306
Marek Behúnc0132152024-04-04 09:51:02 +0200307 if (le16_to_cpu(word) & STS_FEATURES_SUPPORTED) {
308 /* try read 32-bit features */
309 ret = dm_i2c_read(dev, CMD_GET_FEATURES, (void *)&dword,
310 sizeof(dword));
Marek Behúnd38a2be2024-04-04 09:51:01 +0200311 if (ret < 0) {
Marek Behúnc0132152024-04-04 09:51:02 +0200312 /* try read 16-bit features */
313 ret = dm_i2c_read(dev, CMD_GET_FEATURES, (void *)&word,
314 sizeof(word));
315 if (ret < 0) {
316 printf("Error: turris_omnia_mcu CMD_GET_FEATURES failed: %d\n",
317 ret);
318 return ret;
319 }
320
321 info->features = le16_to_cpu(word);
322 } else {
323 info->features = le32_to_cpu(dword);
324 if (info->features & FEAT_FROM_BIT_16_INVALID)
325 info->features &= GENMASK(15, 0);
Pali Roháreb96c0f2022-07-28 13:06:24 +0200326 }
Pali Roháreb96c0f2022-07-28 13:06:24 +0200327 }
328
Marek Behúnea51ee52024-04-04 09:51:03 +0200329 /* bind sysreset if poweroff is supported */
330 if (info->features & FEAT_POWEROFF_WAKEUP) {
331 ret = device_bind_driver_to_node(dev,
332 "turris-omnia-mcu-sysreset",
333 "turris-omnia-mcu-sysreset",
334 dev_ofnode(dev), NULL);
335 if (ret < 0)
336 return ret;
337 }
Pali Roháreb96c0f2022-07-28 13:06:24 +0200338
339 return 0;
340}
341
342static const struct udevice_id turris_omnia_mcu_ids[] = {
343 { .compatible = "cznic,turris-omnia-mcu" },
344 { }
345};
346
347U_BOOT_DRIVER(turris_omnia_mcu) = {
348 .name = "turris-omnia-mcu",
Marek Behúnea51ee52024-04-04 09:51:03 +0200349 .id = UCLASS_MISC,
350 .bind = turris_omnia_mcu_bind,
Pali Roháreb96c0f2022-07-28 13:06:24 +0200351 .probe = turris_omnia_mcu_probe,
Marek Behúnea51ee52024-04-04 09:51:03 +0200352 .priv_auto = sizeof(struct turris_omnia_mcu_info),
Pali Roháreb96c0f2022-07-28 13:06:24 +0200353 .of_match = turris_omnia_mcu_ids,
354};