]> git.ipfire.org Git - thirdparty/openwrt.git/blob
72bd18d144bf9f2dd1172f69db29b9cdbf0ba7dd
[thirdparty/openwrt.git] /
1 From 4a63f684c8badfc43f384df2291ed2566909a3bc Mon Sep 17 00:00:00 2001
2 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
3 Date: Mon, 1 Jul 2024 13:30:04 +0200
4 Subject: [PATCH 02/11] platform: cznic: Add preliminary support for Turris
5 Omnia MCU
6 MIME-Version: 1.0
7 Content-Type: text/plain; charset=UTF-8
8 Content-Transfer-Encoding: 8bit
9
10 Add the basic skeleton for a new platform driver for the microcontroller
11 found on the Turris Omnia board.
12
13 Signed-off-by: Marek Behún <kabel@kernel.org>
14 Reviewed-by: Andy Shevchenko <andy@kernel.org>
15 Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
16 Link: https://lore.kernel.org/r/20240701113010.16447-3-kabel@kernel.org
17 Signed-off-by: Arnd Bergmann <arnd@arndb.de>
18 ---
19 .../sysfs-bus-i2c-devices-turris-omnia-mcu | 81 ++++
20 MAINTAINERS | 3 +
21 drivers/platform/Kconfig | 2 +
22 drivers/platform/Makefile | 1 +
23 drivers/platform/cznic/Kconfig | 25 ++
24 drivers/platform/cznic/Makefile | 4 +
25 .../platform/cznic/turris-omnia-mcu-base.c | 393 ++++++++++++++++++
26 drivers/platform/cznic/turris-omnia-mcu.h | 74 ++++
27 include/linux/turris-omnia-mcu-interface.h | 249 +++++++++++
28 9 files changed, 832 insertions(+)
29 create mode 100644 Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu
30 create mode 100644 drivers/platform/cznic/Kconfig
31 create mode 100644 drivers/platform/cznic/Makefile
32 create mode 100644 drivers/platform/cznic/turris-omnia-mcu-base.c
33 create mode 100644 drivers/platform/cznic/turris-omnia-mcu.h
34 create mode 100644 include/linux/turris-omnia-mcu-interface.h
35
36 --- /dev/null
37 +++ b/Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu
38 @@ -0,0 +1,81 @@
39 +What: /sys/bus/i2c/devices/<mcu_device>/board_revision
40 +Date: September 2024
41 +KernelVersion: 6.11
42 +Contact: Marek Behún <kabel@kernel.org>
43 +Description: (RO) Contains board revision number.
44 +
45 + Only available if board information is burned in the MCU (older
46 + revisions have board information burned in the ATSHA204-A chip).
47 +
48 + Format: %u.
49 +
50 +What: /sys/bus/i2c/devices/<mcu_device>/first_mac_address
51 +Date: September 2024
52 +KernelVersion: 6.11
53 +Contact: Marek Behún <kabel@kernel.org>
54 +Description: (RO) Contains device first MAC address. Each Turris Omnia is
55 + allocated 3 MAC addresses. The two additional addresses are
56 + computed from the first one by incrementing it.
57 +
58 + Only available if board information is burned in the MCU (older
59 + revisions have board information burned in the ATSHA204-A chip).
60 +
61 + Format: %pM.
62 +
63 +What: /sys/bus/i2c/devices/<mcu_device>/fw_features
64 +Date: September 2024
65 +KernelVersion: 6.11
66 +Contact: Marek Behún <kabel@kernel.org>
67 +Description: (RO) Newer versions of the microcontroller firmware report the
68 + features they support. These can be read from this file. If the
69 + MCU firmware is too old, this file reads 0x0.
70 +
71 + Format: 0x%x.
72 +
73 +What: /sys/bus/i2c/devices/<mcu_device>/fw_version_hash_application
74 +Date: September 2024
75 +KernelVersion: 6.11
76 +Contact: Marek Behún <kabel@kernel.org>
77 +Description: (RO) Contains the version hash (commit hash) of the application
78 + part of the microcontroller firmware.
79 +
80 + Format: %s.
81 +
82 +What: /sys/bus/i2c/devices/<mcu_device>/fw_version_hash_bootloader
83 +Date: September 2024
84 +KernelVersion: 6.11
85 +Contact: Marek Behún <kabel@kernel.org>
86 +Description: (RO) Contains the version hash (commit hash) of the bootloader
87 + part of the microcontroller firmware.
88 +
89 + Format: %s.
90 +
91 +What: /sys/bus/i2c/devices/<mcu_device>/mcu_type
92 +Date: September 2024
93 +KernelVersion: 6.11
94 +Contact: Marek Behún <kabel@kernel.org>
95 +Description: (RO) Contains the microcontroller type (STM32, GD32, MKL).
96 +
97 + Format: %s.
98 +
99 +What: /sys/bus/i2c/devices/<mcu_device>/reset_selector
100 +Date: September 2024
101 +KernelVersion: 6.11
102 +Contact: Marek Behún <kabel@kernel.org>
103 +Description: (RO) Contains the selected factory reset level, determined by
104 + how long the rear reset button was held by the user during board
105 + reset.
106 +
107 + Format: %i.
108 +
109 +What: /sys/bus/i2c/devices/<mcu_device>/serial_number
110 +Date: September 2024
111 +KernelVersion: 6.11
112 +Contact: Marek Behún <kabel@kernel.org>
113 +Description: (RO) Contains the 64-bit board serial number in hexadecimal
114 + format.
115 +
116 + Only available if board information is burned in the MCU (older
117 + revisions have board information burned in the ATSHA204-A chip).
118 +
119 + Format: %016X.
120 --- a/MAINTAINERS
121 +++ b/MAINTAINERS
122 @@ -2100,6 +2100,7 @@ M: Marek Behún <kabel@kernel.org>
123 S: Maintained
124 W: https://www.turris.cz/
125 F: Documentation/ABI/testing/debugfs-moxtet
126 +F: Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu
127 F: Documentation/ABI/testing/sysfs-bus-moxtet-devices
128 F: Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm
129 F: Documentation/devicetree/bindings/bus/moxtet.txt
130 @@ -2113,10 +2114,12 @@ F: drivers/firmware/turris-mox-rwtm.c
131 F: drivers/gpio/gpio-moxtet.c
132 F: drivers/leds/leds-turris-omnia.c
133 F: drivers/mailbox/armada-37xx-rwtm-mailbox.c
134 +F: drivers/platform/cznic/
135 F: drivers/watchdog/armada_37xx_wdt.c
136 F: include/dt-bindings/bus/moxtet.h
137 F: include/linux/armada-37xx-rwtm-mailbox.h
138 F: include/linux/moxtet.h
139 +F: include/linux/turris-omnia-mcu-interface.h
140
141 ARM/FARADAY FA526 PORT
142 M: Hans Ulli Kroll <ulli.kroll@googlemail.com>
143 --- a/drivers/platform/Kconfig
144 +++ b/drivers/platform/Kconfig
145 @@ -7,6 +7,8 @@ source "drivers/platform/goldfish/Kconfi
146
147 source "drivers/platform/chrome/Kconfig"
148
149 +source "drivers/platform/cznic/Kconfig"
150 +
151 source "drivers/platform/mellanox/Kconfig"
152
153 source "drivers/platform/olpc/Kconfig"
154 --- a/drivers/platform/Makefile
155 +++ b/drivers/platform/Makefile
156 @@ -10,5 +10,6 @@ obj-$(CONFIG_MIPS) += mips/
157 obj-$(CONFIG_OLPC_EC) += olpc/
158 obj-$(CONFIG_GOLDFISH) += goldfish/
159 obj-$(CONFIG_CHROME_PLATFORMS) += chrome/
160 +obj-$(CONFIG_CZNIC_PLATFORMS) += cznic/
161 obj-$(CONFIG_SURFACE_PLATFORMS) += surface/
162 obj-$(CONFIG_MIKROTIK) += mikrotik/
163 --- /dev/null
164 +++ b/drivers/platform/cznic/Kconfig
165 @@ -0,0 +1,25 @@
166 +# SPDX-License-Identifier: GPL-2.0-only
167 +#
168 +# For a description of the syntax of this configuration file,
169 +# see Documentation/kbuild/kconfig-language.rst.
170 +#
171 +
172 +menuconfig CZNIC_PLATFORMS
173 + bool "Platform support for CZ.NIC's Turris hardware"
174 + help
175 + Say Y here to be able to choose driver support for CZ.NIC's Turris
176 + devices. This option alone does not add any kernel code.
177 +
178 +if CZNIC_PLATFORMS
179 +
180 +config TURRIS_OMNIA_MCU
181 + tristate "Turris Omnia MCU driver"
182 + depends on MACH_ARMADA_38X || COMPILE_TEST
183 + depends on I2C
184 + help
185 + Say Y here to add support for the features implemented by the
186 + microcontroller on the CZ.NIC's Turris Omnia SOHO router.
187 + To compile this driver as a module, choose M here; the module will be
188 + called turris-omnia-mcu.
189 +
190 +endif # CZNIC_PLATFORMS
191 --- /dev/null
192 +++ b/drivers/platform/cznic/Makefile
193 @@ -0,0 +1,4 @@
194 +# SPDX-License-Identifier: GPL-2.0-only
195 +
196 +obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o
197 +turris-omnia-mcu-y := turris-omnia-mcu-base.o
198 --- /dev/null
199 +++ b/drivers/platform/cznic/turris-omnia-mcu-base.c
200 @@ -0,0 +1,393 @@
201 +// SPDX-License-Identifier: GPL-2.0
202 +/*
203 + * CZ.NIC's Turris Omnia MCU driver
204 + *
205 + * 2024 by Marek Behún <kabel@kernel.org>
206 + */
207 +
208 +#include <linux/bits.h>
209 +#include <linux/device.h>
210 +#include <linux/errno.h>
211 +#include <linux/hex.h>
212 +#include <linux/i2c.h>
213 +#include <linux/module.h>
214 +#include <linux/string.h>
215 +#include <linux/sysfs.h>
216 +#include <linux/types.h>
217 +
218 +#include <linux/turris-omnia-mcu-interface.h>
219 +#include "turris-omnia-mcu.h"
220 +
221 +#define OMNIA_FW_VERSION_LEN 20
222 +#define OMNIA_FW_VERSION_HEX_LEN (2 * OMNIA_FW_VERSION_LEN + 1)
223 +#define OMNIA_BOARD_INFO_LEN 16
224 +
225 +int omnia_cmd_write_read(const struct i2c_client *client,
226 + void *cmd, unsigned int cmd_len,
227 + void *reply, unsigned int reply_len)
228 +{
229 + struct i2c_msg msgs[2];
230 + int ret, num;
231 +
232 + msgs[0].addr = client->addr;
233 + msgs[0].flags = 0;
234 + msgs[0].len = cmd_len;
235 + msgs[0].buf = cmd;
236 + num = 1;
237 +
238 + if (reply_len) {
239 + msgs[1].addr = client->addr;
240 + msgs[1].flags = I2C_M_RD;
241 + msgs[1].len = reply_len;
242 + msgs[1].buf = reply;
243 + num++;
244 + }
245 +
246 + ret = i2c_transfer(client->adapter, msgs, num);
247 + if (ret < 0)
248 + return ret;
249 + if (ret != num)
250 + return -EIO;
251 +
252 + return 0;
253 +}
254 +
255 +static int omnia_get_version_hash(struct omnia_mcu *mcu, bool bootloader,
256 + char version[static OMNIA_FW_VERSION_HEX_LEN])
257 +{
258 + u8 reply[OMNIA_FW_VERSION_LEN];
259 + char *p;
260 + int err;
261 +
262 + err = omnia_cmd_read(mcu->client,
263 + bootloader ? OMNIA_CMD_GET_FW_VERSION_BOOT
264 + : OMNIA_CMD_GET_FW_VERSION_APP,
265 + reply, sizeof(reply));
266 + if (err)
267 + return err;
268 +
269 + p = bin2hex(version, reply, OMNIA_FW_VERSION_LEN);
270 + *p = '\0';
271 +
272 + return 0;
273 +}
274 +
275 +static ssize_t fw_version_hash_show(struct device *dev, char *buf,
276 + bool bootloader)
277 +{
278 + struct omnia_mcu *mcu = dev_get_drvdata(dev);
279 + char version[OMNIA_FW_VERSION_HEX_LEN];
280 + int err;
281 +
282 + err = omnia_get_version_hash(mcu, bootloader, version);
283 + if (err)
284 + return err;
285 +
286 + return sysfs_emit(buf, "%s\n", version);
287 +}
288 +
289 +static ssize_t fw_version_hash_application_show(struct device *dev,
290 + struct device_attribute *a,
291 + char *buf)
292 +{
293 + return fw_version_hash_show(dev, buf, false);
294 +}
295 +static DEVICE_ATTR_RO(fw_version_hash_application);
296 +
297 +static ssize_t fw_version_hash_bootloader_show(struct device *dev,
298 + struct device_attribute *a,
299 + char *buf)
300 +{
301 + return fw_version_hash_show(dev, buf, true);
302 +}
303 +static DEVICE_ATTR_RO(fw_version_hash_bootloader);
304 +
305 +static ssize_t fw_features_show(struct device *dev, struct device_attribute *a,
306 + char *buf)
307 +{
308 + struct omnia_mcu *mcu = dev_get_drvdata(dev);
309 +
310 + return sysfs_emit(buf, "0x%x\n", mcu->features);
311 +}
312 +static DEVICE_ATTR_RO(fw_features);
313 +
314 +static ssize_t mcu_type_show(struct device *dev, struct device_attribute *a,
315 + char *buf)
316 +{
317 + struct omnia_mcu *mcu = dev_get_drvdata(dev);
318 +
319 + return sysfs_emit(buf, "%s\n", mcu->type);
320 +}
321 +static DEVICE_ATTR_RO(mcu_type);
322 +
323 +static ssize_t reset_selector_show(struct device *dev,
324 + struct device_attribute *a, char *buf)
325 +{
326 + u8 reply;
327 + int err;
328 +
329 + err = omnia_cmd_read_u8(to_i2c_client(dev), OMNIA_CMD_GET_RESET,
330 + &reply);
331 + if (err)
332 + return err;
333 +
334 + return sysfs_emit(buf, "%d\n", reply);
335 +}
336 +static DEVICE_ATTR_RO(reset_selector);
337 +
338 +static ssize_t serial_number_show(struct device *dev,
339 + struct device_attribute *a, char *buf)
340 +{
341 + struct omnia_mcu *mcu = dev_get_drvdata(dev);
342 +
343 + return sysfs_emit(buf, "%016llX\n", mcu->board_serial_number);
344 +}
345 +static DEVICE_ATTR_RO(serial_number);
346 +
347 +static ssize_t first_mac_address_show(struct device *dev,
348 + struct device_attribute *a, char *buf)
349 +{
350 + struct omnia_mcu *mcu = dev_get_drvdata(dev);
351 +
352 + return sysfs_emit(buf, "%pM\n", mcu->board_first_mac);
353 +}
354 +static DEVICE_ATTR_RO(first_mac_address);
355 +
356 +static ssize_t board_revision_show(struct device *dev,
357 + struct device_attribute *a, char *buf)
358 +{
359 + struct omnia_mcu *mcu = dev_get_drvdata(dev);
360 +
361 + return sysfs_emit(buf, "%u\n", mcu->board_revision);
362 +}
363 +static DEVICE_ATTR_RO(board_revision);
364 +
365 +static struct attribute *omnia_mcu_base_attrs[] = {
366 + &dev_attr_fw_version_hash_application.attr,
367 + &dev_attr_fw_version_hash_bootloader.attr,
368 + &dev_attr_fw_features.attr,
369 + &dev_attr_mcu_type.attr,
370 + &dev_attr_reset_selector.attr,
371 + &dev_attr_serial_number.attr,
372 + &dev_attr_first_mac_address.attr,
373 + &dev_attr_board_revision.attr,
374 + NULL
375 +};
376 +
377 +static umode_t omnia_mcu_base_attrs_visible(struct kobject *kobj,
378 + struct attribute *a, int n)
379 +{
380 + struct device *dev = kobj_to_dev(kobj);
381 + struct omnia_mcu *mcu = dev_get_drvdata(dev);
382 +
383 + if ((a == &dev_attr_serial_number.attr ||
384 + a == &dev_attr_first_mac_address.attr ||
385 + a == &dev_attr_board_revision.attr) &&
386 + !(mcu->features & OMNIA_FEAT_BOARD_INFO))
387 + return 0;
388 +
389 + return a->mode;
390 +}
391 +
392 +static const struct attribute_group omnia_mcu_base_group = {
393 + .attrs = omnia_mcu_base_attrs,
394 + .is_visible = omnia_mcu_base_attrs_visible,
395 +};
396 +
397 +static const struct attribute_group *omnia_mcu_groups[] = {
398 + &omnia_mcu_base_group,
399 + NULL
400 +};
401 +
402 +static void omnia_mcu_print_version_hash(struct omnia_mcu *mcu, bool bootloader)
403 +{
404 + const char *type = bootloader ? "bootloader" : "application";
405 + struct device *dev = &mcu->client->dev;
406 + char version[OMNIA_FW_VERSION_HEX_LEN];
407 + int err;
408 +
409 + err = omnia_get_version_hash(mcu, bootloader, version);
410 + if (err) {
411 + dev_err(dev, "Cannot read MCU %s firmware version: %d\n",
412 + type, err);
413 + return;
414 + }
415 +
416 + dev_info(dev, "MCU %s firmware version hash: %s\n", type, version);
417 +}
418 +
419 +static const char *omnia_status_to_mcu_type(u16 status)
420 +{
421 + switch (status & OMNIA_STS_MCU_TYPE_MASK) {
422 + case OMNIA_STS_MCU_TYPE_STM32:
423 + return "STM32";
424 + case OMNIA_STS_MCU_TYPE_GD32:
425 + return "GD32";
426 + case OMNIA_STS_MCU_TYPE_MKL:
427 + return "MKL";
428 + default:
429 + return "unknown";
430 + }
431 +}
432 +
433 +static void omnia_info_missing_feature(struct device *dev, const char *feature)
434 +{
435 + dev_info(dev,
436 + "Your board's MCU firmware does not support the %s feature.\n",
437 + feature);
438 +}
439 +
440 +static int omnia_mcu_read_features(struct omnia_mcu *mcu)
441 +{
442 + static const struct {
443 + u16 mask;
444 + const char *name;
445 + } features[] = {
446 +#define _DEF_FEAT(_n, _m) { OMNIA_FEAT_ ## _n, _m }
447 + _DEF_FEAT(EXT_CMDS, "extended control and status"),
448 + _DEF_FEAT(WDT_PING, "watchdog pinging"),
449 + _DEF_FEAT(LED_STATE_EXT_MASK, "peripheral LED pins reading"),
450 + _DEF_FEAT(NEW_INT_API, "new interrupt API"),
451 + _DEF_FEAT(POWEROFF_WAKEUP, "poweroff and wakeup"),
452 + _DEF_FEAT(TRNG, "true random number generator"),
453 +#undef _DEF_FEAT
454 + };
455 + struct i2c_client *client = mcu->client;
456 + struct device *dev = &client->dev;
457 + bool suggest_fw_upgrade = false;
458 + u16 status;
459 + int err;
460 +
461 + /* status word holds MCU type, which we need below */
462 + err = omnia_cmd_read_u16(client, OMNIA_CMD_GET_STATUS_WORD, &status);
463 + if (err)
464 + return err;
465 +
466 + /*
467 + * Check whether MCU firmware supports the OMNIA_CMD_GET_FEATURES
468 + * command.
469 + */
470 + if (status & OMNIA_STS_FEATURES_SUPPORTED) {
471 + /* try read 32-bit features */
472 + err = omnia_cmd_read_u32(client, OMNIA_CMD_GET_FEATURES,
473 + &mcu->features);
474 + if (err) {
475 + /* try read 16-bit features */
476 + u16 features16;
477 +
478 + err = omnia_cmd_read_u16(client, OMNIA_CMD_GET_FEATURES,
479 + &features16);
480 + if (err)
481 + return err;
482 +
483 + mcu->features = features16;
484 + } else {
485 + if (mcu->features & OMNIA_FEAT_FROM_BIT_16_INVALID)
486 + mcu->features &= GENMASK(15, 0);
487 + }
488 + } else {
489 + dev_info(dev,
490 + "Your board's MCU firmware does not support feature reading.\n");
491 + suggest_fw_upgrade = true;
492 + }
493 +
494 + mcu->type = omnia_status_to_mcu_type(status);
495 + dev_info(dev, "MCU type %s%s\n", mcu->type,
496 + (mcu->features & OMNIA_FEAT_PERIPH_MCU) ?
497 + ", with peripheral resets wired" : "");
498 +
499 + omnia_mcu_print_version_hash(mcu, true);
500 +
501 + if (mcu->features & OMNIA_FEAT_BOOTLOADER)
502 + dev_warn(dev,
503 + "MCU is running bootloader firmware. Was firmware upgrade interrupted?\n");
504 + else
505 + omnia_mcu_print_version_hash(mcu, false);
506 +
507 + for (unsigned int i = 0; i < ARRAY_SIZE(features); i++) {
508 + if (mcu->features & features[i].mask)
509 + continue;
510 +
511 + omnia_info_missing_feature(dev, features[i].name);
512 + suggest_fw_upgrade = true;
513 + }
514 +
515 + if (suggest_fw_upgrade)
516 + dev_info(dev,
517 + "Consider upgrading MCU firmware with the omnia-mcutool utility.\n");
518 +
519 + return 0;
520 +}
521 +
522 +static int omnia_mcu_read_board_info(struct omnia_mcu *mcu)
523 +{
524 + u8 reply[1 + OMNIA_BOARD_INFO_LEN];
525 + int err;
526 +
527 + err = omnia_cmd_read(mcu->client, OMNIA_CMD_BOARD_INFO_GET, reply,
528 + sizeof(reply));
529 + if (err)
530 + return err;
531 +
532 + if (reply[0] != OMNIA_BOARD_INFO_LEN)
533 + return -EIO;
534 +
535 + mcu->board_serial_number = get_unaligned_le64(&reply[1]);
536 +
537 + /* we can't use ether_addr_copy() because reply is not u16-aligned */
538 + memcpy(mcu->board_first_mac, &reply[9], sizeof(mcu->board_first_mac));
539 +
540 + mcu->board_revision = reply[15];
541 +
542 + return 0;
543 +}
544 +
545 +static int omnia_mcu_probe(struct i2c_client *client)
546 +{
547 + struct device *dev = &client->dev;
548 + struct omnia_mcu *mcu;
549 + int err;
550 +
551 + if (!client->irq)
552 + return dev_err_probe(dev, -EINVAL, "IRQ resource not found\n");
553 +
554 + mcu = devm_kzalloc(dev, sizeof(*mcu), GFP_KERNEL);
555 + if (!mcu)
556 + return -ENOMEM;
557 +
558 + mcu->client = client;
559 + i2c_set_clientdata(client, mcu);
560 +
561 + err = omnia_mcu_read_features(mcu);
562 + if (err)
563 + return dev_err_probe(dev, err,
564 + "Cannot determine MCU supported features\n");
565 +
566 + if (mcu->features & OMNIA_FEAT_BOARD_INFO) {
567 + err = omnia_mcu_read_board_info(mcu);
568 + if (err)
569 + return dev_err_probe(dev, err,
570 + "Cannot read board info\n");
571 + }
572 +
573 + return 0;
574 +}
575 +
576 +static const struct of_device_id of_omnia_mcu_match[] = {
577 + { .compatible = "cznic,turris-omnia-mcu" },
578 + {}
579 +};
580 +
581 +static struct i2c_driver omnia_mcu_driver = {
582 + .probe = omnia_mcu_probe,
583 + .driver = {
584 + .name = "turris-omnia-mcu",
585 + .of_match_table = of_omnia_mcu_match,
586 + .dev_groups = omnia_mcu_groups,
587 + },
588 +};
589 +module_i2c_driver(omnia_mcu_driver);
590 +
591 +MODULE_AUTHOR("Marek Behun <kabel@kernel.org>");
592 +MODULE_DESCRIPTION("CZ.NIC's Turris Omnia MCU");
593 +MODULE_LICENSE("GPL");
594 --- /dev/null
595 +++ b/drivers/platform/cznic/turris-omnia-mcu.h
596 @@ -0,0 +1,74 @@
597 +/* SPDX-License-Identifier: GPL-2.0 */
598 +/*
599 + * CZ.NIC's Turris Omnia MCU driver
600 + *
601 + * 2024 by Marek Behún <kabel@kernel.org>
602 + */
603 +
604 +#ifndef __TURRIS_OMNIA_MCU_H
605 +#define __TURRIS_OMNIA_MCU_H
606 +
607 +#include <linux/if_ether.h>
608 +#include <linux/types.h>
609 +#include <asm/byteorder.h>
610 +
611 +struct i2c_client;
612 +
613 +struct omnia_mcu {
614 + struct i2c_client *client;
615 + const char *type;
616 + u32 features;
617 +
618 + /* board information */
619 + u64 board_serial_number;
620 + u8 board_first_mac[ETH_ALEN];
621 + u8 board_revision;
622 +};
623 +
624 +int omnia_cmd_write_read(const struct i2c_client *client,
625 + void *cmd, unsigned int cmd_len,
626 + void *reply, unsigned int reply_len);
627 +
628 +static inline int omnia_cmd_read(const struct i2c_client *client, u8 cmd,
629 + void *reply, unsigned int len)
630 +{
631 + return omnia_cmd_write_read(client, &cmd, 1, reply, len);
632 +}
633 +
634 +static inline int omnia_cmd_read_u32(const struct i2c_client *client, u8 cmd,
635 + u32 *dst)
636 +{
637 + __le32 reply;
638 + int err;
639 +
640 + err = omnia_cmd_read(client, cmd, &reply, sizeof(reply));
641 + if (err)
642 + return err;
643 +
644 + *dst = le32_to_cpu(reply);
645 +
646 + return 0;
647 +}
648 +
649 +static inline int omnia_cmd_read_u16(const struct i2c_client *client, u8 cmd,
650 + u16 *dst)
651 +{
652 + __le16 reply;
653 + int err;
654 +
655 + err = omnia_cmd_read(client, cmd, &reply, sizeof(reply));
656 + if (err)
657 + return err;
658 +
659 + *dst = le16_to_cpu(reply);
660 +
661 + return 0;
662 +}
663 +
664 +static inline int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd,
665 + u8 *reply)
666 +{
667 + return omnia_cmd_read(client, cmd, reply, sizeof(*reply));
668 +}
669 +
670 +#endif /* __TURRIS_OMNIA_MCU_H */
671 --- /dev/null
672 +++ b/include/linux/turris-omnia-mcu-interface.h
673 @@ -0,0 +1,249 @@
674 +/* SPDX-License-Identifier: GPL-2.0 */
675 +/*
676 + * CZ.NIC's Turris Omnia MCU I2C interface commands definitions
677 + *
678 + * 2024 by Marek Behún <kabel@kernel.org>
679 + */
680 +
681 +#ifndef __TURRIS_OMNIA_MCU_INTERFACE_H
682 +#define __TURRIS_OMNIA_MCU_INTERFACE_H
683 +
684 +#include <linux/bitfield.h>
685 +#include <linux/bits.h>
686 +
687 +enum omnia_commands_e {
688 + OMNIA_CMD_GET_STATUS_WORD = 0x01, /* slave sends status word back */
689 + OMNIA_CMD_GENERAL_CONTROL = 0x02,
690 + OMNIA_CMD_LED_MODE = 0x03, /* default/user */
691 + OMNIA_CMD_LED_STATE = 0x04, /* LED on/off */
692 + OMNIA_CMD_LED_COLOR = 0x05, /* LED number + RED + GREEN + BLUE */
693 + OMNIA_CMD_USER_VOLTAGE = 0x06,
694 + OMNIA_CMD_SET_BRIGHTNESS = 0x07,
695 + OMNIA_CMD_GET_BRIGHTNESS = 0x08,
696 + OMNIA_CMD_GET_RESET = 0x09,
697 + OMNIA_CMD_GET_FW_VERSION_APP = 0x0A, /* 20B git hash number */
698 + OMNIA_CMD_SET_WATCHDOG_STATE = 0x0B, /* 0 - disable
699 + * 1 - enable / ping
700 + * after boot watchdog is started
701 + * with 2 minutes timeout
702 + */
703 +
704 + /* OMNIA_CMD_WATCHDOG_STATUS = 0x0C, not implemented anymore */
705 +
706 + OMNIA_CMD_GET_WATCHDOG_STATE = 0x0D,
707 + OMNIA_CMD_GET_FW_VERSION_BOOT = 0x0E, /* 20B Git hash number */
708 + OMNIA_CMD_GET_FW_CHECKSUM = 0x0F, /* 4B length, 4B checksum */
709 +
710 + /* available if FEATURES_SUPPORTED bit set in status word */
711 + OMNIA_CMD_GET_FEATURES = 0x10,
712 +
713 + /* available if EXT_CMD bit set in features */
714 + OMNIA_CMD_GET_EXT_STATUS_DWORD = 0x11,
715 + OMNIA_CMD_EXT_CONTROL = 0x12,
716 + OMNIA_CMD_GET_EXT_CONTROL_STATUS = 0x13,
717 +
718 + /* available if NEW_INT_API bit set in features */
719 + OMNIA_CMD_GET_INT_AND_CLEAR = 0x14,
720 + OMNIA_CMD_GET_INT_MASK = 0x15,
721 + OMNIA_CMD_SET_INT_MASK = 0x16,
722 +
723 + /* available if FLASHING bit set in features */
724 + OMNIA_CMD_FLASH = 0x19,
725 +
726 + /* available if WDT_PING bit set in features */
727 + OMNIA_CMD_SET_WDT_TIMEOUT = 0x20,
728 + OMNIA_CMD_GET_WDT_TIMELEFT = 0x21,
729 +
730 + /* available if POWEROFF_WAKEUP bit set in features */
731 + OMNIA_CMD_SET_WAKEUP = 0x22,
732 + OMNIA_CMD_GET_UPTIME_AND_WAKEUP = 0x23,
733 + OMNIA_CMD_POWER_OFF = 0x24,
734 +
735 + /* available if USB_OVC_PROT_SETTING bit set in features */
736 + OMNIA_CMD_SET_USB_OVC_PROT = 0x25,
737 + OMNIA_CMD_GET_USB_OVC_PROT = 0x26,
738 +
739 + /* available if TRNG bit set in features */
740 + OMNIA_CMD_TRNG_COLLECT_ENTROPY = 0x28,
741 +
742 + /* available if CRYPTO bit set in features */
743 + OMNIA_CMD_CRYPTO_GET_PUBLIC_KEY = 0x29,
744 + OMNIA_CMD_CRYPTO_SIGN_MESSAGE = 0x2A,
745 + OMNIA_CMD_CRYPTO_COLLECT_SIGNATURE = 0x2B,
746 +
747 + /* available if BOARD_INFO it set in features */
748 + OMNIA_CMD_BOARD_INFO_GET = 0x2C,
749 + OMNIA_CMD_BOARD_INFO_BURN = 0x2D,
750 +
751 + /* available only at address 0x2b (LED-controller) */
752 + /* available only if LED_GAMMA_CORRECTION bit set in features */
753 + OMNIA_CMD_SET_GAMMA_CORRECTION = 0x30,
754 + OMNIA_CMD_GET_GAMMA_CORRECTION = 0x31,
755 +
756 + /* available only at address 0x2b (LED-controller) */
757 + /* available only if PER_LED_CORRECTION bit set in features */
758 + /* available only if FROM_BIT_16_INVALID bit NOT set in features */
759 + OMNIA_CMD_SET_LED_CORRECTIONS = 0x32,
760 + OMNIA_CMD_GET_LED_CORRECTIONS = 0x33,
761 +};
762 +
763 +enum omnia_flashing_commands_e {
764 + OMNIA_FLASH_CMD_UNLOCK = 0x01,
765 + OMNIA_FLASH_CMD_SIZE_AND_CSUM = 0x02,
766 + OMNIA_FLASH_CMD_PROGRAM = 0x03,
767 + OMNIA_FLASH_CMD_RESET = 0x04,
768 +};
769 +
770 +enum omnia_sts_word_e {
771 + OMNIA_STS_MCU_TYPE_MASK = GENMASK(1, 0),
772 + OMNIA_STS_MCU_TYPE_STM32 = FIELD_PREP_CONST(OMNIA_STS_MCU_TYPE_MASK, 0),
773 + OMNIA_STS_MCU_TYPE_GD32 = FIELD_PREP_CONST(OMNIA_STS_MCU_TYPE_MASK, 1),
774 + OMNIA_STS_MCU_TYPE_MKL = FIELD_PREP_CONST(OMNIA_STS_MCU_TYPE_MASK, 2),
775 + OMNIA_STS_FEATURES_SUPPORTED = BIT(2),
776 + OMNIA_STS_USER_REGULATOR_NOT_SUPPORTED = BIT(3),
777 + OMNIA_STS_CARD_DET = BIT(4),
778 + OMNIA_STS_MSATA_IND = BIT(5),
779 + OMNIA_STS_USB30_OVC = BIT(6),
780 + OMNIA_STS_USB31_OVC = BIT(7),
781 + OMNIA_STS_USB30_PWRON = BIT(8),
782 + OMNIA_STS_USB31_PWRON = BIT(9),
783 + OMNIA_STS_ENABLE_4V5 = BIT(10),
784 + OMNIA_STS_BUTTON_MODE = BIT(11),
785 + OMNIA_STS_BUTTON_PRESSED = BIT(12),
786 + OMNIA_STS_BUTTON_COUNTER_MASK = GENMASK(15, 13),
787 +};
788 +
789 +enum omnia_ctl_byte_e {
790 + OMNIA_CTL_LIGHT_RST = BIT(0),
791 + OMNIA_CTL_HARD_RST = BIT(1),
792 + /* BIT(2) is currently reserved */
793 + OMNIA_CTL_USB30_PWRON = BIT(3),
794 + OMNIA_CTL_USB31_PWRON = BIT(4),
795 + OMNIA_CTL_ENABLE_4V5 = BIT(5),
796 + OMNIA_CTL_BUTTON_MODE = BIT(6),
797 + OMNIA_CTL_BOOTLOADER = BIT(7),
798 +};
799 +
800 +enum omnia_features_e {
801 + OMNIA_FEAT_PERIPH_MCU = BIT(0),
802 + OMNIA_FEAT_EXT_CMDS = BIT(1),
803 + OMNIA_FEAT_WDT_PING = BIT(2),
804 + OMNIA_FEAT_LED_STATE_EXT_MASK = GENMASK(4, 3),
805 + OMNIA_FEAT_LED_STATE_EXT = FIELD_PREP_CONST(OMNIA_FEAT_LED_STATE_EXT_MASK, 1),
806 + OMNIA_FEAT_LED_STATE_EXT_V32 = FIELD_PREP_CONST(OMNIA_FEAT_LED_STATE_EXT_MASK, 2),
807 + OMNIA_FEAT_LED_GAMMA_CORRECTION = BIT(5),
808 + OMNIA_FEAT_NEW_INT_API = BIT(6),
809 + OMNIA_FEAT_BOOTLOADER = BIT(7),
810 + OMNIA_FEAT_FLASHING = BIT(8),
811 + OMNIA_FEAT_NEW_MESSAGE_API = BIT(9),
812 + OMNIA_FEAT_BRIGHTNESS_INT = BIT(10),
813 + OMNIA_FEAT_POWEROFF_WAKEUP = BIT(11),
814 + OMNIA_FEAT_CAN_OLD_MESSAGE_API = BIT(12),
815 + OMNIA_FEAT_TRNG = BIT(13),
816 + OMNIA_FEAT_CRYPTO = BIT(14),
817 + OMNIA_FEAT_BOARD_INFO = BIT(15),
818 +
819 + /*
820 + * Orginally the features command replied only 16 bits. If more were
821 + * read, either the I2C transaction failed or 0xff bytes were sent.
822 + * Therefore to consider bits 16 - 31 valid, one bit (20) was reserved
823 + * to be zero.
824 + */
825 +
826 + /* Bits 16 - 19 correspond to bits 0 - 3 of status word */
827 + OMNIA_FEAT_MCU_TYPE_MASK = GENMASK(17, 16),
828 + OMNIA_FEAT_MCU_TYPE_STM32 = FIELD_PREP_CONST(OMNIA_FEAT_MCU_TYPE_MASK, 0),
829 + OMNIA_FEAT_MCU_TYPE_GD32 = FIELD_PREP_CONST(OMNIA_FEAT_MCU_TYPE_MASK, 1),
830 + OMNIA_FEAT_MCU_TYPE_MKL = FIELD_PREP_CONST(OMNIA_FEAT_MCU_TYPE_MASK, 2),
831 + OMNIA_FEAT_FEATURES_SUPPORTED = BIT(18),
832 + OMNIA_FEAT_USER_REGULATOR_NOT_SUPPORTED = BIT(19),
833 +
834 + /* must not be set */
835 + OMNIA_FEAT_FROM_BIT_16_INVALID = BIT(20),
836 +
837 + OMNIA_FEAT_PER_LED_CORRECTION = BIT(21),
838 + OMNIA_FEAT_USB_OVC_PROT_SETTING = BIT(22),
839 +};
840 +
841 +enum omnia_ext_sts_dword_e {
842 + OMNIA_EXT_STS_SFP_nDET = BIT(0),
843 + OMNIA_EXT_STS_LED_STATES_MASK = GENMASK(31, 12),
844 + OMNIA_EXT_STS_WLAN0_MSATA_LED = BIT(12),
845 + OMNIA_EXT_STS_WLAN1_LED = BIT(13),
846 + OMNIA_EXT_STS_WLAN2_LED = BIT(14),
847 + OMNIA_EXT_STS_WPAN0_LED = BIT(15),
848 + OMNIA_EXT_STS_WPAN1_LED = BIT(16),
849 + OMNIA_EXT_STS_WPAN2_LED = BIT(17),
850 + OMNIA_EXT_STS_WAN_LED0 = BIT(18),
851 + OMNIA_EXT_STS_WAN_LED1 = BIT(19),
852 + OMNIA_EXT_STS_LAN0_LED0 = BIT(20),
853 + OMNIA_EXT_STS_LAN0_LED1 = BIT(21),
854 + OMNIA_EXT_STS_LAN1_LED0 = BIT(22),
855 + OMNIA_EXT_STS_LAN1_LED1 = BIT(23),
856 + OMNIA_EXT_STS_LAN2_LED0 = BIT(24),
857 + OMNIA_EXT_STS_LAN2_LED1 = BIT(25),
858 + OMNIA_EXT_STS_LAN3_LED0 = BIT(26),
859 + OMNIA_EXT_STS_LAN3_LED1 = BIT(27),
860 + OMNIA_EXT_STS_LAN4_LED0 = BIT(28),
861 + OMNIA_EXT_STS_LAN4_LED1 = BIT(29),
862 + OMNIA_EXT_STS_LAN5_LED0 = BIT(30),
863 + OMNIA_EXT_STS_LAN5_LED1 = BIT(31),
864 +};
865 +
866 +enum omnia_ext_ctl_e {
867 + OMNIA_EXT_CTL_nRES_MMC = BIT(0),
868 + OMNIA_EXT_CTL_nRES_LAN = BIT(1),
869 + OMNIA_EXT_CTL_nRES_PHY = BIT(2),
870 + OMNIA_EXT_CTL_nPERST0 = BIT(3),
871 + OMNIA_EXT_CTL_nPERST1 = BIT(4),
872 + OMNIA_EXT_CTL_nPERST2 = BIT(5),
873 + OMNIA_EXT_CTL_PHY_SFP = BIT(6),
874 + OMNIA_EXT_CTL_PHY_SFP_AUTO = BIT(7),
875 + OMNIA_EXT_CTL_nVHV_CTRL = BIT(8),
876 +};
877 +
878 +enum omnia_int_e {
879 + OMNIA_INT_CARD_DET = BIT(0),
880 + OMNIA_INT_MSATA_IND = BIT(1),
881 + OMNIA_INT_USB30_OVC = BIT(2),
882 + OMNIA_INT_USB31_OVC = BIT(3),
883 + OMNIA_INT_BUTTON_PRESSED = BIT(4),
884 + OMNIA_INT_SFP_nDET = BIT(5),
885 + OMNIA_INT_BRIGHTNESS_CHANGED = BIT(6),
886 + OMNIA_INT_TRNG = BIT(7),
887 + OMNIA_INT_MESSAGE_SIGNED = BIT(8),
888 +
889 + OMNIA_INT_LED_STATES_MASK = GENMASK(31, 12),
890 + OMNIA_INT_WLAN0_MSATA_LED = BIT(12),
891 + OMNIA_INT_WLAN1_LED = BIT(13),
892 + OMNIA_INT_WLAN2_LED = BIT(14),
893 + OMNIA_INT_WPAN0_LED = BIT(15),
894 + OMNIA_INT_WPAN1_LED = BIT(16),
895 + OMNIA_INT_WPAN2_LED = BIT(17),
896 + OMNIA_INT_WAN_LED0 = BIT(18),
897 + OMNIA_INT_WAN_LED1 = BIT(19),
898 + OMNIA_INT_LAN0_LED0 = BIT(20),
899 + OMNIA_INT_LAN0_LED1 = BIT(21),
900 + OMNIA_INT_LAN1_LED0 = BIT(22),
901 + OMNIA_INT_LAN1_LED1 = BIT(23),
902 + OMNIA_INT_LAN2_LED0 = BIT(24),
903 + OMNIA_INT_LAN2_LED1 = BIT(25),
904 + OMNIA_INT_LAN3_LED0 = BIT(26),
905 + OMNIA_INT_LAN3_LED1 = BIT(27),
906 + OMNIA_INT_LAN4_LED0 = BIT(28),
907 + OMNIA_INT_LAN4_LED1 = BIT(29),
908 + OMNIA_INT_LAN5_LED0 = BIT(30),
909 + OMNIA_INT_LAN5_LED1 = BIT(31),
910 +};
911 +
912 +enum omnia_cmd_poweroff_e {
913 + OMNIA_CMD_POWER_OFF_POWERON_BUTTON = BIT(0),
914 + OMNIA_CMD_POWER_OFF_MAGIC = 0xdead,
915 +};
916 +
917 +enum omnia_cmd_usb_ovc_prot_e {
918 + OMNIA_CMD_xET_USB_OVC_PROT_PORT_MASK = GENMASK(3, 0),
919 + OMNIA_CMD_xET_USB_OVC_PROT_ENABLE = BIT(4),
920 +};
921 +
922 +#endif /* __TURRIS_OMNIA_MCU_INTERFACE_H */