xref: /rk3399_rockchip-uboot/drivers/gpio/intel_ich6_gpio.c (revision 8b097916faa20f04ff4b0147ebcf0331204ec96b)
155ae10f8SBill Richardson /*
255ae10f8SBill Richardson  * Copyright (c) 2012 The Chromium OS Authors.
31a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
455ae10f8SBill Richardson  */
555ae10f8SBill Richardson 
655ae10f8SBill Richardson /*
755ae10f8SBill Richardson  * This is a GPIO driver for Intel ICH6 and later. The x86 GPIOs are accessed
855ae10f8SBill Richardson  * through the PCI bus. Each PCI device has 256 bytes of configuration space,
955ae10f8SBill Richardson  * consisting of a standard header and a device-specific set of registers. PCI
1055ae10f8SBill Richardson  * bus 0, device 31, function 0 gives us access to the chipset GPIOs (among
1155ae10f8SBill Richardson  * other things). Within the PCI configuration space, the GPIOBASE register
1255ae10f8SBill Richardson  * tells us where in the device's I/O region we can find more registers to
1355ae10f8SBill Richardson  * actually access the GPIOs.
1455ae10f8SBill Richardson  *
1555ae10f8SBill Richardson  * PCI bus/device/function 0:1f:0  => PCI config registers
1655ae10f8SBill Richardson  *   PCI config register "GPIOBASE"
1755ae10f8SBill Richardson  *     PCI I/O space + [GPIOBASE]  => start of GPIO registers
1855ae10f8SBill Richardson  *       GPIO registers => gpio pin function, direction, value
1957be9172SBill Richardson  *
2057be9172SBill Richardson  *
2157be9172SBill Richardson  * Danger Will Robinson! Bank 0 (GPIOs 0-31) seems to be fairly stable. Most
2257be9172SBill Richardson  * ICH versions have more, but the decoding the matrix that describes them is
2357be9172SBill Richardson  * absurdly complex and constantly changing. We'll provide Bank 1 and Bank 2,
2457be9172SBill Richardson  * but they will ONLY work for certain unspecified chipsets because the offset
2557be9172SBill Richardson  * from GPIOBASE changes randomly. Even then, many GPIOs are unimplemented or
2657be9172SBill Richardson  * reserved or subject to arcane restrictions.
2755ae10f8SBill Richardson  */
2855ae10f8SBill Richardson 
2955ae10f8SBill Richardson #include <common.h>
307414112dSSimon Glass #include <dm.h>
317414112dSSimon Glass #include <errno.h>
327414112dSSimon Glass #include <fdtdec.h>
3355ae10f8SBill Richardson #include <pci.h>
3455ae10f8SBill Richardson #include <asm/gpio.h>
3555ae10f8SBill Richardson #include <asm/io.h>
361b4f25ffSSimon Glass #include <asm/pci.h>
3755ae10f8SBill Richardson 
38*8b097916SSimon Glass DECLARE_GLOBAL_DATA_PTR;
39*8b097916SSimon Glass 
407414112dSSimon Glass #define GPIO_PER_BANK	32
417414112dSSimon Glass 
427414112dSSimon Glass struct ich6_bank_priv {
437414112dSSimon Glass 	/* These are I/O addresses */
44b71eec31SBin Meng 	uint16_t use_sel;
45b71eec31SBin Meng 	uint16_t io_sel;
46b71eec31SBin Meng 	uint16_t lvl;
4757be9172SBill Richardson };
4855ae10f8SBill Richardson 
495318f18dSGabriel Huau #define GPIO_USESEL_OFFSET(x)	(x)
505318f18dSGabriel Huau #define GPIO_IOSEL_OFFSET(x)	(x + 4)
515318f18dSGabriel Huau #define GPIO_LVL_OFFSET(x)	(x + 8)
525318f18dSGabriel Huau 
535318f18dSGabriel Huau #define IOPAD_MODE_MASK				0x7
545318f18dSGabriel Huau #define IOPAD_PULL_ASSIGN_SHIFT		7
555318f18dSGabriel Huau #define IOPAD_PULL_ASSIGN_MASK		(0x3 << IOPAD_PULL_ASSIGN_SHIFT)
565318f18dSGabriel Huau #define IOPAD_PULL_STRENGTH_SHIFT	9
575318f18dSGabriel Huau #define IOPAD_PULL_STRENGTH_MASK	(0x3 << IOPAD_PULL_STRENGTH_SHIFT)
585318f18dSGabriel Huau 
591b4f25ffSSimon Glass /* TODO: Move this to device tree, or platform data */
601b4f25ffSSimon Glass void ich_gpio_set_gpio_map(const struct pch_gpio_map *map)
611b4f25ffSSimon Glass {
621b4f25ffSSimon Glass 	gd->arch.gpio_map = map;
631b4f25ffSSimon Glass }
641b4f25ffSSimon Glass 
655318f18dSGabriel Huau static int gpio_ich6_get_base(unsigned long base)
6657be9172SBill Richardson {
677414112dSSimon Glass 	pci_dev_t pci_dev;			/* handle for 0:1f:0 */
6855ae10f8SBill Richardson 	u8 tmpbyte;
6955ae10f8SBill Richardson 	u16 tmpword;
7055ae10f8SBill Richardson 	u32 tmplong;
7155ae10f8SBill Richardson 
7255ae10f8SBill Richardson 	/* Where should it be? */
737414112dSSimon Glass 	pci_dev = PCI_BDF(0, 0x1f, 0);
7455ae10f8SBill Richardson 
7555ae10f8SBill Richardson 	/* Is the device present? */
7631f57c28SSimon Glass 	tmpword = x86_pci_read_config16(pci_dev, PCI_VENDOR_ID);
7755ae10f8SBill Richardson 	if (tmpword != PCI_VENDOR_ID_INTEL) {
7855ae10f8SBill Richardson 		debug("%s: wrong VendorID\n", __func__);
797414112dSSimon Glass 		return -ENODEV;
8055ae10f8SBill Richardson 	}
8157be9172SBill Richardson 
8231f57c28SSimon Glass 	tmpword = x86_pci_read_config16(pci_dev, PCI_DEVICE_ID);
8357be9172SBill Richardson 	debug("Found %04x:%04x\n", PCI_VENDOR_ID_INTEL, tmpword);
8455ae10f8SBill Richardson 	/*
8557be9172SBill Richardson 	 * We'd like to validate the Device ID too, but pretty much any
8655ae10f8SBill Richardson 	 * value is either a) correct with slight differences, or b)
8757be9172SBill Richardson 	 * correct but undocumented. We'll have to check a bunch of other
8857be9172SBill Richardson 	 * things instead...
8955ae10f8SBill Richardson 	 */
9055ae10f8SBill Richardson 
9155ae10f8SBill Richardson 	/* I/O should already be enabled (it's a RO bit). */
9231f57c28SSimon Glass 	tmpword = x86_pci_read_config16(pci_dev, PCI_COMMAND);
9355ae10f8SBill Richardson 	if (!(tmpword & PCI_COMMAND_IO)) {
9455ae10f8SBill Richardson 		debug("%s: device IO not enabled\n", __func__);
957414112dSSimon Glass 		return -ENODEV;
9655ae10f8SBill Richardson 	}
9755ae10f8SBill Richardson 
9855ae10f8SBill Richardson 	/* Header Type must be normal (bits 6-0 only; see spec.) */
9931f57c28SSimon Glass 	tmpbyte = x86_pci_read_config8(pci_dev, PCI_HEADER_TYPE);
10055ae10f8SBill Richardson 	if ((tmpbyte & 0x7f) != PCI_HEADER_TYPE_NORMAL) {
10155ae10f8SBill Richardson 		debug("%s: invalid Header type\n", __func__);
1027414112dSSimon Glass 		return -ENODEV;
10355ae10f8SBill Richardson 	}
10455ae10f8SBill Richardson 
10555ae10f8SBill Richardson 	/* Base Class must be a bridge device */
10631f57c28SSimon Glass 	tmpbyte = x86_pci_read_config8(pci_dev, PCI_CLASS_CODE);
10755ae10f8SBill Richardson 	if (tmpbyte != PCI_CLASS_CODE_BRIDGE) {
10855ae10f8SBill Richardson 		debug("%s: invalid class\n", __func__);
1097414112dSSimon Glass 		return -ENODEV;
11055ae10f8SBill Richardson 	}
11155ae10f8SBill Richardson 	/* Sub Class must be ISA */
11231f57c28SSimon Glass 	tmpbyte = x86_pci_read_config8(pci_dev, PCI_CLASS_SUB_CODE);
11355ae10f8SBill Richardson 	if (tmpbyte != PCI_CLASS_SUB_CODE_BRIDGE_ISA) {
11455ae10f8SBill Richardson 		debug("%s: invalid subclass\n", __func__);
1157414112dSSimon Glass 		return -ENODEV;
11655ae10f8SBill Richardson 	}
11755ae10f8SBill Richardson 
11855ae10f8SBill Richardson 	/* Programming Interface must be 0x00 (no others exist) */
11931f57c28SSimon Glass 	tmpbyte = x86_pci_read_config8(pci_dev, PCI_CLASS_PROG);
12055ae10f8SBill Richardson 	if (tmpbyte != 0x00) {
12155ae10f8SBill Richardson 		debug("%s: invalid interface type\n", __func__);
1227414112dSSimon Glass 		return -ENODEV;
12355ae10f8SBill Richardson 	}
12455ae10f8SBill Richardson 
12555ae10f8SBill Richardson 	/*
12655ae10f8SBill Richardson 	 * GPIOBASE moved to its current offset with ICH6, but prior to
12755ae10f8SBill Richardson 	 * that it was unused (or undocumented). Check that it looks
128b71eec31SBin Meng 	 * okay: not all ones or zeros.
129b71eec31SBin Meng 	 *
130b71eec31SBin Meng 	 * Note we don't need check bit0 here, because the Tunnel Creek
131b71eec31SBin Meng 	 * GPIO base address register bit0 is reserved (read returns 0),
132b71eec31SBin Meng 	 * while on the Ivybridge the bit0 is used to indicate it is an
133b71eec31SBin Meng 	 * I/O space.
13455ae10f8SBill Richardson 	 */
1355318f18dSGabriel Huau 	tmplong = x86_pci_read_config32(pci_dev, base);
136b71eec31SBin Meng 	if (tmplong == 0x00000000 || tmplong == 0xffffffff) {
1375318f18dSGabriel Huau 		debug("%s: unexpected BASE value\n", __func__);
1387414112dSSimon Glass 		return -ENODEV;
13955ae10f8SBill Richardson 	}
14055ae10f8SBill Richardson 
14155ae10f8SBill Richardson 	/*
14255ae10f8SBill Richardson 	 * Okay, I guess we're looking at the right device. The actual
14355ae10f8SBill Richardson 	 * GPIO registers are in the PCI device's I/O space, starting
14455ae10f8SBill Richardson 	 * at the offset that we just read. Bit 0 indicates that it's
14555ae10f8SBill Richardson 	 * an I/O address, not a memory address, so mask that off.
14655ae10f8SBill Richardson 	 */
1475318f18dSGabriel Huau 	return tmplong & 0xfffc;
1485318f18dSGabriel Huau }
1495318f18dSGabriel Huau 
1505318f18dSGabriel Huau static int _ich6_gpio_set_value(uint16_t base, unsigned offset, int value)
1515318f18dSGabriel Huau {
1525318f18dSGabriel Huau 	u32 val;
1535318f18dSGabriel Huau 
1545318f18dSGabriel Huau 	val = inl(base);
1555318f18dSGabriel Huau 	if (value)
1565318f18dSGabriel Huau 		val |= (1UL << offset);
1575318f18dSGabriel Huau 	else
1585318f18dSGabriel Huau 		val &= ~(1UL << offset);
1595318f18dSGabriel Huau 	outl(val, base);
1605318f18dSGabriel Huau 
1615318f18dSGabriel Huau 	return 0;
1625318f18dSGabriel Huau }
1635318f18dSGabriel Huau 
1645318f18dSGabriel Huau static int _ich6_gpio_set_function(uint16_t base, unsigned offset, int func)
1655318f18dSGabriel Huau {
1665318f18dSGabriel Huau 	u32 val;
1675318f18dSGabriel Huau 
1685318f18dSGabriel Huau 	if (func) {
1695318f18dSGabriel Huau 		val = inl(base);
1705318f18dSGabriel Huau 		val |= (1UL << offset);
1715318f18dSGabriel Huau 		outl(val, base);
1725318f18dSGabriel Huau 	} else {
1735318f18dSGabriel Huau 		val = inl(base);
1745318f18dSGabriel Huau 		val &= ~(1UL << offset);
1755318f18dSGabriel Huau 		outl(val, base);
1765318f18dSGabriel Huau 	}
1775318f18dSGabriel Huau 
1785318f18dSGabriel Huau 	return 0;
1795318f18dSGabriel Huau }
1805318f18dSGabriel Huau 
1815318f18dSGabriel Huau static int _ich6_gpio_set_direction(uint16_t base, unsigned offset, int dir)
1825318f18dSGabriel Huau {
1835318f18dSGabriel Huau 	u32 val;
1845318f18dSGabriel Huau 
1855318f18dSGabriel Huau 	if (!dir) {
1865318f18dSGabriel Huau 		val = inl(base);
1875318f18dSGabriel Huau 		val |= (1UL << offset);
1885318f18dSGabriel Huau 		outl(val, base);
1895318f18dSGabriel Huau 	} else {
1905318f18dSGabriel Huau 		val = inl(base);
1915318f18dSGabriel Huau 		val &= ~(1UL << offset);
1925318f18dSGabriel Huau 		outl(val, base);
1935318f18dSGabriel Huau 	}
1945318f18dSGabriel Huau 
1955318f18dSGabriel Huau 	return 0;
1965318f18dSGabriel Huau }
1975318f18dSGabriel Huau 
1985318f18dSGabriel Huau static int _gpio_ich6_pinctrl_cfg_pin(s32 gpiobase, s32 iobase, int pin_node)
1995318f18dSGabriel Huau {
2005318f18dSGabriel Huau 	u32 gpio_offset[2];
2015318f18dSGabriel Huau 	int pad_offset;
2025318f18dSGabriel Huau 	int val;
2035318f18dSGabriel Huau 	int ret;
2045318f18dSGabriel Huau 	const void *prop;
2055318f18dSGabriel Huau 
2065318f18dSGabriel Huau 	/*
2075318f18dSGabriel Huau 	 * GPIO node is not mandatory, so we only do the
2085318f18dSGabriel Huau 	 * pinmuxing if the node exist.
2095318f18dSGabriel Huau 	 */
2105318f18dSGabriel Huau 	ret = fdtdec_get_int_array(gd->fdt_blob, pin_node, "gpio-offset",
2115318f18dSGabriel Huau 			     gpio_offset, 2);
2125318f18dSGabriel Huau 	if (!ret) {
2135318f18dSGabriel Huau 		/* Do we want to force the GPIO mode? */
2145318f18dSGabriel Huau 		prop = fdt_getprop(gd->fdt_blob, pin_node, "mode-gpio",
2155318f18dSGabriel Huau 				      NULL);
2165318f18dSGabriel Huau 		if (prop)
2175318f18dSGabriel Huau 			_ich6_gpio_set_function(GPIO_USESEL_OFFSET
2185318f18dSGabriel Huau 						(gpiobase) +
2195318f18dSGabriel Huau 						gpio_offset[0],
2205318f18dSGabriel Huau 						gpio_offset[1], 1);
2215318f18dSGabriel Huau 
2225318f18dSGabriel Huau 		val =
2235318f18dSGabriel Huau 		    fdtdec_get_int(gd->fdt_blob, pin_node, "direction", -1);
2245318f18dSGabriel Huau 		if (val != -1)
2255318f18dSGabriel Huau 			_ich6_gpio_set_direction(GPIO_IOSEL_OFFSET
2265318f18dSGabriel Huau 						 (gpiobase) +
2275318f18dSGabriel Huau 						 gpio_offset[0],
2285318f18dSGabriel Huau 						 gpio_offset[1], val);
2295318f18dSGabriel Huau 
2305318f18dSGabriel Huau 		val =
2315318f18dSGabriel Huau 		    fdtdec_get_int(gd->fdt_blob, pin_node, "output-value", -1);
2325318f18dSGabriel Huau 		if (val != -1)
2335318f18dSGabriel Huau 			_ich6_gpio_set_value(GPIO_LVL_OFFSET(gpiobase)
2345318f18dSGabriel Huau 					     + gpio_offset[0],
2355318f18dSGabriel Huau 					     gpio_offset[1], val);
2365318f18dSGabriel Huau 	}
2375318f18dSGabriel Huau 
2385318f18dSGabriel Huau 	/* if iobase is present, let's configure the pad */
2395318f18dSGabriel Huau 	if (iobase != -1) {
2405318f18dSGabriel Huau 		int iobase_addr;
2415318f18dSGabriel Huau 
2425318f18dSGabriel Huau 		/*
2435318f18dSGabriel Huau 		 * The offset for the same pin for the IOBASE and GPIOBASE are
2445318f18dSGabriel Huau 		 * different, so instead of maintaining a lookup table,
2455318f18dSGabriel Huau 		 * the device tree should provide directly the correct
2465318f18dSGabriel Huau 		 * value for both mapping.
2475318f18dSGabriel Huau 		 */
2485318f18dSGabriel Huau 		pad_offset =
2495318f18dSGabriel Huau 		    fdtdec_get_int(gd->fdt_blob, pin_node, "pad-offset", -1);
2505318f18dSGabriel Huau 		if (pad_offset == -1) {
2515318f18dSGabriel Huau 			debug("%s: Invalid register io offset %d\n",
2525318f18dSGabriel Huau 			      __func__, pad_offset);
2535318f18dSGabriel Huau 			return -EINVAL;
2545318f18dSGabriel Huau 		}
2555318f18dSGabriel Huau 
2565318f18dSGabriel Huau 		/* compute the absolute pad address */
2575318f18dSGabriel Huau 		iobase_addr = iobase + pad_offset;
2585318f18dSGabriel Huau 
2595318f18dSGabriel Huau 		/*
2605318f18dSGabriel Huau 		 * Do we need to set a specific function mode?
2615318f18dSGabriel Huau 		 * If someone put also 'mode-gpio', this option will
2625318f18dSGabriel Huau 		 * be just ignored by the controller
2635318f18dSGabriel Huau 		 */
2645318f18dSGabriel Huau 		val = fdtdec_get_int(gd->fdt_blob, pin_node, "mode-func", -1);
2655318f18dSGabriel Huau 		if (val != -1)
2665318f18dSGabriel Huau 			clrsetbits_le32(iobase_addr, IOPAD_MODE_MASK, val);
2675318f18dSGabriel Huau 
2685318f18dSGabriel Huau 		/* Configure the pull-up/down if needed */
2695318f18dSGabriel Huau 		val = fdtdec_get_int(gd->fdt_blob, pin_node, "pull-assign", -1);
2705318f18dSGabriel Huau 		if (val != -1)
2715318f18dSGabriel Huau 			clrsetbits_le32(iobase_addr,
2725318f18dSGabriel Huau 					IOPAD_PULL_ASSIGN_MASK,
2735318f18dSGabriel Huau 					val << IOPAD_PULL_ASSIGN_SHIFT);
2745318f18dSGabriel Huau 
2755318f18dSGabriel Huau 		val =
2765318f18dSGabriel Huau 		    fdtdec_get_int(gd->fdt_blob, pin_node, "pull-strength", -1);
2775318f18dSGabriel Huau 		if (val != -1)
2785318f18dSGabriel Huau 			clrsetbits_le32(iobase_addr,
2795318f18dSGabriel Huau 					IOPAD_PULL_STRENGTH_MASK,
2805318f18dSGabriel Huau 					val << IOPAD_PULL_STRENGTH_SHIFT);
2815318f18dSGabriel Huau 
2825318f18dSGabriel Huau 		debug("%s: pad cfg [0x%x]: %08x\n", __func__, pad_offset,
2835318f18dSGabriel Huau 		      readl(iobase_addr));
2845318f18dSGabriel Huau 	}
2855318f18dSGabriel Huau 
2865318f18dSGabriel Huau 	return 0;
2875318f18dSGabriel Huau }
2885318f18dSGabriel Huau 
2895318f18dSGabriel Huau int gpio_ich6_pinctrl_init(void)
2905318f18dSGabriel Huau {
2915318f18dSGabriel Huau 	int pin_node;
2925318f18dSGabriel Huau 	int node;
2935318f18dSGabriel Huau 	int ret;
2945318f18dSGabriel Huau 	int gpiobase;
2955318f18dSGabriel Huau 	int iobase_offset;
2965318f18dSGabriel Huau 	int iobase = -1;
2975318f18dSGabriel Huau 
2985318f18dSGabriel Huau 	/*
2995318f18dSGabriel Huau 	 * Get the memory/io base address to configure every pins.
3005318f18dSGabriel Huau 	 * IOBASE is used to configure the mode/pads
3015318f18dSGabriel Huau 	 * GPIOBASE is used to configure the direction and default value
3025318f18dSGabriel Huau 	 */
3035318f18dSGabriel Huau 	gpiobase = gpio_ich6_get_base(PCI_CFG_GPIOBASE);
3045318f18dSGabriel Huau 	if (gpiobase < 0) {
3055318f18dSGabriel Huau 		debug("%s: invalid GPIOBASE address (%08x)\n", __func__,
3065318f18dSGabriel Huau 		      gpiobase);
3075318f18dSGabriel Huau 		return -EINVAL;
3085318f18dSGabriel Huau 	}
3095318f18dSGabriel Huau 
3105318f18dSGabriel Huau 	/* This is not an error to not have a pinctrl node */
3115318f18dSGabriel Huau 	node =
3125318f18dSGabriel Huau 	    fdtdec_next_compatible(gd->fdt_blob, 0, COMPAT_INTEL_X86_PINCTRL);
3135318f18dSGabriel Huau 	if (node <= 0) {
3145318f18dSGabriel Huau 		debug("%s: no pinctrl node\n", __func__);
3155318f18dSGabriel Huau 		return 0;
3165318f18dSGabriel Huau 	}
3175318f18dSGabriel Huau 
3185318f18dSGabriel Huau 	/*
3195318f18dSGabriel Huau 	 * Get the IOBASE, this is not mandatory as this is not
3205318f18dSGabriel Huau 	 * supported by all the CPU
3215318f18dSGabriel Huau 	 */
3225318f18dSGabriel Huau 	iobase_offset = fdtdec_get_int(gd->fdt_blob, node, "io-base", -1);
3235318f18dSGabriel Huau 	if (iobase_offset == -1) {
3245318f18dSGabriel Huau 		debug("%s: io-base offset not present\n", __func__);
3255318f18dSGabriel Huau 	} else {
3265318f18dSGabriel Huau 		iobase = gpio_ich6_get_base(iobase_offset);
3275318f18dSGabriel Huau 		if (iobase < 0) {
3285318f18dSGabriel Huau 			debug("%s: invalid IOBASE address (%08x)\n", __func__,
3295318f18dSGabriel Huau 			      iobase);
3305318f18dSGabriel Huau 			return -EINVAL;
3315318f18dSGabriel Huau 		}
3325318f18dSGabriel Huau 	}
3335318f18dSGabriel Huau 
3345318f18dSGabriel Huau 	for (pin_node = fdt_first_subnode(gd->fdt_blob, node);
3355318f18dSGabriel Huau 	     pin_node > 0;
3365318f18dSGabriel Huau 	     pin_node = fdt_next_subnode(gd->fdt_blob, pin_node)) {
3375318f18dSGabriel Huau 		/* Configure the pin */
3385318f18dSGabriel Huau 		ret = _gpio_ich6_pinctrl_cfg_pin(gpiobase, iobase, pin_node);
3395318f18dSGabriel Huau 		if (ret != 0) {
3405318f18dSGabriel Huau 			debug("%s: invalid configuration for the pin %d\n",
3415318f18dSGabriel Huau 			      __func__, pin_node);
3425318f18dSGabriel Huau 			return ret;
3435318f18dSGabriel Huau 		}
3445318f18dSGabriel Huau 	}
3455318f18dSGabriel Huau 
3465318f18dSGabriel Huau 	return 0;
3475318f18dSGabriel Huau }
3485318f18dSGabriel Huau 
3495318f18dSGabriel Huau static int gpio_ich6_ofdata_to_platdata(struct udevice *dev)
3505318f18dSGabriel Huau {
3515318f18dSGabriel Huau 	struct ich6_bank_platdata *plat = dev_get_platdata(dev);
3525318f18dSGabriel Huau 	u16 gpiobase;
3535318f18dSGabriel Huau 	int offset;
3545318f18dSGabriel Huau 
3555318f18dSGabriel Huau 	gpiobase = gpio_ich6_get_base(PCI_CFG_GPIOBASE);
3567414112dSSimon Glass 	offset = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1);
3577414112dSSimon Glass 	if (offset == -1) {
3587414112dSSimon Glass 		debug("%s: Invalid register offset %d\n", __func__, offset);
3597414112dSSimon Glass 		return -EINVAL;
3607414112dSSimon Glass 	}
3617414112dSSimon Glass 	plat->base_addr = gpiobase + offset;
3627414112dSSimon Glass 	plat->bank_name = fdt_getprop(gd->fdt_blob, dev->of_offset,
3637414112dSSimon Glass 				      "bank-name", NULL);
36455ae10f8SBill Richardson 
36555ae10f8SBill Richardson 	return 0;
36655ae10f8SBill Richardson }
36755ae10f8SBill Richardson 
3681b4f25ffSSimon Glass static int ich6_gpio_probe(struct udevice *dev)
36955ae10f8SBill Richardson {
3707414112dSSimon Glass 	struct ich6_bank_platdata *plat = dev_get_platdata(dev);
371e564f054SSimon Glass 	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
3727414112dSSimon Glass 	struct ich6_bank_priv *bank = dev_get_priv(dev);
3737414112dSSimon Glass 
3741b4f25ffSSimon Glass 	if (gd->arch.gpio_map) {
3752795573aSBin Meng 		setup_pch_gpios(plat->base_addr, gd->arch.gpio_map);
3761b4f25ffSSimon Glass 		gd->arch.gpio_map = NULL;
3771b4f25ffSSimon Glass 	}
3782795573aSBin Meng 
3797414112dSSimon Glass 	uc_priv->gpio_count = GPIO_PER_BANK;
3807414112dSSimon Glass 	uc_priv->bank_name = plat->bank_name;
3817414112dSSimon Glass 	bank->use_sel = plat->base_addr;
3827414112dSSimon Glass 	bank->io_sel = plat->base_addr + 4;
3837414112dSSimon Glass 	bank->lvl = plat->base_addr + 8;
3847414112dSSimon Glass 
3857414112dSSimon Glass 	return 0;
3867414112dSSimon Glass }
3877414112dSSimon Glass 
3881b4f25ffSSimon Glass static int ich6_gpio_request(struct udevice *dev, unsigned offset,
3891b4f25ffSSimon Glass 			     const char *label)
3907414112dSSimon Glass {
3917414112dSSimon Glass 	struct ich6_bank_priv *bank = dev_get_priv(dev);
39255ae10f8SBill Richardson 	u32 tmplong;
39355ae10f8SBill Richardson 
39455ae10f8SBill Richardson 	/*
39555ae10f8SBill Richardson 	 * Make sure that the GPIO pin we want isn't already in use for some
39655ae10f8SBill Richardson 	 * built-in hardware function. We have to check this for every
39755ae10f8SBill Richardson 	 * requested pin.
39855ae10f8SBill Richardson 	 */
3997414112dSSimon Glass 	tmplong = inl(bank->use_sel);
4007414112dSSimon Glass 	if (!(tmplong & (1UL << offset))) {
40157be9172SBill Richardson 		debug("%s: gpio %d is reserved for internal use\n", __func__,
4027414112dSSimon Glass 		      offset);
4037414112dSSimon Glass 		return -EPERM;
40455ae10f8SBill Richardson 	}
40555ae10f8SBill Richardson 
40655ae10f8SBill Richardson 	return 0;
40755ae10f8SBill Richardson }
40855ae10f8SBill Richardson 
4097414112dSSimon Glass static int ich6_gpio_direction_input(struct udevice *dev, unsigned offset)
41055ae10f8SBill Richardson {
4117414112dSSimon Glass 	struct ich6_bank_priv *bank = dev_get_priv(dev);
41257be9172SBill Richardson 
4135318f18dSGabriel Huau 	return _ich6_gpio_set_direction(inl(bank->io_sel), offset, 0);
41455ae10f8SBill Richardson }
41555ae10f8SBill Richardson 
4167414112dSSimon Glass static int ich6_gpio_direction_output(struct udevice *dev, unsigned offset,
4177414112dSSimon Glass 				       int value)
41855ae10f8SBill Richardson {
4195318f18dSGabriel Huau 	int ret;
4207414112dSSimon Glass 	struct ich6_bank_priv *bank = dev_get_priv(dev);
42155ae10f8SBill Richardson 
4225318f18dSGabriel Huau 	ret = _ich6_gpio_set_direction(inl(bank->io_sel), offset, 1);
4235318f18dSGabriel Huau 	if (ret)
4245318f18dSGabriel Huau 		return ret;
4250a54745fSAxel Lin 
4265318f18dSGabriel Huau 	return _ich6_gpio_set_value(bank->lvl, offset, value);
42755ae10f8SBill Richardson }
42855ae10f8SBill Richardson 
4297414112dSSimon Glass static int ich6_gpio_get_value(struct udevice *dev, unsigned offset)
43055ae10f8SBill Richardson {
4317414112dSSimon Glass 	struct ich6_bank_priv *bank = dev_get_priv(dev);
43255ae10f8SBill Richardson 	u32 tmplong;
43357be9172SBill Richardson 	int r;
43455ae10f8SBill Richardson 
4357414112dSSimon Glass 	tmplong = inl(bank->lvl);
4367414112dSSimon Glass 	r = (tmplong & (1UL << offset)) ? 1 : 0;
43757be9172SBill Richardson 	return r;
43855ae10f8SBill Richardson }
43955ae10f8SBill Richardson 
4407414112dSSimon Glass static int ich6_gpio_set_value(struct udevice *dev, unsigned offset,
4417414112dSSimon Glass 			       int value)
44255ae10f8SBill Richardson {
4437414112dSSimon Glass 	struct ich6_bank_priv *bank = dev_get_priv(dev);
4445318f18dSGabriel Huau 	return _ich6_gpio_set_value(bank->lvl, offset, value);
44555ae10f8SBill Richardson }
4467414112dSSimon Glass 
4477414112dSSimon Glass static int ich6_gpio_get_function(struct udevice *dev, unsigned offset)
4487414112dSSimon Glass {
4497414112dSSimon Glass 	struct ich6_bank_priv *bank = dev_get_priv(dev);
4507414112dSSimon Glass 	u32 mask = 1UL << offset;
4517414112dSSimon Glass 
4527414112dSSimon Glass 	if (!(inl(bank->use_sel) & mask))
4537414112dSSimon Glass 		return GPIOF_FUNC;
4547414112dSSimon Glass 	if (inl(bank->io_sel) & mask)
4557414112dSSimon Glass 		return GPIOF_INPUT;
4567414112dSSimon Glass 	else
4577414112dSSimon Glass 		return GPIOF_OUTPUT;
4587414112dSSimon Glass }
4597414112dSSimon Glass 
4607414112dSSimon Glass static const struct dm_gpio_ops gpio_ich6_ops = {
4617414112dSSimon Glass 	.request		= ich6_gpio_request,
4627414112dSSimon Glass 	.direction_input	= ich6_gpio_direction_input,
4637414112dSSimon Glass 	.direction_output	= ich6_gpio_direction_output,
4647414112dSSimon Glass 	.get_value		= ich6_gpio_get_value,
4657414112dSSimon Glass 	.set_value		= ich6_gpio_set_value,
4667414112dSSimon Glass 	.get_function		= ich6_gpio_get_function,
4677414112dSSimon Glass };
4687414112dSSimon Glass 
4697414112dSSimon Glass static const struct udevice_id intel_ich6_gpio_ids[] = {
4707414112dSSimon Glass 	{ .compatible = "intel,ich6-gpio" },
4717414112dSSimon Glass 	{ }
4727414112dSSimon Glass };
4737414112dSSimon Glass 
4747414112dSSimon Glass U_BOOT_DRIVER(gpio_ich6) = {
4757414112dSSimon Glass 	.name	= "gpio_ich6",
4767414112dSSimon Glass 	.id	= UCLASS_GPIO,
4777414112dSSimon Glass 	.of_match = intel_ich6_gpio_ids,
4787414112dSSimon Glass 	.ops	= &gpio_ich6_ops,
4797414112dSSimon Glass 	.ofdata_to_platdata	= gpio_ich6_ofdata_to_platdata,
4807414112dSSimon Glass 	.probe	= ich6_gpio_probe,
4817414112dSSimon Glass 	.priv_auto_alloc_size = sizeof(struct ich6_bank_priv),
4827414112dSSimon Glass 	.platdata_auto_alloc_size = sizeof(struct ich6_bank_platdata),
4837414112dSSimon Glass };
484