[U-Boot-Users] [Patch 5/9]U-boot-V2:cmd: add I2C commands

Sascha Hauer s.hauer at pengutronix.de
Fri Jun 20 12:28:56 CEST 2008


On Thu, Jun 19, 2008 at 10:12:32AM -0500, Menon, Nishanth wrote:
> 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

Some overall comments first.

You seem to register a device for each bus and do all access to slave
devices through the bus. It would be better if we register a new device
for each slave device. Slave devices could either be hardwired by a
board setup or, if the board maintainer or user is brave, through an
autodetect mechanism.
Once we have a device for each slave most of this patch can be removed
since you can use the generic md/mm commands for reading registers. In
the device tree this could look like this:

/dev/i2cbus0			# bus 0
	/dev/i2cbus0_50 	# device 0x50 on bus 0...
		/dev/eeprom0	# which happens to be an eeprom

Please resist to introduce a new command for each type of device.

more inline...

Sascha


> 
> 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 requires documentation and implementation. Why not -f <filename>?

> +    "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

You do not need this ifdef...

> +
> +/**
> + * @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++;
> +       }

Please use getopt instead.

> +
> +       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();

If you want to print the help text here call u_boot_cmd_usage(cmdtp).


> +       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++;
> +       }

getopt instead

<snip>

-- 
Pengutronix e.K. - Linux Solutions for Science and Industry
-----------------------------------------------------------
Kontakt-Informationen finden Sie im Header dieser Mail oder
auf der Webseite -> http://www.pengutronix.de/impressum/ <-




More information about the U-Boot mailing list