[U-Boot] [PATCH v3 3/4] x86: gpio: add pinctrl support from the device tree
gabriel huau
contact at huau-gabriel.fr
Tue May 12 08:21:09 CEST 2015
Please ignore this email/patch, I put the wrong message id ...
On 05/11/2015 11:15 PM, Gabriel Huau wrote:
> Every pin can be configured now from the device tree. A dt-bindings
> has been added to describe the different property available.
>
> Signed-off-by: Gabriel Huau <contact at huau-gabriel.fr>
>
> ---
>
> Changes in v3:
> - Fix the dt-binding copyright header
> - Remove define for IOBASE and use a property
> - Fix issues with signed/unsigned variable type
> - Fix coding style
> - Add a compatible string for the driver
>
> Changes in v2:
> - Clean commit message
> - Rename compatible string 'ich6' to 'x86'
> - Fix coding style
> - Create a dt-bindinds documentation
> - Move x86-gpio defines to a specific header
> - Reorder the functions to avoid the need for forward declarations
> - Rename double underscore functions to only one
> - Create a specific function to configure one pin
> - Use a define to prevent build/support issues with other x86 CPU that
> doesn't have a IOBASE.
>
> arch/x86/dts/minnowmax.dts | 23 ++
> arch/x86/include/asm/gpio.h | 1 +
> .../gpio/intel,x86-pinctrl.txt | 31 +++
> drivers/gpio/intel_ich6_gpio.c | 254 ++++++++++++++++++---
> include/dt-bindings/gpio/x86-gpio.h | 31 +++
> include/fdtdec.h | 1 +
> lib/fdtdec.c | 1 +
> 7 files changed, 312 insertions(+), 30 deletions(-)
> create mode 100644 doc/device-tree-bindings/gpio/intel,x86-pinctrl.txt
> create mode 100644 include/dt-bindings/gpio/x86-gpio.h
>
> diff --git a/arch/x86/dts/minnowmax.dts b/arch/x86/dts/minnowmax.dts
> index 7103bc5..bd21bfb 100644
> --- a/arch/x86/dts/minnowmax.dts
> +++ b/arch/x86/dts/minnowmax.dts
> @@ -6,6 +6,8 @@
>
> /dts-v1/;
>
> +#include <dt-bindings/gpio/x86-gpio.h>
> +
> /include/ "skeleton.dtsi"
> /include/ "serial.dtsi"
>
> @@ -22,6 +24,27 @@
> silent_console = <0>;
> };
>
> + pch_pinctrl {
> + compatible = "intel,x86-pinctrl";
> + io-base = <0x4c>;
> +
> + pin_usb_host_en0 at 0 {
> + gpio-offset = <0x80 8>;
> + pad-offset = <0x260>;
> + mode-gpio;
> + output-value = <1>;
> + direction = <PIN_OUTPUT>;
> + };
> +
> + pin_usb_host_en1 at 0 {
> + gpio-offset = <0x80 9>;
> + pad-offset = <0x258>;
> + mode-gpio;
> + output-value = <1>;
> + direction = <PIN_OUTPUT>;
> + };
> + };
> +
> gpioa {
> compatible = "intel,ich6-gpio";
> u-boot,dm-pre-reloc;
> diff --git a/arch/x86/include/asm/gpio.h b/arch/x86/include/asm/gpio.h
> index 1099427..ed85b08 100644
> --- a/arch/x86/include/asm/gpio.h
> +++ b/arch/x86/include/asm/gpio.h
> @@ -147,6 +147,7 @@ struct pch_gpio_map {
> } set3;
> };
>
> +int gpio_ich6_pinctrl_init(void);
> void setup_pch_gpios(u16 gpiobase, const struct pch_gpio_map *gpio);
> void ich_gpio_set_gpio_map(const struct pch_gpio_map *map);
>
> diff --git a/doc/device-tree-bindings/gpio/intel,x86-pinctrl.txt b/doc/device-tree-bindings/gpio/intel,x86-pinctrl.txt
> new file mode 100644
> index 0000000..45ab1af
> --- /dev/null
> +++ b/doc/device-tree-bindings/gpio/intel,x86-pinctrl.txt
> @@ -0,0 +1,31 @@
> +Intel x86 PINCTRL/GPIO controller
> +
> +Pin-muxing on x86 can be described with a node for the PINCTRL master
> +node and a set of child nodes for each pin on the SoC.
> +
> +The PINCTRL master node requires the following properties:
> +- compatible : "intel,x86-pinctrl"
> +
> +Pin nodes must be children of the pinctrl master node and can
> +contain the following properties:
> +- pad-offset - (required) offset in the IOBASE for the pin to configured.
> +- gpio-offset - (required) offset in the GPIOBASE for the pin to configured and
> + also the bit shift in this register.
> +- mode-gpio - (optional) standalone property to force the pin into GPIO mode.
> +- mode-func - (optional) function number to assign to the pin. if 'mode-gpio'
> + is set, this property will be ignored.
> +in case of 'mode-gpio' property set:
> +- output-value - (optional) this set the default output value of the GPIO.
> +- direction - (optional) this set the direction of the gpio.
> +- pull-str - (optional) this set the pull strength of the pin.
> +- pull-assign - (optional) this set the pull assignement (up/down) of the pin.
> +
> +Example:
> +
> +pin_usb_host_en0 at 0 {
> + gpio-offset = <0x80 8>;
> + pad-offset = <0x260>;
> + mode-gpio;
> + output-value = <1>;
> + direction = <PIN_OUTPUT>;
> +};
> diff --git a/drivers/gpio/intel_ich6_gpio.c b/drivers/gpio/intel_ich6_gpio.c
> index 7e679a0..35e8197 100644
> --- a/drivers/gpio/intel_ich6_gpio.c
> +++ b/drivers/gpio/intel_ich6_gpio.c
> @@ -44,21 +44,28 @@ struct ich6_bank_priv {
> uint16_t lvl;
> };
>
> +#define GPIO_USESEL_OFFSET(x) (x)
> +#define GPIO_IOSEL_OFFSET(x) (x + 4)
> +#define GPIO_LVL_OFFSET(x) (x + 8)
> +
> +#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)
> +
> /* TODO: Move this to device tree, or platform data */
> void ich_gpio_set_gpio_map(const struct pch_gpio_map *map)
> {
> gd->arch.gpio_map = map;
> }
>
> -static int gpio_ich6_ofdata_to_platdata(struct udevice *dev)
> +static int gpio_ich6_get_base(unsigned long base)
> {
> - struct ich6_bank_platdata *plat = dev_get_platdata(dev);
> - pci_dev_t pci_dev; /* handle for 0:1f:0 */
> + pci_dev_t pci_dev; /* handle for 0:1f:0 */
> u8 tmpbyte;
> u16 tmpword;
> u32 tmplong;
> - u16 gpiobase;
> - int offset;
>
> /* Where should it be? */
> pci_dev = PCI_BDF(0, 0x1f, 0);
> @@ -123,9 +130,9 @@ static int gpio_ich6_ofdata_to_platdata(struct udevice *dev)
> * while on the Ivybridge the bit0 is used to indicate it is an
> * I/O space.
> */
> - tmplong = x86_pci_read_config32(pci_dev, PCI_CFG_GPIOBASE);
> + tmplong = x86_pci_read_config32(pci_dev, base);
> if (tmplong == 0x00000000 || tmplong == 0xffffffff) {
> - debug("%s: unexpected GPIOBASE value\n", __func__);
> + debug("%s: unexpected BASE value\n", __func__);
> return -ENODEV;
> }
>
> @@ -135,7 +142,213 @@ static int gpio_ich6_ofdata_to_platdata(struct udevice *dev)
> * at the offset that we just read. Bit 0 indicates that it's
> * an I/O address, not a memory address, so mask that off.
> */
> - gpiobase = tmplong & 0xfffe;
> + return tmplong & 0xfffc;
> +}
> +
> +static int _ich6_gpio_set_value(uint16_t base, unsigned offset, int value)
> +{
> + u32 val;
> +
> + val = inl(base);
> + if (value)
> + val |= (1UL << offset);
> + else
> + val &= ~(1UL << offset);
> + outl(val, base);
> +
> + return 0;
> +}
> +
> +static int _ich6_gpio_set_function(uint16_t base, unsigned offset, int func)
> +{
> + u32 val;
> +
> + if (func) {
> + val = inl(base);
> + val |= (1UL << offset);
> + outl(val, base);
> + } else {
> + val = inl(base);
> + val &= ~(1UL << offset);
> + outl(val, base);
> + }
> +
> + return 0;
> +}
> +
> +static int _ich6_gpio_set_direction(uint16_t base, unsigned offset, int dir)
> +{
> + u32 val;
> +
> + if (!dir) {
> + val = inl(base);
> + val |= (1UL << offset);
> + outl(val, base);
> + } else {
> + val = inl(base);
> + val &= ~(1UL << offset);
> + outl(val, base);
> + }
> +
> + return 0;
> +}
> +
> +static int _gpio_ich6_pinctrl_cfg_pin(s32 gpiobase, s32 iobase, int pin_node)
> +{
> + u32 gpio_offset[2];
> + int pad_offset;
> + int val;
> + int ret;
> + const void *prop;
> +
> + /*
> + * GPIO node is not mandatory, so we only do the
> + * pinmuxing if the node exist.
> + */
> + 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? */
> + prop = fdt_getprop(gd->fdt_blob, pin_node, "mode-gpio", NULL);
> + if (prop)
> + _ich6_gpio_set_function(GPIO_USESEL_OFFSET
> + (gpiobase) +
> + gpio_offset[0],
> + gpio_offset[1], 1);
> +
> + val = fdtdec_get_int(gd->fdt_blob, pin_node, "direction", -1);
> + if (val != -1)
> + _ich6_gpio_set_direction(GPIO_IOSEL_OFFSET
> + (gpiobase) +
> + gpio_offset[0],
> + gpio_offset[1], val);
> +
> + val =
> + fdtdec_get_int(gd->fdt_blob, pin_node, "output-value", -1);
> + if (val != -1)
> + _ich6_gpio_set_value(GPIO_LVL_OFFSET(gpiobase)
> + + gpio_offset[0],
> + gpio_offset[1], val);
> + }
> +
> + /* 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) {
> + debug("%s: Invalid register io offset %d\n",
> + __func__, pad_offset);
> + return -EINVAL;
> + }
> +
> + /* 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;
> +}
> +
> +int gpio_ich6_pinctrl_init(void)
> +{
> + int pin_node;
> + int node;
> + int ret;
> + int gpiobase;
> + int iobase_offset;
> + int iobase = -1;
> +
> + /*
> + * 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
> + */
> + gpiobase = gpio_ich6_get_base(PCI_CFG_GPIOBASE);
> + if (gpiobase < 0) {
> + debug("%s: invalid GPIOBASE address (%08x)\n", __func__,
> + gpiobase);
> + return -EINVAL;
> + }
> +
> + /* This is not an error to not have a pinctrl node */
> + node =
> + fdtdec_next_compatible(gd->fdt_blob, 0, COMPAT_INTEL_X86_PINCTRL);
> + if (node <= 0) {
> + debug("%s: no pinctrl node\n", __func__);
> + return 0;
> + }
> +
> + /*
> + * Get the IOBASE, this is not mandatory as this is not
> + * supported by all the CPU
> + */
> + iobase_offset = fdtdec_get_int(gd->fdt_blob, node, "io-offset", -1);
> + if (iobase_offset == -1) {
> + debug("%s: io-base offset not present\n", __func__);
> + } else {
> + iobase = gpio_ich6_get_base(iobase_offset);
> + if (iobase < 0) {
> + debug("%s: invalid IOBASE address (%08x)\n", __func__,
> + iobase);
> + return -EINVAL;
> + }
> + }
> +
> + for (pin_node = fdt_first_subnode(gd->fdt_blob, node);
> + pin_node > 0;
> + pin_node = fdt_next_subnode(gd->fdt_blob, pin_node)) {
> + /* Configure the pin */
> + ret = _gpio_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;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int gpio_ich6_ofdata_to_platdata(struct udevice *dev)
> +{
> + struct ich6_bank_platdata *plat = dev_get_platdata(dev);
> + u16 gpiobase;
> + int offset;
> +
> + gpiobase = gpio_ich6_get_base(PCI_CFG_GPIOBASE);
> offset = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1);
> if (offset == -1) {
> debug("%s: Invalid register offset %d\n", __func__, offset);
> @@ -192,30 +405,19 @@ static int ich6_gpio_request(struct udevice *dev, unsigned offset,
> static int ich6_gpio_direction_input(struct udevice *dev, unsigned offset)
> {
> struct ich6_bank_priv *bank = dev_get_priv(dev);
> - u32 tmplong;
>
> - tmplong = inl(bank->io_sel);
> - tmplong |= (1UL << offset);
> - outl(bank->io_sel, tmplong);
> - return 0;
> + return _ich6_gpio_set_direction(inl(bank->io_sel), offset, 0);
> }
>
> static int ich6_gpio_direction_output(struct udevice *dev, unsigned offset,
> int value)
> {
> struct ich6_bank_priv *bank = dev_get_priv(dev);
> - u32 tmplong;
>
> - gpio_set_value(offset, value);
> -
> - tmplong = inl(bank->io_sel);
> - tmplong &= ~(1UL << offset);
> - outl(bank->io_sel, tmplong);
> - return 0;
> + return _ich6_gpio_set_direction(inl(bank->io_sel), offset, 1);
> }
>
> static int ich6_gpio_get_value(struct udevice *dev, unsigned offset)
> -
> {
> struct ich6_bank_priv *bank = dev_get_priv(dev);
> u32 tmplong;
> @@ -230,15 +432,7 @@ static int ich6_gpio_set_value(struct udevice *dev, unsigned offset,
> int value)
> {
> struct ich6_bank_priv *bank = dev_get_priv(dev);
> - u32 tmplong;
> -
> - tmplong = inl(bank->lvl);
> - if (value)
> - tmplong |= (1UL << offset);
> - else
> - tmplong &= ~(1UL << offset);
> - outl(bank->lvl, tmplong);
> - return 0;
> + return _ich6_gpio_set_value(bank->lvl, offset, value);
> }
>
> static int ich6_gpio_get_function(struct udevice *dev, unsigned offset)
> diff --git a/include/dt-bindings/gpio/x86-gpio.h b/include/dt-bindings/gpio/x86-gpio.h
> new file mode 100644
> index 0000000..7f1de30
> --- /dev/null
> +++ b/include/dt-bindings/gpio/x86-gpio.h
> @@ -0,0 +1,31 @@
> +/*
> + * This header provides constants for binding intel,x86-pinctrl.
> + */
> +
> +#ifndef _DT_BINDINGS_GPIO_X86_GPIO_H
> +#define _DT_BINDINGS_GPIO_X86_GPIO_H
> +
> +#include <dt-bindings/gpio/gpio.h>
> +
> +#define GPIO_MODE_NATIVE 0
> +#define GPIO_MODE_GPIO 1
> +
> +#define GPIO_MODE_FUNC0 0
> +#define GPIO_MODE_FUNC1 1
> +#define GPIO_MODE_FUNC2 2
> +#define GPIO_MODE_FUNC3 3
> +#define GPIO_MODE_FUNC4 4
> +#define GPIO_MODE_FUNC5 5
> +#define GPIO_MODE_FUNC6 6
> +
> +#define PIN_INPUT 0
> +#define PIN_OUTPUT 1
> +
> +#define PIN_INPUT_NOPULL 0
> +#define PIN_INPUT_PULLUP 1
> +#define PIN_INPUT_PULLDOWN 2
> +
> +#define PULL_STR_2K 0
> +#define PULL_STR_20K 2
> +
> +#endif
> diff --git a/include/fdtdec.h b/include/fdtdec.h
> index 6590470..5146ff1 100644
> --- a/include/fdtdec.h
> +++ b/include/fdtdec.h
> @@ -175,6 +175,7 @@ enum fdt_compat_id {
> COMPAT_AMS_AS3722, /* AMS AS3722 PMIC */
> COMPAT_INTEL_ICH_SPI, /* Intel ICH7/9 SPI controller */
> COMPAT_INTEL_QRK_MRC, /* Intel Quark MRC */
> + COMPAT_INTEL_X86_PINCTRL, /* Intel ICH7/9 pin control */
> COMPAT_SOCIONEXT_XHCI, /* Socionext UniPhier xHCI */
> COMPAT_INTEL_PCH, /* Intel PCH */
>
> diff --git a/lib/fdtdec.c b/lib/fdtdec.c
> index 80b897a..6bdc270 100644
> --- a/lib/fdtdec.c
> +++ b/lib/fdtdec.c
> @@ -74,6 +74,7 @@ static const char * const compat_names[COMPAT_COUNT] = {
> COMPAT(AMS_AS3722, "ams,as3722"),
> COMPAT(INTEL_ICH_SPI, "intel,ich-spi"),
> COMPAT(INTEL_QRK_MRC, "intel,quark-mrc"),
> + COMPAT(INTEL_X86_PINCTRL, "intel,x86-pinctrl"),
> COMPAT(SOCIONEXT_XHCI, "socionext,uniphier-xhci"),
> COMPAT(COMPAT_INTEL_PCH, "intel,bd82x6x"),
> };
More information about the U-Boot
mailing list