1 /*
2  * Copyright (c) 2018 Prevas A/S
3  * Copyright (c) 2022 Intel Corporation
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <stdlib.h>
9 #include <errno.h>
10 #include <zephyr/sys/slist.h>
11 #include <zephyr/drivers/smbus.h>
12 #include <zephyr/shell/shell.h>
13 
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(smbus_shell, CONFIG_LOG_DEFAULT_LEVEL);
16 
17 /**
18  * smbus_shell is a highly modified version from i2c_shell. Basically only scan
19  * logic remains from i2c_shell
20  */
21 
22 /**
23  * Simplify argument parsing, smbus arguments always go in this order:
24  * smbus <shell command> <device> <peripheral address> <command byte>
25  */
26 #define ARGV_DEV	1
27 #define ARGV_ADDR	2
28 #define ARGV_CMD	3
29 
30 /**
31  * This sends SMBUS messages without any data (i.e. stop condition after
32  * sending just the address). If there is an ACK for the address, it
33  * is assumed there is a device present.
34  *
35  * WARNING: As there is no standard SMBUS detection command, this code
36  * uses arbitrary SMBus commands (namely SMBus quick write to probe for
37  * devices.
38  * This operation can confuse your SMBUS bus, cause data loss, and is
39  * known to corrupt the Atmel AT24RF08 EEPROM found on many IBM
40  * Thinkpad laptops.
41  *
42  * https://manpages.debian.org/buster/i2c-tools/i2cdetect.8.en.html
43  */
44 
45 /* smbus scan <device> */
cmd_smbus_scan(const struct shell * sh,size_t argc,char ** argv)46 static int cmd_smbus_scan(const struct shell *sh, size_t argc, char **argv)
47 {
48 	const struct device *dev;
49 	uint8_t cnt = 0, first = 0x04, last = 0x77;
50 
51 	dev = shell_device_get_binding(argv[ARGV_DEV]);
52 	if (!dev) {
53 		shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]);
54 		return -ENODEV;
55 	}
56 
57 	shell_print(sh, "     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f");
58 	for (uint8_t i = 0; i <= last; i += 16) {
59 		shell_fprintf(sh, SHELL_NORMAL, "%02x: ", i);
60 
61 		for (uint8_t j = 0; j < 16; j++) {
62 			if (i + j < first || i + j > last) {
63 				shell_fprintf(sh, SHELL_NORMAL, "   ");
64 				continue;
65 			}
66 
67 			if (smbus_quick(dev, i + j, SMBUS_MSG_WRITE) == 0) {
68 				shell_fprintf(sh, SHELL_NORMAL, "%02x ", i + j);
69 				++cnt;
70 			} else {
71 				shell_fprintf(sh, SHELL_NORMAL, "-- ");
72 			}
73 		}
74 
75 		shell_print(sh, "");
76 	}
77 
78 	shell_print(sh, "%u devices found on %s", cnt, argv[ARGV_DEV]);
79 
80 	return 0;
81 }
82 
83 /* smbus quick <device> <dev_addr> */
cmd_smbus_quick(const struct shell * sh,size_t argc,char ** argv)84 static int cmd_smbus_quick(const struct shell *sh, size_t argc, char **argv)
85 {
86 	const struct device *dev;
87 	uint8_t addr;
88 	int ret;
89 
90 	dev = shell_device_get_binding(argv[ARGV_DEV]);
91 	if (!dev) {
92 		shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]);
93 		return -ENODEV;
94 	}
95 
96 	addr = strtol(argv[ARGV_ADDR], NULL, 16);
97 
98 	ret = smbus_quick(dev, addr, SMBUS_MSG_WRITE);
99 	if (ret < 0) {
100 		shell_error(sh, "SMBus: Failed quick cmd, perip: 0x%02x", addr);
101 	}
102 
103 	return ret;
104 }
105 
106 /* smbus byte_read <device> <dev_addr> */
cmd_smbus_byte_read(const struct shell * sh,size_t argc,char ** argv)107 static int cmd_smbus_byte_read(const struct shell *sh, size_t argc, char **argv)
108 {
109 	const struct device *dev;
110 	uint8_t addr;
111 	uint8_t out;
112 	int ret;
113 
114 	dev = shell_device_get_binding(argv[ARGV_DEV]);
115 	if (!dev) {
116 		shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]);
117 		return -ENODEV;
118 	}
119 
120 	addr = strtol(argv[ARGV_ADDR], NULL, 16);
121 
122 	ret = smbus_byte_read(dev, addr, &out);
123 	if (ret < 0) {
124 		shell_error(sh, "SMBus: Failed to read from periph: 0x%02x",
125 			    addr);
126 		return -EIO;
127 	}
128 
129 	shell_print(sh, "Output: 0x%x", out);
130 
131 	return 0;
132 }
133 
134 /* smbus byte_write <device> <dev_addr> <value> */
cmd_smbus_byte_write(const struct shell * sh,size_t argc,char ** argv)135 static int cmd_smbus_byte_write(const struct shell *sh,
136 				size_t argc, char **argv)
137 {
138 	const struct device *dev;
139 	uint8_t addr;
140 	uint8_t value;
141 	int ret;
142 
143 	dev = shell_device_get_binding(argv[ARGV_DEV]);
144 	if (!dev) {
145 		shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]);
146 		return -ENODEV;
147 	}
148 
149 	addr = strtol(argv[ARGV_ADDR], NULL, 16);
150 	/* First byte is command */
151 	value = strtol(argv[ARGV_CMD], NULL, 16);
152 
153 	ret = smbus_byte_write(dev, addr, value);
154 	if (ret < 0) {
155 		shell_error(sh, "SMBus: Failed to write to periph: 0x%02x",
156 			    addr);
157 		return -EIO;
158 	}
159 
160 	return 0;
161 }
162 
163 /* smbus byte_data_read <device> <dev_addr> <cmd> */
cmd_smbus_byte_data_read(const struct shell * sh,size_t argc,char ** argv)164 static int cmd_smbus_byte_data_read(const struct shell *sh,
165 				    size_t argc, char **argv)
166 {
167 	const struct device *dev;
168 	uint8_t addr, command;
169 	uint8_t out;
170 	int ret;
171 
172 	dev = shell_device_get_binding(argv[ARGV_DEV]);
173 	if (!dev) {
174 		shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]);
175 		return -ENODEV;
176 	}
177 
178 	addr = strtol(argv[ARGV_ADDR], NULL, 16);
179 	command = strtol(argv[ARGV_CMD], NULL, 16);
180 
181 	ret = smbus_byte_data_read(dev, addr, command, &out);
182 	if (ret < 0) {
183 		shell_error(sh, "SMBus: Failed to read from periph: 0x%02x",
184 			    addr);
185 		return -EIO;
186 	}
187 
188 	shell_print(sh, "Output: 0x%x", out);
189 
190 	return 0;
191 }
192 
193 /* smbus byte_data_write <device> <dev_addr> <cmd> <value> */
cmd_smbus_byte_data_write(const struct shell * sh,size_t argc,char ** argv)194 static int cmd_smbus_byte_data_write(const struct shell *sh,
195 				     size_t argc, char **argv)
196 {
197 	const struct device *dev;
198 	uint8_t addr, command;
199 	uint8_t value;
200 	int ret;
201 
202 	dev = shell_device_get_binding(argv[ARGV_DEV]);
203 	if (!dev) {
204 		shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]);
205 		return -ENODEV;
206 	}
207 
208 	addr = strtol(argv[ARGV_ADDR], NULL, 16);
209 	command = strtol(argv[ARGV_CMD], NULL, 16);
210 	value = strtol(argv[4], NULL, 16);
211 
212 	ret = smbus_byte_data_write(dev, addr, command, value);
213 	if (ret < 0) {
214 		shell_error(sh, "SMBus: Failed to write to periph: 0x%02x",
215 			    addr);
216 		return -EIO;
217 	}
218 
219 	return 0;
220 }
221 
222 /* smbus word_data_read <device> <dev_addr> <cmd> */
cmd_smbus_word_data_read(const struct shell * sh,size_t argc,char ** argv)223 static int cmd_smbus_word_data_read(const struct shell *sh,
224 				    size_t argc, char **argv)
225 {
226 	const struct device *dev;
227 	uint8_t addr, command;
228 	uint16_t out;
229 	int ret;
230 
231 	dev = shell_device_get_binding(argv[ARGV_DEV]);
232 	if (!dev) {
233 		shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]);
234 		return -ENODEV;
235 	}
236 
237 	addr = strtol(argv[ARGV_ADDR], NULL, 16);
238 	command = strtol(argv[ARGV_CMD], NULL, 16);
239 
240 	ret = smbus_word_data_read(dev, addr, command, &out);
241 	if (ret < 0) {
242 		shell_error(sh, "SMBus: Failed to read from periph: 0x%02x",
243 			    addr);
244 		return -EIO;
245 	}
246 
247 	shell_print(sh, "Output: 0x%04x", out);
248 
249 	return 0;
250 }
251 
252 /* smbus word_data_write <device> <dev_addr> <cmd> <value> */
cmd_smbus_word_data_write(const struct shell * sh,size_t argc,char ** argv)253 static int cmd_smbus_word_data_write(const struct shell *sh,
254 				     size_t argc, char **argv)
255 {
256 	const struct device *dev;
257 	uint8_t addr, command;
258 	uint16_t value;
259 	int ret;
260 
261 	dev = shell_device_get_binding(argv[ARGV_DEV]);
262 	if (!dev) {
263 		shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]);
264 		return -ENODEV;
265 	}
266 
267 	addr = strtol(argv[ARGV_ADDR], NULL, 16);
268 	command = strtol(argv[ARGV_CMD], NULL, 16);
269 	value = strtol(argv[4], NULL, 16);
270 
271 	ret = smbus_word_data_write(dev, addr, command, value);
272 	if (ret < 0) {
273 		shell_error(sh, "SMBus: Failed to write to periph: 0x%02x",
274 			    addr);
275 		return -EIO;
276 	}
277 
278 	return 0;
279 }
280 
281 /* smbus block_write <device> <dev_addr> <cmd> <bytes ... > */
cmd_smbus_block_write(const struct shell * sh,size_t argc,char ** argv)282 static int cmd_smbus_block_write(const struct shell *sh,
283 				 size_t argc, char **argv)
284 {
285 	const struct device *dev;
286 	uint8_t addr, command;
287 	uint8_t count = argc - 4;
288 	char **p = &argv[4]; /* start data bytes */
289 	uint8_t buf[32]; /* max block count */
290 	int ret;
291 
292 	if (count == 0 || count > sizeof(buf)) {
293 		return -EINVAL;
294 	}
295 
296 	dev = shell_device_get_binding(argv[ARGV_DEV]);
297 	if (!dev) {
298 		shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]);
299 		return -ENODEV;
300 	}
301 
302 	addr = strtol(argv[ARGV_ADDR], NULL, 16);
303 	command = strtol(argv[ARGV_CMD], NULL, 16);
304 
305 	for (int i = 0; i < count; i++) {
306 		buf[i] = (uint8_t)strtoul(p[i], NULL, 16);
307 	}
308 
309 	LOG_HEXDUMP_DBG(buf, count, "Constructed block buffer");
310 
311 	ret = smbus_block_write(dev, addr, command, count, buf);
312 	if (ret < 0) {
313 		shell_error(sh, "Failed block write to periph: 0x%02x",
314 			    addr);
315 		return ret;
316 	}
317 
318 	return 0;
319 }
320 
321 /* smbus block_read <device> <dev_addr> <cmd> */
cmd_smbus_block_read(const struct shell * sh,size_t argc,char ** argv)322 static int cmd_smbus_block_read(const struct shell *sh,
323 				size_t argc, char **argv)
324 {
325 	const struct device *dev;
326 	uint8_t addr, command;
327 	uint8_t buf[32]; /* max block count */
328 	uint8_t count;
329 	int ret;
330 
331 	dev = shell_device_get_binding(argv[ARGV_DEV]);
332 	if (!dev) {
333 		shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]);
334 		return -ENODEV;
335 	}
336 
337 	addr = strtol(argv[ARGV_ADDR], NULL, 16);
338 	command = strtol(argv[ARGV_CMD], NULL, 16);
339 
340 	ret = smbus_block_read(dev, addr, command, &count, buf);
341 	if (ret < 0) {
342 		shell_error(sh, "Failed block read from periph: 0x%02x",
343 			    addr);
344 		return ret;
345 	}
346 
347 	if (count == 0 || count > sizeof(buf)) {
348 		shell_error(sh, "Returned count %u", count);
349 		return -ENODATA;
350 	}
351 
352 	shell_hexdump(sh, buf, count);
353 
354 	return 0;
355 }
356 
357 /* Device name autocompletion support */
device_name_get(size_t idx,struct shell_static_entry * entry)358 static void device_name_get(size_t idx, struct shell_static_entry *entry)
359 {
360 	const struct device *dev = shell_device_lookup(idx, "smbus");
361 
362 	entry->syntax = (dev != NULL) ? dev->name : NULL;
363 	entry->handler = NULL;
364 	entry->help = NULL;
365 	entry->subcmd = NULL;
366 }
367 
368 SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get);
369 
370 SHELL_STATIC_SUBCMD_SET_CREATE(sub_smbus_cmds,
371 	SHELL_CMD_ARG(quick, &dsub_device_name,
372 		      "SMBus Quick command\n"
373 		      "Usage: quick <device> <addr>",
374 		      cmd_smbus_quick, 3, 0),
375 	SHELL_CMD_ARG(scan, &dsub_device_name,
376 		      "Scan SMBus peripheral devices command\n"
377 		      "Usage: scan <device>",
378 		      cmd_smbus_scan, 2, 0),
379 	SHELL_CMD_ARG(byte_read, &dsub_device_name,
380 		      "SMBus: byte read command\n"
381 		      "Usage: byte_read <device> <addr>",
382 		      cmd_smbus_byte_read, 3, 0),
383 	SHELL_CMD_ARG(byte_write, &dsub_device_name,
384 		      "SMBus: byte write command\n"
385 		      "Usage: byte_write <device> <addr> <value>",
386 		      cmd_smbus_byte_write, 4, 0),
387 	SHELL_CMD_ARG(byte_data_read, &dsub_device_name,
388 		      "SMBus: byte data read command\n"
389 		      "Usage: byte_data_read <device> <addr> <cmd>",
390 		      cmd_smbus_byte_data_read, 4, 0),
391 	SHELL_CMD_ARG(byte_data_write, &dsub_device_name,
392 		      "SMBus: byte data write command\n"
393 		      "Usage: byte_data_write <device> <addr> <cmd> <value>",
394 		      cmd_smbus_byte_data_write, 5, 0),
395 	SHELL_CMD_ARG(word_data_read, &dsub_device_name,
396 		      "SMBus: word data read command\n"
397 		      "Usage: word_data_read <device> <addr> <cmd>",
398 		      cmd_smbus_word_data_read, 4, 0),
399 	SHELL_CMD_ARG(word_data_write, &dsub_device_name,
400 		      "SMBus: word data write command\n"
401 		      "Usage: word_data_write <device> <addr> <cmd> <value>",
402 		      cmd_smbus_word_data_write, 5, 0),
403 	SHELL_CMD_ARG(block_write, &dsub_device_name,
404 		      "SMBus: Block Write command\n"
405 		      "Usage: block_write <device> <addr> <cmd> [<byte1>, ...]",
406 		      cmd_smbus_block_write, 4, 32),
407 	SHELL_CMD_ARG(block_read, &dsub_device_name,
408 		      "SMBus: Block Read command\n"
409 		      "Usage: block_read <device> <addr> <cmd>",
410 		      cmd_smbus_block_read, 4, 0),
411 	SHELL_SUBCMD_SET_END     /* Array terminated. */
412 );
413 
414 SHELL_CMD_REGISTER(smbus, &sub_smbus_cmds, "smbus commands", NULL);
415