x86: gpio: add pinctrl support from the device tree

Every pin can be configured now from the device tree. A dt-bindings
has been added to describe the different property available.

Change-Id: I1668886062655f83700d0e7bbbe3ad09b19ee975
Signed-off-by: Gabriel Huau <contact@huau-gabriel.fr>
Acked-by: Simon Glass <sjg@chromium.org>
master
Gabriel Huau 10 years ago committed by Simon Glass
parent afbbd413a3
commit 5318f18d2c
  1. 23
      arch/x86/dts/minnowmax.dts
  2. 1
      arch/x86/include/asm/gpio.h
  3. 31
      doc/device-tree-bindings/gpio/intel,x86-pinctrl.txt
  4. 257
      drivers/gpio/intel_ich6_gpio.c
  5. 31
      include/dt-bindings/gpio/x86-gpio.h
  6. 1
      include/fdtdec.h
  7. 1
      lib/fdtdec.c

@ -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@0 {
gpio-offset = <0x80 8>;
pad-offset = <0x260>;
mode-gpio;
output-value = <1>;
direction = <PIN_OUTPUT>;
};
pin_usb_host_en1@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;

@ -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);

@ -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@0 {
gpio-offset = <0x80 8>;
pad-offset = <0x260>;
mode-gpio;
output-value = <1>;
direction = <PIN_OUTPUT>;
};

@ -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 */
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,215 @@ 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-base", -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 +407,24 @@ 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)
{
int ret;
struct ich6_bank_priv *bank = dev_get_priv(dev);
u32 tmplong;
gpio_set_value(offset, value);
ret = _ich6_gpio_set_direction(inl(bank->io_sel), offset, 1);
if (ret)
return ret;
tmplong = inl(bank->io_sel);
tmplong &= ~(1UL << offset);
outl(bank->io_sel, tmplong);
return 0;
return _ich6_gpio_set_value(bank->lvl, offset, value);
}
static int ich6_gpio_get_value(struct udevice *dev, unsigned offset)
{
struct ich6_bank_priv *bank = dev_get_priv(dev);
u32 tmplong;
@ -230,15 +439,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)

@ -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

@ -176,6 +176,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 */
COMPAT_INTEL_IRQ_ROUTER, /* Intel Interrupt Router */

@ -75,6 +75,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"),
COMPAT(COMPAT_INTEL_IRQ_ROUTER, "intel,irq-router"),

Loading…
Cancel
Save