xref: /rk3399_rockchip-uboot/drivers/gpio/intel_ich6_gpio.c (revision 2795573a8c9c62db7b4bcf4bddf54d73b6ae61d3)
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 
387414112dSSimon Glass #define GPIO_PER_BANK	32
397414112dSSimon Glass 
407414112dSSimon Glass struct ich6_bank_priv {
417414112dSSimon Glass 	/* These are I/O addresses */
427414112dSSimon Glass 	uint32_t use_sel;
437414112dSSimon Glass 	uint32_t io_sel;
447414112dSSimon Glass 	uint32_t lvl;
4557be9172SBill Richardson };
4655ae10f8SBill Richardson 
471b4f25ffSSimon Glass /* TODO: Move this to device tree, or platform data */
481b4f25ffSSimon Glass void ich_gpio_set_gpio_map(const struct pch_gpio_map *map)
491b4f25ffSSimon Glass {
501b4f25ffSSimon Glass 	gd->arch.gpio_map = map;
511b4f25ffSSimon Glass }
521b4f25ffSSimon Glass 
537414112dSSimon Glass static int gpio_ich6_ofdata_to_platdata(struct udevice *dev)
5457be9172SBill Richardson {
557414112dSSimon Glass 	struct ich6_bank_platdata *plat = dev_get_platdata(dev);
567414112dSSimon Glass 	pci_dev_t pci_dev;			/* handle for 0:1f:0 */
5755ae10f8SBill Richardson 	u8 tmpbyte;
5855ae10f8SBill Richardson 	u16 tmpword;
5955ae10f8SBill Richardson 	u32 tmplong;
607414112dSSimon Glass 	u32 gpiobase;
617414112dSSimon Glass 	int offset;
6255ae10f8SBill Richardson 
6355ae10f8SBill Richardson 	/* Where should it be? */
647414112dSSimon Glass 	pci_dev = PCI_BDF(0, 0x1f, 0);
6555ae10f8SBill Richardson 
6655ae10f8SBill Richardson 	/* Is the device present? */
671b4f25ffSSimon Glass 	tmpword = pci_read_config16(pci_dev, PCI_VENDOR_ID);
6855ae10f8SBill Richardson 	if (tmpword != PCI_VENDOR_ID_INTEL) {
6955ae10f8SBill Richardson 		debug("%s: wrong VendorID\n", __func__);
707414112dSSimon Glass 		return -ENODEV;
7155ae10f8SBill Richardson 	}
7257be9172SBill Richardson 
731b4f25ffSSimon Glass 	tmpword = pci_read_config16(pci_dev, PCI_DEVICE_ID);
7457be9172SBill Richardson 	debug("Found %04x:%04x\n", PCI_VENDOR_ID_INTEL, tmpword);
7555ae10f8SBill Richardson 	/*
7657be9172SBill Richardson 	 * We'd like to validate the Device ID too, but pretty much any
7755ae10f8SBill Richardson 	 * value is either a) correct with slight differences, or b)
7857be9172SBill Richardson 	 * correct but undocumented. We'll have to check a bunch of other
7957be9172SBill Richardson 	 * things instead...
8055ae10f8SBill Richardson 	 */
8155ae10f8SBill Richardson 
8255ae10f8SBill Richardson 	/* I/O should already be enabled (it's a RO bit). */
831b4f25ffSSimon Glass 	tmpword = pci_read_config16(pci_dev, PCI_COMMAND);
8455ae10f8SBill Richardson 	if (!(tmpword & PCI_COMMAND_IO)) {
8555ae10f8SBill Richardson 		debug("%s: device IO not enabled\n", __func__);
867414112dSSimon Glass 		return -ENODEV;
8755ae10f8SBill Richardson 	}
8855ae10f8SBill Richardson 
8955ae10f8SBill Richardson 	/* Header Type must be normal (bits 6-0 only; see spec.) */
901b4f25ffSSimon Glass 	tmpbyte = pci_read_config8(pci_dev, PCI_HEADER_TYPE);
9155ae10f8SBill Richardson 	if ((tmpbyte & 0x7f) != PCI_HEADER_TYPE_NORMAL) {
9255ae10f8SBill Richardson 		debug("%s: invalid Header type\n", __func__);
937414112dSSimon Glass 		return -ENODEV;
9455ae10f8SBill Richardson 	}
9555ae10f8SBill Richardson 
9655ae10f8SBill Richardson 	/* Base Class must be a bridge device */
971b4f25ffSSimon Glass 	tmpbyte = pci_read_config8(pci_dev, PCI_CLASS_CODE);
9855ae10f8SBill Richardson 	if (tmpbyte != PCI_CLASS_CODE_BRIDGE) {
9955ae10f8SBill Richardson 		debug("%s: invalid class\n", __func__);
1007414112dSSimon Glass 		return -ENODEV;
10155ae10f8SBill Richardson 	}
10255ae10f8SBill Richardson 	/* Sub Class must be ISA */
1031b4f25ffSSimon Glass 	tmpbyte = pci_read_config8(pci_dev, PCI_CLASS_SUB_CODE);
10455ae10f8SBill Richardson 	if (tmpbyte != PCI_CLASS_SUB_CODE_BRIDGE_ISA) {
10555ae10f8SBill Richardson 		debug("%s: invalid subclass\n", __func__);
1067414112dSSimon Glass 		return -ENODEV;
10755ae10f8SBill Richardson 	}
10855ae10f8SBill Richardson 
10955ae10f8SBill Richardson 	/* Programming Interface must be 0x00 (no others exist) */
1101b4f25ffSSimon Glass 	tmpbyte = pci_read_config8(pci_dev, PCI_CLASS_PROG);
11155ae10f8SBill Richardson 	if (tmpbyte != 0x00) {
11255ae10f8SBill Richardson 		debug("%s: invalid interface type\n", __func__);
1137414112dSSimon Glass 		return -ENODEV;
11455ae10f8SBill Richardson 	}
11555ae10f8SBill Richardson 
11655ae10f8SBill Richardson 	/*
11755ae10f8SBill Richardson 	 * GPIOBASE moved to its current offset with ICH6, but prior to
11855ae10f8SBill Richardson 	 * that it was unused (or undocumented). Check that it looks
11955ae10f8SBill Richardson 	 * okay: not all ones or zeros, and mapped to I/O space (bit 0).
12055ae10f8SBill Richardson 	 */
1211b4f25ffSSimon Glass 	tmplong = pci_read_config32(pci_dev, PCI_CFG_GPIOBASE);
12255ae10f8SBill Richardson 	if (tmplong == 0x00000000 || tmplong == 0xffffffff ||
12355ae10f8SBill Richardson 	    !(tmplong & 0x00000001)) {
12455ae10f8SBill Richardson 		debug("%s: unexpected GPIOBASE value\n", __func__);
1257414112dSSimon Glass 		return -ENODEV;
12655ae10f8SBill Richardson 	}
12755ae10f8SBill Richardson 
12855ae10f8SBill Richardson 	/*
12955ae10f8SBill Richardson 	 * Okay, I guess we're looking at the right device. The actual
13055ae10f8SBill Richardson 	 * GPIO registers are in the PCI device's I/O space, starting
13155ae10f8SBill Richardson 	 * at the offset that we just read. Bit 0 indicates that it's
13255ae10f8SBill Richardson 	 * an I/O address, not a memory address, so mask that off.
13355ae10f8SBill Richardson 	 */
13455ae10f8SBill Richardson 	gpiobase = tmplong & 0xfffffffe;
1357414112dSSimon Glass 	offset = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1);
1367414112dSSimon Glass 	if (offset == -1) {
1377414112dSSimon Glass 		debug("%s: Invalid register offset %d\n", __func__, offset);
1387414112dSSimon Glass 		return -EINVAL;
1397414112dSSimon Glass 	}
1407414112dSSimon Glass 	plat->base_addr = gpiobase + offset;
1417414112dSSimon Glass 	plat->bank_name = fdt_getprop(gd->fdt_blob, dev->of_offset,
1427414112dSSimon Glass 				      "bank-name", NULL);
14355ae10f8SBill Richardson 
14455ae10f8SBill Richardson 	return 0;
14555ae10f8SBill Richardson }
14655ae10f8SBill Richardson 
1471b4f25ffSSimon Glass static int ich6_gpio_probe(struct udevice *dev)
14855ae10f8SBill Richardson {
1497414112dSSimon Glass 	struct ich6_bank_platdata *plat = dev_get_platdata(dev);
1507414112dSSimon Glass 	struct gpio_dev_priv *uc_priv = dev->uclass_priv;
1517414112dSSimon Glass 	struct ich6_bank_priv *bank = dev_get_priv(dev);
1527414112dSSimon Glass 
1531b4f25ffSSimon Glass 	if (gd->arch.gpio_map) {
154*2795573aSBin Meng 		setup_pch_gpios(plat->base_addr, gd->arch.gpio_map);
1551b4f25ffSSimon Glass 		gd->arch.gpio_map = NULL;
1561b4f25ffSSimon Glass 	}
157*2795573aSBin Meng 
1587414112dSSimon Glass 	uc_priv->gpio_count = GPIO_PER_BANK;
1597414112dSSimon Glass 	uc_priv->bank_name = plat->bank_name;
1607414112dSSimon Glass 	bank->use_sel = plat->base_addr;
1617414112dSSimon Glass 	bank->io_sel = plat->base_addr + 4;
1627414112dSSimon Glass 	bank->lvl = plat->base_addr + 8;
1637414112dSSimon Glass 
1647414112dSSimon Glass 	return 0;
1657414112dSSimon Glass }
1667414112dSSimon Glass 
1671b4f25ffSSimon Glass static int ich6_gpio_request(struct udevice *dev, unsigned offset,
1681b4f25ffSSimon Glass 			     const char *label)
1697414112dSSimon Glass {
1707414112dSSimon Glass 	struct ich6_bank_priv *bank = dev_get_priv(dev);
17155ae10f8SBill Richardson 	u32 tmplong;
17255ae10f8SBill Richardson 
17355ae10f8SBill Richardson 	/*
17455ae10f8SBill Richardson 	 * Make sure that the GPIO pin we want isn't already in use for some
17555ae10f8SBill Richardson 	 * built-in hardware function. We have to check this for every
17655ae10f8SBill Richardson 	 * requested pin.
17755ae10f8SBill Richardson 	 */
1787414112dSSimon Glass 	tmplong = inl(bank->use_sel);
1797414112dSSimon Glass 	if (!(tmplong & (1UL << offset))) {
18057be9172SBill Richardson 		debug("%s: gpio %d is reserved for internal use\n", __func__,
1817414112dSSimon Glass 		      offset);
1827414112dSSimon Glass 		return -EPERM;
18355ae10f8SBill Richardson 	}
18455ae10f8SBill Richardson 
18555ae10f8SBill Richardson 	return 0;
18655ae10f8SBill Richardson }
18755ae10f8SBill Richardson 
1887414112dSSimon Glass static int ich6_gpio_direction_input(struct udevice *dev, unsigned offset)
18955ae10f8SBill Richardson {
1907414112dSSimon Glass 	struct ich6_bank_priv *bank = dev_get_priv(dev);
19157be9172SBill Richardson 	u32 tmplong;
19257be9172SBill Richardson 
1937414112dSSimon Glass 	tmplong = inl(bank->io_sel);
1947414112dSSimon Glass 	tmplong |= (1UL << offset);
1957414112dSSimon Glass 	outl(bank->io_sel, tmplong);
19655ae10f8SBill Richardson 	return 0;
19755ae10f8SBill Richardson }
19855ae10f8SBill Richardson 
1997414112dSSimon Glass static int ich6_gpio_direction_output(struct udevice *dev, unsigned offset,
2007414112dSSimon Glass 				       int value)
20155ae10f8SBill Richardson {
2027414112dSSimon Glass 	struct ich6_bank_priv *bank = dev_get_priv(dev);
20355ae10f8SBill Richardson 	u32 tmplong;
20455ae10f8SBill Richardson 
2050a54745fSAxel Lin 	gpio_set_value(offset, value);
2060a54745fSAxel Lin 
2077414112dSSimon Glass 	tmplong = inl(bank->io_sel);
2087414112dSSimon Glass 	tmplong &= ~(1UL << offset);
2097414112dSSimon Glass 	outl(bank->io_sel, tmplong);
21055ae10f8SBill Richardson 	return 0;
21155ae10f8SBill Richardson }
21255ae10f8SBill Richardson 
2137414112dSSimon Glass static int ich6_gpio_get_value(struct udevice *dev, unsigned offset)
2147414112dSSimon Glass 
21555ae10f8SBill Richardson {
2167414112dSSimon Glass 	struct ich6_bank_priv *bank = dev_get_priv(dev);
21755ae10f8SBill Richardson 	u32 tmplong;
21857be9172SBill Richardson 	int r;
21955ae10f8SBill Richardson 
2207414112dSSimon Glass 	tmplong = inl(bank->lvl);
2217414112dSSimon Glass 	r = (tmplong & (1UL << offset)) ? 1 : 0;
22257be9172SBill Richardson 	return r;
22355ae10f8SBill Richardson }
22455ae10f8SBill Richardson 
2257414112dSSimon Glass static int ich6_gpio_set_value(struct udevice *dev, unsigned offset,
2267414112dSSimon Glass 			       int value)
22755ae10f8SBill Richardson {
2287414112dSSimon Glass 	struct ich6_bank_priv *bank = dev_get_priv(dev);
22955ae10f8SBill Richardson 	u32 tmplong;
23055ae10f8SBill Richardson 
2317414112dSSimon Glass 	tmplong = inl(bank->lvl);
23255ae10f8SBill Richardson 	if (value)
2337414112dSSimon Glass 		tmplong |= (1UL << offset);
23455ae10f8SBill Richardson 	else
2357414112dSSimon Glass 		tmplong &= ~(1UL << offset);
2367414112dSSimon Glass 	outl(bank->lvl, tmplong);
23755ae10f8SBill Richardson 	return 0;
23855ae10f8SBill Richardson }
2397414112dSSimon Glass 
2407414112dSSimon Glass static int ich6_gpio_get_function(struct udevice *dev, unsigned offset)
2417414112dSSimon Glass {
2427414112dSSimon Glass 	struct ich6_bank_priv *bank = dev_get_priv(dev);
2437414112dSSimon Glass 	u32 mask = 1UL << offset;
2447414112dSSimon Glass 
2457414112dSSimon Glass 	if (!(inl(bank->use_sel) & mask))
2467414112dSSimon Glass 		return GPIOF_FUNC;
2477414112dSSimon Glass 	if (inl(bank->io_sel) & mask)
2487414112dSSimon Glass 		return GPIOF_INPUT;
2497414112dSSimon Glass 	else
2507414112dSSimon Glass 		return GPIOF_OUTPUT;
2517414112dSSimon Glass }
2527414112dSSimon Glass 
2537414112dSSimon Glass static const struct dm_gpio_ops gpio_ich6_ops = {
2547414112dSSimon Glass 	.request		= ich6_gpio_request,
2557414112dSSimon Glass 	.direction_input	= ich6_gpio_direction_input,
2567414112dSSimon Glass 	.direction_output	= ich6_gpio_direction_output,
2577414112dSSimon Glass 	.get_value		= ich6_gpio_get_value,
2587414112dSSimon Glass 	.set_value		= ich6_gpio_set_value,
2597414112dSSimon Glass 	.get_function		= ich6_gpio_get_function,
2607414112dSSimon Glass };
2617414112dSSimon Glass 
2627414112dSSimon Glass static const struct udevice_id intel_ich6_gpio_ids[] = {
2637414112dSSimon Glass 	{ .compatible = "intel,ich6-gpio" },
2647414112dSSimon Glass 	{ }
2657414112dSSimon Glass };
2667414112dSSimon Glass 
2677414112dSSimon Glass U_BOOT_DRIVER(gpio_ich6) = {
2687414112dSSimon Glass 	.name	= "gpio_ich6",
2697414112dSSimon Glass 	.id	= UCLASS_GPIO,
2707414112dSSimon Glass 	.of_match = intel_ich6_gpio_ids,
2717414112dSSimon Glass 	.ops	= &gpio_ich6_ops,
2727414112dSSimon Glass 	.ofdata_to_platdata	= gpio_ich6_ofdata_to_platdata,
2737414112dSSimon Glass 	.probe	= ich6_gpio_probe,
2747414112dSSimon Glass 	.priv_auto_alloc_size = sizeof(struct ich6_bank_priv),
2757414112dSSimon Glass 	.platdata_auto_alloc_size = sizeof(struct ich6_bank_platdata),
2767414112dSSimon Glass };
277