diff options
author | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
---|---|---|
committer | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/drivers/gpio/lpc32xx_gpio.c | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/drivers/gpio/lpc32xx_gpio.c')
-rw-r--r-- | roms/u-boot/drivers/gpio/lpc32xx_gpio.c | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/gpio/lpc32xx_gpio.c b/roms/u-boot/drivers/gpio/lpc32xx_gpio.c new file mode 100644 index 000000000..de66c765d --- /dev/null +++ b/roms/u-boot/drivers/gpio/lpc32xx_gpio.c @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * LPC32xxGPIO driver + * + * (C) Copyright 2014 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr> + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch-lpc32xx/cpu.h> +#include <asm/arch-lpc32xx/gpio.h> +#include <asm-generic/gpio.h> +#include <dm.h> + +/** + * 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 159. + * + * ** WARNING #1 ** + * + * 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. + * + * ** WARNING #2 ** + * + * Please read NOTE in description of lpc32xx_gpio_get_function(). + */ + +#define LPC32XX_GPIOS 160 + +struct lpc32xx_gpio_priv { + struct gpio_regs *regs; + /* GPIO FUNCTION: SEE WARNING #2 */ + signed char function[LPC32XX_GPIOS]; +}; + +/** + * We have 4 GPIO ports of 32 bits each + * + * Port mapping offset (32 bits each): + * - Port 0: 0 + * - Port 1: 32 + * - Port 2: 64 + * - Port 3: GPO / GPIO (output): 96 + * - Port 3: GPI: 128 + */ + +#define MAX_GPIO 160 + +#define GPIO_TO_PORT(gpio) ((gpio / 32) & 7) +#define GPIO_TO_RANK(gpio) (gpio % 32) +#define GPIO_TO_MASK(gpio) (1 << (gpio % 32)) + +/** + * Configure a GPIO number 'offset' as input + */ + +static int lpc32xx_gpio_direction_input(struct udevice *dev, unsigned offset) +{ + int port, mask; + struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev); + struct gpio_regs *regs = gpio_priv->regs; + + 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 */ + writel(mask, ®s->p2_p3_dir_clr); + break; + case 3: + /* Setup direction only for GPIO_xx. */ + if ((mask >= 25) && (mask <= 30)) + writel(mask, ®s->p2_p3_dir_clr); + break; + case 4: + /* GPI_xx; nothing to do. */ + break; + default: + return -1; + } + + /* GPIO FUNCTION: SEE WARNING #2 */ + gpio_priv->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; + struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev); + struct gpio_regs *regs = gpio_priv->regs; + + 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: + /* Read GPO_xx and GPIO_xx (as output) using p3_outp_state. */ + value = readl(®s->p3_outp_state); + break; + case 4: + /* Read GPI_xx and GPIO_xx (as input) using p3_inp_state. */ + 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(struct udevice *dev, unsigned gpio) +{ + int port, mask; + struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev); + struct gpio_regs *regs = gpio_priv->regs; + + 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; + case 4: + /* GPI_xx; invalid. */ + default: + return -1; + } + return 0; +} + +/** + * Clear a GPIO + */ + +static int gpio_clr(struct udevice *dev, unsigned gpio) +{ + int port, mask; + struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev); + struct gpio_regs *regs = gpio_priv->regs; + + 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; + case 4: + /* GPI_xx; invalid. */ + 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(dev, offset); + else + return gpio_clr(dev, 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; + struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev); + struct gpio_regs *regs = gpio_priv->regs; + + 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 */ + writel(mask, ®s->p2_p3_dir_set); + break; + case 3: + /* Setup direction only for GPIO_xx. */ + if ((mask >= 25) && (mask <= 30)) + writel(mask, ®s->p2_p3_dir_set); + break; + case 4: + /* GPI_xx; invalid. */ + default: + return -1; + } + + /* GPIO FUNCTION: SEE WARNING #2 */ + gpio_priv->function[offset] = GPIOF_OUTPUT; + + return lpc32xx_gpio_set_value(dev, offset, value); +} + +/** + * GPIO functions are supposed to be computed from their current + * configuration, but that's way too complicated in LPC32XX. A simpler + * approach is used, where the GPIO functions are cached in an array. + * When the GPIO is in use, its function is either "input" or "output" + * depending on its direction, otherwise its function is "unknown". + * + * ** NOTE ** + * + * THIS APPROACH WAS CHOSEN DU TO THE COMPLEX NATURE OF THE LPC32XX + * GPIOS; DO NOT TAKE THIS AS AN EXAMPLE FOR NEW CODE. + */ + +static int lpc32xx_gpio_get_function(struct udevice *dev, unsigned offset) +{ + struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev); + return gpio_priv->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 lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + if (dev_of_offset(dev) == -1) { + /* Tell the uclass how many GPIOs we have */ + uc_priv->gpio_count = LPC32XX_GPIOS; + } + + /* set base address for GPIO registers */ + gpio_priv->regs = (struct gpio_regs *)GPIO_BASE; + + /* all GPIO functions are unknown until requested */ + /* GPIO FUNCTION: SEE WARNING #2 */ + memset(gpio_priv->function, GPIOF_UNKNOWN, sizeof(gpio_priv->function)); + + return 0; +} + +U_BOOT_DRIVER(gpio_lpc32xx) = { + .name = "gpio_lpc32xx", + .id = UCLASS_GPIO, + .ops = &gpio_lpc32xx_ops, + .probe = lpc32xx_gpio_probe, + .priv_auto = sizeof(struct lpc32xx_gpio_priv), +}; |