xref: /linux/drivers/platform/cznic/turris-omnia-mcu-base.c (revision 66e72a01b60ae6950ddbb3585fdc1424d303e14b)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * CZ.NIC's Turris Omnia MCU driver
4  *
5  * 2024 by Marek Behún <kabel@kernel.org>
6  */
7 
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>
18 
19 #include <linux/turris-omnia-mcu-interface.h>
20 #include "turris-omnia-mcu.h"
21 
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
25 
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)
29 {
30 	struct i2c_msg msgs[2];
31 	int ret, num;
32 
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;
38 
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++;
45 	}
46 
47 	ret = i2c_transfer(client->adapter, msgs, num);
48 	if (ret < 0)
49 		return ret;
50 	if (ret != num)
51 		return -EIO;
52 
53 	return 0;
54 }
55 
56 static int omnia_get_version_hash(struct omnia_mcu *mcu, bool bootloader,
57 				  char version[static OMNIA_FW_VERSION_HEX_LEN])
58 {
59 	u8 reply[OMNIA_FW_VERSION_LEN];
60 	char *p;
61 	int err;
62 
63 	err = omnia_cmd_read(mcu->client,
64 			     bootloader ? OMNIA_CMD_GET_FW_VERSION_BOOT
65 					: OMNIA_CMD_GET_FW_VERSION_APP,
66 			     reply, sizeof(reply));
67 	if (err)
68 		return err;
69 
70 	p = bin2hex(version, reply, OMNIA_FW_VERSION_LEN);
71 	*p = '\0';
72 
73 	return 0;
74 }
75 
76 static ssize_t fw_version_hash_show(struct device *dev, char *buf,
77 				    bool bootloader)
78 {
79 	struct omnia_mcu *mcu = dev_get_drvdata(dev);
80 	char version[OMNIA_FW_VERSION_HEX_LEN];
81 	int err;
82 
83 	err = omnia_get_version_hash(mcu, bootloader, version);
84 	if (err)
85 		return err;
86 
87 	return sysfs_emit(buf, "%s\n", version);
88 }
89 
90 static ssize_t fw_version_hash_application_show(struct device *dev,
91 						struct device_attribute *a,
92 						char *buf)
93 {
94 	return fw_version_hash_show(dev, buf, false);
95 }
96 static DEVICE_ATTR_RO(fw_version_hash_application);
97 
98 static ssize_t fw_version_hash_bootloader_show(struct device *dev,
99 					       struct device_attribute *a,
100 					       char *buf)
101 {
102 	return fw_version_hash_show(dev, buf, true);
103 }
104 static DEVICE_ATTR_RO(fw_version_hash_bootloader);
105 
106 static ssize_t fw_features_show(struct device *dev, struct device_attribute *a,
107 				char *buf)
108 {
109 	struct omnia_mcu *mcu = dev_get_drvdata(dev);
110 
111 	return sysfs_emit(buf, "0x%x\n", mcu->features);
112 }
113 static DEVICE_ATTR_RO(fw_features);
114 
115 static ssize_t mcu_type_show(struct device *dev, struct device_attribute *a,
116 			     char *buf)
117 {
118 	struct omnia_mcu *mcu = dev_get_drvdata(dev);
119 
120 	return sysfs_emit(buf, "%s\n", mcu->type);
121 }
122 static DEVICE_ATTR_RO(mcu_type);
123 
124 static ssize_t reset_selector_show(struct device *dev,
125 				   struct device_attribute *a, char *buf)
126 {
127 	u8 reply;
128 	int err;
129 
130 	err = omnia_cmd_read_u8(to_i2c_client(dev), OMNIA_CMD_GET_RESET,
131 				&reply);
132 	if (err)
133 		return err;
134 
135 	return sysfs_emit(buf, "%d\n", reply);
136 }
137 static DEVICE_ATTR_RO(reset_selector);
138 
139 static ssize_t serial_number_show(struct device *dev,
140 				  struct device_attribute *a, char *buf)
141 {
142 	struct omnia_mcu *mcu = dev_get_drvdata(dev);
143 
144 	return sysfs_emit(buf, "%016llX\n", mcu->board_serial_number);
145 }
146 static DEVICE_ATTR_RO(serial_number);
147 
148 static ssize_t first_mac_address_show(struct device *dev,
149 				      struct device_attribute *a, char *buf)
150 {
151 	struct omnia_mcu *mcu = dev_get_drvdata(dev);
152 
153 	return sysfs_emit(buf, "%pM\n", mcu->board_first_mac);
154 }
155 static DEVICE_ATTR_RO(first_mac_address);
156 
157 static ssize_t board_revision_show(struct device *dev,
158 				   struct device_attribute *a, char *buf)
159 {
160 	struct omnia_mcu *mcu = dev_get_drvdata(dev);
161 
162 	return sysfs_emit(buf, "%u\n", mcu->board_revision);
163 }
164 static DEVICE_ATTR_RO(board_revision);
165 
166 static struct attribute *omnia_mcu_base_attrs[] = {
167 	&dev_attr_fw_version_hash_application.attr,
168 	&dev_attr_fw_version_hash_bootloader.attr,
169 	&dev_attr_fw_features.attr,
170 	&dev_attr_mcu_type.attr,
171 	&dev_attr_reset_selector.attr,
172 	&dev_attr_serial_number.attr,
173 	&dev_attr_first_mac_address.attr,
174 	&dev_attr_board_revision.attr,
175 	NULL
176 };
177 
178 static umode_t omnia_mcu_base_attrs_visible(struct kobject *kobj,
179 					    struct attribute *a, int n)
180 {
181 	struct device *dev = kobj_to_dev(kobj);
182 	struct omnia_mcu *mcu = dev_get_drvdata(dev);
183 
184 	if ((a == &dev_attr_serial_number.attr ||
185 	     a == &dev_attr_first_mac_address.attr ||
186 	     a == &dev_attr_board_revision.attr) &&
187 	    !(mcu->features & OMNIA_FEAT_BOARD_INFO))
188 		return 0;
189 
190 	return a->mode;
191 }
192 
193 static const struct attribute_group omnia_mcu_base_group = {
194 	.attrs = omnia_mcu_base_attrs,
195 	.is_visible = omnia_mcu_base_attrs_visible,
196 };
197 
198 static const struct attribute_group *omnia_mcu_groups[] = {
199 	&omnia_mcu_base_group,
200 	&omnia_mcu_gpio_group,
201 	&omnia_mcu_poweroff_group,
202 	NULL
203 };
204 
205 static void omnia_mcu_print_version_hash(struct omnia_mcu *mcu, bool bootloader)
206 {
207 	const char *type = bootloader ? "bootloader" : "application";
208 	struct device *dev = &mcu->client->dev;
209 	char version[OMNIA_FW_VERSION_HEX_LEN];
210 	int err;
211 
212 	err = omnia_get_version_hash(mcu, bootloader, version);
213 	if (err) {
214 		dev_err(dev, "Cannot read MCU %s firmware version: %d\n",
215 			type, err);
216 		return;
217 	}
218 
219 	dev_info(dev, "MCU %s firmware version hash: %s\n", type, version);
220 }
221 
222 static const char *omnia_status_to_mcu_type(u16 status)
223 {
224 	switch (status & OMNIA_STS_MCU_TYPE_MASK) {
225 	case OMNIA_STS_MCU_TYPE_STM32:
226 		return "STM32";
227 	case OMNIA_STS_MCU_TYPE_GD32:
228 		return "GD32";
229 	case OMNIA_STS_MCU_TYPE_MKL:
230 		return "MKL";
231 	default:
232 		return "unknown";
233 	}
234 }
235 
236 static void omnia_info_missing_feature(struct device *dev, const char *feature)
237 {
238 	dev_info(dev,
239 		 "Your board's MCU firmware does not support the %s feature.\n",
240 		 feature);
241 }
242 
243 static int omnia_mcu_read_features(struct omnia_mcu *mcu)
244 {
245 	static const struct {
246 		u16 mask;
247 		const char *name;
248 	} features[] = {
249 #define _DEF_FEAT(_n, _m) { OMNIA_FEAT_ ## _n, _m }
250 		_DEF_FEAT(EXT_CMDS,		"extended control and status"),
251 		_DEF_FEAT(WDT_PING,		"watchdog pinging"),
252 		_DEF_FEAT(LED_STATE_EXT_MASK,	"peripheral LED pins reading"),
253 		_DEF_FEAT(NEW_INT_API,		"new interrupt API"),
254 		_DEF_FEAT(POWEROFF_WAKEUP,	"poweroff and wakeup"),
255 		_DEF_FEAT(TRNG,			"true random number generator"),
256 #undef _DEF_FEAT
257 	};
258 	struct i2c_client *client = mcu->client;
259 	struct device *dev = &client->dev;
260 	bool suggest_fw_upgrade = false;
261 	u16 status;
262 	int err;
263 
264 	/* status word holds MCU type, which we need below */
265 	err = omnia_cmd_read_u16(client, OMNIA_CMD_GET_STATUS_WORD, &status);
266 	if (err)
267 		return err;
268 
269 	/*
270 	 * Check whether MCU firmware supports the OMNIA_CMD_GET_FEATURES
271 	 * command.
272 	 */
273 	if (status & OMNIA_STS_FEATURES_SUPPORTED) {
274 		/* try read 32-bit features */
275 		err = omnia_cmd_read_u32(client, OMNIA_CMD_GET_FEATURES,
276 					 &mcu->features);
277 		if (err) {
278 			/* try read 16-bit features */
279 			u16 features16;
280 
281 			err = omnia_cmd_read_u16(client, OMNIA_CMD_GET_FEATURES,
282 						 &features16);
283 			if (err)
284 				return err;
285 
286 			mcu->features = features16;
287 		} else {
288 			if (mcu->features & OMNIA_FEAT_FROM_BIT_16_INVALID)
289 				mcu->features &= GENMASK(15, 0);
290 		}
291 	} else {
292 		dev_info(dev,
293 			 "Your board's MCU firmware does not support feature reading.\n");
294 		suggest_fw_upgrade = true;
295 	}
296 
297 	mcu->type = omnia_status_to_mcu_type(status);
298 	dev_info(dev, "MCU type %s%s\n", mcu->type,
299 		 (mcu->features & OMNIA_FEAT_PERIPH_MCU) ?
300 			", with peripheral resets wired" : "");
301 
302 	omnia_mcu_print_version_hash(mcu, true);
303 
304 	if (mcu->features & OMNIA_FEAT_BOOTLOADER)
305 		dev_warn(dev,
306 			 "MCU is running bootloader firmware. Was firmware upgrade interrupted?\n");
307 	else
308 		omnia_mcu_print_version_hash(mcu, false);
309 
310 	for (unsigned int i = 0; i < ARRAY_SIZE(features); i++) {
311 		if (mcu->features & features[i].mask)
312 			continue;
313 
314 		omnia_info_missing_feature(dev, features[i].name);
315 		suggest_fw_upgrade = true;
316 	}
317 
318 	if (suggest_fw_upgrade)
319 		dev_info(dev,
320 			 "Consider upgrading MCU firmware with the omnia-mcutool utility.\n");
321 
322 	return 0;
323 }
324 
325 static int omnia_mcu_read_board_info(struct omnia_mcu *mcu)
326 {
327 	u8 reply[1 + OMNIA_BOARD_INFO_LEN];
328 	int err;
329 
330 	err = omnia_cmd_read(mcu->client, OMNIA_CMD_BOARD_INFO_GET, reply,
331 			     sizeof(reply));
332 	if (err)
333 		return err;
334 
335 	if (reply[0] != OMNIA_BOARD_INFO_LEN)
336 		return -EIO;
337 
338 	mcu->board_serial_number = get_unaligned_le64(&reply[1]);
339 
340 	/* we can't use ether_addr_copy() because reply is not u16-aligned */
341 	memcpy(mcu->board_first_mac, &reply[9], sizeof(mcu->board_first_mac));
342 
343 	mcu->board_revision = reply[15];
344 
345 	return 0;
346 }
347 
348 static int omnia_mcu_probe(struct i2c_client *client)
349 {
350 	struct device *dev = &client->dev;
351 	struct omnia_mcu *mcu;
352 	int err;
353 
354 	if (!client->irq)
355 		return dev_err_probe(dev, -EINVAL, "IRQ resource not found\n");
356 
357 	mcu = devm_kzalloc(dev, sizeof(*mcu), GFP_KERNEL);
358 	if (!mcu)
359 		return -ENOMEM;
360 
361 	mcu->client = client;
362 	i2c_set_clientdata(client, mcu);
363 
364 	err = omnia_mcu_read_features(mcu);
365 	if (err)
366 		return dev_err_probe(dev, err,
367 				     "Cannot determine MCU supported features\n");
368 
369 	if (mcu->features & OMNIA_FEAT_BOARD_INFO) {
370 		err = omnia_mcu_read_board_info(mcu);
371 		if (err)
372 			return dev_err_probe(dev, err,
373 					     "Cannot read board info\n");
374 	}
375 
376 	err = omnia_mcu_register_sys_off_and_wakeup(mcu);
377 	if (err)
378 		return err;
379 
380 	err = omnia_mcu_register_watchdog(mcu);
381 	if (err)
382 		return err;
383 
384 	err = omnia_mcu_register_gpiochip(mcu);
385 	if (err)
386 		return err;
387 
388 	return omnia_mcu_register_trng(mcu);
389 }
390 
391 static const struct of_device_id of_omnia_mcu_match[] = {
392 	{ .compatible = "cznic,turris-omnia-mcu" },
393 	{}
394 };
395 
396 static struct i2c_driver omnia_mcu_driver = {
397 	.probe		= omnia_mcu_probe,
398 	.driver		= {
399 		.name	= "turris-omnia-mcu",
400 		.of_match_table = of_omnia_mcu_match,
401 		.dev_groups = omnia_mcu_groups,
402 	},
403 };
404 module_i2c_driver(omnia_mcu_driver);
405 
406 MODULE_AUTHOR("Marek Behun <kabel@kernel.org>");
407 MODULE_DESCRIPTION("CZ.NIC's Turris Omnia MCU");
408 MODULE_LICENSE("GPL");
409