Introduce I2c commands from i2c-tools. This allows for Diagnostics capability. Supports ONLY i2c busses at the moment.
This code is based on http://www.lm-sensors.org/wiki/I2CTools This closely follows the functionality, options and man page documentation Signed-off-by: Nishanth Menon <[EMAIL PROTECTED]> --- commands/i2c.c | 1268 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1268 insertions(+) Index: u-boot-v2.git/commands/i2c.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ u-boot-v2.git/commands/i2c.c 2008-06-19 09:31:37.000000000 -0500 @@ -0,0 +1,1268 @@ +/** + * @file + * @brief I2C related applications + * + * FileName: commands/i2c.c + * This provides i2cdump, i2cdetect, i2cset and i2cget appplications + * The code originates from the i2c-tools located here: + * http://www.lm-sensors.org/wiki/I2CTools + * + * This file merges the userspace headers and applications to a single + * entity as we do not maintain a userspace/kernel space isolation + * + * NOTE: 10-bit addresses are NOT supported! + */ +/* + * (C) Copyright 2006-2008 + * Texas Instruments, <www.ti.com> + * Nishanth Menon <[EMAIL PROTECTED]> + * + * This application is based on the following: + * + * i2cbusses: Print the installed i2c busses for both 2.4 and 2.6 kernels. + * Part of user-space programs to access for I2C + * devices. + * Copyright (c) 1999-2003 Frodo Looijaard <[EMAIL PROTECTED]> and + * Mark D. Studebaker <[EMAIL PROTECTED]> + * Copyright (C) 2008 Jean Delvare <[EMAIL PROTECTED]> + * i2cdump.c - a user-space program to dump I2C registers + * Copyright (C) 2002-2003 Frodo Looijaard <[EMAIL PROTECTED]>, and + * Mark D. Studebaker <[EMAIL PROTECTED]> + * Copyright (C) 2004-2008 Jean Delvare <[EMAIL PROTECTED]> + * i2cget.c - A user-space program to read an I2C register. + * Copyright (C) 2005-2008 Jean Delvare <[EMAIL PROTECTED]> + * i2cset.c - A user-space program to write an I2C register. + * Copyright (C) 2001-2003 Frodo Looijaard <[EMAIL PROTECTED]>, and + * Mark D. Studebaker <[EMAIL PROTECTED]> + * Copyright (C) 2004-2008 Jean Delvare <[EMAIL PROTECTED]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA. + */ + +#include <common.h> +#include <command.h> +#include <types.h> +#include <string.h> +#include <stdio.h> +#include <fcntl.h> +#include <malloc.h> +#include <fs.h> +#include <errno.h> +#include <linux/stat.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include "i2c.h" + +#define MISSING_FUNC_FMT "Error: Adapter does not have %s capability\n" + +/*************** Lib Functions *************/ + +/** + * @brief Parse an I2CBUS command line argument + * + * @param i2cbus_arg arg to busparam + * + * @return the corresponding bus number, + * or a negative value if the bus is invalid. + */ +int lookup_i2c_bus(const char *i2cbus_arg) +{ + long i2cbus; + char *end; + + if (i2cbus_arg == NULL) { + fprintf(stderr, "no bus arg! expect 0-9\n"); + return -1; + } + if (strlen(i2cbus_arg) > 2) { + fprintf(stderr, "bad bus arg! expect 0-9 saw %s\n", i2cbus_arg); + return -2; + } + i2cbus = simple_strtol(i2cbus_arg, &end, 0); + if ((*i2cbus_arg != '0') && (i2cbus == 0)) { + fprintf(stderr, "bad bus arg! expect 0-9, saw %s\n", + i2cbus_arg); + return -2; + } + if (i2cbus < 0 || i2cbus > 0xff) { + fprintf(stderr, "Error: I2C bus out of range (0-255)!\n"); + return -3; + } + + return i2cbus; +} + +/** + * @brief open a given i2cdevice + * + * @param i2cbus bus index + * @param filename return the devfs filename for the bus + * @param quiet be silent? + * + * @return return value of open + */ +int open_i2c_dev(const int i2cbus, char *filename, const int quiet) +{ + int file; + + sprintf(filename, "/dev/" I2C_DEV_NAME "%d", i2cbus); + file = open(filename, O_RDWR); + + if (file < 0 && !quiet) { + perror(filename); + fprintf(stderr, "Error: Could not open file " + "`%s'\n", filename); + } + + return file; +} + +/** + * @brief Parse a CHIP-ADDRESS command line argument + * + * @param address_arg -user provided argument + * + * @return The corresponding chip address, + * or a negative value if the address is invalid. + */ +int parse_i2c_address(const char *address_arg) +{ + long address; + char *end; + + address = simple_strtol(address_arg, &end, 0); + if (*end || !*address_arg) { + fprintf(stderr, "Error: Chip address is not a number!\n"); + return -1; + } + if (address < 0x03 || address > 0x77) { + fprintf(stderr, "Error: Chip address out of range " + "(0x03-0x77)!\n"); + return -2; + } + + return address; +} + +/** + * @brief force the slave address + * + * @param file file id + * @param address address to set + * @param force force address + * + * @return returns result of ioctl + */ +int set_slave_addr(int file, int address, int force) +{ + /* With force, let the user read from/write to the registers + even when a driver is also running */ + if (ioctl(file, force ? I2C_SLAVE_FORCE : I2C_SLAVE, (void *)address) < + 0) { + perror(NULL); + fprintf(stderr, + "Error: Could not set address to 0x%02x\n", address); + return -errno; + } + + return 0; +} + +/** + * @brief check functionality of the adapter + * + * @param file file index + * @param size size of the same + * @param daddress device address + * @param pec pec + * + * @return result of ioctl check negative if failed + */ +int check_funcs(int file, int size, int daddress, int pec) +{ + unsigned long funcs; + + /* check adapter functionality */ + if (ioctl(file, I2C_FUNCS, &funcs) < 0) { + perror(NULL); + fprintf(stderr, "Error: Could not get the adapter " + "functionality matrix\n"); + return -1; + } + + if (!(funcs & I2C_FUNC_I2C)) { + fprintf(stderr, MISSING_FUNC_FMT, "I2C support"); + return -EINVAL; + } + + return 0; +} + +int read_i2c(int file, uchar daddr, u16 addr, u8 reg_width, char *buffer, + size_t count) +{ + struct i2c_msg msg[2] = { + {daddr, 0, reg_width, (u8 *) &addr}, + {daddr, 0 | I2C_M_RD, count, buffer} + }; + struct i2c_rdwr_ioctl_data data = { + .msgs = msg, + .nmsgs = 2, + }; + return ioctl(file, I2C_RDWR, &data); +} + +int write_i2c(int file, uchar daddr, u16 addr, u8 reg_width, char *buffer, + size_t count) +{ + unsigned char *temp_buf; + struct i2c_msg msg = { daddr, 0, count + reg_width, NULL }; + struct i2c_rdwr_ioctl_data data = { + .msgs = &msg, + .nmsgs = 1, + }; + int ret; + temp_buf = malloc(count + reg_width); + if (temp_buf == NULL) + return -ENOMEM; + memcpy(temp_buf, &addr, reg_width); + memcpy(temp_buf + reg_width, buffer, count); + msg.buf = temp_buf; + ret = ioctl(file, I2C_RDWR, &data); + free(temp_buf); + return ret; +} + +/****************** Test implementations **************/ +#ifdef CONFIG_CMD_I2C_DETECT + +#define MODE_AUTO 0 +#define MODE_QUICK 1 +#define MODE_READ 2 +#define MODE_FUNC 3 + +static const __maybe_unused char cmd_i2cdetect_help_s[] = + "Usage:\n" + "a) i2cdetect [-a] [-q|-r] I2CBUS [FIRST LAST]\n" + "Where:\n" + " -a : all addresses (default scans from 0x03 - 0x77)\n" + " -q : use write-risky at times\n" + " -r : use read-risky at times\n" + " If provided, FIRST and LAST limit the probing range.\n" + "b) i2cdetect -F I2CBUS\n" + "Where:\n" + " -F : Show functionality of the device\n" + "\n" + "I2CBUS is an integer representing the bus index\n" + "these are present as 'xx' of /dev/" I2C_DEV_NAME + "xx\n" "WARN:\n" + "This program can confuse your I2C bus, cause data loss and worse!\n"; + +#ifdef CONFIG_LONGHELP +static void cmd_i2cdetect_help(void) +{ + fprintf(stderr, "%s", cmd_i2cdetect_help_s); +} +#else +#define cmd_i2cdetect_help() +#endif + +/** + * @brief scan the i2c bus + * + * @param file file index + * @param mode what mode to scan it with + * @param first start address + * @param last end address + * + * @return negative if scan failed, else return 0 + */ +static int scan_i2c_bus(int file, int mode, int first, int last) +{ + int i, j; + int res; + unsigned char dummy = 0x0; + + printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\n"); + + for (i = 0; i < 128; i += 16) { + printf("%02x: ", i); + for (j = 0; j < 16; j++) { + + /* Skip unwanted addresses */ + if (i + j < first || i + j > last) { + printf(" "); + continue; + } + + /* Set slave address */ + if (ioctl(file, I2C_SLAVE, (void *)(i + j)) < 0) { + if (errno == EBUSY) { + printf("UU "); + continue; + } else { + perror(NULL); + fprintf(stderr, "Error: Could not set " + "address to 0x%02x\n", i + j); + return -1; + } + } + + /* Probe this address */ + switch (mode) { + case MODE_QUICK: + /* Attempt a 0 byte write -can lock up many + * devices */ + res = write_i2c(file, i + j, 0x0, 1, NULL, 0); + break; + case MODE_READ: + /* This is known to lock on various + write-only chips (mainly clock chips) */ + res = read_i2c(file, i + j, 0x0, 1, &dummy, 1); + break; + default: + if ((i + j >= 0x30 && i + j <= 0x37) + || (i + j >= 0x50 && i + j <= 0x5F)) + res = + read_i2c(file, i + j, 0x0, 1, + &dummy, 1); + else + res = + write_i2c(file, i + j, 0x0, 1, NULL, + 0); + } + + if (res < 0) + printf("-- "); + else + printf("%02x ", i + j); + } + printf("\n"); + } + + return 0; +} + +struct func { + long value; + const char *name; +}; + +static const struct func all_func[] = { + {.value = I2C_FUNC_I2C, + .name = "I2C"}, + {.value = 0, + .name = ""} +}; + +/** + * @brief print the functionality of the adapter + * + * @param funcs the ioctl returned value + * + * @return none + */ +static void print_functionality(unsigned long funcs) +{ + int i; + for (i = 0; all_func[i].value; i++) { + printf("%-32s %s\n", all_func[i].name, + (funcs & all_func[i].value) ? "yes" : "no"); + } +} + +static int do_cmd_i2c_detect(cmd_tbl_t *cmdtp, int argc, char *argv[]) +{ + char *end; + int i2cbus, file = 0, res = 0; + char filename[PATH_MAX]; + unsigned long funcs; + int mode = MODE_AUTO; + int first = 0x03, last = 0x77; + int flags = 0; + + /* handle (optional) flags first */ + while (1 + flags < argc && argv[1 + flags][0] == '-') { + switch (argv[1 + flags][1]) { + case 'F': + if (mode != MODE_AUTO && mode != MODE_FUNC) { + fprintf(stderr, "Error: Different modes " + "specified!\n"); + goto quit_i2cdetect; + } + mode = MODE_FUNC; + break; + case 'r': + if (mode == MODE_QUICK) { + fprintf(stderr, "Error: Different modes " + "specified!\n"); + goto quit_i2cdetect; + } + mode = MODE_READ; + break; + case 'q': + if (mode == MODE_READ) { + fprintf(stderr, "Error: Different modes " + "specified!\n"); + goto quit_i2cdetect; + } + mode = MODE_QUICK; + break; + case 'a': + first = 0x00; + last = 0x7F; + break; + default: + fprintf(stderr, "Error: Unsupported option " + "\"%s\"!\n", argv[1 + flags]); + goto quit_i2cdetect; + } + flags++; + } + + if (argc < flags + 2) { + fprintf(stderr, "Error: No i2c-bus specified!\n"); + goto quit_i2cdetect; + } + i2cbus = lookup_i2c_bus(argv[flags + 1]); + if (i2cbus < 0) { + fprintf(stderr, "Error: bus not found!\n"); + goto quit_i2cdetect; + } + + /* read address range if present */ + if (argc == flags + 4 && mode != MODE_FUNC) { + int tmp; + + tmp = simple_strtol(argv[flags + 2], &end, 0); + if (*end) { + fprintf(stderr, "Error: FIRST argment not a " + "number!\n"); + goto quit_i2cdetect; + } + if (tmp < first || tmp > last) { + fprintf(stderr, "Error: FIRST argument out of range " + "(0x%02x-0x%02x)!\n", first, last); + goto quit_i2cdetect; + } + first = tmp; + + tmp = simple_strtol(argv[flags + 3], &end, 0); + if (*end) { + fprintf(stderr, "Error: LAST argment not a " + "number!\n"); + goto quit_i2cdetect; + } + if (tmp < first || tmp > last) { + fprintf(stderr, "Error: LAST argument out of range " + "(0x%02x-0x%02x)!\n", first, last); + goto quit_i2cdetect; + } + last = tmp; + } else if (argc != flags + 2) { + goto quit_i2cdetect; + } + + file = open_i2c_dev(i2cbus, filename, 0); + if (file < 0) + return 1; + + res = 1; + if (ioctl(file, I2C_FUNCS, &funcs) < 0) { + perror(filename); + fprintf(stderr, "Error: Could not get the adapter " + "functionality matrix\n"); + goto ret; + } + + /* Special case, we only list the implemented functionalities */ + if (mode == MODE_FUNC) { + printf("Functionalities implemented by %s:\n", filename); + print_functionality(funcs); + res = 0; + goto ret; + } + + res = scan_i2c_bus(file, mode, first, last); +ret: + if (file >= 0) + close(file); + return res ? 1 : 0; + +quit_i2cdetect: + /* Exit after printing help */ + cmd_i2cdetect_help(); + goto ret; +} + +U_BOOT_CMD_START(i2cdetect) + .maxargs = 6, + .cmd = do_cmd_i2c_detect, + .usage = "program to scan for I2C devices", + U_BOOT_CMD_HELP(cmd_i2cdetect_help_s) +U_BOOT_CMD_END +#endif /* CONFIG_CMD_I2C_DETECT */ + +#ifdef CONFIG_CMD_I2C_DUMP +static const __maybe_unused char cmd_i2cdump_help_s[] = + "Usage: i2cdump [-f] [-r first-last] I2CBUS ADDRESS [MODE]\n" + "Where:\n" + " -f : Force set the slave address\n" + " -r : first-last the range of addresses which will be dumped\n" + "I2CBUS is an integer representing the bus index\n" + " these are present as 'xx' of /dev/" I2C_DEV_NAME "xx\n" + "ADDRESS is an integer representing device address (0x03 - 0x77)\n" + "MODE is one of:\n" + " b (byte, default)\n" + " w (word)\n" + " W (word on even register addresses)\n" + " Append p for PEC\n" + "WARN: i2cdump can be dangerous if used improperly.\n"; + +#ifdef CONFIG_LONGHELP +static const __maybe_unused char cmd_i2c_ext_help_s[] = + "Most notably, the c mode " + "starts with WRITING a byte to the chip. On most chips it will be stored\n" + "in the address pointer register, which is OK, but some chips with a\n" + "single register or no (visible)register at all will most likely see this\n" + "as a real WRITE, resulting in possible misbehavior or corruption. Do not\n" + "use i2cdump on random addresses. Anyway, it is of little use unless you\n" + "have good knowledge of the chip youâ€(tm)re working with and an idea of \n" + "what you are looking for.\n"; +static void cmd_i2dump_help(void) +{ + fprintf(stderr, "%s", cmd_i2cdump_help_s); + fprintf(stderr, "%s", cmd_i2c_ext_help_s); +} +#else +#define cmd_i2dump_help() +#endif + +static int do_cmd_i2c_dump(cmd_tbl_t *cmdtp, int argc, char *argv[]) +{ + char *end; + int i, j, res, i2cbus, address, size, file = -1; + char filename[PATH_MAX]; + int block[256]; + int pec = 0, even = 0; + int flags = 0; + int force = 0; + const char *range = NULL; + int first = 0x00, last = 0xff; + + /* handle (optional) flags first */ + while (1 + flags < argc && argv[1 + flags][0] == '-') { + switch (argv[1 + flags][1]) { + case 'f': + force = 1; + break; + case 'r': + range = argv[1 + (++flags)]; + break; + default: + fprintf(stderr, "Error: Unsupported option " + "\"%s\"!\n", argv[1 + flags]); + goto quit_i2cdump; + } + flags++; + } + + if (argc < flags + 2) { + fprintf(stderr, "Error: No i2c-bus specified!\n"); + goto quit_i2cdump; + } + i2cbus = lookup_i2c_bus(argv[flags + 1]); + if (i2cbus < 0) { + fprintf(stderr, "Error: Bus not found!\n"); + goto quit_i2cdump; + } + + if (argc < flags + 3) { + fprintf(stderr, "Error: No address specified!\n"); + goto quit_i2cdump; + } + address = parse_i2c_address(argv[flags + 2]); + if (address < 0) { + fprintf(stderr, "Error: Badd address\n"); + goto quit_i2cdump; + } + + if (argc < flags + 4) { + fprintf(stderr, "No size specified (using byte-data access)\n"); + size = I2C_SMBUS_BYTE_DATA; + } else if (!strncmp(argv[flags + 3], "b", 1)) { + size = I2C_SMBUS_BYTE_DATA; + pec = argv[flags + 3][1] == 'p'; + } else if (!strncmp(argv[flags + 3], "w", 1)) { + size = I2C_SMBUS_WORD_DATA; + pec = argv[flags + 3][1] == 'p'; + } else if (!strncmp(argv[flags + 3], "W", 1)) { + size = I2C_SMBUS_WORD_DATA; + even = 1; + } else { + fprintf(stderr, "Error: Invalid mode!\n"); + goto quit_i2cdump; + } + + /* Parse optional range string */ + if (range) { + char *dash; + + first = simple_strtol(range, &dash, 0); + if ((dash == range) || (*dash != '-') || (first < 0) || + (first > 0xff)) { + fprintf(stderr, "Error: Invalid range parameter!\n"); + goto quit_i2cdump; + } + last = simple_strtol(++dash, &end, 0); + if ((end == dash) || (*end != '\0') || (last < first) || + (last > 0xff)) { + fprintf(stderr, "Error: Invalid range parameter!\n"); + goto quit_i2cdump; + } + + /* Check mode constraints */ + switch (size) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + break; + case I2C_SMBUS_WORD_DATA: + if (!even || (!(first % 2) && last % 2)) + break; + /* Fall through */ + default: + fprintf(stderr, + "Error: Range parameter not compatible" + "with selected mode!\n"); + goto quit_i2cdump; + } + } + + file = open_i2c_dev(i2cbus, filename, 0); + if (file < 0 || check_funcs(file, size, address, pec) + || set_slave_addr(file, address, force)) + goto quit_i2cdump; + + if (pec) { + if (ioctl(file, I2C_PEC, (void *)1) < 0) { + perror(filename); + fprintf(stderr, "Error: Could not set PEC\n"); + goto quit_i2cdump; + } + } + + /* handle all but word data */ + if (size != I2C_SMBUS_WORD_DATA || even) { + printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f" + " 0123456789abcdef\n"); + for (i = 0; i < 256; i += 16) { + if (i / 16 < first / 16) + continue; + if (i / 16 > last / 16) + break; + + printf("%02x: ", i); + for (j = 0; j < 16; j++) { + /* Skip unwanted registers */ + if (i + j < first || i + j > last) { + printf(" "); + if (size == I2C_SMBUS_WORD_DATA) { + printf(" "); + j++; + } + continue; + } + + if (size == I2C_SMBUS_BYTE_DATA) { + u8 buf; + res = + read_i2c(file, address, i + j, 1, + &buf, 1); + block[i + j] = buf; + + } else if (size == I2C_SMBUS_WORD_DATA) { + u8 buf[2]; + res = + read_i2c(file, address, i + j, + (even) ? 2 : 1, buf, 2); + if (res < 0) { + block[i + j] = res; + block[i + j + 1] = res; + } else { + block[i + j] = buf[0]; + block[i + j + 1] = buf[1]; + } + } else + res = block[i + j]; + + if (res < 0) { + printf("XX "); + if (size == I2C_SMBUS_WORD_DATA) + printf("XX "); + } else { + printf("%02x ", block[i + j]); + if (size == I2C_SMBUS_WORD_DATA) + printf("%02x ", + block[i + j + 1]); + } + if (size == I2C_SMBUS_WORD_DATA) + j++; + } + printf(" "); + + for (j = 0; j < 16; j++) { + /* Skip unwanted registers */ + if (i + j < first || i + j > last) { + printf(" "); + continue; + } + + res = block[i + j]; + if (res < 0) + printf("X"); + else if ((res & 0xff) == 0x00 + || (res & 0xff) == 0xff) + printf("."); + else if ((res & 0xff) < 32 + || (res & 0xff) >= 127) + printf("?"); + else + printf("%c", res & 0xff); + } + printf("\n"); + } + } else { + printf(" 0,8 1,9 2,a 3,b 4,c 5,d 6,e 7,f\n"); + for (i = 0; i < 256; i += 8) { + if (i / 8 < first / 8) + continue; + if (i / 8 > last / 8) + break; + + printf("%02x: ", i); + for (j = 0; j < 8; j++) { + u16 buf; + /* Skip unwanted registers */ + if (i + j < first || i + j > last) { + printf(" "); + continue; + } + + res = + read_i2c(file, address, i + j, 1, + (u8 *) &buf, 2); + if (res < 0) + printf("XXXX "); + else + printf("%04x ", buf & 0xffff); + } + printf("\n"); + } + } + return 0; + +quit_i2cdump: + /* Quit after providing help info */ + cmd_i2dump_help(); + if (file >= 0) + close(file); + return 1; +} + +U_BOOT_CMD_START(i2cdump) + .maxargs = 8, + .cmd = do_cmd_i2c_dump, + .usage = "program to dump I2C registers", + U_BOOT_CMD_HELP(cmd_i2cdump_help_s) +U_BOOT_CMD_END +#endif /* CONFIG_CMD_I2C_DUMP */ + +#ifdef CONFIG_CMD_I2C_GET +static const __maybe_unused char cmd_i2cget_help_s[] = + "Usage: i2cget [-y] [-f] I2CBUS CHIP-ADDRESS [DATA-ADDRESS [MODE]]\n" + "Where:\n" + " -y : will allow to get from 0x50 to 0x57 with pec enabled\n" + " -f : Force set the slave address\n" + "I2CBUS is an integer representing the bus index\n" + " these are present as 'xx' of /dev/" I2C_DEV_NAME "xx\n" + "CHIP-ADDRESS is an integer representing device address (0x03 - 0x77)\n" + "DATA-ADDRESS is an integer representing register offset.\n" + " (0x00 - 0xFF). If omitted, the currently active register\n" + " will be read (if that makes sense for the considered chip).\n" + "MODE is one of:\n" + " b (read byte data, default)\n" + " w (read word data)\n" + " c (write byte/read byte)\n" + " Append p for PEC\n" + "WARN: i2cget can be extremely dangerous if used improperly.\n"; + +#ifdef CONFIG_LONGHELP +static const __maybe_unused char cmd_i2cget_help_add_s[] = + "I2C" + "are designed in such a way that an read transaction can be seen\n" + "as a write transaction by certain chips. This is particularly true if\n" + "setting mode to cp (write byte/read byte with PEC). Be extremely careful\n" + "using this program.\n"; +static void cmd_i2get_help(void) +{ + fprintf(stderr, "%s", cmd_i2cget_help_s); + fprintf(stderr, "%s", cmd_i2cget_help_add_s); +} +#else +#define cmd_i2get_help() +#endif + +/** + * @brief confirm if user can really proceed without badly hurting.. + * + * @param filename + * @param address + * @param size + * @param daddress + * @param pec + * + * @return 0 if dont proceed, 1 if ok to go ahead + */ +static int confirm_get(const char *filename, int address, int size, + int daddress, int pec) +{ + /* Don't let the user break his/her EEPROMs */ + if (address >= 0x50 && address <= 0x57 && pec) { + fprintf(stderr, "STOP! EEPROMs are I2C devices, not " + "SMBus devices. Using PEC\non I2C devices may " + "result in unexpected results, such as\n" + "trashing the contents of EEPROMs. We can't " + "let you do that, sorry.\n"); + return 0; + } + + return 1; +} + +/** + * @brief read from I2C chip registers + * + * @param cmdtp + * @param argc + * @param argv + * + * @return + */ +static int do_cmd_i2c_get(cmd_tbl_t *cmdtp, int argc, char *argv[]) +{ + char *end; + int res, i2cbus, address, size, file = -1; + int daddress; + char filename[PATH_MAX]; + int pec = 0; + int flags = 0; + int force = 0, yes = 0; + + /* handle (optional) flags first */ + while (1 + flags < argc && argv[1 + flags][0] == '-') { + switch (argv[1 + flags][1]) { + case 'f': + force = 1; + break; + case 'y': + yes = 1; + break; + default: + fprintf(stderr, "Error: Unsupported option " + "\"%s\"!\n", argv[1 + flags]); + goto quit_i2cget; + } + flags++; + } + + if (argc < flags + 3) { + fprintf(stderr, "Error: Not enough args\n"); + goto quit_i2cget; + } + + i2cbus = lookup_i2c_bus(argv[flags + 1]); + if (i2cbus < 0) { + fprintf(stderr, "Error: Bad bus!\n"); + goto quit_i2cget; + } + + address = parse_i2c_address(argv[flags + 2]); + if (address < 0) { + fprintf(stderr, "Error: Bad address!\n"); + goto quit_i2cget; + } + + if (argc > flags + 3) { + size = I2C_SMBUS_BYTE_DATA; + daddress = simple_strtol(argv[flags + 3], &end, 0); + if (*end || daddress < 0 || daddress > 0xff) { + fprintf(stderr, "Error: Data address invalid!\n"); + goto quit_i2cget; + } + } else { + size = I2C_SMBUS_BYTE; + daddress = -1; + } + + if (argc > flags + 4) { + switch (argv[flags + 4][0]) { + case 'b': + size = I2C_SMBUS_BYTE_DATA; + break; + case 'w': + size = I2C_SMBUS_WORD_DATA; + break; + case 'c': + size = I2C_SMBUS_BYTE; + break; + default: + fprintf(stderr, "Error: Invalid mode!\n"); + goto quit_i2cget; + } + pec = argv[flags + 4][1] == 'p'; + } + + file = open_i2c_dev(i2cbus, filename, 0); + if (file < 0 || check_funcs(file, size, daddress, pec) + || set_slave_addr(file, address, force)) + goto quit_i2cget; + + if (!yes && !confirm_get(filename, address, size, daddress, pec)) { + res = 0; + goto ret1; + } + + if (pec && ioctl(file, I2C_PEC, (void *)1) < 0) { + perror(filename); + fprintf(stderr, "Error: Could not set PEC\n"); + goto quit_i2cget; + } + + switch (size) { + case I2C_SMBUS_BYTE: + res = read_i2c(file, address, daddress, 1, NULL, 0); + break; + default: + { + u16 new_val; + res = + read_i2c(file, address, daddress, 1, + (u8 *) &new_val, + (size == I2C_SMBUS_WORD_DATA) ? 2 : 1); + if (res >= 0) + res = new_val; + } + } +ret1: + close(file); + + if (res < 0) { + fprintf(stderr, "Error: Read failed\n"); + return 2; + } + + printf("0x%0*x\n", size == I2C_SMBUS_WORD_DATA ? 4 : 2, res); + + return 0; + +quit_i2cget: + cmd_i2get_help(); + if (file >= 0) + close(file); + return 1; +} + +U_BOOT_CMD_START(i2cget) + .maxargs = 7, + .cmd = do_cmd_i2c_get, + .usage = "program to read an I2C register", + U_BOOT_CMD_HELP(cmd_i2cget_help_s) +U_BOOT_CMD_END +#endif /* CONFIG_CMD_I2C_GET */ + +#ifdef CONFIG_CMD_I2C_SET +static const __maybe_unused char cmd_i2cset_help_s[] = + "Usage: i2cset [-y] [-f] I2CBUS CHIP-ADDRESS DATA-ADDRESS [VALUE" + " [MODE [MASK]]]\n" + "Where:\n" + " -y : will allow to set 0x50 to 0x57 with pec enabled\n" + " -f : Force set the slave address\n" + "I2CBUS is an integer representing the bus index\n" + " these are present as 'xx' of /dev/" I2C_DEV_NAME + "xx\n" + "CHIP-ADDRESS is an integer representing device address (0x03 - 0x77)\n" + "DATA-ADDRESS is an integer representing register on chip.\n" + " (0x00 - 0xFF).\n" + "VALUE is the value to be written, if ignored, an short write is issued.\n" + "MODE is one of:\n" + " b (read byte data, default)\n" + " w (read word data)\n" + " Append p for PEC\n" + "MASK if specified,describes which bits of value will be actually written\n" + "WARN:\n" "i2cset can be extremely dangerous if used improperly.\n"; + +#ifdef CONFIG_LONGHELP +static const __maybe_unused char cmd_i2cset_help_add_s[] = + "It can confuse " + "your I2C bus, cause data loss, or have more serious side effects.\n" + "Writing to a serial EEPROM on a memory DIMM (chip addresses between\n" + "0x50 and 0x57) may DESTROY your memory, leaving your system unbootable!\n" + "Be extremely careful using this program.\n"; +static void cmd_i2set_help(void) +{ + fprintf(stderr, "%s", cmd_i2cset_help_s); + fprintf(stderr, "%s", cmd_i2cset_help_add_s); +} +#else +#define cmd_i2set_help() +#endif + +/** + * @brief confirm we can write to the reg without killing the user + * + * @param filename + * @param address + * @param size + * @param daddress + * @param value + * @param vmask + * @param pec + * + * @return + */ +static int confirm_set(const char *filename, int address, int size, + int daddress, int value, int vmask, int pec) +{ + if (address >= 0x50 && address <= 0x57) { + fprintf(stderr, "DANGEROUS! Writing to a serial " + "EEPROM on a memory DIMM\nmay render your " + "memory USELESS and make your system " "UNBOOTABLE!\n"); + return 0; + } + return 1; +} + +/** + * @brief set I2C registers + * + * @param cmdtp + * @param argc + * @param argv + * + * @return + */ +static int do_cmd_i2c_set(cmd_tbl_t *cmdtp, int argc, char *argv[]) +{ + char *end; + int res, i2cbus, address, size, file = -1; + int value, daddress, vmask = 0; + char filename[PATH_MAX]; + int pec = 0; + int flags = 0; + int force = 0, yes = 0; + + /* handle (optional) flags first */ + while (1 + flags < argc && argv[1 + flags][0] == '-') { + switch (argv[1 + flags][1]) { + case 'f': + force = 1; + break; + case 'y': + yes = 1; + break; + default: + fprintf(stderr, "Error: Unsupported option " + "\"%s\"!\n", argv[1 + flags]); + goto quit_i2cset; + } + flags++; + } + + if (argc < flags + 4) { + fprintf(stderr, "Error: Not enough arguments\n"); + goto quit_i2cset; + } + + i2cbus = lookup_i2c_bus(argv[flags + 1]); + if (i2cbus < 0) { + fprintf(stderr, "Error: Invalid Bus!\n"); + goto quit_i2cset; + } + + address = parse_i2c_address(argv[flags + 2]); + if (address < 0) { + fprintf(stderr, "Error: Invalid address!\n"); + goto quit_i2cset; + } + + daddress = simple_strtol(argv[flags + 3], &end, 0); + if (*end || daddress < 0 || daddress > 0xff) { + fprintf(stderr, "Error: Data address invalid!\n"); + goto quit_i2cset; + } + + if (argc > flags + 4) { + size = I2C_SMBUS_BYTE_DATA; + value = simple_strtol(argv[flags + 4], &end, 0); + if (*end || value < 0) { + fprintf(stderr, "Error: Data value invalid!\n"); + goto quit_i2cset; + } + } else { + size = I2C_SMBUS_BYTE; + value = -1; + } + + if (argc > flags + 5) { + switch (argv[flags + 5][0]) { + case 'b': + size = I2C_SMBUS_BYTE_DATA; + break; + case 'w': + size = I2C_SMBUS_WORD_DATA; + break; + default: + fprintf(stderr, "Error: Invalid mode!\n"); + goto quit_i2cset; + } + pec = argv[flags + 5][1] == 'p'; + } + + if (argc > flags + 6) { + vmask = simple_strtol(argv[flags + 6], &end, 0); + if (*end || vmask == 0) { + fprintf(stderr, "Error: Data value mask invalid!\n"); + goto quit_i2cset; + } + } + + if ((size == I2C_SMBUS_BYTE_DATA && value > 0xff) + || (size == I2C_SMBUS_WORD_DATA && value > 0xffff)) { + fprintf(stderr, "Error: Data value out of range!\n"); + goto quit_i2cset; + } + + file = open_i2c_dev(i2cbus, filename, 0); + if (file < 0 || check_funcs(file, size, daddress, pec) + || set_slave_addr(file, address, force)) + goto quit_i2cset; + + if (!yes && !confirm_set(filename, address, size, daddress, + value, vmask, pec)) { + close(file); + return 0; + } + + if (vmask) { + int oldvalue; + u16 dat; + oldvalue = + read_i2c(file, address, daddress, 1, (u8 *) &dat, + (size == I2C_SMBUS_WORD_DATA) ? 2 : 1); + + if (oldvalue < 0) { + fprintf(stderr, "Error: Failed to read old value\n"); + goto quit_i2cset; + } + oldvalue = dat; + + value = (value & vmask) | (oldvalue & ~vmask); + + fprintf(stderr, "Old value 0x%0*x, write mask " + "0x%0*x: Will write 0x%0*x to register " + "0x%02x\n", + size == I2C_SMBUS_WORD_DATA ? 4 : 2, oldvalue, + size == I2C_SMBUS_WORD_DATA ? 4 : 2, vmask, + size == I2C_SMBUS_WORD_DATA ? 4 : 2, value, daddress); + + } + + if (pec && ioctl(file, I2C_PEC, (void *)1) < 0) { + perror(filename); + fprintf(stderr, "Error: Could not set PEC\n"); + goto quit_i2cset; + } + + switch (size) { + case I2C_SMBUS_BYTE: + res = write_i2c(file, address, daddress, 1, NULL, 0); + break; + default: + res = + write_i2c(file, address, daddress, 1, + (u8 *) &value, + (size == I2C_SMBUS_WORD_DATA) ? 2 : 1); + } + if (res < 0) { + fprintf(stderr, "Error: Write failed\n"); + goto quit_i2cset; + } + + if (pec) { + if (ioctl(file, I2C_PEC, (void *)0) < 0) { + perror(filename); + fprintf(stderr, "Error: Could not clear PEC\n"); + goto quit_i2cset; + } + } + + switch (size) { + case I2C_SMBUS_BYTE: + /* No readback */ + break; + default: + { + u16 new_val; + res = + read_i2c(file, address, daddress, 1, + (u8 *) &new_val, + (size == I2C_SMBUS_WORD_DATA) ? 2 : 1); + if (res >= 0) + res = new_val; + } + } + close(file); + + if (size == I2C_SMBUS_BYTE) /* We're done */ + return 0; + + if (res < 0) { + printf("Warning - readback failed\n"); + } else if (res != value) { + printf("Warning - data mismatch - wrote " + "0x%0*x, read back 0x%0*x\n", + size == I2C_SMBUS_WORD_DATA ? 4 : 2, value, + size == I2C_SMBUS_WORD_DATA ? 4 : 2, res); + } else { + printf("Value 0x%0*x written, readback matched\n", + size == I2C_SMBUS_WORD_DATA ? 4 : 2, value); + } + + return 0; +quit_i2cset: + if (file >= 0) + close(file); + cmd_i2set_help(); + return 1; +} + +U_BOOT_CMD_START(i2cset) + .maxargs = 8, + .cmd = do_cmd_i2c_set, + .usage = "program to write an I2C register", + U_BOOT_CMD_HELP(cmd_i2cset_help_s) +U_BOOT_CMD_END +#endif /* CONFIG_CMD_I2C_SET */ ------------------------------------------------------------------------- Check out the new SourceForge.net Marketplace. It's the best place to buy or sell services for just about anything Open Source. http://sourceforge.net/services/buy/index.php _______________________________________________ U-Boot-Users mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/u-boot-users
