uboot/drivers/gpio/intel_ich6_gpio.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2012 The Chromium OS Authors.
   4 */
   5
   6/*
   7 * This is a GPIO driver for Intel ICH6 and later. The x86 GPIOs are accessed
   8 * through the PCI bus. Each PCI device has 256 bytes of configuration space,
   9 * consisting of a standard header and a device-specific set of registers. PCI
  10 * bus 0, device 31, function 0 gives us access to the chipset GPIOs (among
  11 * other things). Within the PCI configuration space, the GPIOBASE register
  12 * tells us where in the device's I/O region we can find more registers to
  13 * actually access the GPIOs.
  14 *
  15 * PCI bus/device/function 0:1f:0  => PCI config registers
  16 *   PCI config register "GPIOBASE"
  17 *     PCI I/O space + [GPIOBASE]  => start of GPIO registers
  18 *       GPIO registers => gpio pin function, direction, value
  19 *
  20 *
  21 * Danger Will Robinson! Bank 0 (GPIOs 0-31) seems to be fairly stable. Most
  22 * ICH versions have more, but the decoding the matrix that describes them is
  23 * absurdly complex and constantly changing. We'll provide Bank 1 and Bank 2,
  24 * but they will ONLY work for certain unspecified chipsets because the offset
  25 * from GPIOBASE changes randomly. Even then, many GPIOs are unimplemented or
  26 * reserved or subject to arcane restrictions.
  27 */
  28
  29#include <common.h>
  30#include <dm.h>
  31#include <errno.h>
  32#include <fdtdec.h>
  33#include <pch.h>
  34#include <pci.h>
  35#include <asm/cpu.h>
  36#include <asm/gpio.h>
  37#include <asm/io.h>
  38#include <asm/pci.h>
  39
  40DECLARE_GLOBAL_DATA_PTR;
  41
  42#define GPIO_PER_BANK   32
  43
  44struct ich6_bank_priv {
  45        /* These are I/O addresses */
  46        uint16_t use_sel;
  47        uint16_t io_sel;
  48        uint16_t lvl;
  49        u32 lvl_write_cache;
  50        bool use_lvl_write_cache;
  51};
  52
  53#define GPIO_USESEL_OFFSET(x)   (x)
  54#define GPIO_IOSEL_OFFSET(x)    (x + 4)
  55#define GPIO_LVL_OFFSET(x)      (x + 8)
  56
  57static int _ich6_gpio_set_value(struct ich6_bank_priv *bank, unsigned offset,
  58                                int value)
  59{
  60        u32 val;
  61
  62        if (bank->use_lvl_write_cache)
  63                val = bank->lvl_write_cache;
  64        else
  65                val = inl(bank->lvl);
  66
  67        if (value)
  68                val |= (1UL << offset);
  69        else
  70                val &= ~(1UL << offset);
  71        outl(val, bank->lvl);
  72        if (bank->use_lvl_write_cache)
  73                bank->lvl_write_cache = val;
  74
  75        return 0;
  76}
  77
  78static int _ich6_gpio_set_direction(uint16_t base, unsigned offset, int dir)
  79{
  80        u32 val;
  81
  82        if (!dir) {
  83                val = inl(base);
  84                val |= (1UL << offset);
  85                outl(val, base);
  86        } else {
  87                val = inl(base);
  88                val &= ~(1UL << offset);
  89                outl(val, base);
  90        }
  91
  92        return 0;
  93}
  94
  95static int gpio_ich6_ofdata_to_platdata(struct udevice *dev)
  96{
  97        struct ich6_bank_platdata *plat = dev_get_platdata(dev);
  98        u32 gpiobase;
  99        int offset;
 100        int ret;
 101
 102        ret = pch_get_gpio_base(dev->parent, &gpiobase);
 103        if (ret)
 104                return ret;
 105
 106        offset = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "reg", -1);
 107        if (offset == -1) {
 108                debug("%s: Invalid register offset %d\n", __func__, offset);
 109                return -EINVAL;
 110        }
 111        plat->offset = offset;
 112        plat->base_addr = gpiobase + offset;
 113        plat->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
 114                                      "bank-name", NULL);
 115
 116        return 0;
 117}
 118
 119static int ich6_gpio_probe(struct udevice *dev)
 120{
 121        struct ich6_bank_platdata *plat = dev_get_platdata(dev);
 122        struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
 123        struct ich6_bank_priv *bank = dev_get_priv(dev);
 124        const void *prop;
 125
 126        uc_priv->gpio_count = GPIO_PER_BANK;
 127        uc_priv->bank_name = plat->bank_name;
 128        bank->use_sel = plat->base_addr;
 129        bank->io_sel = plat->base_addr + 4;
 130        bank->lvl = plat->base_addr + 8;
 131
 132        prop = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
 133                           "use-lvl-write-cache", NULL);
 134        if (prop)
 135                bank->use_lvl_write_cache = true;
 136        else
 137                bank->use_lvl_write_cache = false;
 138        bank->lvl_write_cache = 0;
 139
 140        return 0;
 141}
 142
 143static int ich6_gpio_request(struct udevice *dev, unsigned offset,
 144                             const char *label)
 145{
 146        struct ich6_bank_priv *bank = dev_get_priv(dev);
 147        u32 tmplong;
 148
 149        /*
 150         * Make sure that the GPIO pin we want isn't already in use for some
 151         * built-in hardware function. We have to check this for every
 152         * requested pin.
 153         */
 154        tmplong = inl(bank->use_sel);
 155        if (!(tmplong & (1UL << offset))) {
 156                debug("%s: gpio %d is reserved for internal use\n", __func__,
 157                      offset);
 158                return -EPERM;
 159        }
 160
 161        return 0;
 162}
 163
 164static int ich6_gpio_direction_input(struct udevice *dev, unsigned offset)
 165{
 166        struct ich6_bank_priv *bank = dev_get_priv(dev);
 167
 168        return _ich6_gpio_set_direction(bank->io_sel, offset, 0);
 169}
 170
 171static int ich6_gpio_direction_output(struct udevice *dev, unsigned offset,
 172                                       int value)
 173{
 174        int ret;
 175        struct ich6_bank_priv *bank = dev_get_priv(dev);
 176
 177        ret = _ich6_gpio_set_direction(bank->io_sel, offset, 1);
 178        if (ret)
 179                return ret;
 180
 181        return _ich6_gpio_set_value(bank, offset, value);
 182}
 183
 184static int ich6_gpio_get_value(struct udevice *dev, unsigned offset)
 185{
 186        struct ich6_bank_priv *bank = dev_get_priv(dev);
 187        u32 tmplong;
 188        int r;
 189
 190        tmplong = inl(bank->lvl);
 191        if (bank->use_lvl_write_cache)
 192                tmplong |= bank->lvl_write_cache;
 193        r = (tmplong & (1UL << offset)) ? 1 : 0;
 194        return r;
 195}
 196
 197static int ich6_gpio_set_value(struct udevice *dev, unsigned offset,
 198                               int value)
 199{
 200        struct ich6_bank_priv *bank = dev_get_priv(dev);
 201        return _ich6_gpio_set_value(bank, offset, value);
 202}
 203
 204static int ich6_gpio_get_function(struct udevice *dev, unsigned offset)
 205{
 206        struct ich6_bank_priv *bank = dev_get_priv(dev);
 207        u32 mask = 1UL << offset;
 208
 209        if (!(inl(bank->use_sel) & mask))
 210                return GPIOF_FUNC;
 211        if (inl(bank->io_sel) & mask)
 212                return GPIOF_INPUT;
 213        else
 214                return GPIOF_OUTPUT;
 215}
 216
 217static const struct dm_gpio_ops gpio_ich6_ops = {
 218        .request                = ich6_gpio_request,
 219        .direction_input        = ich6_gpio_direction_input,
 220        .direction_output       = ich6_gpio_direction_output,
 221        .get_value              = ich6_gpio_get_value,
 222        .set_value              = ich6_gpio_set_value,
 223        .get_function           = ich6_gpio_get_function,
 224};
 225
 226static const struct udevice_id intel_ich6_gpio_ids[] = {
 227        { .compatible = "intel,ich6-gpio" },
 228        { }
 229};
 230
 231U_BOOT_DRIVER(gpio_ich6) = {
 232        .name   = "gpio_ich6",
 233        .id     = UCLASS_GPIO,
 234        .of_match = intel_ich6_gpio_ids,
 235        .ops    = &gpio_ich6_ops,
 236        .ofdata_to_platdata     = gpio_ich6_ofdata_to_platdata,
 237        .probe  = ich6_gpio_probe,
 238        .priv_auto_alloc_size = sizeof(struct ich6_bank_priv),
 239        .platdata_auto_alloc_size = sizeof(struct ich6_bank_platdata),
 240};
 241