From patchwork Sun Jul 9 22:30:04 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Beniamino Galvani X-Patchwork-Id: 9832115 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 07BB660317 for ; Sun, 9 Jul 2017 22:31:01 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 044B9280DE for ; Sun, 9 Jul 2017 22:31:01 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id ECDBE283C5; Sun, 9 Jul 2017 22:31:00 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED,DKIM_VALID,FREEMAIL_FROM autolearn=ham version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 2C172280DE for ; Sun, 9 Jul 2017 22:31:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=vopc3dbqDG0/ospVfIGplp2L6U6YN9CxeTWTX194HXE=; b=VZKJdCNwqWD2DiJ9Qj09Qlz4GC 8Np4dEboyPg5H2ItylhHohmtuTuP254EljRKyj9P8w0xNTJ/1bVmvFhtjAqIYySk6DcdjELm9THcv 8UTlG9suyzbcLDr8uhMuU/o6sBl/sr1r4GZ0QEtwt3z2uxisY8g62WXrBF4/BAcBppripDIk8D8Jw 8CmcJvWiqnI7CS7KfS4UbRGhY806enFLSppHoyGAgs+zWdv8d8eiEk21Xl65a3/SUhJuUSMTUoeZ/ FN5dLNeORUBXscI1DVX8Wj+txQs7ZwZJhuUiZQcubB6t3XjbQfBD+63A6nWKgfyMXD+ggDrTybIFx tkXMJ9uQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1dUKjD-0006ep-Pq; Sun, 09 Jul 2017 22:30:59 +0000 Received: from mail-wr0-x242.google.com ([2a00:1450:400c:c0c::242]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1dUKj7-0006Yr-O9 for linux-amlogic@lists.infradead.org; Sun, 09 Jul 2017 22:30:57 +0000 Received: by mail-wr0-x242.google.com with SMTP id x23so21108998wrb.0 for ; Sun, 09 Jul 2017 15:30:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=+wVBWBjpXU+5nvoYosBd/C/YSJQT7IokKcSt0O8WsFs=; b=Vbh8RtpMx3c1+GSI5yG1J+NEUGENZyxvFtiRU7u/8HMC3wLsMHTZoAl2HD/29t1SXr BxAgpbPtNiyieEuCAQ3RXPoltiW8OH5O2MlhKjI1neKrXRvSIFKCYPpl02lcmnzBykM9 jgiarHrSxOGW5+NJqHOsahq5wjNjqpZKvTN44NR/v72lzlhPPLA+zgDxjYzv8qPCqYAe vjtT9fvtAN2XQgjENhuosx44WhYEprhwHlazEAAYDCMaFXHDSIJUPYrlclQiINjbV4kJ Gqd0a+XHQ8POiRkT2ySGWKludUXWGwO9VdJU+CTpZ9WDjczQWR0r83+9KzaDvRZQ+8Hx Sfeg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=+wVBWBjpXU+5nvoYosBd/C/YSJQT7IokKcSt0O8WsFs=; b=O+5RZBAFJ4cZvJXUHsPOEa55jHGxjmh5bZGrAq0/CTk4OtWQAElga7M1QvKBLZ2SnN YGZRoTu0nia92zs5ETNJqUlLfltCeZ5Pxn+1a9G9gtvxuxChI2lhCPLn5UjmLihLPxqN kbNeA2GCttZoFnsnhyLeMH9bOdiCRBpK9BQJ85CNjqvsNyWhgi9UBc0yMCCPFTGRo6+a 8O8xAfAVl5bb6EUFJaADbS2lh1ZXdekrwHKexi7/zRmtcmmISHZOOkrJxE2PKVtPQo9f K5a140EP0WSR+6Oobtdm2MrgGM2jOeW4+g4AIq9gpxSn1pbrs1a1oaVHKqB8EKh1P+0A uLBg== X-Gm-Message-State: AIVw112Xhu3SprVdBYQfpzvhtSXTxmqUTH696Cah0u8vxCUmcIwyTBnk w5u4+Mm5T99agw== X-Received: by 10.28.32.5 with SMTP id g5mr5695367wmg.70.1499639431770; Sun, 09 Jul 2017 15:30:31 -0700 (PDT) Received: from sark.homenet.telecomitalia.it (host230-116-dynamic.211-62-r.retail.telecomitalia.it. [62.211.116.230]) by smtp.gmail.com with ESMTPSA id u80sm10697089wrb.1.2017.07.09.15.30.30 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 09 Jul 2017 15:30:31 -0700 (PDT) From: Beniamino Galvani To: u-boot@lists.denx.de Subject: [PATCH v2 2/4] pinctrl: meson: add GPIO support Date: Mon, 10 Jul 2017 00:30:04 +0200 Message-Id: <20170709223006.3998-3-b.galvani@gmail.com> X-Mailer: git-send-email 2.9.3 In-Reply-To: <20170709223006.3998-1-b.galvani@gmail.com> References: <20170709223006.3998-1-b.galvani@gmail.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170709_153054_092252_A115194E X-CRM114-Status: GOOD ( 22.47 ) X-BeenThere: linux-amlogic@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Beniamino Galvani , Tom Rini , Simon Glass , linux-amlogic@lists.infradead.org, Albert Aribaud MIME-Version: 1.0 Sender: "linux-amlogic" Errors-To: linux-amlogic-bounces+patchwork-linux-amlogic=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP This commit adds GPIO support to the Amlogic Meson pin controller driver, based on code from Linux kernel. Reviewed-by: Simon Glass Signed-off-by: Beniamino Galvani --- arch/arm/include/asm/arch-meson/gpio.h | 11 ++ drivers/pinctrl/meson/pinctrl-meson-gxbb.c | 21 ++++ drivers/pinctrl/meson/pinctrl-meson.c | 167 ++++++++++++++++++++++++++++- drivers/pinctrl/meson/pinctrl-meson.h | 63 +++++++++++ 4 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 arch/arm/include/asm/arch-meson/gpio.h diff --git a/arch/arm/include/asm/arch-meson/gpio.h b/arch/arm/include/asm/arch-meson/gpio.h new file mode 100644 index 0000000..7079ab3 --- /dev/null +++ b/arch/arm/include/asm/arch-meson/gpio.h @@ -0,0 +1,11 @@ +/* + * (C) Copyright 2017 - Beniamino Galvani + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ASM_ARCH_MESON_GPIO_H +#define __ASM_ARCH_MESON_GPIO_H + + +#endif /* __ASM_ARCH_MESON_GPIO_H */ diff --git a/drivers/pinctrl/meson/pinctrl-meson-gxbb.c b/drivers/pinctrl/meson/pinctrl-meson-gxbb.c index 2fa840c..87c9912 100644 --- a/drivers/pinctrl/meson/pinctrl-meson-gxbb.c +++ b/drivers/pinctrl/meson/pinctrl-meson-gxbb.c @@ -391,14 +391,33 @@ static struct meson_pmx_func meson_gxbb_aobus_functions[] = { FUNCTION(i2c_slave_ao), }; +static struct meson_bank meson_gxbb_periphs_banks[] = { + /* name first last pullen pull dir out in */ + BANK("X", PIN(GPIOX_0, EE_OFF), PIN(GPIOX_22, EE_OFF), 4, 0, 4, 0, 12, 0, 13, 0, 14, 0), + BANK("Y", PIN(GPIOY_0, EE_OFF), PIN(GPIOY_16, EE_OFF), 1, 0, 1, 0, 3, 0, 4, 0, 5, 0), + BANK("DV", PIN(GPIODV_0, EE_OFF), PIN(GPIODV_29, EE_OFF), 0, 0, 0, 0, 0, 0, 1, 0, 2, 0), + BANK("H", PIN(GPIOH_0, EE_OFF), PIN(GPIOH_3, EE_OFF), 1, 20, 1, 20, 3, 20, 4, 20, 5, 20), + BANK("Z", PIN(GPIOZ_0, EE_OFF), PIN(GPIOZ_15, EE_OFF), 3, 0, 3, 0, 9, 0, 10, 0, 11, 0), + BANK("CARD", PIN(CARD_0, EE_OFF), PIN(CARD_6, EE_OFF), 2, 20, 2, 20, 6, 20, 7, 20, 8, 20), + BANK("BOOT", PIN(BOOT_0, EE_OFF), PIN(BOOT_17, EE_OFF), 2, 0, 2, 0, 6, 0, 7, 0, 8, 0), + BANK("CLK", PIN(GPIOCLK_0, EE_OFF), PIN(GPIOCLK_3, EE_OFF), 3, 28, 3, 28, 9, 28, 10, 28, 11, 28), +}; + +static struct meson_bank meson_gxbb_aobus_banks[] = { + /* name first last pullen pull dir out in */ + BANK("AO", PIN(GPIOAO_0, 0), PIN(GPIOAO_13, 0), 0, 0, 0, 16, 0, 0, 0, 16, 1, 0), +}; + struct meson_pinctrl_data meson_gxbb_periphs_pinctrl_data = { .name = "periphs-banks", .pin_base = 14, .groups = meson_gxbb_periphs_groups, .funcs = meson_gxbb_periphs_functions, + .banks = meson_gxbb_periphs_banks, .num_pins = 120, .num_groups = ARRAY_SIZE(meson_gxbb_periphs_groups), .num_funcs = ARRAY_SIZE(meson_gxbb_periphs_functions), + .num_banks = ARRAY_SIZE(meson_gxbb_periphs_banks), }; struct meson_pinctrl_data meson_gxbb_aobus_pinctrl_data = { @@ -406,9 +425,11 @@ struct meson_pinctrl_data meson_gxbb_aobus_pinctrl_data = { .pin_base = 0, .groups = meson_gxbb_aobus_groups, .funcs = meson_gxbb_aobus_functions, + .banks = meson_gxbb_aobus_banks, .num_pins = 14, .num_groups = ARRAY_SIZE(meson_gxbb_aobus_groups), .num_funcs = ARRAY_SIZE(meson_gxbb_aobus_functions), + .num_banks = ARRAY_SIZE(meson_gxbb_aobus_banks), }; static const struct udevice_id meson_gxbb_pinctrl_match[] = { diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c index 6281f52..a860200 100644 --- a/drivers/pinctrl/meson/pinctrl-meson.c +++ b/drivers/pinctrl/meson/pinctrl-meson.c @@ -6,11 +6,14 @@ #include #include +#include +#include #include #include #include #include #include +#include #include "pinctrl-meson.h" @@ -117,6 +120,143 @@ const struct pinctrl_ops meson_pinctrl_ops = { .set_state = pinctrl_generic_set_state, }; +static int meson_gpio_calc_reg_and_bit(struct udevice *dev, unsigned int offset, + enum meson_reg_type reg_type, + unsigned int *reg, unsigned int *bit) +{ + struct meson_pinctrl *priv = dev_get_priv(dev->parent); + struct meson_bank *bank = NULL; + struct meson_reg_desc *desc; + unsigned int pin; + int i; + + pin = priv->data->pin_base + offset; + + for (i = 0; i < priv->data->num_banks; i++) { + if (pin >= priv->data->banks[i].first && + pin <= priv->data->banks[i].last) { + bank = &priv->data->banks[i]; + break; + } + } + + if (!bank) + return -EINVAL; + + desc = &bank->regs[reg_type]; + *reg = desc->reg * 4; + *bit = desc->bit + pin - bank->first; + + return 0; +} + +static int meson_gpio_get(struct udevice *dev, unsigned int offset) +{ + struct meson_pinctrl *priv = dev_get_priv(dev->parent); + unsigned int reg, bit; + int ret; + + ret = meson_gpio_calc_reg_and_bit(dev, offset, REG_IN, ®, &bit); + if (ret) + return ret; + + return !!(readl(priv->reg_gpio + reg) & BIT(bit)); +} + +static int meson_gpio_set(struct udevice *dev, unsigned int offset, int value) +{ + struct meson_pinctrl *priv = dev_get_priv(dev->parent); + unsigned int reg, bit; + int ret; + + ret = meson_gpio_calc_reg_and_bit(dev, offset, REG_OUT, ®, &bit); + if (ret) + return ret; + + clrsetbits_le32(priv->reg_gpio + reg, BIT(bit), value ? BIT(bit) : 0); + + return 0; +} + +static int meson_gpio_get_direction(struct udevice *dev, unsigned int offset) +{ + struct meson_pinctrl *priv = dev_get_priv(dev->parent); + unsigned int reg, bit, val; + int ret; + + ret = meson_gpio_calc_reg_and_bit(dev, offset, REG_DIR, ®, &bit); + if (ret) + return ret; + + val = readl(priv->reg_gpio + reg); + + return (val & BIT(bit)) ? GPIOF_INPUT : GPIOF_OUTPUT; +} + +static int meson_gpio_direction_input(struct udevice *dev, unsigned int offset) +{ + struct meson_pinctrl *priv = dev_get_priv(dev->parent); + unsigned int reg, bit; + int ret; + + ret = meson_gpio_calc_reg_and_bit(dev, offset, REG_DIR, ®, &bit); + if (ret) + return ret; + + clrsetbits_le32(priv->reg_gpio + reg, BIT(bit), 1); + + return 0; +} + +static int meson_gpio_direction_output(struct udevice *dev, + unsigned int offset, int value) +{ + struct meson_pinctrl *priv = dev_get_priv(dev->parent); + unsigned int reg, bit; + int ret; + + ret = meson_gpio_calc_reg_and_bit(dev, offset, REG_DIR, ®, &bit); + if (ret) + return ret; + + clrsetbits_le32(priv->reg_gpio + reg, BIT(bit), 0); + + ret = meson_gpio_calc_reg_and_bit(dev, offset, REG_OUT, ®, &bit); + if (ret) + return ret; + + clrsetbits_le32(priv->reg_gpio + reg, BIT(bit), value ? BIT(bit) : 0); + + return 0; +} + +static int meson_gpio_probe(struct udevice *dev) +{ + struct meson_pinctrl *priv = dev_get_priv(dev->parent); + struct gpio_dev_priv *uc_priv; + + uc_priv = dev_get_uclass_priv(dev); + uc_priv->bank_name = priv->data->name; + uc_priv->gpio_count = priv->data->num_pins; + + return 0; +} + +static const struct dm_gpio_ops meson_gpio_ops = { + .set_value = meson_gpio_set, + .get_value = meson_gpio_get, + .get_function = meson_gpio_get_direction, + .direction_input = meson_gpio_direction_input, + .direction_output = meson_gpio_direction_output, +}; + +static struct driver meson_gpio_driver = { + .name = "meson-gpio", + .id = UCLASS_GPIO, + .probe = meson_gpio_probe, + .ops = &meson_gpio_ops, +}; + static fdt_addr_t parse_address(int offset, const char *name, int na, int ns) { int index, len = 0; @@ -138,9 +278,12 @@ static fdt_addr_t parse_address(int offset, const char *name, int na, int ns) int meson_pinctrl_probe(struct udevice *dev) { struct meson_pinctrl *priv = dev_get_priv(dev); + struct uclass_driver *drv; + struct udevice *gpio_dev; fdt_addr_t addr; int node, gpio = -1, len; int na, ns; + char *name; na = fdt_address_cells(gd->fdt_blob, dev_of_offset(dev->parent)); if (na < 1) { @@ -168,12 +311,32 @@ int meson_pinctrl_probe(struct udevice *dev) addr = parse_address(gpio, "mux", na, ns); if (addr == FDT_ADDR_T_NONE) { - debug("mux not found\n"); + debug("mux address not found\n"); return -EINVAL; } - priv->reg_mux = (void __iomem *)addr; + + addr = parse_address(gpio, "gpio", na, ns); + if (addr == FDT_ADDR_T_NONE) { + debug("gpio address not found\n"); + return -EINVAL; + } + priv->reg_gpio = (void __iomem *)addr; priv->data = (struct meson_pinctrl_data *)dev_get_driver_data(dev); + /* Lookup GPIO driver */ + drv = lists_uclass_lookup(UCLASS_GPIO); + if (!drv) { + puts("Cannot find GPIO driver\n"); + return -ENOENT; + } + + name = calloc(1, 32); + sprintf(name, "meson-gpio"); + + /* Create child device UCLASS_GPIO and bind it */ + device_bind(dev, &meson_gpio_driver, name, NULL, gpio, &gpio_dev); + dev_set_of_offset(gpio_dev, gpio); + return 0; } diff --git a/drivers/pinctrl/meson/pinctrl-meson.h b/drivers/pinctrl/meson/pinctrl-meson.h index 4127a60..90d2369 100644 --- a/drivers/pinctrl/meson/pinctrl-meson.h +++ b/drivers/pinctrl/meson/pinctrl-meson.h @@ -28,15 +28,64 @@ struct meson_pinctrl_data { const char *name; struct meson_pmx_group *groups; struct meson_pmx_func *funcs; + struct meson_bank *banks; unsigned int pin_base; unsigned int num_pins; unsigned int num_groups; unsigned int num_funcs; + unsigned int num_banks; }; struct meson_pinctrl { struct meson_pinctrl_data *data; void __iomem *reg_mux; + void __iomem *reg_gpio; +}; + +/** + * struct meson_reg_desc - a register descriptor + * + * @reg: register offset in the regmap + * @bit: bit index in register + * + * The structure describes the information needed to control pull, + * pull-enable, direction, etc. for a single pin + */ +struct meson_reg_desc { + unsigned int reg; + unsigned int bit; +}; + +/** + * enum meson_reg_type - type of registers encoded in @meson_reg_desc + */ +enum meson_reg_type { + REG_PULLEN, + REG_PULL, + REG_DIR, + REG_OUT, + REG_IN, + NUM_REG, +}; + +/** + * struct meson bank + * + * @name: bank name + * @first: first pin of the bank + * @last: last pin of the bank + * @regs: array of register descriptors + * + * A bank represents a set of pins controlled by a contiguous set of + * bits in the domain registers. The structure specifies which bits in + * the regmap control the different functionalities. Each member of + * the @regs array refers to the first pin of the bank. + */ +struct meson_bank { + const char *name; + unsigned int first; + unsigned int last; + struct meson_reg_desc regs[NUM_REG]; }; #define PIN(x, b) (b + x) @@ -65,6 +114,20 @@ struct meson_pinctrl { .num_groups = ARRAY_SIZE(fn ## _groups), \ } +#define BANK(n, f, l, per, peb, pr, pb, dr, db, or, ob, ir, ib) \ + { \ + .name = n, \ + .first = f, \ + .last = l, \ + .regs = { \ + [REG_PULLEN] = { per, peb }, \ + [REG_PULL] = { pr, pb }, \ + [REG_DIR] = { dr, db }, \ + [REG_OUT] = { or, ob }, \ + [REG_IN] = { ir, ib }, \ + }, \ + } + #define MESON_PIN(x, b) PINCTRL_PIN(PIN(x, b), #x) extern const struct pinctrl_ops meson_pinctrl_ops;