/* * 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 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->uclass_priv; if (dev->of_offset == -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_alloc_size = sizeof(struct lpc32xx_gpio_priv), };