xref: /rk3399_rockchip-uboot/drivers/gpio/intel_ich6_gpio.c (revision 15cf75ec15db3dd7f72fff340a5986dca5f9ad45)
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>
333ddc1c7bSBin Meng #include <pch.h>
3455ae10f8SBill Richardson #include <pci.h>
35*15cf75ecSSimon Glass #include <syscon.h>
36*15cf75ecSSimon Glass #include <asm/cpu.h>
3755ae10f8SBill Richardson #include <asm/gpio.h>
3855ae10f8SBill Richardson #include <asm/io.h>
391b4f25ffSSimon Glass #include <asm/pci.h>
4055ae10f8SBill Richardson 
418b097916SSimon Glass DECLARE_GLOBAL_DATA_PTR;
428b097916SSimon Glass 
437414112dSSimon Glass #define GPIO_PER_BANK	32
447414112dSSimon Glass 
457414112dSSimon Glass struct ich6_bank_priv {
467414112dSSimon Glass 	/* These are I/O addresses */
47b71eec31SBin Meng 	uint16_t use_sel;
48b71eec31SBin Meng 	uint16_t io_sel;
49b71eec31SBin Meng 	uint16_t lvl;
5057be9172SBill Richardson };
5155ae10f8SBill Richardson 
525318f18dSGabriel Huau #define GPIO_USESEL_OFFSET(x)	(x)
535318f18dSGabriel Huau #define GPIO_IOSEL_OFFSET(x)	(x + 4)
545318f18dSGabriel Huau #define GPIO_LVL_OFFSET(x)	(x + 8)
555318f18dSGabriel Huau 
561b4f25ffSSimon Glass /* TODO: Move this to device tree, or platform data */
571b4f25ffSSimon Glass void ich_gpio_set_gpio_map(const struct pch_gpio_map *map)
581b4f25ffSSimon Glass {
591b4f25ffSSimon Glass 	gd->arch.gpio_map = map;
601b4f25ffSSimon Glass }
611b4f25ffSSimon Glass 
625318f18dSGabriel Huau static int _ich6_gpio_set_value(uint16_t base, unsigned offset, int value)
635318f18dSGabriel Huau {
645318f18dSGabriel Huau 	u32 val;
655318f18dSGabriel Huau 
665318f18dSGabriel Huau 	val = inl(base);
675318f18dSGabriel Huau 	if (value)
685318f18dSGabriel Huau 		val |= (1UL << offset);
695318f18dSGabriel Huau 	else
705318f18dSGabriel Huau 		val &= ~(1UL << offset);
715318f18dSGabriel Huau 	outl(val, base);
725318f18dSGabriel Huau 
735318f18dSGabriel Huau 	return 0;
745318f18dSGabriel Huau }
755318f18dSGabriel Huau 
765318f18dSGabriel Huau static int _ich6_gpio_set_direction(uint16_t base, unsigned offset, int dir)
775318f18dSGabriel Huau {
785318f18dSGabriel Huau 	u32 val;
795318f18dSGabriel Huau 
805318f18dSGabriel Huau 	if (!dir) {
815318f18dSGabriel Huau 		val = inl(base);
825318f18dSGabriel Huau 		val |= (1UL << offset);
835318f18dSGabriel Huau 		outl(val, base);
845318f18dSGabriel Huau 	} else {
855318f18dSGabriel Huau 		val = inl(base);
865318f18dSGabriel Huau 		val &= ~(1UL << offset);
875318f18dSGabriel Huau 		outl(val, base);
885318f18dSGabriel Huau 	}
895318f18dSGabriel Huau 
905318f18dSGabriel Huau 	return 0;
915318f18dSGabriel Huau }
925318f18dSGabriel Huau 
935318f18dSGabriel Huau int gpio_ich6_pinctrl_init(void)
945318f18dSGabriel Huau {
955318f18dSGabriel Huau 	return 0;
965318f18dSGabriel Huau }
975318f18dSGabriel Huau 
985318f18dSGabriel Huau static int gpio_ich6_ofdata_to_platdata(struct udevice *dev)
995318f18dSGabriel Huau {
1005318f18dSGabriel Huau 	struct ich6_bank_platdata *plat = dev_get_platdata(dev);
1013ddc1c7bSBin Meng 	u32 gpiobase;
1025318f18dSGabriel Huau 	int offset;
1033ddc1c7bSBin Meng 	int ret;
1045318f18dSGabriel Huau 
1053ddc1c7bSBin Meng 	ret = pch_get_gpio_base(dev->parent, &gpiobase);
1063ddc1c7bSBin Meng 	if (ret)
1073ddc1c7bSBin Meng 		return ret;
1083ddc1c7bSBin Meng 
1097414112dSSimon Glass 	offset = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1);
1107414112dSSimon Glass 	if (offset == -1) {
1117414112dSSimon Glass 		debug("%s: Invalid register offset %d\n", __func__, offset);
1127414112dSSimon Glass 		return -EINVAL;
1137414112dSSimon Glass 	}
114d6d50db8SSimon Glass 	plat->offset = offset;
1157414112dSSimon Glass 	plat->base_addr = gpiobase + offset;
1167414112dSSimon Glass 	plat->bank_name = fdt_getprop(gd->fdt_blob, dev->of_offset,
1177414112dSSimon Glass 				      "bank-name", NULL);
11855ae10f8SBill Richardson 
11955ae10f8SBill Richardson 	return 0;
12055ae10f8SBill Richardson }
12155ae10f8SBill Richardson 
1221b4f25ffSSimon Glass static int ich6_gpio_probe(struct udevice *dev)
12355ae10f8SBill Richardson {
1247414112dSSimon Glass 	struct ich6_bank_platdata *plat = dev_get_platdata(dev);
125e564f054SSimon Glass 	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
1267414112dSSimon Glass 	struct ich6_bank_priv *bank = dev_get_priv(dev);
127*15cf75ecSSimon Glass 	struct udevice *pinctrl;
1287414112dSSimon Glass 
129*15cf75ecSSimon Glass 	/* Set up pin control if available */
130*15cf75ecSSimon Glass 	syscon_get_by_driver_data(X86_SYSCON_PINCONF, &pinctrl);
1312795573aSBin Meng 
1327414112dSSimon Glass 	uc_priv->gpio_count = GPIO_PER_BANK;
1337414112dSSimon Glass 	uc_priv->bank_name = plat->bank_name;
1347414112dSSimon Glass 	bank->use_sel = plat->base_addr;
1357414112dSSimon Glass 	bank->io_sel = plat->base_addr + 4;
1367414112dSSimon Glass 	bank->lvl = plat->base_addr + 8;
1377414112dSSimon Glass 
1387414112dSSimon Glass 	return 0;
1397414112dSSimon Glass }
1407414112dSSimon Glass 
1411b4f25ffSSimon Glass static int ich6_gpio_request(struct udevice *dev, unsigned offset,
1421b4f25ffSSimon Glass 			     const char *label)
1437414112dSSimon Glass {
1447414112dSSimon Glass 	struct ich6_bank_priv *bank = dev_get_priv(dev);
14555ae10f8SBill Richardson 	u32 tmplong;
14655ae10f8SBill Richardson 
14755ae10f8SBill Richardson 	/*
14855ae10f8SBill Richardson 	 * Make sure that the GPIO pin we want isn't already in use for some
14955ae10f8SBill Richardson 	 * built-in hardware function. We have to check this for every
15055ae10f8SBill Richardson 	 * requested pin.
15155ae10f8SBill Richardson 	 */
1527414112dSSimon Glass 	tmplong = inl(bank->use_sel);
1537414112dSSimon Glass 	if (!(tmplong & (1UL << offset))) {
15457be9172SBill Richardson 		debug("%s: gpio %d is reserved for internal use\n", __func__,
1557414112dSSimon Glass 		      offset);
1567414112dSSimon Glass 		return -EPERM;
15755ae10f8SBill Richardson 	}
15855ae10f8SBill Richardson 
15955ae10f8SBill Richardson 	return 0;
16055ae10f8SBill Richardson }
16155ae10f8SBill Richardson 
1627414112dSSimon Glass static int ich6_gpio_direction_input(struct udevice *dev, unsigned offset)
16355ae10f8SBill Richardson {
1647414112dSSimon Glass 	struct ich6_bank_priv *bank = dev_get_priv(dev);
16557be9172SBill Richardson 
166e7cc0b6fSSimon Glass 	return _ich6_gpio_set_direction(bank->io_sel, offset, 0);
16755ae10f8SBill Richardson }
16855ae10f8SBill Richardson 
1697414112dSSimon Glass static int ich6_gpio_direction_output(struct udevice *dev, unsigned offset,
1707414112dSSimon Glass 				       int value)
17155ae10f8SBill Richardson {
1725318f18dSGabriel Huau 	int ret;
1737414112dSSimon Glass 	struct ich6_bank_priv *bank = dev_get_priv(dev);
17455ae10f8SBill Richardson 
175e7cc0b6fSSimon Glass 	ret = _ich6_gpio_set_direction(bank->io_sel, offset, 1);
1765318f18dSGabriel Huau 	if (ret)
1775318f18dSGabriel Huau 		return ret;
1780a54745fSAxel Lin 
1795318f18dSGabriel Huau 	return _ich6_gpio_set_value(bank->lvl, offset, value);
18055ae10f8SBill Richardson }
18155ae10f8SBill Richardson 
1827414112dSSimon Glass static int ich6_gpio_get_value(struct udevice *dev, unsigned offset)
18355ae10f8SBill Richardson {
1847414112dSSimon Glass 	struct ich6_bank_priv *bank = dev_get_priv(dev);
18555ae10f8SBill Richardson 	u32 tmplong;
18657be9172SBill Richardson 	int r;
18755ae10f8SBill Richardson 
1887414112dSSimon Glass 	tmplong = inl(bank->lvl);
1897414112dSSimon Glass 	r = (tmplong & (1UL << offset)) ? 1 : 0;
19057be9172SBill Richardson 	return r;
19155ae10f8SBill Richardson }
19255ae10f8SBill Richardson 
1937414112dSSimon Glass static int ich6_gpio_set_value(struct udevice *dev, unsigned offset,
1947414112dSSimon Glass 			       int value)
19555ae10f8SBill Richardson {
1967414112dSSimon Glass 	struct ich6_bank_priv *bank = dev_get_priv(dev);
1975318f18dSGabriel Huau 	return _ich6_gpio_set_value(bank->lvl, offset, value);
19855ae10f8SBill Richardson }
1997414112dSSimon Glass 
2007414112dSSimon Glass static int ich6_gpio_get_function(struct udevice *dev, unsigned offset)
2017414112dSSimon Glass {
2027414112dSSimon Glass 	struct ich6_bank_priv *bank = dev_get_priv(dev);
2037414112dSSimon Glass 	u32 mask = 1UL << offset;
2047414112dSSimon Glass 
2057414112dSSimon Glass 	if (!(inl(bank->use_sel) & mask))
2067414112dSSimon Glass 		return GPIOF_FUNC;
2077414112dSSimon Glass 	if (inl(bank->io_sel) & mask)
2087414112dSSimon Glass 		return GPIOF_INPUT;
2097414112dSSimon Glass 	else
2107414112dSSimon Glass 		return GPIOF_OUTPUT;
2117414112dSSimon Glass }
2127414112dSSimon Glass 
2137414112dSSimon Glass static const struct dm_gpio_ops gpio_ich6_ops = {
2147414112dSSimon Glass 	.request		= ich6_gpio_request,
2157414112dSSimon Glass 	.direction_input	= ich6_gpio_direction_input,
2167414112dSSimon Glass 	.direction_output	= ich6_gpio_direction_output,
2177414112dSSimon Glass 	.get_value		= ich6_gpio_get_value,
2187414112dSSimon Glass 	.set_value		= ich6_gpio_set_value,
2197414112dSSimon Glass 	.get_function		= ich6_gpio_get_function,
2207414112dSSimon Glass };
2217414112dSSimon Glass 
2227414112dSSimon Glass static const struct udevice_id intel_ich6_gpio_ids[] = {
2237414112dSSimon Glass 	{ .compatible = "intel,ich6-gpio" },
2247414112dSSimon Glass 	{ }
2257414112dSSimon Glass };
2267414112dSSimon Glass 
2277414112dSSimon Glass U_BOOT_DRIVER(gpio_ich6) = {
2287414112dSSimon Glass 	.name	= "gpio_ich6",
2297414112dSSimon Glass 	.id	= UCLASS_GPIO,
2307414112dSSimon Glass 	.of_match = intel_ich6_gpio_ids,
2317414112dSSimon Glass 	.ops	= &gpio_ich6_ops,
2327414112dSSimon Glass 	.ofdata_to_platdata	= gpio_ich6_ofdata_to_platdata,
2337414112dSSimon Glass 	.probe	= ich6_gpio_probe,
2347414112dSSimon Glass 	.priv_auto_alloc_size = sizeof(struct ich6_bank_priv),
2357414112dSSimon Glass 	.platdata_auto_alloc_size = sizeof(struct ich6_bank_platdata),
2367414112dSSimon Glass };
237