[U-Boot-Users] [Patch 5/9]U-boot-V2:cmd: add I2C commands
Menon, Nishanth
x0nishan at ti.com
Thu Jun 19 17:12:32 CEST 2008
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 <x0nishan at ti.com>
---
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 <x0nishan at ti.com>
+ *
+ * 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 <frodol at dds.nl> and
+ * Mark D. Studebaker <mdsxyz123 at yahoo.com>
+ * Copyright (C) 2008 Jean Delvare <khali at linux-fr.org>
+ * i2cdump.c - a user-space program to dump I2C registers
+ * Copyright (C) 2002-2003 Frodo Looijaard <frodol at dds.nl>, and
+ * Mark D. Studebaker <mdsxyz123 at yahoo.com>
+ * Copyright (C) 2004-2008 Jean Delvare <khali at linux-fr.org>
+ * i2cget.c - A user-space program to read an I2C register.
+ * Copyright (C) 2005-2008 Jean Delvare <khali at linux-fr.org>
+ * i2cset.c - A user-space program to write an I2C register.
+ * Copyright (C) 2001-2003 Frodo Looijaard <frodol at dds.nl>, and
+ * Mark D. Studebaker <mdsxyz123 at yahoo.com>
+ * Copyright (C) 2004-2008 Jean Delvare <khali at linux-fr.org>
+ *
+ * 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 */
More information about the U-Boot
mailing list