linux/drivers/gpio/gpio-cs5535.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * AMD CS5535/CS5536 GPIO driver
   4 * Copyright (C) 2006  Advanced Micro Devices, Inc.
   5 * Copyright (C) 2007-2009  Andres Salomon <dilinger@collabora.co.uk>
   6 */
   7
   8#include <linux/kernel.h>
   9#include <linux/spinlock.h>
  10#include <linux/module.h>
  11#include <linux/platform_device.h>
  12#include <linux/gpio/driver.h>
  13#include <linux/io.h>
  14#include <linux/cs5535.h>
  15#include <asm/msr.h>
  16
  17#define DRV_NAME "cs5535-gpio"
  18
  19/*
  20 * Some GPIO pins
  21 *  31-29,23 : reserved (always mask out)
  22 *  28       : Power Button
  23 *  26       : PME#
  24 *  22-16    : LPC
  25 *  14,15    : SMBus
  26 *  9,8      : UART1
  27 *  7        : PCI INTB
  28 *  3,4      : UART2/DDC
  29 *  2        : IDE_IRQ0
  30 *  1        : AC_BEEP
  31 *  0        : PCI INTA
  32 *
  33 * If a mask was not specified, allow all except
  34 * reserved and Power Button
  35 */
  36#define GPIO_DEFAULT_MASK 0x0F7FFFFF
  37
  38static ulong mask = GPIO_DEFAULT_MASK;
  39module_param_named(mask, mask, ulong, 0444);
  40MODULE_PARM_DESC(mask, "GPIO channel mask.");
  41
  42/*
  43 * FIXME: convert this singleton driver to use the state container
  44 * design pattern, see Documentation/driver-api/driver-model/design-patterns.rst
  45 */
  46static struct cs5535_gpio_chip {
  47        struct gpio_chip chip;
  48        resource_size_t base;
  49
  50        struct platform_device *pdev;
  51        spinlock_t lock;
  52} cs5535_gpio_chip;
  53
  54/*
  55 * The CS5535/CS5536 GPIOs support a number of extra features not defined
  56 * by the gpio_chip API, so these are exported.  For a full list of the
  57 * registers, see include/linux/cs5535.h.
  58 */
  59
  60static void errata_outl(struct cs5535_gpio_chip *chip, u32 val,
  61                unsigned int reg)
  62{
  63        unsigned long addr = chip->base + 0x80 + reg;
  64
  65        /*
  66         * According to the CS5536 errata (#36), after suspend
  67         * a write to the high bank GPIO register will clear all
  68         * non-selected bits; the recommended workaround is a
  69         * read-modify-write operation.
  70         *
  71         * Don't apply this errata to the edge status GPIOs, as writing
  72         * to their lower bits will clear them.
  73         */
  74        if (reg != GPIO_POSITIVE_EDGE_STS && reg != GPIO_NEGATIVE_EDGE_STS) {
  75                if (val & 0xffff)
  76                        val |= (inl(addr) & 0xffff); /* ignore the high bits */
  77                else
  78                        val |= (inl(addr) ^ (val >> 16));
  79        }
  80        outl(val, addr);
  81}
  82
  83static void __cs5535_gpio_set(struct cs5535_gpio_chip *chip, unsigned offset,
  84                unsigned int reg)
  85{
  86        if (offset < 16)
  87                /* low bank register */
  88                outl(1 << offset, chip->base + reg);
  89        else
  90                /* high bank register */
  91                errata_outl(chip, 1 << (offset - 16), reg);
  92}
  93
  94void cs5535_gpio_set(unsigned offset, unsigned int reg)
  95{
  96        struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
  97        unsigned long flags;
  98
  99        spin_lock_irqsave(&chip->lock, flags);
 100        __cs5535_gpio_set(chip, offset, reg);
 101        spin_unlock_irqrestore(&chip->lock, flags);
 102}
 103EXPORT_SYMBOL_GPL(cs5535_gpio_set);
 104
 105static void __cs5535_gpio_clear(struct cs5535_gpio_chip *chip, unsigned offset,
 106                unsigned int reg)
 107{
 108        if (offset < 16)
 109                /* low bank register */
 110                outl(1 << (offset + 16), chip->base + reg);
 111        else
 112                /* high bank register */
 113                errata_outl(chip, 1 << offset, reg);
 114}
 115
 116void cs5535_gpio_clear(unsigned offset, unsigned int reg)
 117{
 118        struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
 119        unsigned long flags;
 120
 121        spin_lock_irqsave(&chip->lock, flags);
 122        __cs5535_gpio_clear(chip, offset, reg);
 123        spin_unlock_irqrestore(&chip->lock, flags);
 124}
 125EXPORT_SYMBOL_GPL(cs5535_gpio_clear);
 126
 127int cs5535_gpio_isset(unsigned offset, unsigned int reg)
 128{
 129        struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
 130        unsigned long flags;
 131        long val;
 132
 133        spin_lock_irqsave(&chip->lock, flags);
 134        if (offset < 16)
 135                /* low bank register */
 136                val = inl(chip->base + reg);
 137        else {
 138                /* high bank register */
 139                val = inl(chip->base + 0x80 + reg);
 140                offset -= 16;
 141        }
 142        spin_unlock_irqrestore(&chip->lock, flags);
 143
 144        return (val & (1 << offset)) ? 1 : 0;
 145}
 146EXPORT_SYMBOL_GPL(cs5535_gpio_isset);
 147
 148int cs5535_gpio_set_irq(unsigned group, unsigned irq)
 149{
 150        uint32_t lo, hi;
 151
 152        if (group > 7 || irq > 15)
 153                return -EINVAL;
 154
 155        rdmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
 156
 157        lo &= ~(0xF << (group * 4));
 158        lo |= (irq & 0xF) << (group * 4);
 159
 160        wrmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
 161        return 0;
 162}
 163EXPORT_SYMBOL_GPL(cs5535_gpio_set_irq);
 164
 165void cs5535_gpio_setup_event(unsigned offset, int pair, int pme)
 166{
 167        struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
 168        uint32_t shift = (offset % 8) * 4;
 169        unsigned long flags;
 170        uint32_t val;
 171
 172        if (offset >= 24)
 173                offset = GPIO_MAP_W;
 174        else if (offset >= 16)
 175                offset = GPIO_MAP_Z;
 176        else if (offset >= 8)
 177                offset = GPIO_MAP_Y;
 178        else
 179                offset = GPIO_MAP_X;
 180
 181        spin_lock_irqsave(&chip->lock, flags);
 182        val = inl(chip->base + offset);
 183
 184        /* Clear whatever was there before */
 185        val &= ~(0xF << shift);
 186
 187        /* Set the new value */
 188        val |= ((pair & 7) << shift);
 189
 190        /* Set the PME bit if this is a PME event */
 191        if (pme)
 192                val |= (1 << (shift + 3));
 193
 194        outl(val, chip->base + offset);
 195        spin_unlock_irqrestore(&chip->lock, flags);
 196}
 197EXPORT_SYMBOL_GPL(cs5535_gpio_setup_event);
 198
 199/*
 200 * Generic gpio_chip API support.
 201 */
 202
 203static int chip_gpio_request(struct gpio_chip *c, unsigned offset)
 204{
 205        struct cs5535_gpio_chip *chip = gpiochip_get_data(c);
 206        unsigned long flags;
 207
 208        spin_lock_irqsave(&chip->lock, flags);
 209
 210        /* check if this pin is available */
 211        if ((mask & (1 << offset)) == 0) {
 212                dev_info(&chip->pdev->dev,
 213                        "pin %u is not available (check mask)\n", offset);
 214                spin_unlock_irqrestore(&chip->lock, flags);
 215                return -EINVAL;
 216        }
 217
 218        /* disable output aux 1 & 2 on this pin */
 219        __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX1);
 220        __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX2);
 221
 222        /* disable input aux 1 on this pin */
 223        __cs5535_gpio_clear(chip, offset, GPIO_INPUT_AUX1);
 224
 225        spin_unlock_irqrestore(&chip->lock, flags);
 226
 227        return 0;
 228}
 229
 230static int chip_gpio_get(struct gpio_chip *chip, unsigned offset)
 231{
 232        return cs5535_gpio_isset(offset, GPIO_READ_BACK);
 233}
 234
 235static void chip_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
 236{
 237        if (val)
 238                cs5535_gpio_set(offset, GPIO_OUTPUT_VAL);
 239        else
 240                cs5535_gpio_clear(offset, GPIO_OUTPUT_VAL);
 241}
 242
 243static int chip_direction_input(struct gpio_chip *c, unsigned offset)
 244{
 245        struct cs5535_gpio_chip *chip = gpiochip_get_data(c);
 246        unsigned long flags;
 247
 248        spin_lock_irqsave(&chip->lock, flags);
 249        __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE);
 250        __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_ENABLE);
 251        spin_unlock_irqrestore(&chip->lock, flags);
 252
 253        return 0;
 254}
 255
 256static int chip_direction_output(struct gpio_chip *c, unsigned offset, int val)
 257{
 258        struct cs5535_gpio_chip *chip = gpiochip_get_data(c);
 259        unsigned long flags;
 260
 261        spin_lock_irqsave(&chip->lock, flags);
 262
 263        __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE);
 264        __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_ENABLE);
 265        if (val)
 266                __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_VAL);
 267        else
 268                __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_VAL);
 269
 270        spin_unlock_irqrestore(&chip->lock, flags);
 271
 272        return 0;
 273}
 274
 275static const char * const cs5535_gpio_names[] = {
 276        "GPIO0", "GPIO1", "GPIO2", "GPIO3",
 277        "GPIO4", "GPIO5", "GPIO6", "GPIO7",
 278        "GPIO8", "GPIO9", "GPIO10", "GPIO11",
 279        "GPIO12", "GPIO13", "GPIO14", "GPIO15",
 280        "GPIO16", "GPIO17", "GPIO18", "GPIO19",
 281        "GPIO20", "GPIO21", "GPIO22", NULL,
 282        "GPIO24", "GPIO25", "GPIO26", "GPIO27",
 283        "GPIO28", NULL, NULL, NULL,
 284};
 285
 286static struct cs5535_gpio_chip cs5535_gpio_chip = {
 287        .chip = {
 288                .owner = THIS_MODULE,
 289                .label = DRV_NAME,
 290
 291                .base = 0,
 292                .ngpio = 32,
 293                .names = cs5535_gpio_names,
 294                .request = chip_gpio_request,
 295
 296                .get = chip_gpio_get,
 297                .set = chip_gpio_set,
 298
 299                .direction_input = chip_direction_input,
 300                .direction_output = chip_direction_output,
 301        },
 302};
 303
 304static int cs5535_gpio_probe(struct platform_device *pdev)
 305{
 306        struct resource *res;
 307        int err = -EIO;
 308        ulong mask_orig = mask;
 309
 310        /* There are two ways to get the GPIO base address; one is by
 311         * fetching it from MSR_LBAR_GPIO, the other is by reading the
 312         * PCI BAR info.  The latter method is easier (especially across
 313         * different architectures), so we'll stick with that for now.  If
 314         * it turns out to be unreliable in the face of crappy BIOSes, we
 315         * can always go back to using MSRs.. */
 316
 317        res = platform_get_resource(pdev, IORESOURCE_IO, 0);
 318        if (!res) {
 319                dev_err(&pdev->dev, "can't fetch device resource info\n");
 320                return err;
 321        }
 322
 323        if (!devm_request_region(&pdev->dev, res->start, resource_size(res),
 324                                 pdev->name)) {
 325                dev_err(&pdev->dev, "can't request region\n");
 326                return err;
 327        }
 328
 329        /* set up the driver-specific struct */
 330        cs5535_gpio_chip.base = res->start;
 331        cs5535_gpio_chip.pdev = pdev;
 332        spin_lock_init(&cs5535_gpio_chip.lock);
 333
 334        dev_info(&pdev->dev, "reserved resource region %pR\n", res);
 335
 336        /* mask out reserved pins */
 337        mask &= 0x1F7FFFFF;
 338
 339        /* do not allow pin 28, Power Button, as there's special handling
 340         * in the PMC needed. (note 12, p. 48) */
 341        mask &= ~(1 << 28);
 342
 343        if (mask_orig != mask)
 344                dev_info(&pdev->dev, "mask changed from 0x%08lX to 0x%08lX\n",
 345                                mask_orig, mask);
 346
 347        /* finally, register with the generic GPIO API */
 348        return devm_gpiochip_add_data(&pdev->dev, &cs5535_gpio_chip.chip,
 349                                      &cs5535_gpio_chip);
 350}
 351
 352static struct platform_driver cs5535_gpio_driver = {
 353        .driver = {
 354                .name = DRV_NAME,
 355        },
 356        .probe = cs5535_gpio_probe,
 357};
 358
 359module_platform_driver(cs5535_gpio_driver);
 360
 361MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>");
 362MODULE_DESCRIPTION("AMD CS5535/CS5536 GPIO driver");
 363MODULE_LICENSE("GPL");
 364MODULE_ALIAS("platform:" DRV_NAME);
 365