Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Andy Shevchenko | 475 | 25.89% | 5 | 20.83% |
Jan Kiszka | 403 | 21.96% | 1 | 4.17% |
Mika Westerberg | 392 | 21.36% | 1 | 4.17% |
Denis Turischev | 372 | 20.27% | 2 | 8.33% |
Chang Rebecca Swee Fun | 112 | 6.10% | 2 | 8.33% |
Linus Walleij | 23 | 1.25% | 3 | 12.50% |
Abanoub Sameh | 15 | 0.82% | 1 | 4.17% |
Colin Pitrat | 13 | 0.71% | 1 | 4.17% |
Seth Heasley | 11 | 0.60% | 1 | 4.17% |
Matti Vaittinen | 8 | 0.44% | 1 | 4.17% |
Laxman Dewangan | 4 | 0.22% | 1 | 4.17% |
Laurent Navet | 2 | 0.11% | 1 | 4.17% |
Mark Brown | 2 | 0.11% | 1 | 4.17% |
Hui Wang | 1 | 0.05% | 1 | 4.17% |
Marc Zyngier | 1 | 0.05% | 1 | 4.17% |
Julia Lawall | 1 | 0.05% | 1 | 4.17% |
Total | 1835 | 24 |
// SPDX-License-Identifier: GPL-2.0 /* * GPIO interface for Intel Poulsbo SCH * * Copyright (c) 2010 CompuLab Ltd * Author: Denis Turischev <denis@compulab.co.il> */ #include <linux/acpi.h> #include <linux/bitops.h> #include <linux/errno.h> #include <linux/gpio/driver.h> #include <linux/io.h> #include <linux/irq.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci_ids.h> #include <linux/platform_device.h> #include <linux/types.h> #define GEN 0x00 #define GIO 0x04 #define GLV 0x08 #define GTPE 0x0c #define GTNE 0x10 #define GGPE 0x14 #define GSMI 0x18 #define GTS 0x1c #define CORE_BANK_OFFSET 0x00 #define RESUME_BANK_OFFSET 0x20 /* * iLB datasheet describes GPE0BLK registers, in particular GPE0E.GPIO bit. * Document Number: 328195-001 */ #define GPE0E_GPIO 14 struct sch_gpio { struct gpio_chip chip; void __iomem *regs; spinlock_t lock; unsigned short resume_base; /* GPE handling */ u32 gpe; acpi_gpe_handler gpe_handler; }; static unsigned int sch_gpio_offset(struct sch_gpio *sch, unsigned int gpio, unsigned int reg) { unsigned int base = CORE_BANK_OFFSET; if (gpio >= sch->resume_base) { gpio -= sch->resume_base; base = RESUME_BANK_OFFSET; } return base + reg + gpio / 8; } static unsigned int sch_gpio_bit(struct sch_gpio *sch, unsigned int gpio) { if (gpio >= sch->resume_base) gpio -= sch->resume_base; return gpio % 8; } static int sch_gpio_reg_get(struct sch_gpio *sch, unsigned int gpio, unsigned int reg) { unsigned short offset, bit; u8 reg_val; offset = sch_gpio_offset(sch, gpio, reg); bit = sch_gpio_bit(sch, gpio); reg_val = !!(ioread8(sch->regs + offset) & BIT(bit)); return reg_val; } static void sch_gpio_reg_set(struct sch_gpio *sch, unsigned int gpio, unsigned int reg, int val) { unsigned short offset, bit; u8 reg_val; offset = sch_gpio_offset(sch, gpio, reg); bit = sch_gpio_bit(sch, gpio); reg_val = ioread8(sch->regs + offset); if (val) reg_val |= BIT(bit); else reg_val &= ~BIT(bit); iowrite8(reg_val, sch->regs + offset); } static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned int gpio_num) { struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GIO, 1); spin_unlock_irqrestore(&sch->lock, flags); return 0; } static int sch_gpio_get(struct gpio_chip *gc, unsigned int gpio_num) { struct sch_gpio *sch = gpiochip_get_data(gc); return sch_gpio_reg_get(sch, gpio_num, GLV); } static void sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val) { struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GLV, val); spin_unlock_irqrestore(&sch->lock, flags); } static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num, int val) { struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GIO, 0); spin_unlock_irqrestore(&sch->lock, flags); /* * according to the datasheet, writing to the level register has no * effect when GPIO is programmed as input. * Actually the level register is read-only when configured as input. * Thus presetting the output level before switching to output is _NOT_ possible. * Hence we set the level after configuring the GPIO as output. * But we cannot prevent a short low pulse if direction is set to high * and an external pull-up is connected. */ sch_gpio_set(gc, gpio_num, val); return 0; } static int sch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio_num) { struct sch_gpio *sch = gpiochip_get_data(gc); if (sch_gpio_reg_get(sch, gpio_num, GIO)) return GPIO_LINE_DIRECTION_IN; return GPIO_LINE_DIRECTION_OUT; } static const struct gpio_chip sch_gpio_chip = { .label = "sch_gpio", .owner = THIS_MODULE, .direction_input = sch_gpio_direction_in, .get = sch_gpio_get, .direction_output = sch_gpio_direction_out, .set = sch_gpio_set, .get_direction = sch_gpio_get_direction, }; static int sch_irq_type(struct irq_data *d, unsigned int type) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct sch_gpio *sch = gpiochip_get_data(gc); irq_hw_number_t gpio_num = irqd_to_hwirq(d); unsigned long flags; int rising, falling; switch (type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_EDGE_RISING: rising = 1; falling = 0; break; case IRQ_TYPE_EDGE_FALLING: rising = 0; falling = 1; break; case IRQ_TYPE_EDGE_BOTH: rising = 1; falling = 1; break; default: return -EINVAL; } spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GTPE, rising); sch_gpio_reg_set(sch, gpio_num, GTNE, falling); irq_set_handler_locked(d, handle_edge_irq); spin_unlock_irqrestore(&sch->lock, flags); return 0; } static void sch_irq_ack(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct sch_gpio *sch = gpiochip_get_data(gc); irq_hw_number_t gpio_num = irqd_to_hwirq(d); unsigned long flags; spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GTS, 1); spin_unlock_irqrestore(&sch->lock, flags); } static void sch_irq_mask_unmask(struct gpio_chip *gc, irq_hw_number_t gpio_num, int val) { struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GGPE, val); spin_unlock_irqrestore(&sch->lock, flags); } static void sch_irq_mask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); irq_hw_number_t gpio_num = irqd_to_hwirq(d); sch_irq_mask_unmask(gc, gpio_num, 0); gpiochip_disable_irq(gc, gpio_num); } static void sch_irq_unmask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); irq_hw_number_t gpio_num = irqd_to_hwirq(d); gpiochip_enable_irq(gc, gpio_num); sch_irq_mask_unmask(gc, gpio_num, 1); } static const struct irq_chip sch_irqchip = { .name = "sch_gpio", .irq_ack = sch_irq_ack, .irq_mask = sch_irq_mask, .irq_unmask = sch_irq_unmask, .irq_set_type = sch_irq_type, .flags = IRQCHIP_IMMUTABLE, GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static u32 sch_gpio_gpe_handler(acpi_handle gpe_device, u32 gpe, void *context) { struct sch_gpio *sch = context; struct gpio_chip *gc = &sch->chip; unsigned long core_status, resume_status; unsigned long pending; unsigned long flags; int offset; u32 ret; spin_lock_irqsave(&sch->lock, flags); core_status = ioread32(sch->regs + CORE_BANK_OFFSET + GTS); resume_status = ioread32(sch->regs + RESUME_BANK_OFFSET + GTS); spin_unlock_irqrestore(&sch->lock, flags); pending = (resume_status << sch->resume_base) | core_status; for_each_set_bit(offset, &pending, sch->chip.ngpio) generic_handle_domain_irq(gc->irq.domain, offset); /* Set returning value depending on whether we handled an interrupt */ ret = pending ? ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED; /* Acknowledge GPE to ACPICA */ ret |= ACPI_REENABLE_GPE; return ret; } static void sch_gpio_remove_gpe_handler(void *data) { struct sch_gpio *sch = data; acpi_disable_gpe(NULL, sch->gpe); acpi_remove_gpe_handler(NULL, sch->gpe, sch->gpe_handler); } static int sch_gpio_install_gpe_handler(struct sch_gpio *sch) { struct device *dev = sch->chip.parent; acpi_status status; status = acpi_install_gpe_handler(NULL, sch->gpe, ACPI_GPE_LEVEL_TRIGGERED, sch->gpe_handler, sch); if (ACPI_FAILURE(status)) { dev_err(dev, "Failed to install GPE handler for %u: %s\n", sch->gpe, acpi_format_exception(status)); return -ENODEV; } status = acpi_enable_gpe(NULL, sch->gpe); if (ACPI_FAILURE(status)) { dev_err(dev, "Failed to enable GPE handler for %u: %s\n", sch->gpe, acpi_format_exception(status)); acpi_remove_gpe_handler(NULL, sch->gpe, sch->gpe_handler); return -ENODEV; } return devm_add_action_or_reset(dev, sch_gpio_remove_gpe_handler, sch); } static int sch_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct gpio_irq_chip *girq; struct sch_gpio *sch; struct resource *res; void __iomem *regs; int ret; sch = devm_kzalloc(dev, sizeof(*sch), GFP_KERNEL); if (!sch) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!res) return -EBUSY; regs = devm_ioport_map(dev, res->start, resource_size(res)); if (!regs) return -EBUSY; sch->regs = regs; spin_lock_init(&sch->lock); sch->chip = sch_gpio_chip; sch->chip.label = dev_name(dev); sch->chip.parent = dev; switch (pdev->id) { case PCI_DEVICE_ID_INTEL_SCH_LPC: sch->resume_base = 10; sch->chip.ngpio = 14; /* * GPIO[6:0] enabled by default * GPIO7 is configured by the CMC as SLPIOVR * Enable GPIO[9:8] core powered gpios explicitly */ sch_gpio_reg_set(sch, 8, GEN, 1); sch_gpio_reg_set(sch, 9, GEN, 1); /* * SUS_GPIO[2:0] enabled by default * Enable SUS_GPIO3 resume powered gpio explicitly */ sch_gpio_reg_set(sch, 13, GEN, 1); break; case PCI_DEVICE_ID_INTEL_ITC_LPC: sch->resume_base = 5; sch->chip.ngpio = 14; break; case PCI_DEVICE_ID_INTEL_CENTERTON_ILB: sch->resume_base = 21; sch->chip.ngpio = 30; break; case PCI_DEVICE_ID_INTEL_QUARK_X1000_ILB: sch->resume_base = 2; sch->chip.ngpio = 8; break; default: return -ENODEV; } girq = &sch->chip.irq; gpio_irq_chip_set_chip(girq, &sch_irqchip); girq->num_parents = 0; girq->parents = NULL; girq->parent_handler = NULL; girq->default_type = IRQ_TYPE_NONE; girq->handler = handle_bad_irq; /* GPE setup is optional */ sch->gpe = GPE0E_GPIO; sch->gpe_handler = sch_gpio_gpe_handler; ret = sch_gpio_install_gpe_handler(sch); if (ret) dev_warn(dev, "Can't setup GPE, no IRQ support\n"); return devm_gpiochip_add_data(dev, &sch->chip, sch); } static struct platform_driver sch_gpio_driver = { .driver = { .name = "sch_gpio", }, .probe = sch_gpio_probe, }; module_platform_driver(sch_gpio_driver); MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>"); MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:sch_gpio");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1