123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- /*
- * GPIO controller driver for Intel Lynxpoint PCH chipset>
- * Copyright (c) 2012, Intel Corporation.
- *
- * Author: Mathias Nyman <mathias.nyman@linux.intel.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/types.h>
- #include <linux/bitops.h>
- #include <linux/interrupt.h>
- #include <linux/gpio.h>
- #include <linux/slab.h>
- #include <linux/acpi.h>
- #include <linux/platform_device.h>
- #include <linux/pm_runtime.h>
- #include <linux/io.h>
- /* LynxPoint chipset has support for 94 gpio pins */
- #define LP_NUM_GPIO 94
- /* Bitmapped register offsets */
- #define LP_ACPI_OWNED 0x00 /* Bitmap, set by bios, 0: pin reserved for ACPI */
- #define LP_GC 0x7C /* set APIC IRQ to IRQ14 or IRQ15 for all pins */
- #define LP_INT_STAT 0x80
- #define LP_INT_ENABLE 0x90
- /* Each pin has two 32 bit config registers, starting at 0x100 */
- #define LP_CONFIG1 0x100
- #define LP_CONFIG2 0x104
- /* LP_CONFIG1 reg bits */
- #define OUT_LVL_BIT BIT(31)
- #define IN_LVL_BIT BIT(30)
- #define TRIG_SEL_BIT BIT(4) /* 0: Edge, 1: Level */
- #define INT_INV_BIT BIT(3) /* Invert interrupt triggering */
- #define DIR_BIT BIT(2) /* 0: Output, 1: Input */
- #define USE_SEL_BIT BIT(0) /* 0: Native, 1: GPIO */
- /* LP_CONFIG2 reg bits */
- #define GPINDIS_BIT BIT(2) /* disable input sensing */
- #define GPIWP_BIT (BIT(0) | BIT(1)) /* weak pull options */
- struct lp_gpio {
- struct gpio_chip chip;
- struct platform_device *pdev;
- spinlock_t lock;
- unsigned long reg_base;
- };
- /*
- * Lynxpoint gpios are controlled through both bitmapped registers and
- * per gpio specific registers. The bitmapped registers are in chunks of
- * 3 x 32bit registers to cover all 94 gpios
- *
- * per gpio specific registers consist of two 32bit registers per gpio
- * (LP_CONFIG1 and LP_CONFIG2), with 94 gpios there's a total of
- * 188 config registers.
- *
- * A simplified view of the register layout look like this:
- *
- * LP_ACPI_OWNED[31:0] gpio ownerships for gpios 0-31 (bitmapped registers)
- * LP_ACPI_OWNED[63:32] gpio ownerships for gpios 32-63
- * LP_ACPI_OWNED[94:64] gpio ownerships for gpios 63-94
- * ...
- * LP_INT_ENABLE[31:0] ...
- * LP_INT_ENABLE[63:31] ...
- * LP_INT_ENABLE[94:64] ...
- * LP0_CONFIG1 (gpio 0) config1 reg for gpio 0 (per gpio registers)
- * LP0_CONFIG2 (gpio 0) config2 reg for gpio 0
- * LP1_CONFIG1 (gpio 1) config1 reg for gpio 1
- * LP1_CONFIG2 (gpio 1) config2 reg for gpio 1
- * LP2_CONFIG1 (gpio 2) ...
- * LP2_CONFIG2 (gpio 2) ...
- * ...
- * LP94_CONFIG1 (gpio 94) ...
- * LP94_CONFIG2 (gpio 94) ...
- */
- static unsigned long lp_gpio_reg(struct gpio_chip *chip, unsigned offset,
- int reg)
- {
- struct lp_gpio *lg = container_of(chip, struct lp_gpio, chip);
- int reg_offset;
- if (reg == LP_CONFIG1 || reg == LP_CONFIG2)
- /* per gpio specific config registers */
- reg_offset = offset * 8;
- else
- /* bitmapped registers */
- reg_offset = (offset / 32) * 4;
- return lg->reg_base + reg + reg_offset;
- }
- static int lp_gpio_request(struct gpio_chip *chip, unsigned offset)
- {
- struct lp_gpio *lg = container_of(chip, struct lp_gpio, chip);
- unsigned long reg = lp_gpio_reg(chip, offset, LP_CONFIG1);
- unsigned long conf2 = lp_gpio_reg(chip, offset, LP_CONFIG2);
- unsigned long acpi_use = lp_gpio_reg(chip, offset, LP_ACPI_OWNED);
- pm_runtime_get(&lg->pdev->dev); /* should we put if failed */
- /* Fail if BIOS reserved pin for ACPI use */
- if (!(inl(acpi_use) & BIT(offset % 32))) {
- dev_err(&lg->pdev->dev, "gpio %d reserved for ACPI\n", offset);
- return -EBUSY;
- }
- /* Fail if pin is in alternate function mode (not GPIO mode) */
- if (!(inl(reg) & USE_SEL_BIT))
- return -ENODEV;
- /* enable input sensing */
- outl(inl(conf2) & ~GPINDIS_BIT, conf2);
- return 0;
- }
- static void lp_gpio_free(struct gpio_chip *chip, unsigned offset)
- {
- struct lp_gpio *lg = container_of(chip, struct lp_gpio, chip);
- unsigned long conf2 = lp_gpio_reg(chip, offset, LP_CONFIG2);
- /* disable input sensing */
- outl(inl(conf2) | GPINDIS_BIT, conf2);
- pm_runtime_put(&lg->pdev->dev);
- }
- static int lp_irq_type(struct irq_data *d, unsigned type)
- {
- struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct lp_gpio *lg = container_of(gc, struct lp_gpio, chip);
- u32 hwirq = irqd_to_hwirq(d);
- unsigned long flags;
- u32 value;
- unsigned long reg = lp_gpio_reg(&lg->chip, hwirq, LP_CONFIG1);
- if (hwirq >= lg->chip.ngpio)
- return -EINVAL;
- spin_lock_irqsave(&lg->lock, flags);
- value = inl(reg);
- /* set both TRIG_SEL and INV bits to 0 for rising edge */
- if (type & IRQ_TYPE_EDGE_RISING)
- value &= ~(TRIG_SEL_BIT | INT_INV_BIT);
- /* TRIG_SEL bit 0, INV bit 1 for falling edge */
- if (type & IRQ_TYPE_EDGE_FALLING)
- value = (value | INT_INV_BIT) & ~TRIG_SEL_BIT;
- /* TRIG_SEL bit 1, INV bit 0 for level low */
- if (type & IRQ_TYPE_LEVEL_LOW)
- value = (value | TRIG_SEL_BIT) & ~INT_INV_BIT;
- /* TRIG_SEL bit 1, INV bit 1 for level high */
- if (type & IRQ_TYPE_LEVEL_HIGH)
- value |= TRIG_SEL_BIT | INT_INV_BIT;
- outl(value, reg);
- spin_unlock_irqrestore(&lg->lock, flags);
- return 0;
- }
- static int lp_gpio_get(struct gpio_chip *chip, unsigned offset)
- {
- unsigned long reg = lp_gpio_reg(chip, offset, LP_CONFIG1);
- return !!(inl(reg) & IN_LVL_BIT);
- }
- static void lp_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
- {
- struct lp_gpio *lg = container_of(chip, struct lp_gpio, chip);
- unsigned long reg = lp_gpio_reg(chip, offset, LP_CONFIG1);
- unsigned long flags;
- spin_lock_irqsave(&lg->lock, flags);
- if (value)
- outl(inl(reg) | OUT_LVL_BIT, reg);
- else
- outl(inl(reg) & ~OUT_LVL_BIT, reg);
- spin_unlock_irqrestore(&lg->lock, flags);
- }
- static int lp_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
- {
- struct lp_gpio *lg = container_of(chip, struct lp_gpio, chip);
- unsigned long reg = lp_gpio_reg(chip, offset, LP_CONFIG1);
- unsigned long flags;
- spin_lock_irqsave(&lg->lock, flags);
- outl(inl(reg) | DIR_BIT, reg);
- spin_unlock_irqrestore(&lg->lock, flags);
- return 0;
- }
- static int lp_gpio_direction_output(struct gpio_chip *chip,
- unsigned offset, int value)
- {
- struct lp_gpio *lg = container_of(chip, struct lp_gpio, chip);
- unsigned long reg = lp_gpio_reg(chip, offset, LP_CONFIG1);
- unsigned long flags;
- lp_gpio_set(chip, offset, value);
- spin_lock_irqsave(&lg->lock, flags);
- outl(inl(reg) & ~DIR_BIT, reg);
- spin_unlock_irqrestore(&lg->lock, flags);
- return 0;
- }
- static void lp_gpio_irq_handler(struct irq_desc *desc)
- {
- struct irq_data *data = irq_desc_get_irq_data(desc);
- struct gpio_chip *gc = irq_desc_get_handler_data(desc);
- struct lp_gpio *lg = container_of(gc, struct lp_gpio, chip);
- struct irq_chip *chip = irq_data_get_irq_chip(data);
- u32 base, pin, mask;
- unsigned long reg, ena, pending;
- /* check from GPIO controller which pin triggered the interrupt */
- for (base = 0; base < lg->chip.ngpio; base += 32) {
- reg = lp_gpio_reg(&lg->chip, base, LP_INT_STAT);
- ena = lp_gpio_reg(&lg->chip, base, LP_INT_ENABLE);
- while ((pending = (inl(reg) & inl(ena)))) {
- unsigned irq;
- pin = __ffs(pending);
- mask = BIT(pin);
- /* Clear before handling so we don't lose an edge */
- outl(mask, reg);
- irq = irq_find_mapping(lg->chip.irqdomain, base + pin);
- generic_handle_irq(irq);
- }
- }
- chip->irq_eoi(data);
- }
- static void lp_irq_unmask(struct irq_data *d)
- {
- }
- static void lp_irq_mask(struct irq_data *d)
- {
- }
- static void lp_irq_enable(struct irq_data *d)
- {
- struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct lp_gpio *lg = container_of(gc, struct lp_gpio, chip);
- u32 hwirq = irqd_to_hwirq(d);
- unsigned long reg = lp_gpio_reg(&lg->chip, hwirq, LP_INT_ENABLE);
- unsigned long flags;
- spin_lock_irqsave(&lg->lock, flags);
- outl(inl(reg) | BIT(hwirq % 32), reg);
- spin_unlock_irqrestore(&lg->lock, flags);
- }
- static void lp_irq_disable(struct irq_data *d)
- {
- struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct lp_gpio *lg = container_of(gc, struct lp_gpio, chip);
- u32 hwirq = irqd_to_hwirq(d);
- unsigned long reg = lp_gpio_reg(&lg->chip, hwirq, LP_INT_ENABLE);
- unsigned long flags;
- spin_lock_irqsave(&lg->lock, flags);
- outl(inl(reg) & ~BIT(hwirq % 32), reg);
- spin_unlock_irqrestore(&lg->lock, flags);
- }
- static struct irq_chip lp_irqchip = {
- .name = "LP-GPIO",
- .irq_mask = lp_irq_mask,
- .irq_unmask = lp_irq_unmask,
- .irq_enable = lp_irq_enable,
- .irq_disable = lp_irq_disable,
- .irq_set_type = lp_irq_type,
- .flags = IRQCHIP_SKIP_SET_WAKE,
- };
- static void lp_gpio_irq_init_hw(struct lp_gpio *lg)
- {
- unsigned long reg;
- unsigned base;
- for (base = 0; base < lg->chip.ngpio; base += 32) {
- /* disable gpio pin interrupts */
- reg = lp_gpio_reg(&lg->chip, base, LP_INT_ENABLE);
- outl(0, reg);
- /* Clear interrupt status register */
- reg = lp_gpio_reg(&lg->chip, base, LP_INT_STAT);
- outl(0xffffffff, reg);
- }
- }
- static int lp_gpio_probe(struct platform_device *pdev)
- {
- struct lp_gpio *lg;
- struct gpio_chip *gc;
- struct resource *io_rc, *irq_rc;
- struct device *dev = &pdev->dev;
- unsigned long reg_len;
- int ret = -ENODEV;
- lg = devm_kzalloc(dev, sizeof(struct lp_gpio), GFP_KERNEL);
- if (!lg)
- return -ENOMEM;
- lg->pdev = pdev;
- platform_set_drvdata(pdev, lg);
- io_rc = platform_get_resource(pdev, IORESOURCE_IO, 0);
- irq_rc = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!io_rc) {
- dev_err(dev, "missing IO resources\n");
- return -EINVAL;
- }
- lg->reg_base = io_rc->start;
- reg_len = resource_size(io_rc);
- if (!devm_request_region(dev, lg->reg_base, reg_len, "lp-gpio")) {
- dev_err(dev, "failed requesting IO region 0x%x\n",
- (unsigned int)lg->reg_base);
- return -EBUSY;
- }
- spin_lock_init(&lg->lock);
- gc = &lg->chip;
- gc->label = dev_name(dev);
- gc->owner = THIS_MODULE;
- gc->request = lp_gpio_request;
- gc->free = lp_gpio_free;
- gc->direction_input = lp_gpio_direction_input;
- gc->direction_output = lp_gpio_direction_output;
- gc->get = lp_gpio_get;
- gc->set = lp_gpio_set;
- gc->base = -1;
- gc->ngpio = LP_NUM_GPIO;
- gc->can_sleep = false;
- gc->dev = dev;
- ret = gpiochip_add(gc);
- if (ret) {
- dev_err(dev, "failed adding lp-gpio chip\n");
- return ret;
- }
- /* set up interrupts */
- if (irq_rc && irq_rc->start) {
- lp_gpio_irq_init_hw(lg);
- ret = gpiochip_irqchip_add(gc, &lp_irqchip, 0,
- handle_simple_irq, IRQ_TYPE_NONE);
- if (ret) {
- dev_err(dev, "failed to add irqchip\n");
- gpiochip_remove(gc);
- return ret;
- }
- gpiochip_set_chained_irqchip(gc, &lp_irqchip,
- (unsigned)irq_rc->start,
- lp_gpio_irq_handler);
- }
- pm_runtime_enable(dev);
- return 0;
- }
- static int lp_gpio_runtime_suspend(struct device *dev)
- {
- return 0;
- }
- static int lp_gpio_runtime_resume(struct device *dev)
- {
- return 0;
- }
- static int lp_gpio_resume(struct device *dev)
- {
- struct platform_device *pdev = to_platform_device(dev);
- struct lp_gpio *lg = platform_get_drvdata(pdev);
- unsigned long reg;
- int i;
- /* on some hardware suspend clears input sensing, re-enable it here */
- for (i = 0; i < lg->chip.ngpio; i++) {
- if (gpiochip_is_requested(&lg->chip, i) != NULL) {
- reg = lp_gpio_reg(&lg->chip, i, LP_CONFIG2);
- outl(inl(reg) & ~GPINDIS_BIT, reg);
- }
- }
- return 0;
- }
- static const struct dev_pm_ops lp_gpio_pm_ops = {
- .runtime_suspend = lp_gpio_runtime_suspend,
- .runtime_resume = lp_gpio_runtime_resume,
- .resume = lp_gpio_resume,
- };
- static const struct acpi_device_id lynxpoint_gpio_acpi_match[] = {
- { "INT33C7", 0 },
- { "INT3437", 0 },
- { }
- };
- MODULE_DEVICE_TABLE(acpi, lynxpoint_gpio_acpi_match);
- static int lp_gpio_remove(struct platform_device *pdev)
- {
- struct lp_gpio *lg = platform_get_drvdata(pdev);
- pm_runtime_disable(&pdev->dev);
- gpiochip_remove(&lg->chip);
- return 0;
- }
- static struct platform_driver lp_gpio_driver = {
- .probe = lp_gpio_probe,
- .remove = lp_gpio_remove,
- .driver = {
- .name = "lp_gpio",
- .pm = &lp_gpio_pm_ops,
- .acpi_match_table = ACPI_PTR(lynxpoint_gpio_acpi_match),
- },
- };
- static int __init lp_gpio_init(void)
- {
- return platform_driver_register(&lp_gpio_driver);
- }
- static void __exit lp_gpio_exit(void)
- {
- platform_driver_unregister(&lp_gpio_driver);
- }
- subsys_initcall(lp_gpio_init);
- module_exit(lp_gpio_exit);
- MODULE_AUTHOR("Mathias Nyman (Intel)");
- MODULE_DESCRIPTION("GPIO interface for Intel Lynxpoint");
- MODULE_LICENSE("GPL");
- MODULE_ALIAS("platform:lp_gpio");
|