From patchwork Fri Jul 29 11:20:22 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Raghavendra, Vignesh" X-Patchwork-Id: 654092 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 3s15t04mkJz9t0j for ; Fri, 29 Jul 2016 21:22:16 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 447A1B38AA; Fri, 29 Jul 2016 13:22:14 +0200 (CEST) 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 0BJDwJUjUFFO; Fri, 29 Jul 2016 13:22:14 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 216E7B38B5; Fri, 29 Jul 2016 13:22:06 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 5CF44A761D for ; Fri, 29 Jul 2016 13:21:24 +0200 (CEST) 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 hB0ARkANT_ng for ; Fri, 29 Jul 2016 13:21:24 +0200 (CEST) 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 1E721B38AB for ; Fri, 29 Jul 2016 13:20:53 +0200 (CEST) Received: from dlelxv90.itg.ti.com ([172.17.2.17]) by devils.ext.ti.com (8.13.7/8.13.7) with ESMTP id u6TBHFuV017145; Fri, 29 Jul 2016 06:17:15 -0500 Received: from DLEE71.ent.ti.com (dlee71.ent.ti.com [157.170.170.114]) by dlelxv90.itg.ti.com (8.14.3/8.13.8) with ESMTP id u6TBKlCk023462; Fri, 29 Jul 2016 06:20:47 -0500 Received: from dflp33.itg.ti.com (10.64.6.16) by DLEE71.ent.ti.com (157.170.170.114) with Microsoft SMTP Server id 14.3.294.0; Fri, 29 Jul 2016 06:20:46 -0500 Received: from uda0132425.india.ti.com (ileax41-snat.itg.ti.com [10.172.224.153]) by dflp33.itg.ti.com (8.14.3/8.13.8) with ESMTP id u6TBKeFe016156; Fri, 29 Jul 2016 06:20:44 -0500 From: Vignesh R To: Tom Rini , Joe Hershberger , Simon Glass Date: Fri, 29 Jul 2016 16:50:22 +0530 Message-ID: <20160729112025.13948-2-vigneshr@ti.com> X-Mailer: git-send-email 2.9.2 In-Reply-To: <20160729112025.13948-1-vigneshr@ti.com> References: <20160729112025.13948-1-vigneshr@ti.com> MIME-Version: 1.0 Cc: Stefan Roese , u-boot@lists.denx.de Subject: [U-Boot] [PATCH v2 1/4] gpio: Add driver for TI PCF8575 I2C GPIO expander 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 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. Read the device documentation for more details[1]. This driver is based on pcf857x driver available in Linux v4.7 kernel. It supports basic reading and writing of gpio pins. [1] http://www.ti.com/lit/ds/symlink/pcf8575.pdf Signed-off-by: Vignesh R Reviewed-by: Tom Rini --- v2: Drop GPL license text from driver as SPDX tag is enough. doc/device-tree-bindings/gpio/gpio-pcf857x.txt | 71 +++++++++ drivers/gpio/Kconfig | 7 + drivers/gpio/Makefile | 1 + drivers/gpio/pcf8575_gpio.c | 190 +++++++++++++++++++++++++ 4 files changed, 269 insertions(+) create mode 100644 doc/device-tree-bindings/gpio/gpio-pcf857x.txt create mode 100644 drivers/gpio/pcf8575_gpio.c diff --git a/doc/device-tree-bindings/gpio/gpio-pcf857x.txt b/doc/device-tree-bindings/gpio/gpio-pcf857x.txt new file mode 100644 index 000000000000..ada4e2973323 --- /dev/null +++ b/doc/device-tree-bindings/gpio/gpio-pcf857x.txt @@ -0,0 +1,71 @@ +* PCF857x-compatible I/O expanders + +The PCF857x-compatible chips have "quasi-bidirectional" I/O lines that can be +driven high by a pull-up current source or driven low to ground. This combines +the direction and output level into a single bit per line, which can't be read +back. We can't actually know at initialization time whether a line is configured +(a) as output and driving the signal low/high, or (b) as input and reporting a +low/high value, without knowing the last value written since the chip came out +of reset (if any). The only reliable solution for setting up line direction is +thus to do it explicitly. + +Required Properties: + + - compatible: should be one of the following. + - "maxim,max7328": For the Maxim MAX7378 + - "maxim,max7329": For the Maxim MAX7329 + - "nxp,pca8574": For the NXP PCA8574 + - "nxp,pca8575": For the NXP PCA8575 + - "nxp,pca9670": For the NXP PCA9670 + - "nxp,pca9671": For the NXP PCA9671 + - "nxp,pca9672": For the NXP PCA9672 + - "nxp,pca9673": For the NXP PCA9673 + - "nxp,pca9674": For the NXP PCA9674 + - "nxp,pca9675": For the NXP PCA9675 + - "nxp,pcf8574": For the NXP PCF8574 + - "nxp,pcf8574a": For the NXP PCF8574A + - "nxp,pcf8575": For the NXP PCF8575 + - "ti,tca9554": For the TI TCA9554 + + - reg: I2C slave address. + + - gpio-controller: Marks the device node as a gpio controller. + - #gpio-cells: Should be 2. The first cell is the GPIO number and the second + cell specifies GPIO flags, as defined in . Only the + GPIO_ACTIVE_HIGH and GPIO_ACTIVE_LOW flags are supported. + +Optional Properties: + + - lines-initial-states: Bitmask that specifies the initial state of each + line. When a bit is set to zero, the corresponding line will be initialized to + the input (pulled-up) state. When the bit is set to one, the line will be + initialized the low-level output state. If the property is not specified + all lines will be initialized to the input state. + + The I/O expander can detect input state changes, and thus optionally act as + an interrupt controller. When the expander interrupt line is connected all the + following properties must be set. For more information please see the + interrupt controller device tree bindings documentation available at + Documentation/devicetree/bindings/interrupt-controller/interrupts.txt. + + - interrupt-controller: Identifies the node as an interrupt controller. + - #interrupt-cells: Number of cells to encode an interrupt source, shall be 2. + - interrupt-parent: phandle of the parent interrupt controller. + - interrupts: Interrupt specifier for the controllers interrupt. + + +Please refer to gpio.txt in this directory for details of the common GPIO +bindings used by client devices. + +Example: PCF8575 I/O expander node + + pcf8575: gpio@20 { + compatible = "nxp,pcf8575"; + reg = <0x20>; + interrupt-parent = <&irqpin2>; + interrupts = <3 0>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 73b862dc0b21..1af05358ec76 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -79,6 +79,13 @@ config PM8916_GPIO Power and reset buttons are placed in "pm8916_key" bank and have gpio numbers 0 and 1 respectively. +config PCF8575_GPIO + bool "PCF8575 I2C GPIO Expander driver" + depends on DM_GPIO && DM_I2C + help + Support for PCF8575 I2C 16 bit GPIO expander. Most of these + chips are from NXP and TI. + config ROCKCHIP_GPIO bool "Rockchip GPIO driver" depends on DM_GPIO diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 792d19186aad..8f426c82cdca 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -57,3 +57,4 @@ obj-$(CONFIG_PIC32_GPIO) += pic32_gpio.o obj-$(CONFIG_MVEBU_GPIO) += mvebu_gpio.o obj-$(CONFIG_MSM_GPIO) += msm_gpio.o obj-$(CONFIG_PM8916_GPIO) += pm8916_gpio.o +obj-$(CONFIG_PCF8575_GPIO) += pcf8575_gpio.o diff --git a/drivers/gpio/pcf8575_gpio.c b/drivers/gpio/pcf8575_gpio.c new file mode 100644 index 000000000000..94667b7a201e --- /dev/null +++ b/drivers/gpio/pcf8575_gpio.c @@ -0,0 +1,190 @@ +/* + * PCF8575 I2C GPIO EXPANDER DRIVER + * + * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/ + * + * Vignesh R + * + * SPDX-License-Identifier: GPL-2.0 + * + * + * Driver for TI PCF-8575 16 bit I2C gpio expander. Based on + * gpio-pcf857x Linux Kernel(v4.7) driver. + * + * Copyright (C) 2007 David Brownell + * + */ + +/* + * NOTE: The driver and devicetree bindings are borrowed from Linux + * Kernel, but driver does not support all PCF857x devices. It currently + * supports PCF8575 16Bit expander by TI and NXP. + * + * TODO: + * Support 8 bit PCF857x compatible expanders. + */ + +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct pcf8575_chip { + int gpio_count; /* No GPIOs supported by the chip */ + unsigned int out; /* software latch */ + const char *bank_name; /* Name of the expander bank */ +}; + +/* Read/Write to 16-bit I/O expander */ + +static int pcf8575_i2c_write_le16(struct udevice *dev, unsigned int word) +{ + struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); + struct i2c_msg msg; + u8 buf[2] = { word & 0xff, word >> 8, }; + int ret; + + msg.addr = chip->chip_addr; + msg.buf = buf; + msg.flags = 0; + msg.len = 2; + ret = dm_i2c_xfer(dev, &msg, 1); + + if (ret) + printf("%s i2c write failed to addr %x\n", __func__, msg.addr); + + return ret; +} + +static int pcf8575_i2c_read_le16(struct udevice *dev) +{ + struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); + struct i2c_msg msg; + u8 buf[2]; + int ret; + + msg.addr = chip->chip_addr; + msg.buf = buf; + msg.flags = I2C_M_RD; + msg.len = 2; + + ret = dm_i2c_xfer(dev, &msg, 1); + if (ret) { + printf("%s i2c read failed to addr %x\n", __func__, msg.addr); + return ret; + } + + return (msg.buf[1] << 8) | msg.buf[0]; +} + +static int pcf8575_direction_input(struct udevice *dev, unsigned offset) +{ + struct pcf8575_chip *pc = dev_get_platdata(dev); + int status; + + pc->out |= BIT(offset); + status = pcf8575_i2c_write_le16(dev, pc->out); + + return status; +} + +static int pcf8575_direction_output(struct udevice *dev, + unsigned int offset, int value) +{ + struct pcf8575_chip *pc = dev_get_platdata(dev); + int ret; + + if (value) + pc->out |= BIT(offset); + else + pc->out &= ~BIT(offset); + + ret = pcf8575_i2c_write_le16(dev, pc->out); + + return ret; +} + +static int pcf8575_get_value(struct udevice *dev, unsigned int offset) +{ + int value; + + value = pcf8575_i2c_read_le16(dev); + + return (value < 0) ? value : ((value & BIT(offset)) >> offset); +} + +static int pcf8575_set_value(struct udevice *dev, unsigned int offset, + int value) +{ + return pcf8575_direction_output(dev, offset, value); +} + +static int pcf8575_ofdata_platdata(struct udevice *dev) +{ + struct pcf8575_chip *pc = dev_get_platdata(dev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + int n_latch; + + uc_priv->gpio_count = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "gpio-count", 16); + uc_priv->bank_name = fdt_getprop(gd->fdt_blob, dev->of_offset, + "gpio-bank-name", NULL); + if (!uc_priv->bank_name) + uc_priv->bank_name = fdt_get_name(gd->fdt_blob, + dev->of_offset, NULL); + + /* 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 n_latch avoids that trouble. When left initialized to zero, + * our software copy of the "latch" then matches the chip's all-ones + * reset state. Otherwise it flags pins to be driven low. + */ + + n_latch = fdtdec_get_uint(gd->fdt_blob, dev->of_offset, + "lines-initial-states", 0); + pc->out = ~n_latch; + + return 0; +} + +static int pcf8575_gpio_probe(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + debug("%s GPIO controller with %d gpios probed\n", + uc_priv->bank_name, uc_priv->gpio_count); + + return 0; +} + +static const struct dm_gpio_ops pcf8575_gpio_ops = { + .direction_input = pcf8575_direction_input, + .direction_output = pcf8575_direction_output, + .get_value = pcf8575_get_value, + .set_value = pcf8575_set_value, +}; + +static const struct udevice_id pcf8575_gpio_ids[] = { + { .compatible = "nxp,pcf8575" }, + { .compatible = "ti,pcf8575" }, + { } +}; + +U_BOOT_DRIVER(gpio_pcf8575) = { + .name = "gpio_pcf8575", + .id = UCLASS_GPIO, + .ops = &pcf8575_gpio_ops, + .of_match = pcf8575_gpio_ids, + .ofdata_to_platdata = pcf8575_ofdata_platdata, + .probe = pcf8575_gpio_probe, + .platdata_auto_alloc_size = sizeof(struct pcf8575_chip), +};