[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