From patchwork Fri Jan 16 07:19:16 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: 429749 X-Patchwork-Delegate: trini@ti.com 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 0AB6E1401DA for ; Fri, 16 Jan 2015 18:37:45 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 6D1804B661; Fri, 16 Jan 2015 08:37: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 PtV8UP+9vfIK; Fri, 16 Jan 2015 08:37:33 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id F40174B669; Fri, 16 Jan 2015 08:37:10 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id AF30A4B616 for ; Fri, 16 Jan 2015 08:20:27 +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 h7WujfqTHlQs for ; Fri, 16 Jan 2015 08:20:27 +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 smtp2-g21.free.fr (smtp2-g21.free.fr [212.27.42.2]) by theia.denx.de (Postfix) with ESMTPS id 7FA7F4B615 for ; Fri, 16 Jan 2015 08:20:27 +0100 (CET) Received: from localhost.localdomain (unknown [IPv6:2a01:e35:2eb9:21:d504:b12c:7b53:5149]) (Authenticated sender: aribaud.smtp) by smtp2-g21.free.fr (Postfix) with ESMTPSA id 604B54B01B9; Fri, 16 Jan 2015 08:17:56 +0100 (CET) From: "Albert ARIBAUD (3ADEV)" To: u-boot@lists.denx.de Date: Fri, 16 Jan 2015 08:19:16 +0100 Message-Id: <1421392760-21810-5-git-send-email-albert.aribaud@3adev.fr> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1421392760-21810-4-git-send-email-albert.aribaud@3adev.fr> References: <1421392760-21810-1-git-send-email-albert.aribaud@3adev.fr> <1421392760-21810-2-git-send-email-albert.aribaud@3adev.fr> <1421392760-21810-3-git-send-email-albert.aribaud@3adev.fr> <1421392760-21810-4-git-send-email-albert.aribaud@3adev.fr> X-Mailman-Approved-At: Fri, 16 Jan 2015 08:37:02 +0100 Cc: Ian Campbell , "Albert ARIBAUD \(3ADEV\)" , Tim Kryger , Tomas Hlavacek , Darwin Rambo Subject: [U-Boot] [PATCH v1 4/8] lpc32xx: add GPIO support X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.13 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de Signed-off-by: Albert ARIBAUD (3ADEV) --- arch/arm/include/asm/arch-lpc32xx/gpio.h | 43 ++++++ drivers/gpio/Makefile | 1 + drivers/gpio/lpc32xx_gpio.c | 223 +++++++++++++++++++++++++++++++ 3 files changed, 267 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/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..0c08b00 --- /dev/null +++ b/drivers/gpio/lpc32xx_gpio.c @@ -0,0 +1,223 @@ +/* + * LPC32xxGPIO driver + * + * (C) Copyright 2014 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#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 reading back a GPIO is done on a different + * register and bit than writing it. + */ + +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)) + +/** + * GPIO requesting and freeing are not implemented + */ + +int gpio_request(unsigned gpio, const char *label) +{ + debug("%s: GPIO %d requested as \"%s\"\n", __func__, gpio, label); + return 0; +} + +int gpio_free(unsigned gpio) +{ + debug("%s: GPIO %d freed\n", __func__, gpio); + return 0; +} + +/** + * Configure a GPIO as input + */ + +int gpio_direction_input(unsigned gpio) +{ + int port, mask; + + port = GPIO_TO_PORT(gpio); + mask = GPIO_TO_MASK(gpio); + + 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; + } + return 0; +} + +/** + * Configure a GPIO as output + */ + +int gpio_direction_output(unsigned gpio, int value) +{ + int port, mask; + + port = GPIO_TO_PORT(gpio); + mask = GPIO_TO_MASK(gpio); + + 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; + } + return 0; +} + +/** + * Get the value of a GPIO + */ + +int gpio_get_value(unsigned gpio) +{ + int port, rank, mask, value; + + port = GPIO_TO_PORT(gpio); + + 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(gpio); + mask = GPIO_TO_MASK(gpio); + + 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 + */ + +int gpio_set_value(unsigned gpio, int value) +{ + if (value) + return gpio_set(gpio); + else + return gpio_clr(gpio); +}