diff options
-rw-r--r-- | drivers/gpio/Kconfig | 9 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/intel_broadwell_gpio.c | 198 |
3 files changed, 208 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 94fabb9e18..a5da5e7e88 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -39,6 +39,15 @@ config ATMEL_PIO4 may be dedicated as a general purpose I/O or be assigned to a function of an embedded peripheral. +config INTEL_BROADWELL_GPIO + bool "Intel Broadwell GPIO driver" + depends on DM + help + This driver supports Broadwell U devices which have an expanded + GPIO feature set. The difference is large enough to merit a separate + driver from the common Intel ICH6 driver. It supports a total of + 95 GPIOs which can be configured from the device tree. + config LPC32XX_GPIO bool "LPC32XX GPIO driver" depends on DM diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index ca8c4879e4..e7b7ec45b7 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_DM_GPIO) += gpio-uclass.o obj-$(CONFIG_AT91_GPIO) += at91_gpio.o obj-$(CONFIG_ATMEL_PIO4) += atmel_pio4.o obj-$(CONFIG_INTEL_ICH6_GPIO) += intel_ich6_gpio.o +obj-$(CONFIG_INTEL_BROADWELL_GPIO) += intel_broadwell_gpio.o obj-$(CONFIG_KIRKWOOD_GPIO) += kw_gpio.o obj-$(CONFIG_KONA_GPIO) += kona_gpio.o obj-$(CONFIG_MARVELL_GPIO) += mvgpio.o diff --git a/drivers/gpio/intel_broadwell_gpio.c b/drivers/gpio/intel_broadwell_gpio.c new file mode 100644 index 0000000000..8cf76f96c2 --- /dev/null +++ b/drivers/gpio/intel_broadwell_gpio.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2012 The Chromium OS Authors. + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <pch.h> +#include <pci.h> +#include <syscon.h> +#include <asm/cpu.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <asm/pci.h> +#include <asm/arch/gpio.h> +#include <dt-bindings/gpio/x86-gpio.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * struct broadwell_bank_priv - Private driver data + * + * @regs: Pointer to GPIO registers + * @bank: Bank number for this bank (0, 1 or 2) + * @offset: GPIO offset for this bank (0, 32 or 64) + */ +struct broadwell_bank_priv { + struct pch_lp_gpio_regs *regs; + int bank; + int offset; +}; + +static int broadwell_gpio_request(struct udevice *dev, unsigned offset, + const char *label) +{ + struct broadwell_bank_priv *priv = dev_get_priv(dev); + struct pch_lp_gpio_regs *regs = priv->regs; + u32 val; + + /* + * Make sure that the GPIO pin we want isn't already in use for some + * built-in hardware function. We have to check this for every + * requested pin. + */ + debug("%s: request bank %d offset %d: ", __func__, priv->bank, offset); + val = inl(®s->own[priv->bank]); + if (!(val & (1UL << offset))) { + debug("gpio is reserved for internal use\n"); + return -EPERM; + } + debug("ok\n"); + + return 0; +} + +static int broadwell_gpio_direction_input(struct udevice *dev, unsigned offset) +{ + struct broadwell_bank_priv *priv = dev_get_priv(dev); + struct pch_lp_gpio_regs *regs = priv->regs; + + setio_32(®s->config[priv->offset + offset], CONFA_DIR_INPUT); + + return 0; +} + +static int broadwell_gpio_get_value(struct udevice *dev, unsigned offset) +{ + struct broadwell_bank_priv *priv = dev_get_priv(dev); + struct pch_lp_gpio_regs *regs = priv->regs; + + return inl(®s->config[priv->offset + offset]) & CONFA_LEVEL_HIGH ? + 1 : 0; +} + +static int broadwell_gpio_set_value(struct udevice *dev, unsigned offset, + int value) +{ + struct broadwell_bank_priv *priv = dev_get_priv(dev); + struct pch_lp_gpio_regs *regs = priv->regs; + + debug("%s: dev=%s, offset=%d, value=%d\n", __func__, dev->name, offset, + value); + clrsetio_32(®s->config[priv->offset + offset], CONFA_OUTPUT_HIGH, + value ? CONFA_OUTPUT_HIGH : 0); + + return 0; +} + +static int broadwell_gpio_direction_output(struct udevice *dev, unsigned offset, + int value) +{ + struct broadwell_bank_priv *priv = dev_get_priv(dev); + struct pch_lp_gpio_regs *regs = priv->regs; + + broadwell_gpio_set_value(dev, offset, value); + clrio_32(®s->config[priv->offset + offset], CONFA_DIR_INPUT); + + return 0; +} + +static int broadwell_gpio_get_function(struct udevice *dev, unsigned offset) +{ + struct broadwell_bank_priv *priv = dev_get_priv(dev); + struct pch_lp_gpio_regs *regs = priv->regs; + u32 mask = 1UL << offset; + + if (!(inl(®s->own[priv->bank]) & mask)) + return GPIOF_FUNC; + if (inl(®s->config[priv->offset + offset]) & CONFA_DIR_INPUT) + return GPIOF_INPUT; + else + return GPIOF_OUTPUT; +} + +static int broadwell_gpio_probe(struct udevice *dev) +{ + struct broadwell_bank_platdata *plat = dev_get_platdata(dev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct broadwell_bank_priv *priv = dev_get_priv(dev); + struct udevice *pinctrl; + int ret; + + /* Set up pin control if available */ + ret = syscon_get_by_driver_data(X86_SYSCON_PINCONF, &pinctrl); + debug("%s, pinctrl=%p, ret=%d\n", __func__, pinctrl, ret); + + uc_priv->gpio_count = GPIO_PER_BANK; + uc_priv->bank_name = plat->bank_name; + + priv->regs = (struct pch_lp_gpio_regs *)(uintptr_t)plat->base_addr; + priv->bank = plat->bank; + priv->offset = priv->bank * 32; + debug("%s: probe done, regs %p, bank %d\n", __func__, priv->regs, + priv->bank); + + return 0; +} + +static int broadwell_gpio_ofdata_to_platdata(struct udevice *dev) +{ + struct broadwell_bank_platdata *plat = dev_get_platdata(dev); + u32 gpiobase; + int bank; + int ret; + + ret = pch_get_gpio_base(dev->parent, &gpiobase); + if (ret) + return ret; + + bank = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1); + if (bank == -1) { + debug("%s: Invalid bank number %d\n", __func__, bank); + return -EINVAL; + } + plat->bank = bank; + plat->base_addr = gpiobase; + plat->bank_name = fdt_getprop(gd->fdt_blob, dev->of_offset, + "bank-name", NULL); + + return 0; +} + +static int broadwell_gpio_xlate(struct udevice *dev, struct gpio_desc *desc, + struct fdtdec_phandle_args *args) +{ + desc->offset = args->args[0]; + desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0; + + return 0; +} + +static const struct dm_gpio_ops gpio_broadwell_ops = { + .request = broadwell_gpio_request, + .direction_input = broadwell_gpio_direction_input, + .direction_output = broadwell_gpio_direction_output, + .get_value = broadwell_gpio_get_value, + .set_value = broadwell_gpio_set_value, + .get_function = broadwell_gpio_get_function, + .xlate = broadwell_gpio_xlate, +}; + +static const struct udevice_id intel_broadwell_gpio_ids[] = { + { .compatible = "intel,broadwell-gpio" }, + { } +}; + +U_BOOT_DRIVER(gpio_broadwell) = { + .name = "gpio_broadwell", + .id = UCLASS_GPIO, + .of_match = intel_broadwell_gpio_ids, + .ops = &gpio_broadwell_ops, + .ofdata_to_platdata = broadwell_gpio_ofdata_to_platdata, + .probe = broadwell_gpio_probe, + .priv_auto_alloc_size = sizeof(struct broadwell_bank_priv), + .platdata_auto_alloc_size = sizeof(struct broadwell_bank_platdata), +}; |