1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2022 Pali Rohár <pali@kernel.org>
4  * Copyright (C) 2024 Marek Behún <kabel@kernel.org>
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 
omnia_gpio_get_function(struct udevice * dev,uint offset)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 
omnia_gpio_get_value(struct udevice * dev,uint offset)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 
omnia_gpio_set_value(struct udevice * dev,uint offset,int value)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 
omnia_gpio_direction_input(struct udevice * dev,uint offset)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 
omnia_gpio_direction_output(struct udevice * dev,uint offset,int value)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 
omnia_gpio_xlate(struct udevice * dev,struct gpio_desc * desc,struct ofnode_phandle_args * args)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 
omnia_gpio_probe(struct udevice * dev)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 
omnia_sysreset_request(struct udevice * dev,enum sysreset_t type)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 
omnia_rng_read(struct udevice * dev,void * data,size_t count)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 
turris_omnia_mcu_bind(struct udevice * dev)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 
turris_omnia_mcu_probe(struct udevice * dev)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 };
411