[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:20:48 CEST 2015


Please ignore this email/patch, I put the wrong message id ...

On 05/11/2015 11:10 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