[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