[PATCH] gpio: mcp230xx: Introduce new driver
Simon Glass
sjg at chromium.org
Sun Jul 11 02:01:00 CEST 2021
Hi Sebastian,
On Tue, 6 Jul 2021 at 10:34, Sebastian Reichel
<sebastian.reichel at collabora.com> wrote:
>
> Introduce driver for I2C based MCP230xx GPIO chips, which are
> quite common and already well supported by the Linux kernel.
>
> Signed-off-by: Sebastian Reichel <sebastian.reichel at collabora.com>
> ---
> drivers/gpio/Kconfig | 10 ++
> drivers/gpio/Makefile | 1 +
> drivers/gpio/mcp230xx_gpio.c | 235 +++++++++++++++++++++++++++++++++++
> 3 files changed, 246 insertions(+)
> create mode 100644 drivers/gpio/mcp230xx_gpio.c
Reviewed-by: Simon Glass <sjg at chromium.org>
a few nits below
>
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index de4dc51d4b48..f93e736639bb 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -179,6 +179,16 @@ config LPC32XX_GPIO
> help
> Support for the LPC32XX GPIO driver.
>
> +config MCP230XX_GPIO
> + bool "MCP230XX GPIO driver"
> + depends on DM
> + help
> + Support for Microchip's MCP230XX I2C connected GPIO devices.
> + The following chips are supported:
> + - MCP23008
> + - MCP23017
> + - MCP23018
> +
> config MSCC_SGPIO
> bool "Microsemi Serial GPIO driver"
> depends on DM_GPIO && SOC_VCOREIII
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index 8541ba0b0aec..00ffcc753534 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -24,6 +24,7 @@ obj-$(CONFIG_KIRKWOOD_GPIO) += kw_gpio.o
> obj-$(CONFIG_KONA_GPIO) += kona_gpio.o
> obj-$(CONFIG_MARVELL_GPIO) += mvgpio.o
> obj-$(CONFIG_MARVELL_MFP) += mvmfp.o
> +obj-$(CONFIG_MCP230XX_GPIO) += mcp230xx_gpio.o
> obj-$(CONFIG_MXC_GPIO) += mxc_gpio.o
> obj-$(CONFIG_MXS_GPIO) += mxs_gpio.o
> obj-$(CONFIG_PCA953X) += pca953x.o
> diff --git a/drivers/gpio/mcp230xx_gpio.c b/drivers/gpio/mcp230xx_gpio.c
> new file mode 100644
> index 000000000000..44def86ca7d9
> --- /dev/null
> +++ b/drivers/gpio/mcp230xx_gpio.c
> @@ -0,0 +1,235 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2021, Collabora Ltd.
> + * Copyright (C) 2021, General Electric Company
> + * Author(s): Sebastian Reichel <sebastian.reichel at collabora.com>
> + */
> +
> +#define LOG_CATEGORY UCLASS_GPIO
> +
> +#include <common.h>
> +#include <errno.h>
> +#include <dm.h>
> +#include <i2c.h>
> +#include <asm/gpio.h>
> +#include <dm/device_compat.h>
> +#include <dt-bindings/gpio/gpio.h>
> +
> +enum mcp230xx_type {
> + UNKNOWN = 0,
> + MCP23008,
> + MCP23017,
> + MCP23018,
> +};
> +
> +#define MCP230XX_IODIR 0x00
> +#define MCP230XX_GPPU 0x06
> +#define MCP230XX_GPIO 0x09
> +#define MCP230XX_OLAT 0x0a
> +
> +#define BANKSIZE 8
> +
> +static int mcp230xx_read(struct udevice *dev, uint reg, uint offset)
> +{
> + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
> + int bank = offset / BANKSIZE;
> + int mask = 1 << (offset % BANKSIZE);
> + int shift = (uc_priv->gpio_count / BANKSIZE) - 1;
> + int ret;
> +
> + ret = dm_i2c_reg_read(dev, (reg << shift) + bank);
> + if (ret < 0)
> + return ret;
blank line before final return
> + return !!(ret & mask);
> +}
> +
> +static int mcp230xx_write(struct udevice *dev, uint reg, uint offset, bool val)
> +{
> + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
> + int bank = offset / BANKSIZE;
> + int mask = 1 << (offset % BANKSIZE);
> + int shift = (uc_priv->gpio_count / BANKSIZE) - 1;
> + int regval;
> +
> + regval = dm_i2c_reg_read(dev, (reg << shift) + bank);
Can I suggest adding something like
int i2c_reg_clrset(struct udevice *dev, uint offset, u32 clr, u32 set)
which would be useful in this situation. If you do that, add a test in
test/dm/i2c.c
> + if (regval < 0)
> + return regval;
> +
> + regval &= ~mask;
> + if (val)
> + regval |= mask;
> +
> + return dm_i2c_reg_write(dev, (reg << shift) + bank, regval);
To my eye this would be better as
reg << shift | bank
because + indicates addition. I suspect it is actually ORing in the addres?
> +}
> +
> +static int mcp230xx_get_value(struct udevice *dev, uint offset)
> +{
> + int ret;
> +
> + ret = mcp230xx_read(dev, MCP230XX_GPIO, offset);
> + if (ret < 0) {
> + dev_err(dev, "%s error: %d\n", __func__, ret);
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +static int mcp230xx_set_value(struct udevice *dev, uint offset, int val)
> +{
> + int ret;
> +
> + ret = mcp230xx_write(dev, MCP230XX_GPIO, offset, val);
> + if (ret < 0) {
> + dev_err(dev, "%s error: %d\n", __func__, ret);
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +static int mcp230xx_get_flags(struct udevice *dev, unsigned int offset,
> + ulong *flags)
> +{
> + int direction, pullup;
> +
> + pullup = mcp230xx_read(dev, MCP230XX_GPPU, offset);
> + if (pullup < 0) {
> + dev_err(dev, "%s error: %d\n", __func__, pullup);
> + return pullup;
> + }
> +
> + direction = mcp230xx_read(dev, MCP230XX_IODIR, offset);
> + if (direction < 0) {
> + dev_err(dev, "%s error: %d\n", __func__, direction);
> + return direction;
> + }
> +
> + *flags = direction ? GPIOD_IS_IN : GPIOD_IS_OUT;
> +
> + if (pullup)
> + *flags |= GPIOD_PULL_UP;
> +
> + return 0;
> +}
> +
> +static int mcp230xx_set_flags(struct udevice *dev, uint offset, ulong flags)
> +{
> + int direction = !(flags & GPIOD_IS_OUT);
> + int pullup = !!(flags & GPIOD_PULL_UP);
If you use bool you don't need the !!
> + ulong supported_mask;
> + int ret;
> +
> + /* Note: active-low is ignored (handled by core) */
> + supported_mask = GPIOD_ACTIVE_LOW | GPIOD_IS_IN | GPIOD_IS_OUT | GPIOD_PULL_UP;
> + if (flags & ~supported_mask)
> + return -EINVAL;
> +
[..]
Regards,
Simon
More information about the U-Boot
mailing list