/* * Copyright (c) 2018 Prevas A/S * Copyright (c) 2022 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include LOG_MODULE_REGISTER(smbus_shell, CONFIG_LOG_DEFAULT_LEVEL); /** * smbus_shell is a highly modified version from i2c_shell. Basically only scan * logic remains from i2c_shell */ /** * Simplify argument parsing, smbus arguments always go in this order: * smbus */ #define ARGV_DEV 1 #define ARGV_ADDR 2 #define ARGV_CMD 3 /** * This sends SMBUS messages without any data (i.e. stop condition after * sending just the address). If there is an ACK for the address, it * is assumed there is a device present. * * WARNING: As there is no standard SMBUS detection command, this code * uses arbitrary SMBus commands (namely SMBus quick write to probe for * devices. * This operation can confuse your SMBUS bus, cause data loss, and is * known to corrupt the Atmel AT24RF08 EEPROM found on many IBM * Thinkpad laptops. * * https://manpages.debian.org/buster/i2c-tools/i2cdetect.8.en.html */ /* smbus scan */ static int cmd_smbus_scan(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; uint8_t cnt = 0, first = 0x04, last = 0x77; dev = device_get_binding(argv[ARGV_DEV]); if (!dev) { shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]); return -ENODEV; } shell_print(sh, " 0 1 2 3 4 5 6 7 8 9 a b c d e f"); for (uint8_t i = 0; i <= last; i += 16) { shell_fprintf(sh, SHELL_NORMAL, "%02x: ", i); for (uint8_t j = 0; j < 16; j++) { if (i + j < first || i + j > last) { shell_fprintf(sh, SHELL_NORMAL, " "); continue; } if (smbus_quick(dev, i + j, SMBUS_MSG_WRITE) == 0) { shell_fprintf(sh, SHELL_NORMAL, "%02x ", i + j); ++cnt; } else { shell_fprintf(sh, SHELL_NORMAL, "-- "); } } shell_print(sh, ""); } shell_print(sh, "%u devices found on %s", cnt, argv[ARGV_DEV]); return 0; } /* smbus quick */ static int cmd_smbus_quick(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; uint8_t addr; int ret; dev = device_get_binding(argv[ARGV_DEV]); if (!dev) { shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]); return -ENODEV; } addr = strtol(argv[ARGV_ADDR], NULL, 16); ret = smbus_quick(dev, addr, SMBUS_MSG_WRITE); if (ret < 0) { shell_error(sh, "SMBus: Failed quick cmd, perip: 0x%02x", addr); } return ret; } /* smbus byte_read */ static int cmd_smbus_byte_read(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; uint8_t addr; uint8_t out; int ret; dev = device_get_binding(argv[ARGV_DEV]); if (!dev) { shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]); return -ENODEV; } addr = strtol(argv[ARGV_ADDR], NULL, 16); ret = smbus_byte_read(dev, addr, &out); if (ret < 0) { shell_error(sh, "SMBus: Failed to read from periph: 0x%02x", addr); return -EIO; } shell_print(sh, "Output: 0x%x", out); return 0; } /* smbus byte_write */ static int cmd_smbus_byte_write(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; uint8_t addr; uint8_t value; int ret; dev = device_get_binding(argv[ARGV_DEV]); if (!dev) { shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]); return -ENODEV; } addr = strtol(argv[ARGV_ADDR], NULL, 16); /* First byte is command */ value = strtol(argv[ARGV_CMD], NULL, 16); ret = smbus_byte_write(dev, addr, value); if (ret < 0) { shell_error(sh, "SMBus: Failed to write to periph: 0x%02x", addr); return -EIO; } return 0; } /* smbus byte_data_read */ static int cmd_smbus_byte_data_read(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; uint8_t addr, command; uint8_t out; int ret; dev = device_get_binding(argv[ARGV_DEV]); if (!dev) { shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]); return -ENODEV; } addr = strtol(argv[ARGV_ADDR], NULL, 16); command = strtol(argv[ARGV_CMD], NULL, 16); ret = smbus_byte_data_read(dev, addr, command, &out); if (ret < 0) { shell_error(sh, "SMBus: Failed to read from periph: 0x%02x", addr); return -EIO; } shell_print(sh, "Output: 0x%x", out); return 0; } /* smbus byte_data_write */ static int cmd_smbus_byte_data_write(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; uint8_t addr, command; uint8_t value; int ret; dev = device_get_binding(argv[ARGV_DEV]); if (!dev) { shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]); return -ENODEV; } addr = strtol(argv[ARGV_ADDR], NULL, 16); command = strtol(argv[ARGV_CMD], NULL, 16); value = strtol(argv[4], NULL, 16); ret = smbus_byte_data_write(dev, addr, command, value); if (ret < 0) { shell_error(sh, "SMBus: Failed to write to periph: 0x%02x", addr); return -EIO; } return 0; } /* smbus word_data_read */ static int cmd_smbus_word_data_read(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; uint8_t addr, command; uint16_t out; int ret; dev = device_get_binding(argv[ARGV_DEV]); if (!dev) { shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]); return -ENODEV; } addr = strtol(argv[ARGV_ADDR], NULL, 16); command = strtol(argv[ARGV_CMD], NULL, 16); ret = smbus_word_data_read(dev, addr, command, &out); if (ret < 0) { shell_error(sh, "SMBus: Failed to read from periph: 0x%02x", addr); return -EIO; } shell_print(sh, "Output: 0x%04x", out); return 0; } /* smbus word_data_write */ static int cmd_smbus_word_data_write(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; uint8_t addr, command; uint16_t value; int ret; dev = device_get_binding(argv[ARGV_DEV]); if (!dev) { shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]); return -ENODEV; } addr = strtol(argv[ARGV_ADDR], NULL, 16); command = strtol(argv[ARGV_CMD], NULL, 16); value = strtol(argv[4], NULL, 16); ret = smbus_word_data_write(dev, addr, command, value); if (ret < 0) { shell_error(sh, "SMBus: Failed to write to periph: 0x%02x", addr); return -EIO; } return 0; } /* smbus block_write */ static int cmd_smbus_block_write(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; uint8_t addr, command; uint8_t count = argc - 4; char **p = &argv[4]; /* start data bytes */ uint8_t buf[32]; /* max block count */ int ret; if (count == 0 || count > sizeof(buf)) { return -EINVAL; } dev = device_get_binding(argv[ARGV_DEV]); if (!dev) { shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]); return -ENODEV; } addr = strtol(argv[ARGV_ADDR], NULL, 16); command = strtol(argv[ARGV_CMD], NULL, 16); for (int i = 0; i < count; i++) { buf[i] = (uint8_t)strtoul(p[i], NULL, 16); } LOG_HEXDUMP_DBG(buf, count, "Constructed block buffer"); ret = smbus_block_write(dev, addr, command, count, buf); if (ret < 0) { shell_error(sh, "Failed block write to periph: 0x%02x", addr); return ret; } return 0; } /* smbus block_read */ static int cmd_smbus_block_read(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; uint8_t addr, command; uint8_t buf[32]; /* max block count */ uint8_t count; int ret; dev = device_get_binding(argv[ARGV_DEV]); if (!dev) { shell_error(sh, "SMBus: Device %s not found", argv[ARGV_DEV]); return -ENODEV; } addr = strtol(argv[ARGV_ADDR], NULL, 16); command = strtol(argv[ARGV_CMD], NULL, 16); ret = smbus_block_read(dev, addr, command, &count, buf); if (ret < 0) { shell_error(sh, "Failed block read from periph: 0x%02x", addr); return ret; } if (count == 0 || count > sizeof(buf)) { shell_error(sh, "Returned count %u", count); return -ENODATA; } shell_hexdump(sh, buf, count); return 0; } /* Device name autocompletion support */ static void device_name_get(size_t idx, struct shell_static_entry *entry) { const struct device *dev = shell_device_lookup(idx, "smbus"); entry->syntax = (dev != NULL) ? dev->name : NULL; entry->handler = NULL; entry->help = NULL; entry->subcmd = NULL; } SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get); SHELL_STATIC_SUBCMD_SET_CREATE(sub_smbus_cmds, SHELL_CMD_ARG(quick, &dsub_device_name, "SMBus Quick command\n" "Usage: quick ", cmd_smbus_quick, 3, 0), SHELL_CMD_ARG(scan, &dsub_device_name, "Scan SMBus peripheral devices command\n" "Usage: scan ", cmd_smbus_scan, 2, 0), SHELL_CMD_ARG(byte_read, &dsub_device_name, "SMBus: byte read command\n" "Usage: byte_read ", cmd_smbus_byte_read, 3, 0), SHELL_CMD_ARG(byte_write, &dsub_device_name, "SMBus: byte write command\n" "Usage: byte_write ", cmd_smbus_byte_write, 4, 0), SHELL_CMD_ARG(byte_data_read, &dsub_device_name, "SMBus: byte data read command\n" "Usage: byte_data_read ", cmd_smbus_byte_data_read, 4, 0), SHELL_CMD_ARG(byte_data_write, &dsub_device_name, "SMBus: byte data write command\n" "Usage: byte_data_write ", cmd_smbus_byte_data_write, 5, 0), SHELL_CMD_ARG(word_data_read, &dsub_device_name, "SMBus: word data read command\n" "Usage: word_data_read ", cmd_smbus_word_data_read, 4, 0), SHELL_CMD_ARG(word_data_write, &dsub_device_name, "SMBus: word data write command\n" "Usage: word_data_write ", cmd_smbus_word_data_write, 5, 0), SHELL_CMD_ARG(block_write, &dsub_device_name, "SMBus: Block Write command\n" "Usage: block_write [, ...]", cmd_smbus_block_write, 4, 32), SHELL_CMD_ARG(block_read, &dsub_device_name, "SMBus: Block Read command\n" "Usage: block_read ", cmd_smbus_block_read, 4, 0), SHELL_SUBCMD_SET_END /* Array terminated. */ ); SHELL_CMD_REGISTER(smbus, &sub_smbus_cmds, "smbus commands", NULL);