1 // SPDX-License-Identifier: GPL-2.0
3 * CZ.NIC's Turris Omnia MCU driver
5 * 2024 by Marek BehĂșn <kabel@kernel.org>
8 #include <linux/array_size.h>
9 #include <linux/bits.h>
10 #include <linux/device.h>
11 #include <linux/errno.h>
12 #include <linux/hex.h>
13 #include <linux/i2c.h>
14 #include <linux/module.h>
15 #include <linux/string.h>
16 #include <linux/sysfs.h>
17 #include <linux/types.h>
19 #include <linux/turris-omnia-mcu-interface.h>
20 #include "turris-omnia-mcu.h"
22 #define OMNIA_FW_VERSION_LEN 20
23 #define OMNIA_FW_VERSION_HEX_LEN (2 * OMNIA_FW_VERSION_LEN + 1)
24 #define OMNIA_BOARD_INFO_LEN 16
26 int omnia_cmd_write_read(const struct i2c_client
*client
,
27 void *cmd
, unsigned int cmd_len
,
28 void *reply
, unsigned int reply_len
)
30 struct i2c_msg msgs
[2];
33 msgs
[0].addr
= client
->addr
;
35 msgs
[0].len
= cmd_len
;
40 msgs
[1].addr
= client
->addr
;
41 msgs
[1].flags
= I2C_M_RD
;
42 msgs
[1].len
= reply_len
;
47 ret
= i2c_transfer(client
->adapter
, msgs
, num
);
55 EXPORT_SYMBOL_GPL(omnia_cmd_write_read
);
57 static int omnia_get_version_hash(struct omnia_mcu
*mcu
, bool bootloader
,
58 char version
[static OMNIA_FW_VERSION_HEX_LEN
])
60 u8 reply
[OMNIA_FW_VERSION_LEN
];
64 err
= omnia_cmd_read(mcu
->client
,
65 bootloader
? OMNIA_CMD_GET_FW_VERSION_BOOT
66 : OMNIA_CMD_GET_FW_VERSION_APP
,
67 reply
, sizeof(reply
));
71 p
= bin2hex(version
, reply
, OMNIA_FW_VERSION_LEN
);
77 static ssize_t
fw_version_hash_show(struct device
*dev
, char *buf
,
80 struct omnia_mcu
*mcu
= dev_get_drvdata(dev
);
81 char version
[OMNIA_FW_VERSION_HEX_LEN
];
84 err
= omnia_get_version_hash(mcu
, bootloader
, version
);
88 return sysfs_emit(buf
, "%s\n", version
);
91 static ssize_t
fw_version_hash_application_show(struct device
*dev
,
92 struct device_attribute
*a
,
95 return fw_version_hash_show(dev
, buf
, false);
97 static DEVICE_ATTR_RO(fw_version_hash_application
);
99 static ssize_t
fw_version_hash_bootloader_show(struct device
*dev
,
100 struct device_attribute
*a
,
103 return fw_version_hash_show(dev
, buf
, true);
105 static DEVICE_ATTR_RO(fw_version_hash_bootloader
);
107 static ssize_t
fw_features_show(struct device
*dev
, struct device_attribute
*a
,
110 struct omnia_mcu
*mcu
= dev_get_drvdata(dev
);
112 return sysfs_emit(buf
, "0x%x\n", mcu
->features
);
114 static DEVICE_ATTR_RO(fw_features
);
116 static ssize_t
mcu_type_show(struct device
*dev
, struct device_attribute
*a
,
119 struct omnia_mcu
*mcu
= dev_get_drvdata(dev
);
121 return sysfs_emit(buf
, "%s\n", mcu
->type
);
123 static DEVICE_ATTR_RO(mcu_type
);
125 static ssize_t
reset_selector_show(struct device
*dev
,
126 struct device_attribute
*a
, char *buf
)
131 err
= omnia_cmd_read_u8(to_i2c_client(dev
), OMNIA_CMD_GET_RESET
,
136 return sysfs_emit(buf
, "%d\n", reply
);
138 static DEVICE_ATTR_RO(reset_selector
);
140 static ssize_t
serial_number_show(struct device
*dev
,
141 struct device_attribute
*a
, char *buf
)
143 struct omnia_mcu
*mcu
= dev_get_drvdata(dev
);
145 return sysfs_emit(buf
, "%016llX\n", mcu
->board_serial_number
);
147 static DEVICE_ATTR_RO(serial_number
);
149 static ssize_t
first_mac_address_show(struct device
*dev
,
150 struct device_attribute
*a
, char *buf
)
152 struct omnia_mcu
*mcu
= dev_get_drvdata(dev
);
154 return sysfs_emit(buf
, "%pM\n", mcu
->board_first_mac
);
156 static DEVICE_ATTR_RO(first_mac_address
);
158 static ssize_t
board_revision_show(struct device
*dev
,
159 struct device_attribute
*a
, char *buf
)
161 struct omnia_mcu
*mcu
= dev_get_drvdata(dev
);
163 return sysfs_emit(buf
, "%u\n", mcu
->board_revision
);
165 static DEVICE_ATTR_RO(board_revision
);
167 static struct attribute
*omnia_mcu_base_attrs
[] = {
168 &dev_attr_fw_version_hash_application
.attr
,
169 &dev_attr_fw_version_hash_bootloader
.attr
,
170 &dev_attr_fw_features
.attr
,
171 &dev_attr_mcu_type
.attr
,
172 &dev_attr_reset_selector
.attr
,
173 &dev_attr_serial_number
.attr
,
174 &dev_attr_first_mac_address
.attr
,
175 &dev_attr_board_revision
.attr
,
179 static umode_t
omnia_mcu_base_attrs_visible(struct kobject
*kobj
,
180 struct attribute
*a
, int n
)
182 struct device
*dev
= kobj_to_dev(kobj
);
183 struct omnia_mcu
*mcu
= dev_get_drvdata(dev
);
185 if ((a
== &dev_attr_serial_number
.attr
||
186 a
== &dev_attr_first_mac_address
.attr
||
187 a
== &dev_attr_board_revision
.attr
) &&
188 !(mcu
->features
& OMNIA_FEAT_BOARD_INFO
))
194 static const struct attribute_group omnia_mcu_base_group
= {
195 .attrs
= omnia_mcu_base_attrs
,
196 .is_visible
= omnia_mcu_base_attrs_visible
,
199 static const struct attribute_group
*omnia_mcu_groups
[] = {
200 &omnia_mcu_base_group
,
201 #ifdef CONFIG_TURRIS_OMNIA_MCU_GPIO
202 &omnia_mcu_gpio_group
,
204 #ifdef CONFIG_TURRIS_OMNIA_MCU_SYSOFF_WAKEUP
205 &omnia_mcu_poweroff_group
,
210 static void omnia_mcu_print_version_hash(struct omnia_mcu
*mcu
, bool bootloader
)
212 const char *type
= bootloader
? "bootloader" : "application";
213 struct device
*dev
= &mcu
->client
->dev
;
214 char version
[OMNIA_FW_VERSION_HEX_LEN
];
217 err
= omnia_get_version_hash(mcu
, bootloader
, version
);
219 dev_err(dev
, "Cannot read MCU %s firmware version: %d\n",
224 dev_info(dev
, "MCU %s firmware version hash: %s\n", type
, version
);
227 static const char *omnia_status_to_mcu_type(u16 status
)
229 switch (status
& OMNIA_STS_MCU_TYPE_MASK
) {
230 case OMNIA_STS_MCU_TYPE_STM32
:
232 case OMNIA_STS_MCU_TYPE_GD32
:
234 case OMNIA_STS_MCU_TYPE_MKL
:
241 static void omnia_info_missing_feature(struct device
*dev
, const char *feature
)
244 "Your board's MCU firmware does not support the %s feature.\n",
248 static int omnia_mcu_read_features(struct omnia_mcu
*mcu
)
250 static const struct {
254 #define _DEF_FEAT(_n, _m) { OMNIA_FEAT_ ## _n, _m }
255 _DEF_FEAT(EXT_CMDS
, "extended control and status"),
256 _DEF_FEAT(WDT_PING
, "watchdog pinging"),
257 _DEF_FEAT(LED_STATE_EXT_MASK
, "peripheral LED pins reading"),
258 _DEF_FEAT(NEW_INT_API
, "new interrupt API"),
259 _DEF_FEAT(POWEROFF_WAKEUP
, "poweroff and wakeup"),
260 _DEF_FEAT(TRNG
, "true random number generator"),
261 _DEF_FEAT(BRIGHTNESS_INT
, "LED panel brightness change interrupt"),
262 _DEF_FEAT(LED_GAMMA_CORRECTION
, "LED gamma correction"),
265 struct i2c_client
*client
= mcu
->client
;
266 struct device
*dev
= &client
->dev
;
267 bool suggest_fw_upgrade
= false;
271 /* status word holds MCU type, which we need below */
272 err
= omnia_cmd_read_u16(client
, OMNIA_CMD_GET_STATUS_WORD
, &status
);
277 * Check whether MCU firmware supports the OMNIA_CMD_GET_FEATURES
280 if (status
& OMNIA_STS_FEATURES_SUPPORTED
) {
281 /* try read 32-bit features */
282 err
= omnia_cmd_read_u32(client
, OMNIA_CMD_GET_FEATURES
,
285 /* try read 16-bit features */
288 err
= omnia_cmd_read_u16(client
, OMNIA_CMD_GET_FEATURES
,
293 mcu
->features
= features16
;
295 if (mcu
->features
& OMNIA_FEAT_FROM_BIT_16_INVALID
)
296 mcu
->features
&= GENMASK(15, 0);
300 "Your board's MCU firmware does not support feature reading.\n");
301 suggest_fw_upgrade
= true;
304 mcu
->type
= omnia_status_to_mcu_type(status
);
305 dev_info(dev
, "MCU type %s%s\n", mcu
->type
,
306 (mcu
->features
& OMNIA_FEAT_PERIPH_MCU
) ?
307 ", with peripheral resets wired" : "");
309 omnia_mcu_print_version_hash(mcu
, true);
311 if (mcu
->features
& OMNIA_FEAT_BOOTLOADER
)
313 "MCU is running bootloader firmware. Was firmware upgrade interrupted?\n");
315 omnia_mcu_print_version_hash(mcu
, false);
317 for (unsigned int i
= 0; i
< ARRAY_SIZE(features
); i
++) {
318 if (mcu
->features
& features
[i
].mask
)
321 omnia_info_missing_feature(dev
, features
[i
].name
);
322 suggest_fw_upgrade
= true;
325 if (suggest_fw_upgrade
)
327 "Consider upgrading MCU firmware with the omnia-mcutool utility.\n");
332 static int omnia_mcu_read_board_info(struct omnia_mcu
*mcu
)
334 u8 reply
[1 + OMNIA_BOARD_INFO_LEN
];
337 err
= omnia_cmd_read(mcu
->client
, OMNIA_CMD_BOARD_INFO_GET
, reply
,
342 if (reply
[0] != OMNIA_BOARD_INFO_LEN
)
345 mcu
->board_serial_number
= get_unaligned_le64(&reply
[1]);
347 /* we can't use ether_addr_copy() because reply is not u16-aligned */
348 memcpy(mcu
->board_first_mac
, &reply
[9], sizeof(mcu
->board_first_mac
));
350 mcu
->board_revision
= reply
[15];
355 static int omnia_mcu_probe(struct i2c_client
*client
)
357 struct device
*dev
= &client
->dev
;
358 struct omnia_mcu
*mcu
;
362 return dev_err_probe(dev
, -EINVAL
, "IRQ resource not found\n");
364 mcu
= devm_kzalloc(dev
, sizeof(*mcu
), GFP_KERNEL
);
368 mcu
->client
= client
;
369 i2c_set_clientdata(client
, mcu
);
371 err
= omnia_mcu_read_features(mcu
);
373 return dev_err_probe(dev
, err
,
374 "Cannot determine MCU supported features\n");
376 if (mcu
->features
& OMNIA_FEAT_BOARD_INFO
) {
377 err
= omnia_mcu_read_board_info(mcu
);
379 return dev_err_probe(dev
, err
,
380 "Cannot read board info\n");
383 err
= omnia_mcu_register_sys_off_and_wakeup(mcu
);
387 err
= omnia_mcu_register_watchdog(mcu
);
391 err
= omnia_mcu_register_gpiochip(mcu
);
395 return omnia_mcu_register_trng(mcu
);
398 static const struct of_device_id of_omnia_mcu_match
[] = {
399 { .compatible
= "cznic,turris-omnia-mcu" },
403 static struct i2c_driver omnia_mcu_driver
= {
404 .probe
= omnia_mcu_probe
,
406 .name
= "turris-omnia-mcu",
407 .of_match_table
= of_omnia_mcu_match
,
408 .dev_groups
= omnia_mcu_groups
,
411 module_i2c_driver(omnia_mcu_driver
);
413 MODULE_AUTHOR("Marek Behun <kabel@kernel.org>");
414 MODULE_DESCRIPTION("CZ.NIC's Turris Omnia MCU");
415 MODULE_LICENSE("GPL");