From patchwork Tue Mar 10 15:11:21 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Raghavendra, Vignesh" X-Patchwork-Id: 448546 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 6D0C3140157 for ; Wed, 11 Mar 2015 02:17:07 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id D90D44A04E; Tue, 10 Mar 2015 16:17:03 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id X6JyXqRtuiEv; Tue, 10 Mar 2015 16:17:03 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 4352D4A03B; Tue, 10 Mar 2015 16:17:03 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id BD0A74A03B for ; Tue, 10 Mar 2015 16:11:54 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id gJyJb16kTfc0 for ; Tue, 10 Mar 2015 16:11:54 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from devils.ext.ti.com (devils.ext.ti.com [198.47.26.153]) by theia.denx.de (Postfix) with ESMTPS id 252624A033 for ; Tue, 10 Mar 2015 16:11:50 +0100 (CET) Received: from dlelxv90.itg.ti.com ([172.17.2.17]) by devils.ext.ti.com (8.13.7/8.13.7) with ESMTP id t2AFBmrW014752; Tue, 10 Mar 2015 10:11:48 -0500 Received: from DFLE72.ent.ti.com (dfle72.ent.ti.com [128.247.5.109]) by dlelxv90.itg.ti.com (8.14.3/8.13.8) with ESMTP id t2AFBlB9030619; Tue, 10 Mar 2015 10:11:47 -0500 Received: from dlep32.itg.ti.com (157.170.170.100) by DFLE72.ent.ti.com (128.247.5.109) with Microsoft SMTP Server id 14.3.224.2; Tue, 10 Mar 2015 10:11:47 -0500 Received: from uda0132425.apr.dhcp.ti.com (ileax41-snat.itg.ti.com [10.172.224.153]) by dlep32.itg.ti.com (8.14.3/8.13.8) with ESMTP id t2AFBgnG004472; Tue, 10 Mar 2015 10:11:45 -0500 From: Vignesh R To: , Date: Tue, 10 Mar 2015 20:41:21 +0530 Message-ID: <1426000283-21818-2-git-send-email-vigneshr@ti.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1426000283-21818-1-git-send-email-vigneshr@ti.com> References: <1426000283-21818-1-git-send-email-vigneshr@ti.com> MIME-Version: 1.0 X-Mailman-Approved-At: Tue, 10 Mar 2015 16:17:01 +0100 Cc: nsekhar@ti.com Subject: [U-Boot] [U-Boot PATCH 1/3] gpio: pcf8575: Add pcf8575 driver to control gpio lines X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.15 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" TI's pcf8575 is a 16-bit I2C based GPIO expander.The device features a 16-bit quasi-bidirectional I/O ports. Each quasi-bidirectional I/O can be used as an input or output without the use of a data-direction control signal. The I/Os should be high before being used as inputs. This driver is based on pcf857x driver available in Linux 4.0 kernel. It supports basic reading and writing of gpio pins. Signed-off-by: Vignesh R --- drivers/gpio/Makefile | 1 + drivers/gpio/pcf8575.c | 248 +++++++++++++++++++++++++++++++++++++++++++++++++ include/pcf8575.h | 25 +++++ 3 files changed, 274 insertions(+) create mode 100644 drivers/gpio/pcf8575.c create mode 100644 include/pcf8575.h diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index aa11f15423a4..1e0e521b4d95 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -9,6 +9,7 @@ ifndef CONFIG_SPL_BUILD obj-$(CONFIG_DM_GPIO) += gpio-uclass.o endif +obj-$(CONFIG_PCF8575) += pcf8575.o obj-$(CONFIG_AT91_GPIO) += at91_gpio.o obj-$(CONFIG_INTEL_ICH6_GPIO) += intel_ich6_gpio.o obj-$(CONFIG_KIRKWOOD_GPIO) += kw_gpio.o diff --git a/drivers/gpio/pcf8575.c b/drivers/gpio/pcf8575.c new file mode 100644 index 000000000000..1ee92a29760a --- /dev/null +++ b/drivers/gpio/pcf8575.c @@ -0,0 +1,248 @@ +/* + * PCF8575 I2C GPIO EXPANDER DRIVER + * + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * Driver for TI pcf-8575 16 bit I2C gpio expander. Based on + * gpio-pcf857x Linux 4.0 kernel driver and pca953x driver in u-boot + */ + +#include +#include +#include + +enum { + PCF8575_CMD_INFO, + PCF8575_CMD_DEVICE, + PCF8575_CMD_OUTPUT, + PCF8575_CMD_INPUT, +}; + +struct pcf8575_chip { + uint8_t addr; +/* current direction of the pcf lines */ + unsigned int out; +}; + + +/* NOTE: these chips have strange "quasi-bidirectional" I/O pins. + * We can't actually know whether a pin is configured (a) as output + * and driving the signal low, or (b) as input and reporting a low + * value ... without knowing the last value written since the chip + * came out of reset (if any). We can't read the latched output. + * In short, the only reliable solution for setting up pin direction + * is to do it explicitly. + * + * Using "out" avoids that trouble. It flags the status of the pins at + * boot. + * + * Each struct stores address of an instance of pcf + * and state(direction) of each gpio line for that instance. + */ +static struct pcf8575_chip pcf8575_chips[] = + CONFIG_SYS_I2C_PCF8575_CHIP; + +static struct pcf8575_chip *pcf8575_chip_get(uint8_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pcf8575_chips); i++) + if (pcf8575_chips[i].addr == addr) + return &pcf8575_chips[i]; + + return 0; +} + + +/* Read/Write to 16-bit I/O expander */ + +static int pcf8575_i2c_write(uint8_t addr, unsigned word) +{ + unsigned word_be = ((word & 0xff) << 8) | + ((word & 0xff00) >> 8); + uint8_t buf = 0; + int status; + + status = i2c_write(addr, word_be, 2, &buf, 1); + + return (status < 0) ? status : 0; +} + +static int pcf8575_i2c_read(uint8_t addr) +{ + u8 buf[2]; + int status; + + status = i2c_read(addr, 0, 1, buf, 2); + if (status < 0) + return status; + + return (buf[1] << 8) | buf[0]; +} + +int pcf8575_input(uint8_t addr, unsigned offset) +{ + struct pcf8575_chip *chip = pcf8575_chip_get(addr); + int status; + + chip->out |= (1 << offset); + status = pcf8575_i2c_write(addr, chip->out); + + return status; +} + +int pcf8575_get_val(uint8_t addr, unsigned offset) +{ + int value; + + value = pcf8575_i2c_read(addr); + return (value < 0) ? 0 : (value & (1 << offset)); +} + +int pcf8575_output(uint8_t addr, unsigned offset, int value) +{ + struct pcf8575_chip *chip = pcf8575_chip_get(addr); + unsigned bit = 1 << offset; + int status; + + if (value) + chip->out |= bit; + else + chip->out &= ~bit; + status = pcf8575_i2c_write(addr, chip->out); + + return status; +} + +/* + * Display pcf8575 information + */ +int pcf8575_info(uint8_t addr) +{ + int i; + uint data; + struct pcf8575_chip *chip = pcf8575_chip_get(addr); + int nr_gpio = 16; + int msb = nr_gpio - 1; + + printf("pcf8575@ 0x%x (%d pins):\n\n", addr, nr_gpio); + printf("gpio pins: "); + for (i = msb; i >= 0; i--) + printf("%x", i); + printf("\n"); + for (i = 11 + nr_gpio; i > 0; i--) + printf("-"); + printf("\n"); + + data = chip->out; + printf("dir: "); + for (i = msb; i >= 0; i--) + printf("%c", data & (1 << i) ? 'i' : 'o'); + printf("\n"); + + data = pcf8575_i2c_read(addr); + if (data < 0) + return -1; + printf("input: "); + for (i = msb; i >= 0; i--) + printf("%c", data & (1 << i) ? '1' : '0'); + printf("\n"); + + return 0; +} + +cmd_tbl_t cmd_pcf8575[] = { + U_BOOT_CMD_MKENT(device, 3, 0, (void *)PCF8575_CMD_DEVICE, "", ""), + U_BOOT_CMD_MKENT(output, 4, 0, (void *)PCF8575_CMD_OUTPUT, "", ""), + U_BOOT_CMD_MKENT(input, 3, 0, (void *)PCF8575_CMD_INPUT, "", ""), + U_BOOT_CMD_MKENT(info, 2, 0, (void *)PCF8575_CMD_INFO, "", ""), +}; + + + +int do_pcf8575(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + static uint8_t chip = 0x21; + int ret = CMD_RET_USAGE, val; + ulong ul_arg2 = 0; + ulong ul_arg3 = 0; + cmd_tbl_t *c; + + c = find_cmd_tbl(argv[1], cmd_pcf8575, ARRAY_SIZE(cmd_pcf8575)); + + /* All commands but "device" require 'maxargs' arguments */ + if (!c || !((argc == (c->maxargs)) || + (((int)c->cmd == PCF8575_CMD_DEVICE) && + (argc == (c->maxargs - 1))))) { + return CMD_RET_USAGE; + } + + /* arg2 used as chip number or pin number */ + if (argc > 2) + ul_arg2 = simple_strtoul(argv[2], NULL, 16); + + /* arg3 used as pin or invert value */ + if (argc > 3) + ul_arg3 = simple_strtoul(argv[3], NULL, 16) & 0x1; + + switch ((int)c->cmd) { + case PCF8575_CMD_INFO: + ret = pcf8575_info(chip); + if (ret) + ret = CMD_RET_FAILURE; + break; + + case PCF8575_CMD_DEVICE: + if (argc == 3) + chip = (uint8_t)ul_arg2; + printf("Current device address: 0x%x\n", chip); + ret = CMD_RET_SUCCESS; + break; + + case PCF8575_CMD_INPUT: + ret = pcf8575_input(chip, ul_arg2); + val = pcf8575_get_val(chip, ul_arg2); + + if (val) + ret = CMD_RET_FAILURE; + else + printf("chip 0x%02x, pin 0x%lx = %d\n", chip, ul_arg2, + val); + break; + + case PCF8575_CMD_OUTPUT: + ret = pcf8575_output(chip, ul_arg2, ul_arg3); + if (ret) + ret = CMD_RET_FAILURE; + break; + } + + if (ret == CMD_RET_FAILURE) + eprintf("Error talking to chip at 0x%x\n", chip); + + return ret; +} + +U_BOOT_CMD( + pcf8575, 5, 1, do_pcf8575, + "pcf8575 gpio access", + "device [dev]\n" + " - show or set current device address\n" + "pcf8575 info\n" + " - display info for current chip\n" + "pcf8575 output pin 0|1\n" + " - set pin as output and drive low or high\n" + "pcf8575 input pin\n" + " - set pin as input and read value" +); diff --git a/include/pcf8575.h b/include/pcf8575.h new file mode 100644 index 000000000000..a082554b4a5a --- /dev/null +++ b/include/pcf8575.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __PCF8575_H_ +#define __PCF8575_H_ + +#define PCF8575_OUT_LOW 0 +#define PCF8575_OUT_HIGH 1 + + +int pcf8575_input(uint8_t addr, unsigned offset); +int pcf8575_get_val(uint8_t addr, unsigned offset); +int pcf8575_output(uint8_t addr, unsigned offset, int value); + +#endif /* __PCF8575_H_ */