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 <log.h>
  34#include <pch.h>
  35#include <pci.h>
  36#include <asm/cpu.h>
  37#include <asm/global_data.h>
  38#include <asm/gpio.h>
  39#include <asm/io.h>
  40#include <asm/pci.h>
  41
  42DECLARE_GLOBAL_DATA_PTR;
  43
  44#define GPIO_PER_BANK   32
  45
  46struct ich6_bank_priv {
  47        /* These are I/O addresses */
  48        uint16_t use_sel;
  49        uint16_t io_sel;
  50        uint16_t lvl;
  51        u32 lvl_write_cache;
  52        bool use_lvl_write_cache;
  53};
  54
  55#define GPIO_USESEL_OFFSET(x)   (x)
  56#define GPIO_IOSEL_OFFSET(x)    (x + 4)
  57#define GPIO_LVL_OFFSET(x)      (x + 8)
  58
  59static int _ich6_gpio_set_value(struct ich6_bank_priv *bank, unsigned offset,
  60                                int value)
  61{
  62        u32 val;
  63
  64        if (bank->use_lvl_write_cache)
  65                val = bank->lvl_write_cache;
  66        else
  67                val = inl(bank->lvl);
  68
  69        if (value)
  70                val |= (1UL << offset);
  71        else
  72                val &= ~(1UL << offset);
  73        outl(val, bank->lvl);
  74        if (bank->use_lvl_write_cache)
  75                bank->lvl_write_cache = val;
  76
  77        return 0;
  78}
  79
  80static int _ich6_gpio_set_direction(uint16_t base, unsigned offset, int dir)
  81{
  82        u32 val;
  83
  84        if (!dir) {
  85                val = inl(base);
  86                val |= (1UL << offset);
  87                outl(val, base);
  88        } else {
  89                val = inl(base);
  90                val &= ~(1UL << offset);
  91                outl(val, base);
  92        }
  93
  94        return 0;
  95}
  96
  97static int gpio_ich6_of_to_plat(struct udevice *dev)
  98{
  99        struct ich6_bank_plat *plat = dev_get_plat(dev);
 100        u32 gpiobase;
 101        int offset;
 102        int ret;
 103
 104        ret = pch_get_gpio_base(dev->parent, &gpiobase);
 105        if (ret)
 106                return ret;
 107
 108        offset = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "reg", -1);
 109        if (offset == -1) {
 110                debug("%s: Invalid register offset %d\n", __func__, offset);
 111                return -EINVAL;
 112        }
 113        plat->offset = offset;
 114        plat->base_addr = gpiobase + offset;
 115        plat->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
 116                                      "bank-name", NULL);
 117
 118        return 0;
 119}
 120
 121static int ich6_gpio_probe(struct udevice *dev)
 122{
 123        struct ich6_bank_plat *plat = dev_get_plat(dev);
 124        struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
 125        struct ich6_bank_priv *bank = dev_get_priv(dev);
 126        const void *prop;
 127
 128        uc_priv->gpio_count = GPIO_PER_BANK;
 129        uc_priv->bank_name = plat->bank_name;
 130        bank->use_sel = plat->base_addr;
 131        bank->io_sel = plat->base_addr + 4;
 132        bank->lvl = plat->base_addr + 8;
 133
 134        prop = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
 135                           "use-lvl-write-cache", NULL);
 136        if (prop)
 137                bank->use_lvl_write_cache = true;
 138        else
 139                bank->use_lvl_write_cache = false;
 140        bank->lvl_write_cache = 0;
 141
 142        return 0;
 143}
 144
 145static int ich6_gpio_request(struct udevice *dev, unsigned offset,
 146                             const char *label)
 147{
 148        struct ich6_bank_priv *bank = dev_get_priv(dev);
 149        u32 tmplong;
 150
 151        /*
 152         * Make sure that the GPIO pin we want isn't already in use for some
 153         * built-in hardware function. We have to check this for every
 154         * requested pin.
 155         */
 156        tmplong = inl(bank->use_sel);
 157        if (!(tmplong & (1UL << offset))) {
 158                debug("%s: gpio %d is reserved for internal use\n", __func__,
 159                      offset);
 160                return -EPERM;
 161        }
 162
 163        return 0;
 164}
 165
 166static int ich6_gpio_direction_input(struct udevice *dev, unsigned offset)
 167{
 168        struct ich6_bank_priv *bank = dev_get_priv(dev);
 169
 170        return _ich6_gpio_set_direction(bank->io_sel, offset, 0);
 171}
 172
 173static int ich6_gpio_direction_output(struct udevice *dev, unsigned offset,
 174                                       int value)
 175{
 176        int ret;
 177        struct ich6_bank_priv *bank = dev_get_priv(dev);
 178
 179        ret = _ich6_gpio_set_direction(bank->io_sel, offset, 1);
 180        if (ret)
 181                return ret;
 182
 183        return _ich6_gpio_set_value(bank, offset, value);
 184}
 185
 186static int ich6_gpio_get_value(struct udevice *dev, unsigned offset)
 187{
 188        struct ich6_bank_priv *bank = dev_get_priv(dev);
 189        u32 tmplong;
 190        int r;
 191
 192        tmplong = inl(bank->lvl);
 193        if (bank->use_lvl_write_cache)
 194                tmplong |= bank->lvl_write_cache;
 195        r = (tmplong & (1UL << offset)) ? 1 : 0;
 196        return r;
 197}
 198
 199static int ich6_gpio_set_value(struct udevice *dev, unsigned offset,
 200                               int value)
 201{
 202        struct ich6_bank_priv *bank = dev_get_priv(dev);
 203        return _ich6_gpio_set_value(bank, offset, value);
 204}
 205
 206static int ich6_gpio_get_function(struct udevice *dev, unsigned offset)
 207{
 208        struct ich6_bank_priv *bank = dev_get_priv(dev);
 209        u32 mask = 1UL << offset;
 210
 211        if (!(inl(bank->use_sel) & mask))
 212                return GPIOF_FUNC;
 213        if (inl(bank->io_sel) & mask)
 214                return GPIOF_INPUT;
 215        else
 216                return GPIOF_OUTPUT;
 217}
 218
 219static const struct dm_gpio_ops gpio_ich6_ops = {
 220        .request                = ich6_gpio_request,
 221        .direction_input        = ich6_gpio_direction_input,
 222        .direction_output       = ich6_gpio_direction_output,
 223        .get_value              = ich6_gpio_get_value,
 224        .set_value              = ich6_gpio_set_value,
 225        .get_function           = ich6_gpio_get_function,
 226};
 227
 228static const struct udevice_id intel_ich6_gpio_ids[] = {
 229        { .compatible = "intel,ich6-gpio" },
 230        { }
 231};
 232
 233U_BOOT_DRIVER(gpio_ich6) = {
 234        .name   = "gpio_ich6",
 235        .id     = UCLASS_GPIO,
 236        .of_match = intel_ich6_gpio_ids,
 237        .ops    = &gpio_ich6_ops,
 238        .of_to_plat     = gpio_ich6_of_to_plat,
 239        .probe  = ich6_gpio_probe,
 240        .priv_auto      = sizeof(struct ich6_bank_priv),
 241        .plat_auto      = sizeof(struct ich6_bank_plat),
 242};
 243