Merge tag 'uml-for-linus-6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux.git] / drivers / platform / cznic / turris-omnia-mcu-base.c
blob770e680b96f95d91f54f55dc3738565d05d19fcd
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * CZ.NIC's Turris Omnia MCU driver
5 * 2024 by Marek BehĂșn <kabel@kernel.org>
6 */
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];
31 int ret, num;
33 msgs[0].addr = client->addr;
34 msgs[0].flags = 0;
35 msgs[0].len = cmd_len;
36 msgs[0].buf = cmd;
37 num = 1;
39 if (reply_len) {
40 msgs[1].addr = client->addr;
41 msgs[1].flags = I2C_M_RD;
42 msgs[1].len = reply_len;
43 msgs[1].buf = reply;
44 num++;
47 ret = i2c_transfer(client->adapter, msgs, num);
48 if (ret < 0)
49 return ret;
50 if (ret != num)
51 return -EIO;
53 return 0;
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];
61 char *p;
62 int err;
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));
68 if (err)
69 return err;
71 p = bin2hex(version, reply, OMNIA_FW_VERSION_LEN);
72 *p = '\0';
74 return 0;
77 static ssize_t fw_version_hash_show(struct device *dev, char *buf,
78 bool bootloader)
80 struct omnia_mcu *mcu = dev_get_drvdata(dev);
81 char version[OMNIA_FW_VERSION_HEX_LEN];
82 int err;
84 err = omnia_get_version_hash(mcu, bootloader, version);
85 if (err)
86 return err;
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,
93 char *buf)
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,
101 char *buf)
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,
108 char *buf)
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,
117 char *buf)
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)
128 u8 reply;
129 int err;
131 err = omnia_cmd_read_u8(to_i2c_client(dev), OMNIA_CMD_GET_RESET,
132 &reply);
133 if (err)
134 return err;
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,
176 NULL
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))
189 return 0;
191 return a->mode;
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,
203 #endif
204 #ifdef CONFIG_TURRIS_OMNIA_MCU_SYSOFF_WAKEUP
205 &omnia_mcu_poweroff_group,
206 #endif
207 NULL
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];
215 int err;
217 err = omnia_get_version_hash(mcu, bootloader, version);
218 if (err) {
219 dev_err(dev, "Cannot read MCU %s firmware version: %d\n",
220 type, err);
221 return;
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:
231 return "STM32";
232 case OMNIA_STS_MCU_TYPE_GD32:
233 return "GD32";
234 case OMNIA_STS_MCU_TYPE_MKL:
235 return "MKL";
236 default:
237 return "unknown";
241 static void omnia_info_missing_feature(struct device *dev, const char *feature)
243 dev_info(dev,
244 "Your board's MCU firmware does not support the %s feature.\n",
245 feature);
248 static int omnia_mcu_read_features(struct omnia_mcu *mcu)
250 static const struct {
251 u16 mask;
252 const char *name;
253 } features[] = {
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"),
263 #undef _DEF_FEAT
265 struct i2c_client *client = mcu->client;
266 struct device *dev = &client->dev;
267 bool suggest_fw_upgrade = false;
268 u16 status;
269 int err;
271 /* status word holds MCU type, which we need below */
272 err = omnia_cmd_read_u16(client, OMNIA_CMD_GET_STATUS_WORD, &status);
273 if (err)
274 return err;
277 * Check whether MCU firmware supports the OMNIA_CMD_GET_FEATURES
278 * command.
280 if (status & OMNIA_STS_FEATURES_SUPPORTED) {
281 /* try read 32-bit features */
282 err = omnia_cmd_read_u32(client, OMNIA_CMD_GET_FEATURES,
283 &mcu->features);
284 if (err) {
285 /* try read 16-bit features */
286 u16 features16;
288 err = omnia_cmd_read_u16(client, OMNIA_CMD_GET_FEATURES,
289 &features16);
290 if (err)
291 return err;
293 mcu->features = features16;
294 } else {
295 if (mcu->features & OMNIA_FEAT_FROM_BIT_16_INVALID)
296 mcu->features &= GENMASK(15, 0);
298 } else {
299 dev_info(dev,
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)
312 dev_warn(dev,
313 "MCU is running bootloader firmware. Was firmware upgrade interrupted?\n");
314 else
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)
319 continue;
321 omnia_info_missing_feature(dev, features[i].name);
322 suggest_fw_upgrade = true;
325 if (suggest_fw_upgrade)
326 dev_info(dev,
327 "Consider upgrading MCU firmware with the omnia-mcutool utility.\n");
329 return 0;
332 static int omnia_mcu_read_board_info(struct omnia_mcu *mcu)
334 u8 reply[1 + OMNIA_BOARD_INFO_LEN];
335 int err;
337 err = omnia_cmd_read(mcu->client, OMNIA_CMD_BOARD_INFO_GET, reply,
338 sizeof(reply));
339 if (err)
340 return err;
342 if (reply[0] != OMNIA_BOARD_INFO_LEN)
343 return -EIO;
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];
352 return 0;
355 static int omnia_mcu_probe(struct i2c_client *client)
357 struct device *dev = &client->dev;
358 struct omnia_mcu *mcu;
359 int err;
361 if (!client->irq)
362 return dev_err_probe(dev, -EINVAL, "IRQ resource not found\n");
364 mcu = devm_kzalloc(dev, sizeof(*mcu), GFP_KERNEL);
365 if (!mcu)
366 return -ENOMEM;
368 mcu->client = client;
369 i2c_set_clientdata(client, mcu);
371 err = omnia_mcu_read_features(mcu);
372 if (err)
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);
378 if (err)
379 return dev_err_probe(dev, err,
380 "Cannot read board info\n");
383 err = omnia_mcu_register_sys_off_and_wakeup(mcu);
384 if (err)
385 return err;
387 err = omnia_mcu_register_watchdog(mcu);
388 if (err)
389 return err;
391 err = omnia_mcu_register_gpiochip(mcu);
392 if (err)
393 return err;
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,
405 .driver = {
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");