From mboxrd@z Thu Jan 1 00:00:00 1970 From: Sascha Hauer Date: Fri, 20 Jun 2008 12:28:56 +0200 Subject: [U-Boot-Users] [Patch 5/9]U-boot-V2:cmd: add I2C commands In-Reply-To: <7A436F7769CA33409C6B44B358BFFF0CD3211BBD@dlee02.ent.ti.com> References: <7A436F7769CA33409C6B44B358BFFF0CD3192D75@dlee02.ent.ti.com> <48592C32.60900@freescale.com> <20080618155822.GF6603@pengutronix.de> <7A436F7769CA33409C6B44B358BFFF0CD3192E2E@dlee02.ent.ti.com> <20080618183806.GK1189@pengutronix.de> <7A436F7769CA33409C6B44B358BFFF0CD3192FBC@dlee02.ent.ti.com> <20080618191142.GN1189@pengutronix.de> <7A436F7769CA33409C6B44B358BFFF0CD3193101@dlee02.ent.ti.com> <20080618204407.GR1189@pengutronix.de> <7A436F7769CA33409C6B44B358BFFF0CD3211BBD@dlee02.ent.ti.com> Message-ID: <20080620102856.GI1479@pengutronix.de> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de 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 > > --- > 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, > + * Nishanth Menon > + * > + * 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 and > + * Mark D. Studebaker > + * Copyright (C) 2008 Jean Delvare > + * i2cdump.c - a user-space program to dump I2C registers > + * Copyright (C) 2002-2003 Frodo Looijaard , and > + * Mark D. Studebaker > + * Copyright (C) 2004-2008 Jean Delvare > + * i2cget.c - A user-space program to read an I2C register. > + * Copyright (C) 2005-2008 Jean Delvare > + * i2cset.c - A user-space program to write an I2C register. > + * Copyright (C) 2001-2003 Frodo Looijaard , and > + * Mark D. Studebaker > + * Copyright (C) 2004-2008 Jean Delvare > + * > + * 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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#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 ? > + "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 -- 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/ <-