164b17977SSimon Glass /*
264b17977SSimon Glass * Copyright (c) 2012 The Chromium OS Authors.
364b17977SSimon Glass * SPDX-License-Identifier: GPL-2.0+
464b17977SSimon Glass */
564b17977SSimon Glass
664b17977SSimon Glass #include <common.h>
764b17977SSimon Glass #include <dm.h>
864b17977SSimon Glass #include <errno.h>
964b17977SSimon Glass #include <fdtdec.h>
1064b17977SSimon Glass #include <pch.h>
1164b17977SSimon Glass #include <pci.h>
1226f50fbeSSimon Glass #include <syscon.h>
1364b17977SSimon Glass #include <asm/cpu.h>
1464b17977SSimon Glass #include <asm/gpio.h>
1564b17977SSimon Glass #include <asm/io.h>
1664b17977SSimon Glass #include <asm/pci.h>
1764b17977SSimon Glass #include <asm/arch/gpio.h>
1864b17977SSimon Glass #include <dt-bindings/gpio/x86-gpio.h>
1964b17977SSimon Glass
2064b17977SSimon Glass DECLARE_GLOBAL_DATA_PTR;
2164b17977SSimon Glass
2264b17977SSimon Glass /**
2364b17977SSimon Glass * struct broadwell_bank_priv - Private driver data
2464b17977SSimon Glass *
2564b17977SSimon Glass * @regs: Pointer to GPIO registers
2664b17977SSimon Glass * @bank: Bank number for this bank (0, 1 or 2)
2764b17977SSimon Glass * @offset: GPIO offset for this bank (0, 32 or 64)
2864b17977SSimon Glass */
2964b17977SSimon Glass struct broadwell_bank_priv {
3064b17977SSimon Glass struct pch_lp_gpio_regs *regs;
3164b17977SSimon Glass int bank;
3264b17977SSimon Glass int offset;
3364b17977SSimon Glass };
3464b17977SSimon Glass
broadwell_gpio_request(struct udevice * dev,unsigned offset,const char * label)3564b17977SSimon Glass static int broadwell_gpio_request(struct udevice *dev, unsigned offset,
3664b17977SSimon Glass const char *label)
3764b17977SSimon Glass {
3864b17977SSimon Glass struct broadwell_bank_priv *priv = dev_get_priv(dev);
3964b17977SSimon Glass struct pch_lp_gpio_regs *regs = priv->regs;
4064b17977SSimon Glass u32 val;
4164b17977SSimon Glass
4264b17977SSimon Glass /*
4364b17977SSimon Glass * Make sure that the GPIO pin we want isn't already in use for some
4464b17977SSimon Glass * built-in hardware function. We have to check this for every
4564b17977SSimon Glass * requested pin.
4664b17977SSimon Glass */
4764b17977SSimon Glass debug("%s: request bank %d offset %d: ", __func__, priv->bank, offset);
4864b17977SSimon Glass val = inl(®s->own[priv->bank]);
4964b17977SSimon Glass if (!(val & (1UL << offset))) {
5064b17977SSimon Glass debug("gpio is reserved for internal use\n");
5164b17977SSimon Glass return -EPERM;
5264b17977SSimon Glass }
5364b17977SSimon Glass debug("ok\n");
5464b17977SSimon Glass
5564b17977SSimon Glass return 0;
5664b17977SSimon Glass }
5764b17977SSimon Glass
broadwell_gpio_direction_input(struct udevice * dev,unsigned offset)5864b17977SSimon Glass static int broadwell_gpio_direction_input(struct udevice *dev, unsigned offset)
5964b17977SSimon Glass {
6064b17977SSimon Glass struct broadwell_bank_priv *priv = dev_get_priv(dev);
6164b17977SSimon Glass struct pch_lp_gpio_regs *regs = priv->regs;
6264b17977SSimon Glass
6364b17977SSimon Glass setio_32(®s->config[priv->offset + offset], CONFA_DIR_INPUT);
6464b17977SSimon Glass
6564b17977SSimon Glass return 0;
6664b17977SSimon Glass }
6764b17977SSimon Glass
broadwell_gpio_get_value(struct udevice * dev,unsigned offset)6864b17977SSimon Glass static int broadwell_gpio_get_value(struct udevice *dev, unsigned offset)
6964b17977SSimon Glass {
7064b17977SSimon Glass struct broadwell_bank_priv *priv = dev_get_priv(dev);
7164b17977SSimon Glass struct pch_lp_gpio_regs *regs = priv->regs;
7264b17977SSimon Glass
7364b17977SSimon Glass return inl(®s->config[priv->offset + offset]) & CONFA_LEVEL_HIGH ?
7464b17977SSimon Glass 1 : 0;
7564b17977SSimon Glass }
7664b17977SSimon Glass
broadwell_gpio_set_value(struct udevice * dev,unsigned offset,int value)7764b17977SSimon Glass static int broadwell_gpio_set_value(struct udevice *dev, unsigned offset,
7864b17977SSimon Glass int value)
7964b17977SSimon Glass {
8064b17977SSimon Glass struct broadwell_bank_priv *priv = dev_get_priv(dev);
8164b17977SSimon Glass struct pch_lp_gpio_regs *regs = priv->regs;
8264b17977SSimon Glass
8364b17977SSimon Glass debug("%s: dev=%s, offset=%d, value=%d\n", __func__, dev->name, offset,
8464b17977SSimon Glass value);
8564b17977SSimon Glass clrsetio_32(®s->config[priv->offset + offset], CONFA_OUTPUT_HIGH,
8664b17977SSimon Glass value ? CONFA_OUTPUT_HIGH : 0);
8764b17977SSimon Glass
8864b17977SSimon Glass return 0;
8964b17977SSimon Glass }
9064b17977SSimon Glass
broadwell_gpio_direction_output(struct udevice * dev,unsigned offset,int value)9164b17977SSimon Glass static int broadwell_gpio_direction_output(struct udevice *dev, unsigned offset,
9264b17977SSimon Glass int value)
9364b17977SSimon Glass {
9464b17977SSimon Glass struct broadwell_bank_priv *priv = dev_get_priv(dev);
9564b17977SSimon Glass struct pch_lp_gpio_regs *regs = priv->regs;
9664b17977SSimon Glass
9764b17977SSimon Glass broadwell_gpio_set_value(dev, offset, value);
9864b17977SSimon Glass clrio_32(®s->config[priv->offset + offset], CONFA_DIR_INPUT);
9964b17977SSimon Glass
10064b17977SSimon Glass return 0;
10164b17977SSimon Glass }
10264b17977SSimon Glass
broadwell_gpio_get_function(struct udevice * dev,unsigned offset)10364b17977SSimon Glass static int broadwell_gpio_get_function(struct udevice *dev, unsigned offset)
10464b17977SSimon Glass {
10564b17977SSimon Glass struct broadwell_bank_priv *priv = dev_get_priv(dev);
10664b17977SSimon Glass struct pch_lp_gpio_regs *regs = priv->regs;
10764b17977SSimon Glass u32 mask = 1UL << offset;
10864b17977SSimon Glass
10964b17977SSimon Glass if (!(inl(®s->own[priv->bank]) & mask))
11064b17977SSimon Glass return GPIOF_FUNC;
11164b17977SSimon Glass if (inl(®s->config[priv->offset + offset]) & CONFA_DIR_INPUT)
11264b17977SSimon Glass return GPIOF_INPUT;
11364b17977SSimon Glass else
11464b17977SSimon Glass return GPIOF_OUTPUT;
11564b17977SSimon Glass }
11664b17977SSimon Glass
broadwell_gpio_probe(struct udevice * dev)11764b17977SSimon Glass static int broadwell_gpio_probe(struct udevice *dev)
11864b17977SSimon Glass {
11964b17977SSimon Glass struct broadwell_bank_platdata *plat = dev_get_platdata(dev);
12064b17977SSimon Glass struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
12164b17977SSimon Glass struct broadwell_bank_priv *priv = dev_get_priv(dev);
12226f50fbeSSimon Glass struct udevice *pinctrl;
12326f50fbeSSimon Glass int ret;
12426f50fbeSSimon Glass
12526f50fbeSSimon Glass /* Set up pin control if available */
12626f50fbeSSimon Glass ret = syscon_get_by_driver_data(X86_SYSCON_PINCONF, &pinctrl);
12726f50fbeSSimon Glass debug("%s, pinctrl=%p, ret=%d\n", __func__, pinctrl, ret);
12864b17977SSimon Glass
12964b17977SSimon Glass uc_priv->gpio_count = GPIO_PER_BANK;
13064b17977SSimon Glass uc_priv->bank_name = plat->bank_name;
13164b17977SSimon Glass
13264b17977SSimon Glass priv->regs = (struct pch_lp_gpio_regs *)(uintptr_t)plat->base_addr;
13364b17977SSimon Glass priv->bank = plat->bank;
13464b17977SSimon Glass priv->offset = priv->bank * 32;
13564b17977SSimon Glass debug("%s: probe done, regs %p, bank %d\n", __func__, priv->regs,
13664b17977SSimon Glass priv->bank);
13764b17977SSimon Glass
13864b17977SSimon Glass return 0;
13964b17977SSimon Glass }
14064b17977SSimon Glass
broadwell_gpio_ofdata_to_platdata(struct udevice * dev)14164b17977SSimon Glass static int broadwell_gpio_ofdata_to_platdata(struct udevice *dev)
14264b17977SSimon Glass {
14364b17977SSimon Glass struct broadwell_bank_platdata *plat = dev_get_platdata(dev);
14464b17977SSimon Glass u32 gpiobase;
14564b17977SSimon Glass int bank;
14664b17977SSimon Glass int ret;
14764b17977SSimon Glass
14864b17977SSimon Glass ret = pch_get_gpio_base(dev->parent, &gpiobase);
14964b17977SSimon Glass if (ret)
15064b17977SSimon Glass return ret;
15164b17977SSimon Glass
152*e160f7d4SSimon Glass bank = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "reg", -1);
15364b17977SSimon Glass if (bank == -1) {
15464b17977SSimon Glass debug("%s: Invalid bank number %d\n", __func__, bank);
15564b17977SSimon Glass return -EINVAL;
15664b17977SSimon Glass }
15764b17977SSimon Glass plat->bank = bank;
15864b17977SSimon Glass plat->base_addr = gpiobase;
159*e160f7d4SSimon Glass plat->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
16064b17977SSimon Glass "bank-name", NULL);
16164b17977SSimon Glass
16264b17977SSimon Glass return 0;
16364b17977SSimon Glass }
16464b17977SSimon Glass
16564b17977SSimon Glass static const struct dm_gpio_ops gpio_broadwell_ops = {
16664b17977SSimon Glass .request = broadwell_gpio_request,
16764b17977SSimon Glass .direction_input = broadwell_gpio_direction_input,
16864b17977SSimon Glass .direction_output = broadwell_gpio_direction_output,
16964b17977SSimon Glass .get_value = broadwell_gpio_get_value,
17064b17977SSimon Glass .set_value = broadwell_gpio_set_value,
17164b17977SSimon Glass .get_function = broadwell_gpio_get_function,
17264b17977SSimon Glass };
17364b17977SSimon Glass
17464b17977SSimon Glass static const struct udevice_id intel_broadwell_gpio_ids[] = {
17564b17977SSimon Glass { .compatible = "intel,broadwell-gpio" },
17664b17977SSimon Glass { }
17764b17977SSimon Glass };
17864b17977SSimon Glass
17964b17977SSimon Glass U_BOOT_DRIVER(gpio_broadwell) = {
18064b17977SSimon Glass .name = "gpio_broadwell",
18164b17977SSimon Glass .id = UCLASS_GPIO,
18264b17977SSimon Glass .of_match = intel_broadwell_gpio_ids,
18364b17977SSimon Glass .ops = &gpio_broadwell_ops,
18464b17977SSimon Glass .ofdata_to_platdata = broadwell_gpio_ofdata_to_platdata,
18564b17977SSimon Glass .probe = broadwell_gpio_probe,
18664b17977SSimon Glass .priv_auto_alloc_size = sizeof(struct broadwell_bank_priv),
18764b17977SSimon Glass .platdata_auto_alloc_size = sizeof(struct broadwell_bank_platdata),
18864b17977SSimon Glass };
189