[U-Boot] [U-Boot PATCH 0/3] Add support for pcf8575

Hi,
This adds driver for pcf8575 in uboot. The driver supports basic read and write operations on pcf gpio pins.
The first patch adds the driver, second patch and third patch switches cpsw to use slave0 on DRA72 EVM.
Tested pcf driver on DRA72 EVM by probing pcf lines and cpsw by runnning tftp.
Vignesh R (3): gpio: pcf8575: Add pcf8575 driver to control gpio lines ARM: dra7xx_evm: Enable pcf driver ARM: DRA7-EVM: switch cpsw to use slave0
board/ti/dra7xx/evm.c | 13 ++- drivers/gpio/Makefile | 1 + drivers/gpio/pcf8575.c | 248 +++++++++++++++++++++++++++++++++++++++++++ include/configs/dra7xx_evm.h | 4 + include/pcf8575.h | 25 +++++ 5 files changed, 289 insertions(+), 2 deletions(-) create mode 100644 drivers/gpio/pcf8575.c create mode 100644 include/pcf8575.h

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 vigneshr@ti.com --- 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 <common.h> +#include <i2c.h> +#include <pcf8575.h> + +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_ */

On Tue, Mar 10, 2015 at 08:41:21PM +0530, Vignesh R wrote:
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.
For attribution of stuff ported from the kernel please see http://www.denx.de/wiki/view/U-Boot/Patches#Attributing_Code_Copyrights_Sign
[snip]
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.
- */
Please use SPDX tags for new files, thanks.
And right up front, this driver isn't using device model, please re-work and update to use that framework, thanks!
+/* NOTE: these chips have strange "quasi-bidirectional" I/O pins.
/* * NOTE: ...
+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, "", ""),
+};
Why isn't that just done with the normal gpio command?

On Tuesday 10 March 2015 09:33 PM, Tom Rini wrote:
On Tue, Mar 10, 2015 at 08:41:21PM +0530, Vignesh R wrote:
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.
For attribution of stuff ported from the kernel please see http://www.denx.de/wiki/view/U-Boot/Patches#Attributing_Code_Copyrights_Sign
[snip]
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.
- */
Please use SPDX tags for new files, thanks.
And right up front, this driver isn't using device model, please re-work and update to use that framework, thanks!
Thanks for reviewing.
I can switch to device model, but DRA72 does not support device tree and hence I will not be able use device model based pcf driver in my use-case.
One approach is that, I will re-write driver to support both deprecated gpio APIs (like gpio_get_value) and gpio device model APIs (dm_gpio_ops). Is this approach okay? But, somebody will have to test the dm_gpio_ops implementations as DRA72 does not support DT.
Regards Vignesh

On Wed, Mar 11, 2015 at 02:19:32PM +0530, Vignesh R wrote:
On Tuesday 10 March 2015 09:33 PM, Tom Rini wrote:
On Tue, Mar 10, 2015 at 08:41:21PM +0530, Vignesh R wrote:
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.
For attribution of stuff ported from the kernel please see http://www.denx.de/wiki/view/U-Boot/Patches#Attributing_Code_Copyrights_Sign
[snip]
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.
- */
Please use SPDX tags for new files, thanks.
And right up front, this driver isn't using device model, please re-work and update to use that framework, thanks!
Thanks for reviewing.
I can switch to device model, but DRA72 does not support device tree and hence I will not be able use device model based pcf driver in my use-case.
Starting the move to DT is very easy, I would swear. For example, am335x_boneblack_vboot switches am335x over to DM and includes a device tree, but it's only used for the verified boot part now.

Enable pcf driver to control the pcf chip present at address 0x21 on i2c1.
Signed-off-by: Vignesh R vigneshr@ti.com --- include/configs/dra7xx_evm.h | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/include/configs/dra7xx_evm.h b/include/configs/dra7xx_evm.h index dee2b11056e7..0714f920efe6 100644 --- a/include/configs/dra7xx_evm.h +++ b/include/configs/dra7xx_evm.h @@ -229,4 +229,8 @@ #endif #endif /* NOR support */
+/* pcf support */ +#define CONFIG_PCF8575 +#define CONFIG_SYS_I2C_PCF8575_CHIP { {0x21, 0xeaf7} } + #endif /* __CONFIG_DRA7XX_EVM_H */

On Tue, Mar 10, 2015 at 08:41:22PM +0530, Vignesh R wrote:
Enable pcf driver to control the pcf chip present at address 0x21 on i2c1.
Signed-off-by: Vignesh R vigneshr@ti.com
include/configs/dra7xx_evm.h | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/include/configs/dra7xx_evm.h b/include/configs/dra7xx_evm.h index dee2b11056e7..0714f920efe6 100644 --- a/include/configs/dra7xx_evm.h +++ b/include/configs/dra7xx_evm.h @@ -229,4 +229,8 @@ #endif #endif /* NOR support */
+/* pcf support */ +#define CONFIG_PCF8575 +#define CONFIG_SYS_I2C_PCF8575_CHIP { {0x21, 0xeaf7} }
As part of switching to DM this will also be done differently (via device tree).

On DRA72 EVM cpsw slave1 is muxed with VIN, therefore switch cpsw to use slave0 using pcf driver. DRA72 has only one cpsw phy(phy#3). Hence, set phy_id to 3 for slave0, in case of DRA72 EVM.
Signed-off-by: Vignesh R vigneshr@ti.com --- board/ti/dra7xx/evm.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/board/ti/dra7xx/evm.c b/board/ti/dra7xx/evm.c index 65222419ebbd..cc391cb0a065 100644 --- a/board/ti/dra7xx/evm.c +++ b/board/ti/dra7xx/evm.c @@ -19,6 +19,7 @@ #include <asm/arch/mmc_host_def.h> #include <asm/arch/sata.h> #include <environment.h> +#include <pcf8575.h>
#include "mux_data.h"
@@ -31,6 +32,10 @@ DECLARE_GLOBAL_DATA_PTR; /* GPIO 7_11 */ #define GPIO_DDR_VTT_EN 203
+/* pcf chip address enet_mux_s0 */ +#define PCF_ENET_MUX_ADDR 0x21 +#define PCF_SEL_ENET_MUX_S0 4 + const struct omap_sysinfo sysinfo = { "Board: DRA7xx\n" }; @@ -254,8 +259,12 @@ int board_eth_init(bd_t *bis) ctrl_val |= 0x22; writel(ctrl_val, (*ctrl)->control_core_control_io1);
- if (*omap_si_rev == DRA722_ES1_0) - cpsw_data.active_slave = 1; + if (*omap_si_rev == DRA722_ES1_0) { + cpsw_data.active_slave = 0; + cpsw_data.slave_data[0].phy_addr = 3; + pcf8575_output(PCF_ENET_MUX_ADDR, PCF_SEL_ENET_MUX_S0, + PCF8575_OUT_LOW); + }
ret = cpsw_register(&cpsw_data); if (ret < 0)

On Tue, Mar 10, 2015 at 08:41:23PM +0530, Vignesh R wrote:
On DRA72 EVM cpsw slave1 is muxed with VIN, therefore switch cpsw to use slave0 using pcf driver. DRA72 has only one cpsw phy(phy#3). Hence, set phy_id to 3 for slave0, in case of DRA72 EVM.
Signed-off-by: Vignesh R vigneshr@ti.com
Reviewed-by: Tom Rini trini@konsulko.com
participants (2)
-
Tom Rini
-
Vignesh R