From patchwork Thu Feb 12 17:37:03 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Albert ARIBAUD (3ADEV)" X-Patchwork-Id: 439291 X-Patchwork-Delegate: albert.aribaud@free.fr 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 C4BE514008F for ; Fri, 13 Feb 2015 04:38:47 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id B9ABFA7485; Thu, 12 Feb 2015 18:38:36 +0100 (CET) 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 2XbHWUcu3k3o; Thu, 12 Feb 2015 18:38:36 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 229FCA743C; Thu, 12 Feb 2015 18:38:36 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 87CA5A7474 for ; Thu, 12 Feb 2015 18:38:33 +0100 (CET) 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 pphxhs0mkLK0 for ; Thu, 12 Feb 2015 18:38:33 +0100 (CET) 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 smtp6-g21.free.fr (smtp6-g21.free.fr [212.27.42.6]) by theia.denx.de (Postfix) with ESMTPS id 06010A747A for ; Thu, 12 Feb 2015 18:38:30 +0100 (CET) Received: from localhost.localdomain (unknown [IPv6:2a01:e35:2eb9:21:8d90:72c7:bffa:cc05]) (Authenticated sender: aribaud.smtp) by smtp6-g21.free.fr (Postfix) with ESMTPSA id 9A74D82349; Thu, 12 Feb 2015 18:38:16 +0100 (CET) From: "Albert ARIBAUD (3ADEV)" To: u-boot@lists.denx.de Date: Thu, 12 Feb 2015 18:37:03 +0100 Message-Id: <1423762636-18353-5-git-send-email-albert.aribaud@3adev.fr> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1423762636-18353-4-git-send-email-albert.aribaud@3adev.fr> References: <1423762636-18353-1-git-send-email-albert.aribaud@3adev.fr> <1423762636-18353-2-git-send-email-albert.aribaud@3adev.fr> <1423762636-18353-3-git-send-email-albert.aribaud@3adev.fr> <1423762636-18353-4-git-send-email-albert.aribaud@3adev.fr> Cc: Steve Rae , "Albert ARIBAUD \(3ADEV\)" , Ian Campbell , Darwin Rambo Subject: [U-Boot] [PATCH v2 4/8] lpc32xx: add GPIO support 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: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" This driver only supports Driver Model, not legacy model. Signed-off-by: Albert ARIBAUD (3ADEV) --- Changes in v2: - move from legacy to Driver Model support arch/arm/cpu/arm926ejs/lpc32xx/devices.c | 5 + arch/arm/include/asm/arch-lpc32xx/gpio.h | 43 +++++ drivers/gpio/Makefile | 1 + drivers/gpio/lpc32xx_gpio.c | 268 +++++++++++++++++++++++++++++++ 4 files changed, 317 insertions(+) create mode 100644 arch/arm/include/asm/arch-lpc32xx/gpio.h create mode 100644 drivers/gpio/lpc32xx_gpio.c diff --git a/arch/arm/cpu/arm926ejs/lpc32xx/devices.c b/arch/arm/cpu/arm926ejs/lpc32xx/devices.c index 81b53ea..a407098 100644 --- a/arch/arm/cpu/arm926ejs/lpc32xx/devices.c +++ b/arch/arm/cpu/arm926ejs/lpc32xx/devices.c @@ -9,6 +9,7 @@ #include #include #include +#include static struct clk_pm_regs *clk = (struct clk_pm_regs *)CLK_PM_BASE; static struct uart_ctrl_regs *ctrl = (struct uart_ctrl_regs *)UART_CTRL_BASE; @@ -61,3 +62,7 @@ void lpc32xx_i2c_init(unsigned int devnum) ctrl |= CLK_I2C2_ENABLE; writel(ctrl, &clk->i2cclk_ctrl); } + +U_BOOT_DEVICE(lpc32xx_gpios) = { + .name = "gpio_lpc32xx" +}; diff --git a/arch/arm/include/asm/arch-lpc32xx/gpio.h b/arch/arm/include/asm/arch-lpc32xx/gpio.h new file mode 100644 index 0000000..3bd94e3 --- /dev/null +++ b/arch/arm/include/asm/arch-lpc32xx/gpio.h @@ -0,0 +1,43 @@ +/* + * LPC32xx GPIO interface + * + * (C) Copyright 2014 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/** + * GPIO Register map for LPC32xx + */ + +struct gpio_regs { + u32 p3_inp_state; + u32 p3_outp_set; + u32 p3_outp_clr; + u32 p3_outp_state; + /* Watch out! the following are shared between p2 and p3 */ + u32 p2_p3_dir_set; + u32 p2_p3_dir_clr; + u32 p2_p3_dir_state; + /* Now back to 'one register for one port' */ + u32 p2_inp_state; + u32 p2_outp_set; + u32 p2_outp_clr; + u32 reserved1[6]; + u32 p0_inp_state; + u32 p0_outp_set; + u32 p0_outp_clr; + u32 p0_outp_state; + u32 p0_dir_set; + u32 p0_dir_clr; + u32 p0_dir_state; + u32 reserved2; + u32 p1_inp_state; + u32 p1_outp_set; + u32 p1_outp_clr; + u32 p1_outp_state; + u32 p1_dir_set; + u32 p1_dir_clr; + u32 p1_dir_state; +}; diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index aa11f15..559894a 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -37,3 +37,4 @@ obj-$(CONFIG_ADI_GPIO2) += adi_gpio2.o obj-$(CONFIG_TCA642X) += tca642x.o oby-$(CONFIG_SX151X) += sx151x.o obj-$(CONFIG_SUNXI_GPIO) += sunxi_gpio.o +obj-$(CONFIG_LPC32XX_GPIO) += lpc32xx_gpio.o diff --git a/drivers/gpio/lpc32xx_gpio.c b/drivers/gpio/lpc32xx_gpio.c new file mode 100644 index 0000000..861975e --- /dev/null +++ b/drivers/gpio/lpc32xx_gpio.c @@ -0,0 +1,268 @@ +/* + * LPC32xxGPIO driver + * + * (C) Copyright 2014 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/** + * We only support driver model + */ +#ifndef CONFIG_DM_GPIO +#error Please enable Driver Model GPIO in your target configuration. +#endif + +#include +#include +#include +#include +#include +#include + +/** + * LPC32xx GPIOs work in banks but are non-homogeneous: + * - each bank holds a different number of GPIOs + * - some GPIOs are input/ouput, some input only, some output only; + * - some GPIOs have different meanings as an input and as an output; + * - some GPIOs are controlled on a given port and bit index, but + * read on another one. +* + * In order to keep this code simple, GPIOS are considered here as + * homogeneous and linear, from 0 to 127. + * + * ** WARNING ** + * + * Client code is responsible for properly using valid GPIO numbers, + * including cases where a single physical GPIO has differing numbers + * for setting its direction, reading it and/or writing to it. + */ + +#define LPC32XX_GPIOS 128 + +static struct gpio_regs *regs = (struct gpio_regs *)GPIO_BASE; + +/** + * We have 4 GPIO ports of 32 bits each + */ + +#define MAX_GPIO 128 + +#define GPIO_TO_PORT(gpio) ((gpio / 32) & 3) +#define GPIO_TO_RANK(gpio) (gpio % 32) +#define GPIO_TO_MASK(gpio) (1 << (gpio % 32)) + +/** + * Array of current GPIO functions. Allocated as unsigned chars to + * limit memory consumption. + */ + +static signed char lpc32xx_function[LPC32XX_GPIOS]; + +/** + * Configure a GPIO number 'offset' as input + */ + +static int lpc32xx_gpio_direction_input(struct udevice *dev, unsigned offset) +{ + int port, mask; + + port = GPIO_TO_PORT(offset); + mask = GPIO_TO_MASK(offset); + + switch (port) { + case 0: + writel(mask, ®s->p0_dir_clr); + break; + case 1: + writel(mask, ®s->p1_dir_clr); + break; + case 2: + /* ports 2 and 3 share a common direction */ + case 3: + writel(mask, ®s->p2_p3_dir_clr); + break; + default: + return -1; + } + + lpc32xx_function[offset] = GPIOF_INPUT; + + return 0; +} + +/** + * Get the value of a GPIO + */ + +static int lpc32xx_gpio_get_value(struct udevice *dev, unsigned offset) +{ + int port, rank, mask, value; + + port = GPIO_TO_PORT(offset); + + switch (port) { + case 0: + value = readl(®s->p0_inp_state); + break; + case 1: + value = readl(®s->p1_inp_state); + break; + case 2: + value = readl(®s->p2_inp_state); + break; + case 3: + value = readl(®s->p3_inp_state); + break; + default: + return -1; + } + + rank = GPIO_TO_RANK(offset); + mask = GPIO_TO_MASK(offset); + + return (value & mask) >> rank; +} + +/** + * Set a GPIO + */ + +static int gpio_set(unsigned gpio) +{ + int port, mask; + + port = GPIO_TO_PORT(gpio); + mask = GPIO_TO_MASK(gpio); + + switch (port) { + case 0: + writel(mask, ®s->p0_outp_set); + break; + case 1: + writel(mask, ®s->p1_outp_set); + break; + case 2: + writel(mask, ®s->p2_outp_set); + break; + case 3: + writel(mask, ®s->p3_outp_set); + break; + default: + return -1; + } + return 0; +} + +/** + * Clear a GPIO + */ + +static int gpio_clr(unsigned gpio) +{ + int port, mask; + + port = GPIO_TO_PORT(gpio); + mask = GPIO_TO_MASK(gpio); + + switch (port) { + case 0: + writel(mask, ®s->p0_outp_clr); + break; + case 1: + writel(mask, ®s->p1_outp_clr); + break; + case 2: + writel(mask, ®s->p2_outp_clr); + break; + case 3: + writel(mask, ®s->p3_outp_clr); + break; + default: + return -1; + } + return 0; +} + +/** + * Set the value of a GPIO + */ + +static int lpc32xx_gpio_set_value(struct udevice *dev, unsigned offset, + int value) +{ + if (value) + return gpio_set(offset); + else + return gpio_clr(offset); +} + +/** + * Configure a GPIO number 'offset' as output with given initial value. + */ + +static int lpc32xx_gpio_direction_output(struct udevice *dev, unsigned offset, + int value) +{ + int port, mask; + + port = GPIO_TO_PORT(offset); + mask = GPIO_TO_MASK(offset); + + switch (port) { + case 0: + writel(mask, ®s->p0_dir_set); + break; + case 1: + writel(mask, ®s->p1_dir_set); + break; + case 2: + /* ports 2 and 3 share a common direction */ + case 3: + writel(mask, ®s->p2_p3_dir_set); + break; + default: + return -1; + } + + lpc32xx_function[offset] = GPIOF_OUTPUT; + + return lpc32xx_gpio_set_value(dev, offset, value); +} + +static int lpc32xx_gpio_get_function(struct udevice *dev, unsigned offset) +{ + return lpc32xx_function[offset]; +} + +static const struct dm_gpio_ops gpio_lpc32xx_ops = { + .direction_input = lpc32xx_gpio_direction_input, + .direction_output = lpc32xx_gpio_direction_output, + .get_value = lpc32xx_gpio_get_value, + .set_value = lpc32xx_gpio_set_value, + .get_function = lpc32xx_gpio_get_function, +}; + +static int lpc32xx_gpio_probe(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev->uclass_priv; + + if (dev->of_offset == -1) { + /* Tell the uclass how many GPIOs we have */ + uc_priv->gpio_count = LPC32XX_GPIOS; + } + + /* all GPIO functions are unknown until requested */ + memset(lpc32xx_function, GPIOF_UNKNOWN, LPC32XX_GPIOS); + + return 0; +} + +U_BOOT_DRIVER(gpio_lpc32xx) = { + .name = "gpio_lpc32xx", + .id = UCLASS_GPIO, + .ops = &gpio_lpc32xx_ops, + .probe = lpc32xx_gpio_probe, + .priv_auto_alloc_size = 0, +};