linux/drivers/gpio/gpio-amd8111.c
<<
>>
Prefs
   1/*
   2 * GPIO driver for AMD 8111 south bridges
   3 *
   4 * Copyright (c) 2012 Dmitry Eremin-Solenikov
   5 *
   6 * Based on the AMD RNG driver:
   7 * Copyright 2005 (c) MontaVista Software, Inc.
   8 * with the majority of the code coming from:
   9 *
  10 * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
  11 * (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
  12 *
  13 * derived from
  14 *
  15 * Hardware driver for the AMD 768 Random Number Generator (RNG)
  16 * (c) Copyright 2001 Red Hat Inc
  17 *
  18 * derived from
  19 *
  20 * Hardware driver for Intel i810 Random Number Generator (RNG)
  21 * Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
  22 * Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
  23 *
  24 * This file is licensed under  the terms of the GNU General Public
  25 * License version 2. This program is licensed "as is" without any
  26 * warranty of any kind, whether express or implied.
  27 */
  28#include <linux/ioport.h>
  29#include <linux/module.h>
  30#include <linux/kernel.h>
  31#include <linux/gpio/driver.h>
  32#include <linux/pci.h>
  33#include <linux/spinlock.h>
  34
  35#define PMBASE_OFFSET 0xb0
  36#define PMBASE_SIZE   0x30
  37
  38#define AMD_REG_GPIO(i) (0x10 + (i))
  39
  40#define AMD_GPIO_LTCH_STS       0x40 /* Latch status, w1 */
  41#define AMD_GPIO_RTIN           0x20 /* Real Time in, ro */
  42#define AMD_GPIO_DEBOUNCE       0x10 /* Debounce, rw */
  43#define AMD_GPIO_MODE_MASK      0x0c /* Pin Mode Select, rw */
  44#define AMD_GPIO_MODE_IN        0x00
  45#define AMD_GPIO_MODE_OUT       0x04
  46/* Enable alternative (e.g. clkout, IRQ, etc) function of the pin */
  47#define AMD_GPIO_MODE_ALTFN     0x08 /* Or 0x09 */
  48#define AMD_GPIO_X_MASK         0x03 /* In/Out specific, rw */
  49#define AMD_GPIO_X_IN_ACTIVEHI  0x01 /* Active High */
  50#define AMD_GPIO_X_IN_LATCH     0x02 /* Latched version is selected */
  51#define AMD_GPIO_X_OUT_LOW      0x00
  52#define AMD_GPIO_X_OUT_HI       0x01
  53#define AMD_GPIO_X_OUT_CLK0     0x02
  54#define AMD_GPIO_X_OUT_CLK1     0x03
  55
  56/*
  57 * Data for PCI driver interface
  58 *
  59 * This data only exists for exporting the supported
  60 * PCI ids via MODULE_DEVICE_TABLE.  We do not actually
  61 * register a pci_driver, because someone else might one day
  62 * want to register another driver on the same PCI id.
  63 */
  64static const struct pci_device_id pci_tbl[] = {
  65        { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS), 0 },
  66        { 0, }, /* terminate list */
  67};
  68MODULE_DEVICE_TABLE(pci, pci_tbl);
  69
  70struct amd_gpio {
  71        struct gpio_chip        chip;
  72        u32                     pmbase;
  73        void __iomem            *pm;
  74        struct pci_dev          *pdev;
  75        spinlock_t              lock; /* guards hw registers and orig table */
  76        u8                      orig[32];
  77};
  78
  79static int amd_gpio_request(struct gpio_chip *chip, unsigned offset)
  80{
  81        struct amd_gpio *agp = gpiochip_get_data(chip);
  82
  83        agp->orig[offset] = ioread8(agp->pm + AMD_REG_GPIO(offset)) &
  84                (AMD_GPIO_DEBOUNCE | AMD_GPIO_MODE_MASK | AMD_GPIO_X_MASK);
  85
  86        dev_dbg(&agp->pdev->dev, "Requested gpio %d, data %x\n", offset, agp->orig[offset]);
  87
  88        return 0;
  89}
  90
  91static void amd_gpio_free(struct gpio_chip *chip, unsigned offset)
  92{
  93        struct amd_gpio *agp = gpiochip_get_data(chip);
  94
  95        dev_dbg(&agp->pdev->dev, "Freed gpio %d, data %x\n", offset, agp->orig[offset]);
  96
  97        iowrite8(agp->orig[offset], agp->pm + AMD_REG_GPIO(offset));
  98}
  99
 100static void amd_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
 101{
 102        struct amd_gpio *agp = gpiochip_get_data(chip);
 103        u8 temp;
 104        unsigned long flags;
 105
 106        spin_lock_irqsave(&agp->lock, flags);
 107        temp = ioread8(agp->pm + AMD_REG_GPIO(offset));
 108        temp = (temp & AMD_GPIO_DEBOUNCE) | AMD_GPIO_MODE_OUT | (value ? AMD_GPIO_X_OUT_HI : AMD_GPIO_X_OUT_LOW);
 109        iowrite8(temp, agp->pm + AMD_REG_GPIO(offset));
 110        spin_unlock_irqrestore(&agp->lock, flags);
 111
 112        dev_dbg(&agp->pdev->dev, "Setting gpio %d, value %d, reg=%02x\n", offset, !!value, temp);
 113}
 114
 115static int amd_gpio_get(struct gpio_chip *chip, unsigned offset)
 116{
 117        struct amd_gpio *agp = gpiochip_get_data(chip);
 118        u8 temp;
 119
 120        temp = ioread8(agp->pm + AMD_REG_GPIO(offset));
 121
 122        dev_dbg(&agp->pdev->dev, "Getting gpio %d, reg=%02x\n", offset, temp);
 123
 124        return (temp & AMD_GPIO_RTIN) ? 1 : 0;
 125}
 126
 127static int amd_gpio_dirout(struct gpio_chip *chip, unsigned offset, int value)
 128{
 129        struct amd_gpio *agp = gpiochip_get_data(chip);
 130        u8 temp;
 131        unsigned long flags;
 132
 133        spin_lock_irqsave(&agp->lock, flags);
 134        temp = ioread8(agp->pm + AMD_REG_GPIO(offset));
 135        temp = (temp & AMD_GPIO_DEBOUNCE) | AMD_GPIO_MODE_OUT | (value ? AMD_GPIO_X_OUT_HI : AMD_GPIO_X_OUT_LOW);
 136        iowrite8(temp, agp->pm + AMD_REG_GPIO(offset));
 137        spin_unlock_irqrestore(&agp->lock, flags);
 138
 139        dev_dbg(&agp->pdev->dev, "Dirout gpio %d, value %d, reg=%02x\n", offset, !!value, temp);
 140
 141        return 0;
 142}
 143
 144static int amd_gpio_dirin(struct gpio_chip *chip, unsigned offset)
 145{
 146        struct amd_gpio *agp = gpiochip_get_data(chip);
 147        u8 temp;
 148        unsigned long flags;
 149
 150        spin_lock_irqsave(&agp->lock, flags);
 151        temp = ioread8(agp->pm + AMD_REG_GPIO(offset));
 152        temp = (temp & AMD_GPIO_DEBOUNCE) | AMD_GPIO_MODE_IN;
 153        iowrite8(temp, agp->pm + AMD_REG_GPIO(offset));
 154        spin_unlock_irqrestore(&agp->lock, flags);
 155
 156        dev_dbg(&agp->pdev->dev, "Dirin gpio %d, reg=%02x\n", offset, temp);
 157
 158        return 0;
 159}
 160
 161static struct amd_gpio gp = {
 162        .chip = {
 163                .label          = "AMD GPIO",
 164                .owner          = THIS_MODULE,
 165                .base           = -1,
 166                .ngpio          = 32,
 167                .request        = amd_gpio_request,
 168                .free           = amd_gpio_free,
 169                .set            = amd_gpio_set,
 170                .get            = amd_gpio_get,
 171                .direction_output = amd_gpio_dirout,
 172                .direction_input = amd_gpio_dirin,
 173        },
 174};
 175
 176static int __init amd_gpio_init(void)
 177{
 178        int err = -ENODEV;
 179        struct pci_dev *pdev = NULL;
 180        const struct pci_device_id *ent;
 181
 182
 183        /* We look for our device - AMD South Bridge
 184         * I don't know about a system with two such bridges,
 185         * so we can assume that there is max. one device.
 186         *
 187         * We can't use plain pci_driver mechanism,
 188         * as the device is really a multiple function device,
 189         * main driver that binds to the pci_device is an smbus
 190         * driver and have to find & bind to the device this way.
 191         */
 192        for_each_pci_dev(pdev) {
 193                ent = pci_match_id(pci_tbl, pdev);
 194                if (ent)
 195                        goto found;
 196        }
 197        /* Device not found. */
 198        goto out;
 199
 200found:
 201        err = pci_read_config_dword(pdev, 0x58, &gp.pmbase);
 202        if (err)
 203                goto out;
 204        err = -EIO;
 205        gp.pmbase &= 0x0000FF00;
 206        if (gp.pmbase == 0)
 207                goto out;
 208        if (!devm_request_region(&pdev->dev, gp.pmbase + PMBASE_OFFSET,
 209                PMBASE_SIZE, "AMD GPIO")) {
 210                dev_err(&pdev->dev, "AMD GPIO region 0x%x already in use!\n",
 211                        gp.pmbase + PMBASE_OFFSET);
 212                err = -EBUSY;
 213                goto out;
 214        }
 215        gp.pm = ioport_map(gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
 216        if (!gp.pm) {
 217                dev_err(&pdev->dev, "Couldn't map io port into io memory\n");
 218                err = -ENOMEM;
 219                goto out;
 220        }
 221        gp.pdev = pdev;
 222        gp.chip.parent = &pdev->dev;
 223
 224        spin_lock_init(&gp.lock);
 225
 226        printk(KERN_INFO "AMD-8111 GPIO detected\n");
 227        err = gpiochip_add_data(&gp.chip, &gp);
 228        if (err) {
 229                printk(KERN_ERR "GPIO registering failed (%d)\n",
 230                       err);
 231                ioport_unmap(gp.pm);
 232                goto out;
 233        }
 234out:
 235        return err;
 236}
 237
 238static void __exit amd_gpio_exit(void)
 239{
 240        gpiochip_remove(&gp.chip);
 241        ioport_unmap(gp.pm);
 242}
 243
 244module_init(amd_gpio_init);
 245module_exit(amd_gpio_exit);
 246
 247MODULE_AUTHOR("The Linux Kernel team");
 248MODULE_DESCRIPTION("GPIO driver for AMD chipsets");
 249MODULE_LICENSE("GPL");
 250