From patchwork Thu Jul 15 15:39:59 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sebastian Reichel X-Patchwork-Id: 1505768 X-Patchwork-Delegate: hs@denx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GQdqY5zLCz9sPf for ; Fri, 16 Jul 2021 01:40:23 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id D3F0381FC6; Thu, 15 Jul 2021 17:40:14 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=fail (p=none dis=none) header.from=collabora.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Received: by phobos.denx.de (Postfix, from userid 109) id A8DD282999; Thu, 15 Jul 2021 17:40:11 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,SPF_HELO_PASS, UNPARSEABLE_RELAY autolearn=ham autolearn_force=no version=3.4.2 Received: from bhuna.collabora.co.uk (bhuna.collabora.co.uk [IPv6:2a00:1098:0:82:1000:25:2eeb:e3e3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 5C7F081E47 for ; Thu, 15 Jul 2021 17:40:08 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=sebastian.reichel@collabora.com Received: from [127.0.0.1] (localhost [127.0.0.1]) (Authenticated sender: sre) with ESMTPSA id DF2631F42024 Received: by jupiter.universe (Postfix, from userid 1000) id D3B654800C7; Thu, 15 Jul 2021 17:40:04 +0200 (CEST) From: Sebastian Reichel To: Sebastian Reichel , u-boot@lists.denx.de Cc: Simon Glass , Tom Rini Subject: [PATCHv2 1/2] i2c: add dm_i2c_reg_clrset Date: Thu, 15 Jul 2021 17:39:59 +0200 Message-Id: <20210715154000.92119-2-sebastian.reichel@collabora.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210715154000.92119-1-sebastian.reichel@collabora.com> References: <20210715154000.92119-1-sebastian.reichel@collabora.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 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" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean Add function to apply a bitmask to an i2c register, so that specific bits can be cleared and/or set. Suggested-by: Simon Glass Signed-off-by: Sebastian Reichel Reviewed-by: Simon Glass --- drivers/i2c/i2c-uclass.c | 15 +++++++++++++++ include/i2c.h | 14 ++++++++++++++ test/dm/i2c.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index be56785217c3..8adcdaf86411 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -245,6 +245,21 @@ int dm_i2c_reg_write(struct udevice *dev, uint offset, uint value) return dm_i2c_write(dev, offset, &val, 1); } +int dm_i2c_reg_clrset(struct udevice *dev, uint offset, u32 clr, u32 set) +{ + uint8_t val; + int ret; + + ret = dm_i2c_read(dev, offset, &val, 1); + if (ret < 0) + return ret; + + val &= ~clr; + val |= set; + + return dm_i2c_write(dev, offset, &val, 1); +} + /** * i2c_probe_chip() - probe for a chip on a bus * diff --git a/include/i2c.h b/include/i2c.h index c0fe94c1f333..4fa2222a15ae 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -242,6 +242,20 @@ int dm_i2c_reg_read(struct udevice *dev, uint offset); */ int dm_i2c_reg_write(struct udevice *dev, uint offset, unsigned int val); +/** + * dm_i2c_reg_clrset() - Apply bitmask to an I2C register + * + * Read value, apply bitmask and write modified value back to the + * given address in an I2C chip + * + * @dev: Device to use for transfer + * @offset: Address for the R/W operation + * @clr: Bitmask of bits that should be cleared + * @set: Bitmask of bits that should be set + * @return 0 on success, -ve on error + */ +int dm_i2c_reg_clrset(struct udevice *dev, uint offset, u32 clr, u32 set); + /** * dm_i2c_xfer() - Transfer messages over I2C * diff --git a/test/dm/i2c.c b/test/dm/i2c.c index d74f5f9fbc7c..74b209719560 100644 --- a/test/dm/i2c.c +++ b/test/dm/i2c.c @@ -304,3 +304,32 @@ static int dm_test_i2c_addr_offset(struct unit_test_state *uts) } DM_TEST(dm_test_i2c_addr_offset, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_i2c_reg_clrset(struct unit_test_state *uts) +{ + struct udevice *eeprom; + struct udevice *dev; + u8 buf[5]; + + ut_assertok(i2c_get_chip_for_busnum(busnum, chip, 1, &dev)); + + /* Do a transfer so we can find the emulator */ + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_assertok(uclass_first_device(UCLASS_I2C_EMUL, &eeprom)); + + /* Dummy data for the test */ + ut_assertok(dm_i2c_write(dev, 0, "\xff\x00\xff\x00\x10", 5)); + + /* Do some clrset tests */ + ut_assertok(dm_i2c_reg_clrset(dev, 0, 0xff, 0x10)); + ut_assertok(dm_i2c_reg_clrset(dev, 1, 0x00, 0x11)); + ut_assertok(dm_i2c_reg_clrset(dev, 2, 0xed, 0x00)); + ut_assertok(dm_i2c_reg_clrset(dev, 3, 0xff, 0x13)); + ut_assertok(dm_i2c_reg_clrset(dev, 4, 0x00, 0x14)); + + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_asserteq_mem("\x10\x11\x12\x13\x14", buf, sizeof(buf)); + + return 0; +} +DM_TEST(dm_test_i2c_reg_clrset, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); From patchwork Thu Jul 15 15:40:00 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sebastian Reichel X-Patchwork-Id: 1505769 X-Patchwork-Delegate: hs@denx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GQdqh5mfxz9sSs for ; Fri, 16 Jul 2021 01:40:32 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 56D7482BC7; Thu, 15 Jul 2021 17:40:18 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=fail (p=none dis=none) header.from=collabora.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Received: by phobos.denx.de (Postfix, from userid 109) id 8F81282000; Thu, 15 Jul 2021 17:40:12 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,SPF_HELO_PASS, UNPARSEABLE_RELAY autolearn=ham autolearn_force=no version=3.4.2 Received: from bhuna.collabora.co.uk (bhuna.collabora.co.uk [46.235.227.227]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 67AB1829E7 for ; Thu, 15 Jul 2021 17:40:08 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=sebastian.reichel@collabora.com Received: from [127.0.0.1] (localhost [127.0.0.1]) (Authenticated sender: sre) with ESMTPSA id E9C9E1F42025 Received: by jupiter.universe (Postfix, from userid 1000) id D69DD4800C8; Thu, 15 Jul 2021 17:40:04 +0200 (CEST) From: Sebastian Reichel To: Sebastian Reichel , u-boot@lists.denx.de Cc: Simon Glass , Tom Rini Subject: [PATCHv2 2/2] gpio: mcp230xx: Introduce new driver Date: Thu, 15 Jul 2021 17:40:00 +0200 Message-Id: <20210715154000.92119-3-sebastian.reichel@collabora.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210715154000.92119-1-sebastian.reichel@collabora.com> References: <20210715154000.92119-1-sebastian.reichel@collabora.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 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" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean Introduce driver for I2C based MCP230xx GPIO chips, which are quite common and already well supported by the Linux kernel. Reviewed-by: Simon Glass Signed-off-by: Sebastian Reichel Reviewed-by: Simon Glass --- 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 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..9f02fd42b35f --- /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 + */ + +#define LOG_CATEGORY UCLASS_GPIO + +#include +#include +#include +#include +#include +#include +#include + +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; + + 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; + + return dm_i2c_reg_clrset(dev, (reg << shift) | bank, mask, val ? mask : 0); +} + +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) +{ + bool input = !(flags & (GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE)); + bool pullup = flags & GPIOD_PULL_UP; + ulong supported_mask; + int ret; + + /* Note: active-low is ignored (handled by core) */ + supported_mask = GPIOD_ACTIVE_LOW | GPIOD_MASK_DIR | GPIOD_PULL_UP; + if (flags & ~supported_mask) { + dev_err(dev, "%s unsupported flag(s): %lx\n", __func__, flags); + return -EINVAL; + } + + ret = mcp230xx_write(dev, MCP230XX_OLAT, offset, !!(flags & GPIOD_IS_OUT_ACTIVE)); + if (ret) { + dev_err(dev, "%s failed to setup output latch: %d\n", __func__, ret); + return ret; + } + + ret = mcp230xx_write(dev, MCP230XX_GPPU, offset, pullup); + if (ret) { + dev_err(dev, "%s failed to setup pull-up: %d\n", __func__, ret); + return ret; + } + + ret = mcp230xx_write(dev, MCP230XX_IODIR, offset, input); + if (ret) { + dev_err(dev, "%s failed to setup direction: %d\n", __func__, ret); + return ret; + } + + return 0; +} + +static int mcp230xx_direction_input(struct udevice *dev, uint offset) +{ + return mcp230xx_set_flags(dev, offset, GPIOD_IS_IN); +} + +static int mcp230xx_direction_output(struct udevice *dev, uint offset, int val) +{ + int ret = mcp230xx_set_value(dev, offset, val); + if (ret < 0) { + dev_err(dev, "%s error: %d\n", __func__, ret); + return ret; + } + return mcp230xx_set_flags(dev, offset, GPIOD_IS_OUT); +} + +static int mcp230xx_get_function(struct udevice *dev, uint offset) +{ + int ret; + + ret = mcp230xx_read(dev, MCP230XX_IODIR, offset); + if (ret < 0) { + dev_err(dev, "%s error: %d\n", __func__, ret); + return ret; + } + + return ret ? GPIOF_INPUT : GPIOF_OUTPUT; +} + +static const struct dm_gpio_ops mcp230xx_ops = { + .direction_input = mcp230xx_direction_input, + .direction_output = mcp230xx_direction_output, + .get_value = mcp230xx_get_value, + .set_value = mcp230xx_set_value, + .get_function = mcp230xx_get_function, + .set_flags = mcp230xx_set_flags, + .get_flags = mcp230xx_get_flags, +}; + +static int mcp230xx_probe(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + char name[32], label[8], *str; + int addr, gpio_count, size; + const u8 *tmp; + + switch (dev_get_driver_data(dev)) { + case MCP23008: + gpio_count = 8; + break; + case MCP23017: + case MCP23018: + gpio_count = 16; + break; + default: + return -ENODEV; + } + + addr = dev_read_addr(dev); + tmp = dev_read_prop(dev, "label", &size); + if (tmp) { + memcpy(label, tmp, sizeof(label) - 1); + label[sizeof(label) - 1] = '\0'; + snprintf(name, sizeof(name), "%s@%x_", label, addr); + } else { + snprintf(name, sizeof(name), "gpio@%x_", addr); + } + + str = strdup(name); + if (!str) + return -ENOMEM; + + uc_priv->bank_name = str; + uc_priv->gpio_count = gpio_count; + + dev_dbg(dev, "%s is ready\n", str); + + return 0; +} + +static const struct udevice_id mcp230xx_ids[] = { + { .compatible = "microchip,mcp23008", .data = MCP23008, }, + { .compatible = "microchip,mcp23017", .data = MCP23017, }, + { .compatible = "microchip,mcp23018", .data = MCP23018, }, + { } +}; + +U_BOOT_DRIVER(mcp230xx) = { + .name = "mcp230xx", + .id = UCLASS_GPIO, + .ops = &mcp230xx_ops, + .probe = mcp230xx_probe, + .of_match = mcp230xx_ids, +};