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