Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- commit 90bf20c80db47eca56d6a2099121218c2d5e47c9
- Author: Tobias Diedrich <ranma+coreboot@tdiedrich.de>
- Date: Sat Jun 13 21:36:39 2015 +0200
- Tmp: Add gpio-sb8xx driver
- diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
- index caefe80..f4d7dd5 100644
- --- a/drivers/gpio/Kconfig
- +++ b/drivers/gpio/Kconfig
- @@ -884,6 +884,18 @@ config GPIO_BT8XX
- If unsure, say N.
- +config GPIO_SB8XX
- + tristate "AMD SB8XX GPIO driver"
- + depends on PCI
- + depends on GPIO_ACPI
- + help
- + Say Y here to support AMD SB8XX/SB9XX/A5X/A8X south bridge GPIO.
- +
- + This driver also requires a corresponding entry in the ACPI DSDT,
- + see the documentation file at Documentation/gpio/gpio-sb8xx.txt
- +
- + If unsure, say N.
- +
- config GPIO_INTEL_MID
- bool "Intel Mid GPIO support"
- depends on PCI && X86
- diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
- index f71bb97..b6d7d9d 100644
- --- a/drivers/gpio/Makefile
- +++ b/drivers/gpio/Makefile
- @@ -35,6 +35,7 @@ obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o
- obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o
- obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o
- obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o
- +obj-$(CONFIG_GPIO_SB8XX) += gpio-sb8xx.o
- obj-$(CONFIG_GPIO_ICH) += gpio-ich.o
- obj-$(CONFIG_GPIO_IOP) += gpio-iop.o
- obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.o
- diff --git a/drivers/gpio/gpio-sb8xx.c b/drivers/gpio/gpio-sb8xx.c
- new file mode 100644
- index 0000000..ead6f78
- --- /dev/null
- +++ b/drivers/gpio/gpio-sb8xx.c
- @@ -0,0 +1,313 @@
- +/*
- + * GPIO driver for AMD SB8XX/SB9XX/A5X/A8X south bridges
- + *
- + * Copyright (c) 2015 Tobias Diedrich
- + *
- + * Based on the AMD 8111 GPIO driver:
- + * Copyright (c) 2012 Dmitry Eremin-Solenikov
- + *
- + * Based on the AMD RNG driver:
- + * Copyright 2005 (c) MontaVista Software, Inc.
- + * with the majority of the code coming from:
- + *
- + * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
- + * (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
- + *
- + * derived from
- + *
- + * Hardware driver for the AMD 768 Random Number Generator (RNG)
- + * (c) Copyright 2001 Red Hat Inc
- + *
- + * derived from
- + *
- + * Hardware driver for Intel i810 Random Number Generator (RNG)
- + * Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
- + * Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
- + *
- + * This file is licensed under the terms of the GNU General Public
- + * License version 2. This program is licensed "as is" without any
- + * warranty of any kind, whether express or implied.
- + */
- +#include <linux/module.h>
- +#include <linux/kernel.h>
- +#include <linux/io.h>
- +#include <linux/gpio.h>
- +#include <linux/pci.h>
- +#include <linux/spinlock.h>
- +#include <linux/platform_device.h>
- +
- +#define PMBASE_OFFSET 0x100
- +#define PMBASE_SIZE 0x100
- +
- +#define MAX_GPIO 256
- +
- +#define ACPI_MMIO_BAR 0x24
- +
- +#define REG_GPIO(i) (0x00 + (i))
- +
- +#define GPIO_OWN_IMC 0x01 /* OwnedByImc */
- +#define GPIO_OWN_HOST 0x02 /* OwnedByHost */
- +#define GPIO_STICKY 0x04 /* Settings sticky across reset */
- +#define GPIO_PULLUP_B 0x08 /* 0 to enable pull-up */
- +#define GPIO_PULLDOWN 0x10 /* 1 to enable pull-down */
- +#define GPIO_OUT_EN_B 0x20 /* 0 to enable output */
- +#define GPIO_OUT 0x40
- +#define GPIO_IN 0x80
- +
- +/*
- + * Data for PCI driver interface
- + *
- + * This data only exists for exporting the supported
- + * PCI ids via MODULE_DEVICE_TABLE. We do not actually
- + * register a pci_driver, because the smbus driver is
- + * using the same PCI id.
- + */
- +static const struct pci_device_id pci_tbl[] = {
- + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS), 0 },
- + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS), 0 },
- + { 0, }, /* terminate list */
- +};
- +MODULE_DEVICE_TABLE(pci, pci_tbl);
- +
- +struct amd_sb8xx_gpio {
- + struct gpio_chip chip;
- + u32 pmbase;
- + void __iomem *pm;
- + struct device *dev;
- + spinlock_t lock; /* guards hw registers and orig table */
- + u8 orig[MAX_GPIO];
- +};
- +
- +#define to_agp(chip) container_of(chip, struct amd_sb8xx_gpio, chip)
- +
- +static int amd_sb8xx_gpio_request(struct gpio_chip *chip, unsigned offset)
- +{
- + struct amd_sb8xx_gpio *agp = to_agp(chip);
- + u8 orig = ioread8(agp->pm + offset);
- +
- + if (orig & GPIO_OWN_IMC) {
- + dev_err(agp->dev, "Requested gpio %d is owned by IMC\n", offset);
- + return -EINVAL;
- + }
- +
- + agp->orig[offset] = orig;
- + dev_dbg(agp->dev, "Requested gpio %d, data %x\n", offset, orig);
- + return 0;
- +}
- +
- +static void amd_sb8xx_gpio_free(struct gpio_chip *chip, unsigned offset)
- +{
- + struct amd_sb8xx_gpio *agp = to_agp(chip);
- +
- + dev_dbg(agp->dev, "Freed gpio %d, data %x\n", offset, agp->orig[offset]);
- +
- + iowrite8(agp->orig[offset], agp->pm + offset);
- +}
- +
- +static void amd_sb8xx_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
- +{
- + struct amd_sb8xx_gpio *agp = to_agp(chip);
- + u8 temp;
- + unsigned long flags;
- +
- + spin_lock_irqsave(&agp->lock, flags);
- + temp = ioread8(agp->pm + offset);
- + temp = (temp & ~(GPIO_OUT | GPIO_OUT_EN_B)) | (value ? GPIO_OUT : 0);
- + iowrite8(temp, agp->pm + offset);
- + spin_unlock_irqrestore(&agp->lock, flags);
- +
- + dev_dbg(agp->dev, "Setting gpio %d, value %d, reg=%02x\n", offset, !!value, temp);
- +}
- +
- +static int amd_sb8xx_gpio_get(struct gpio_chip *chip, unsigned offset)
- +{
- + struct amd_sb8xx_gpio *agp = to_agp(chip);
- + u8 temp;
- +
- + temp = ioread8(agp->pm + offset);
- +
- + dev_dbg(agp->dev, "Getting gpio %d, reg=%02x\n", offset, temp);
- +
- + return (temp & GPIO_IN) ? 1 : 0;
- +}
- +
- +static int amd_sb8xx_gpio_dirout(struct gpio_chip *chip, unsigned offset, int value)
- +{
- + struct amd_sb8xx_gpio *agp = to_agp(chip);
- + u8 temp;
- + unsigned long flags;
- +
- + spin_lock_irqsave(&agp->lock, flags);
- + temp = ioread8(agp->pm + offset);
- + temp = (temp & ~(GPIO_OUT | GPIO_OUT_EN_B)) | (value ? GPIO_OUT : 0);
- + iowrite8(temp, agp->pm + offset);
- + spin_unlock_irqrestore(&agp->lock, flags);
- +
- + dev_dbg(agp->dev, "Dirout gpio %d, value %d, reg=%02x\n", offset, !!value, temp);
- +
- + return 0;
- +}
- +
- +static int amd_sb8xx_gpio_dirin(struct gpio_chip *chip, unsigned offset)
- +{
- + struct amd_sb8xx_gpio *agp = to_agp(chip);
- + u8 temp;
- + unsigned long flags;
- +
- + spin_lock_irqsave(&agp->lock, flags);
- + temp = ioread8(agp->pm + offset);
- + temp = temp | GPIO_OUT_EN_B;
- + iowrite8(temp, agp->pm + offset);
- + spin_unlock_irqrestore(&agp->lock, flags);
- +
- + dev_dbg(agp->dev, "Dirin gpio %d, reg=%02x\n", offset, temp);
- +
- + return 0;
- +}
- +
- +static struct amd_sb8xx_gpio gp = {
- + .chip = {
- + .label = "AMD SB8XX/SB9XX/A5X/A8X GPIO driver",
- + .owner = THIS_MODULE,
- + .base = -1,
- + .ngpio = 224,
- + .request = amd_sb8xx_gpio_request,
- + .free = amd_sb8xx_gpio_free,
- + .set = amd_sb8xx_gpio_set,
- + .get = amd_sb8xx_gpio_get,
- + .direction_output = amd_sb8xx_gpio_dirout,
- + .direction_input = amd_sb8xx_gpio_dirin,
- + },
- +};
- +
- +static u32 amd_acpi_mmio_base(void)
- +{
- + u32 bar = 0;
- + outb_p(ACPI_MMIO_BAR, 0xcd6);
- + bar = inb_p(0xcd7);
- + outb_p(ACPI_MMIO_BAR + 1, 0xcd6);
- + bar |= inb_p(0xcd7) << 8;
- + outb_p(ACPI_MMIO_BAR + 2, 0xcd6);
- + bar |= inb_p(0xcd7) << 16;
- + outb_p(ACPI_MMIO_BAR + 3, 0xcd6);
- + bar |= inb_p(0xcd7) << 24;
- +
- + return bar;
- +}
- +
- +static struct pci_dev *amd_get_sb_pdev(void)
- +{
- + struct pci_dev *pdev = NULL;
- + const struct pci_device_id *ent;
- +
- + /* We look for our device - AMD South Bridge
- + * I don't know about a system with two such bridges,
- + * so we can assume that there is max. one device.
- + *
- + * We can't use plain pci_driver mechanism,
- + * as the device is really a multiple function device,
- + * main driver that binds to the pci_device is an smbus
- + * driver and have to find & bind to the device this way.
- + */
- + for_each_pci_dev(pdev) {
- + ent = pci_match_id(pci_tbl, pdev);
- + if (ent)
- + return pdev;
- + }
- +
- + return NULL;
- +}
- +
- +static int sb8xx_plat_probe(struct platform_device *pdev)
- +{
- + int err = -ENODEV;
- + struct pci_dev *pcidev = amd_get_sb_pdev();
- + u32 acpi_bar = 0;
- +
- + printk(KERN_INFO "sb8xx_plat_probe()\n");
- +
- + /* Device not found. */
- + if (!pcidev)
- + goto out;
- +
- + acpi_bar = amd_acpi_mmio_base();
- + printk(KERN_INFO "Found AMD SB8XX-style southbridge, AcpiMmioEn=0x%08x\n", acpi_bar);
- + if ((acpi_bar & 3) != 1) {
- + printk(KERN_ERR "acpi bar not memory mapped\n");
- + goto out;
- + }
- +
- + gp.pmbase = acpi_bar & ~3;
- + printk(KERN_INFO "Requesting region @0x%08x\n", gp.pmbase + PMBASE_OFFSET);
- + if (!devm_request_mem_region(&pdev->dev, gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE, "AMD SB8XX GPIO")) {
- + dev_err(&pdev->dev, "AMD GPIO region 0x%x already in use!\n",
- + gp.pmbase + PMBASE_OFFSET);
- + err = -EBUSY;
- + goto out;
- + }
- +
- + printk(KERN_INFO "Mapping region @0x%08x\n", gp.pmbase + PMBASE_OFFSET);
- + gp.pm = devm_ioremap(&pdev->dev, gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
- + if (!gp.pm) {
- + dev_err(&pdev->dev, "Couldn't map io port into io memory\n");
- + devm_release_mem_region(&pdev->dev, gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
- + err = -ENOMEM;
- + goto out;
- + }
- + gp.dev = &pdev->dev;
- + gp.chip.dev = &pdev->dev;
- +
- + spin_lock_init(&gp.lock);
- +
- + printk(KERN_INFO "AMD SB8XX GPIO detected\n");
- + err = gpiochip_add(&gp.chip);
- + if (err) {
- + printk(KERN_ERR "GPIO registering failed (%d)\n",
- + err);
- + devm_release_mem_region(&pdev->dev, gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
- + goto out;
- + }
- +
- +out:
- + return err;
- +}
- +
- +static int sb8xx_plat_remove(struct platform_device *pdev)
- +{
- + printk(KERN_INFO "sb8xx_plat_remove()\n");
- + gpiochip_remove(&gp.chip);
- + devm_release_mem_region(&pdev->dev, gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
- + return 0;
- +}
- +
- +static struct of_device_id sb8xx_gpio_of_match[] = {
- + { .compatible = "gpio-sb8xx", },
- + { /* end of table */ }
- +};
- +MODULE_DEVICE_TABLE(of, sb8xx_gpio_of_match);
- +
- +static struct platform_driver sb8xx_gpio_driver = {
- + .driver = {
- + .name = "gpio-sb8xx",
- + .of_match_table = sb8xx_gpio_of_match,
- + },
- + .probe = sb8xx_plat_probe,
- + .remove = sb8xx_plat_remove,
- +};
- +
- +static int __init amd_sb8xx_gpio_init(void)
- +{
- + return platform_driver_register(&sb8xx_gpio_driver);
- +}
- +
- +static void __exit amd_sb8xx_gpio_exit(void)
- +{
- + platform_driver_unregister(&sb8xx_gpio_driver);
- +}
- +
- +module_init(amd_sb8xx_gpio_init);
- +module_exit(amd_sb8xx_gpio_exit);
- +
- +MODULE_AUTHOR("Tobias Diedrich");
- +MODULE_DESCRIPTION("GPIO driver for AMD SB8XX/SB9XX/A5X/A8X chipsets");
- +MODULE_LICENSE("GPL");
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement