diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/include/asm/cpu.h | 1 | ||||
-rw-r--r-- | arch/x86/lib/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/lib/pinctrl_ich6.c | 216 |
3 files changed, 218 insertions, 0 deletions
diff --git a/arch/x86/include/asm/cpu.h b/arch/x86/include/asm/cpu.h index 987dc6512a3..85386da47a0 100644 --- a/arch/x86/include/asm/cpu.h +++ b/arch/x86/include/asm/cpu.h @@ -54,6 +54,7 @@ enum { X86_NONE, X86_SYSCON_ME, /* Intel Management Engine */ X86_SYSCON_GMA, /* Intel Graphics Media Accelerator */ + X86_SYSCON_PINCONF, /* Intel x86 pin configuration */ }; struct cpuid_result { diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index 6e0234af5bd..dc90df20500 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -23,6 +23,7 @@ obj-y += cmd_mtrr.o obj-y += northbridge-uclass.o obj-$(CONFIG_I8259_PIC) += i8259.o obj-$(CONFIG_I8254_TIMER) += i8254.o +obj-y += pinctrl_ich6.o obj-y += pirq_routing.o obj-y += relocate.o obj-y += physmem.o diff --git a/arch/x86/lib/pinctrl_ich6.c b/arch/x86/lib/pinctrl_ich6.c new file mode 100644 index 00000000000..3f94cdf2dac --- /dev/null +++ b/arch/x86/lib/pinctrl_ich6.c @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2016 Google, Inc + * + * 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 <asm/cpu.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <asm/pci.h> +#include <dm/pinctrl.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define GPIO_USESEL_OFFSET(x) (x) +#define GPIO_IOSEL_OFFSET(x) (x + 4) +#define GPIO_LVL_OFFSET(x) ((x) ? (x) + 8 : 0xc) +#define GPI_INV 0x2c + +#define IOPAD_MODE_MASK 0x7 +#define IOPAD_PULL_ASSIGN_SHIFT 7 +#define IOPAD_PULL_ASSIGN_MASK (0x3 << IOPAD_PULL_ASSIGN_SHIFT) +#define IOPAD_PULL_STRENGTH_SHIFT 9 +#define IOPAD_PULL_STRENGTH_MASK (0x3 << IOPAD_PULL_STRENGTH_SHIFT) + +static int ich6_pinctrl_set_value(uint16_t base, unsigned offset, int value) +{ + if (value) + setio_32(base, 1UL << offset); + else + clrio_32(base, 1UL << offset); + + return 0; +} + +static int ich6_pinctrl_set_function(uint16_t base, unsigned offset, int func) +{ + if (func) + setio_32(base, 1UL << offset); + else + clrio_32(base, 1UL << offset); + + return 0; +} + +static int ich6_pinctrl_set_direction(uint16_t base, unsigned offset, int dir) +{ + if (!dir) + setio_32(base, 1UL << offset); + else + clrio_32(base, 1UL << offset); + + return 0; +} + +static int ich6_pinctrl_cfg_pin(s32 gpiobase, s32 iobase, int pin_node) +{ + bool is_gpio, invert; + u32 gpio_offset[2]; + int pad_offset; + int dir, val; + int ret; + + /* + * GPIO node is not mandatory, so we only do the pinmuxing if the + * node exists. + */ + ret = fdtdec_get_int_array(gd->fdt_blob, pin_node, "gpio-offset", + gpio_offset, 2); + if (!ret) { + /* Do we want to force the GPIO mode? */ + is_gpio = fdtdec_get_bool(gd->fdt_blob, pin_node, "mode-gpio"); + if (is_gpio) + ich6_pinctrl_set_function(GPIO_USESEL_OFFSET(gpiobase) + + gpio_offset[0], gpio_offset[1], + 1); + + dir = fdtdec_get_int(gd->fdt_blob, pin_node, "direction", -1); + if (dir != -1) + ich6_pinctrl_set_direction(GPIO_IOSEL_OFFSET(gpiobase) + + gpio_offset[0], gpio_offset[1], + dir); + + val = fdtdec_get_int(gd->fdt_blob, pin_node, "output-value", + -1); + if (val != -1) + ich6_pinctrl_set_value(GPIO_LVL_OFFSET(gpiobase) + + gpio_offset[0], gpio_offset[1], + val); + + invert = fdtdec_get_bool(gd->fdt_blob, pin_node, "invert"); + if (invert) + setio_32(gpiobase + GPI_INV, 1 << gpio_offset[1]); + debug("gpio %#x bit %d, is_gpio %d, dir %d, val %d, invert %d\n", + gpio_offset[0], gpio_offset[1], is_gpio, dir, val, + invert); + } + + /* if iobase is present, let's configure the pad */ + if (iobase != -1) { + int iobase_addr; + + /* + * The offset for the same pin for the IOBASE and GPIOBASE are + * different, so instead of maintaining a lookup table, + * the device tree should provide directly the correct + * value for both mapping. + */ + pad_offset = fdtdec_get_int(gd->fdt_blob, pin_node, + "pad-offset", -1); + if (pad_offset == -1) + return 0; + + /* compute the absolute pad address */ + iobase_addr = iobase + pad_offset; + + /* + * Do we need to set a specific function mode? + * If someone put also 'mode-gpio', this option will + * be just ignored by the controller + */ + val = fdtdec_get_int(gd->fdt_blob, pin_node, "mode-func", -1); + if (val != -1) + clrsetbits_le32(iobase_addr, IOPAD_MODE_MASK, val); + + /* Configure the pull-up/down if needed */ + val = fdtdec_get_int(gd->fdt_blob, pin_node, "pull-assign", -1); + if (val != -1) + clrsetbits_le32(iobase_addr, + IOPAD_PULL_ASSIGN_MASK, + val << IOPAD_PULL_ASSIGN_SHIFT); + + val = fdtdec_get_int(gd->fdt_blob, pin_node, "pull-strength", + -1); + if (val != -1) + clrsetbits_le32(iobase_addr, + IOPAD_PULL_STRENGTH_MASK, + val << IOPAD_PULL_STRENGTH_SHIFT); + + debug("%s: pad cfg [0x%x]: %08x\n", __func__, pad_offset, + readl(iobase_addr)); + } + + return 0; +} + +static int ich6_pinctrl_probe(struct udevice *dev) +{ + struct udevice *pch; + int pin_node; + int ret; + u32 gpiobase; + u32 iobase = -1; + + debug("%s: start\n", __func__); + ret = uclass_first_device(UCLASS_PCH, &pch); + if (ret) + return ret; + if (!pch) + return -ENODEV; + + /* + * Get the memory/io base address to configure every pins. + * IOBASE is used to configure the mode/pads + * GPIOBASE is used to configure the direction and default value + */ + ret = pch_get_gpio_base(pch, &gpiobase); + if (ret) { + debug("%s: invalid GPIOBASE address (%08x)\n", __func__, + gpiobase); + return -EINVAL; + } + + /* + * Get the IOBASE, this is not mandatory as this is not + * supported by all the CPU + */ + ret = pch_get_io_base(pch, &iobase); + if (ret && ret != -ENOSYS) { + debug("%s: invalid IOBASE address (%08x)\n", __func__, iobase); + return -EINVAL; + } + + for (pin_node = fdt_first_subnode(gd->fdt_blob, dev->of_offset); + pin_node > 0; + pin_node = fdt_next_subnode(gd->fdt_blob, pin_node)) { + /* Configure the pin */ + ret = ich6_pinctrl_cfg_pin(gpiobase, iobase, pin_node); + if (ret != 0) { + debug("%s: invalid configuration for the pin %d\n", + __func__, pin_node); + return ret; + } + } + debug("%s: done\n", __func__); + + return 0; +} + +static const struct udevice_id ich6_pinctrl_match[] = { + { .compatible = "intel,x86-pinctrl", .data = X86_SYSCON_PINCONF }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(ich6_pinctrl) = { + .name = "ich6_pinctrl", + .id = UCLASS_SYSCON, + .of_match = ich6_pinctrl_match, + .probe = ich6_pinctrl_probe, +}; |