diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-05 09:51:41 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-05 09:51:41 -0700 |
commit | 1b2951dd99af3970c1c1a8385a12b90236b837de (patch) | |
tree | f0263fd6e22c23078b38360ea7495b1dd2d212dc /drivers/gpio/gpio-eic-sprd.c | |
parent | 06dd3dfeea60e2a6457a6aedf97afc8e6d2ba497 (diff) | |
parent | 348f3cde84ab5b1f53cd3c0eaac1ca99a4dcb148 (diff) | |
download | linux-1b2951dd99af3970c1c1a8385a12b90236b837de.tar.bz2 |
Merge tag 'gpio-v4.17-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio
Pull GPIO updates from Linus Walleij:
"This is the bulk of GPIO changes for the v4.17 kernel cycle:
New drivers:
- Nintendo Wii GameCube GPIO, known as "Hollywood"
- Raspberry Pi mailbox service GPIO expander
- Spreadtrum main SC9860 SoC and IEC GPIO controllers.
Improvements:
- Implemented .get_multiple() callback for most of the
high-performance industrial GPIO cards for the ISA bus.
- ISA GPIO drivers now select the ISA_BUS_API instead of depending on
it. This is merged with the same pattern for all the ISA drivers
and some other Kconfig cleanups related to this.
Cleanup:
- Delete the TZ1090 GPIO drivers following the deletion of this SoC
from the ARM tree.
- Move the documentation over to driver-api to conform with the rest
of the kernel documentation build.
- Continue to make the GPIO drivers include only
<linux/gpio/driver.h> and not the too broad <linux/gpio.h> that we
want to get rid of.
- Managed to remove VLA allocation from two drivers pending more
fixes in this area for the next merge window.
- Misc janitorial fixes"
* tag 'gpio-v4.17-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (77 commits)
gpio: Add Spreadtrum PMIC EIC driver support
gpio: Add Spreadtrum EIC driver support
dt-bindings: gpio: Add Spreadtrum EIC controller documentation
gpio: ath79: Fix potential NULL dereference in ath79_gpio_probe()
pinctrl: qcom: Don't allow protected pins to be requested
gpiolib: Support 'gpio-reserved-ranges' property
gpiolib: Change bitmap allocation to kmalloc_array
gpiolib: Extract mask allocation into subroutine
dt-bindings: gpio: Add a gpio-reserved-ranges property
gpio: mockup: fix a potential crash when creating debugfs entries
gpio: pca953x: add compatibility for pcal6524 and pcal9555a
gpio: dwapb: Add support for a bus clock
gpio: Remove VLA from xra1403 driver
gpio: Remove VLA from MAX3191X driver
gpio: ws16c48: Implement get_multiple callback
gpio: gpio-mm: Implement get_multiple callback
gpio: 104-idi-48: Implement get_multiple callback
gpio: 104-dio-48e: Implement get_multiple callback
gpio: pcie-idio-24: Implement get_multiple/set_multiple callbacks
gpio: pci-idio-16: Implement get_multiple callback
...
Diffstat (limited to 'drivers/gpio/gpio-eic-sprd.c')
-rw-r--r-- | drivers/gpio/gpio-eic-sprd.c | 606 |
1 files changed, 606 insertions, 0 deletions
diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c new file mode 100644 index 000000000000..de7dd939c043 --- /dev/null +++ b/drivers/gpio/gpio-eic-sprd.c @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Spreadtrum Communications Inc. + * Copyright (C) 2018 Linaro Ltd. + */ + +#include <linux/bitops.h> +#include <linux/gpio/driver.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> + +/* EIC registers definition */ +#define SPRD_EIC_DBNC_DATA 0x0 +#define SPRD_EIC_DBNC_DMSK 0x4 +#define SPRD_EIC_DBNC_IEV 0x14 +#define SPRD_EIC_DBNC_IE 0x18 +#define SPRD_EIC_DBNC_RIS 0x1c +#define SPRD_EIC_DBNC_MIS 0x20 +#define SPRD_EIC_DBNC_IC 0x24 +#define SPRD_EIC_DBNC_TRIG 0x28 +#define SPRD_EIC_DBNC_CTRL0 0x40 + +#define SPRD_EIC_LATCH_INTEN 0x0 +#define SPRD_EIC_LATCH_INTRAW 0x4 +#define SPRD_EIC_LATCH_INTMSK 0x8 +#define SPRD_EIC_LATCH_INTCLR 0xc +#define SPRD_EIC_LATCH_INTPOL 0x10 +#define SPRD_EIC_LATCH_INTMODE 0x14 + +#define SPRD_EIC_ASYNC_INTIE 0x0 +#define SPRD_EIC_ASYNC_INTRAW 0x4 +#define SPRD_EIC_ASYNC_INTMSK 0x8 +#define SPRD_EIC_ASYNC_INTCLR 0xc +#define SPRD_EIC_ASYNC_INTMODE 0x10 +#define SPRD_EIC_ASYNC_INTBOTH 0x14 +#define SPRD_EIC_ASYNC_INTPOL 0x18 +#define SPRD_EIC_ASYNC_DATA 0x1c + +#define SPRD_EIC_SYNC_INTIE 0x0 +#define SPRD_EIC_SYNC_INTRAW 0x4 +#define SPRD_EIC_SYNC_INTMSK 0x8 +#define SPRD_EIC_SYNC_INTCLR 0xc +#define SPRD_EIC_SYNC_INTMODE 0x10 +#define SPRD_EIC_SYNC_INTBOTH 0x14 +#define SPRD_EIC_SYNC_INTPOL 0x18 +#define SPRD_EIC_SYNC_DATA 0x1c + +/* + * The digital-chip EIC controller can support maximum 3 banks, and each bank + * contains 8 EICs. + */ +#define SPRD_EIC_MAX_BANK 3 +#define SPRD_EIC_PER_BANK_NR 8 +#define SPRD_EIC_DATA_MASK GENMASK(7, 0) +#define SPRD_EIC_BIT(x) ((x) & (SPRD_EIC_PER_BANK_NR - 1)) +#define SPRD_EIC_DBNC_MASK GENMASK(11, 0) + +/* + * The Spreadtrum EIC (external interrupt controller) can be used only in + * input mode to generate interrupts if detecting input signals. + * + * The Spreadtrum digital-chip EIC controller contains 4 sub-modules: + * debounce EIC, latch EIC, async EIC and sync EIC, + * + * The debounce EIC is used to capture the input signals' stable status + * (millisecond resolution) and a single-trigger mechanism is introduced + * into this sub-module to enhance the input event detection reliability. + * The debounce range is from 1ms to 4s with a step size of 1ms. + * + * The latch EIC is used to latch some special power down signals and + * generate interrupts, since the latch EIC does not depend on the APB clock + * to capture signals. + * + * The async EIC uses a 32k clock to capture the short signals (microsecond + * resolution) to generate interrupts by level or edge trigger. + * + * The EIC-sync is similar with GPIO's input function, which is a synchronized + * signal input register. + */ +enum sprd_eic_type { + SPRD_EIC_DEBOUNCE, + SPRD_EIC_LATCH, + SPRD_EIC_ASYNC, + SPRD_EIC_SYNC, + SPRD_EIC_MAX, +}; + +struct sprd_eic { + struct gpio_chip chip; + struct irq_chip intc; + void __iomem *base[SPRD_EIC_MAX_BANK]; + enum sprd_eic_type type; + spinlock_t lock; + int irq; +}; + +struct sprd_eic_variant_data { + enum sprd_eic_type type; + u32 num_eics; +}; + +static const char *sprd_eic_label_name[SPRD_EIC_MAX] = { + "eic-debounce", "eic-latch", "eic-async", + "eic-sync", +}; + +static const struct sprd_eic_variant_data sc9860_eic_dbnc_data = { + .type = SPRD_EIC_DEBOUNCE, + .num_eics = 8, +}; + +static const struct sprd_eic_variant_data sc9860_eic_latch_data = { + .type = SPRD_EIC_LATCH, + .num_eics = 8, +}; + +static const struct sprd_eic_variant_data sc9860_eic_async_data = { + .type = SPRD_EIC_ASYNC, + .num_eics = 8, +}; + +static const struct sprd_eic_variant_data sc9860_eic_sync_data = { + .type = SPRD_EIC_SYNC, + .num_eics = 8, +}; + +static inline void __iomem *sprd_eic_offset_base(struct sprd_eic *sprd_eic, + unsigned int bank) +{ + if (bank >= SPRD_EIC_MAX_BANK) + return NULL; + + return sprd_eic->base[bank]; +} + +static void sprd_eic_update(struct gpio_chip *chip, unsigned int offset, + u16 reg, unsigned int val) +{ + struct sprd_eic *sprd_eic = gpiochip_get_data(chip); + void __iomem *base = + sprd_eic_offset_base(sprd_eic, offset / SPRD_EIC_PER_BANK_NR); + unsigned long flags; + u32 tmp; + + spin_lock_irqsave(&sprd_eic->lock, flags); + tmp = readl_relaxed(base + reg); + + if (val) + tmp |= BIT(SPRD_EIC_BIT(offset)); + else + tmp &= ~BIT(SPRD_EIC_BIT(offset)); + + writel_relaxed(tmp, base + reg); + spin_unlock_irqrestore(&sprd_eic->lock, flags); +} + +static int sprd_eic_read(struct gpio_chip *chip, unsigned int offset, u16 reg) +{ + struct sprd_eic *sprd_eic = gpiochip_get_data(chip); + void __iomem *base = + sprd_eic_offset_base(sprd_eic, offset / SPRD_EIC_PER_BANK_NR); + + return !!(readl_relaxed(base + reg) & BIT(SPRD_EIC_BIT(offset))); +} + +static int sprd_eic_request(struct gpio_chip *chip, unsigned int offset) +{ + sprd_eic_update(chip, offset, SPRD_EIC_DBNC_DMSK, 1); + return 0; +} + +static void sprd_eic_free(struct gpio_chip *chip, unsigned int offset) +{ + sprd_eic_update(chip, offset, SPRD_EIC_DBNC_DMSK, 0); +} + +static int sprd_eic_get(struct gpio_chip *chip, unsigned int offset) +{ + return sprd_eic_read(chip, offset, SPRD_EIC_DBNC_DATA); +} + +static int sprd_eic_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + /* EICs are always input, nothing need to do here. */ + return 0; +} + +static void sprd_eic_set(struct gpio_chip *chip, unsigned int offset, int value) +{ + /* EICs are always input, nothing need to do here. */ +} + +static int sprd_eic_set_debounce(struct gpio_chip *chip, unsigned int offset, + unsigned int debounce) +{ + struct sprd_eic *sprd_eic = gpiochip_get_data(chip); + void __iomem *base = + sprd_eic_offset_base(sprd_eic, offset / SPRD_EIC_PER_BANK_NR); + u32 reg = SPRD_EIC_DBNC_CTRL0 + SPRD_EIC_BIT(offset) * 0x4; + u32 value = readl_relaxed(base + reg) & ~SPRD_EIC_DBNC_MASK; + + value |= (debounce / 1000) & SPRD_EIC_DBNC_MASK; + writel_relaxed(value, base + reg); + + return 0; +} + +static int sprd_eic_set_config(struct gpio_chip *chip, unsigned int offset, + unsigned long config) +{ + unsigned long param = pinconf_to_config_param(config); + u32 arg = pinconf_to_config_argument(config); + + if (param == PIN_CONFIG_INPUT_DEBOUNCE) + return sprd_eic_set_debounce(chip, offset, arg); + + return -ENOTSUPP; +} + +static void sprd_eic_irq_mask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct sprd_eic *sprd_eic = gpiochip_get_data(chip); + u32 offset = irqd_to_hwirq(data); + + switch (sprd_eic->type) { + case SPRD_EIC_DEBOUNCE: + sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IE, 0); + sprd_eic_update(chip, offset, SPRD_EIC_DBNC_TRIG, 0); + break; + case SPRD_EIC_LATCH: + sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTEN, 0); + break; + case SPRD_EIC_ASYNC: + sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTIE, 0); + break; + case SPRD_EIC_SYNC: + sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTIE, 0); + break; + default: + dev_err(chip->parent, "Unsupported EIC type.\n"); + } +} + +static void sprd_eic_irq_unmask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct sprd_eic *sprd_eic = gpiochip_get_data(chip); + u32 offset = irqd_to_hwirq(data); + + switch (sprd_eic->type) { + case SPRD_EIC_DEBOUNCE: + sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IE, 1); + sprd_eic_update(chip, offset, SPRD_EIC_DBNC_TRIG, 1); + break; + case SPRD_EIC_LATCH: + sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTEN, 1); + break; + case SPRD_EIC_ASYNC: + sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTIE, 1); + break; + case SPRD_EIC_SYNC: + sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTIE, 1); + break; + default: + dev_err(chip->parent, "Unsupported EIC type.\n"); + } +} + +static void sprd_eic_irq_ack(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct sprd_eic *sprd_eic = gpiochip_get_data(chip); + u32 offset = irqd_to_hwirq(data); + + switch (sprd_eic->type) { + case SPRD_EIC_DEBOUNCE: + sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IC, 1); + break; + case SPRD_EIC_LATCH: + sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTCLR, 1); + break; + case SPRD_EIC_ASYNC: + sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTCLR, 1); + break; + case SPRD_EIC_SYNC: + sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTCLR, 1); + break; + default: + dev_err(chip->parent, "Unsupported EIC type.\n"); + } +} + +static int sprd_eic_irq_set_type(struct irq_data *data, unsigned int flow_type) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct sprd_eic *sprd_eic = gpiochip_get_data(chip); + u32 offset = irqd_to_hwirq(data); + + switch (sprd_eic->type) { + case SPRD_EIC_DEBOUNCE: + switch (flow_type) { + case IRQ_TYPE_LEVEL_HIGH: + sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IEV, 1); + break; + case IRQ_TYPE_LEVEL_LOW: + sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IEV, 0); + break; + default: + return -ENOTSUPP; + } + + irq_set_handler_locked(data, handle_level_irq); + break; + case SPRD_EIC_LATCH: + switch (flow_type) { + case IRQ_TYPE_LEVEL_HIGH: + sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTPOL, 0); + break; + case IRQ_TYPE_LEVEL_LOW: + sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTPOL, 1); + break; + default: + return -ENOTSUPP; + } + + irq_set_handler_locked(data, handle_level_irq); + break; + case SPRD_EIC_ASYNC: + switch (flow_type) { + case IRQ_TYPE_EDGE_RISING: + sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTBOTH, 0); + sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTMODE, 0); + sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTPOL, 1); + irq_set_handler_locked(data, handle_edge_irq); + break; + case IRQ_TYPE_EDGE_FALLING: + sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTBOTH, 0); + sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTMODE, 0); + sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTPOL, 0); + irq_set_handler_locked(data, handle_edge_irq); + break; + case IRQ_TYPE_EDGE_BOTH: + sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTBOTH, 1); + irq_set_handler_locked(data, handle_edge_irq); + break; + case IRQ_TYPE_LEVEL_HIGH: + sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTBOTH, 0); + sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTMODE, 1); + sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTPOL, 1); + irq_set_handler_locked(data, handle_level_irq); + break; + case IRQ_TYPE_LEVEL_LOW: + sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTBOTH, 0); + sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTMODE, 1); + sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTPOL, 0); + irq_set_handler_locked(data, handle_level_irq); + break; + default: + return -ENOTSUPP; + } + break; + case SPRD_EIC_SYNC: + switch (flow_type) { + case IRQ_TYPE_EDGE_RISING: + sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTBOTH, 0); + sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTMODE, 0); + sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTPOL, 1); + irq_set_handler_locked(data, handle_edge_irq); + break; + case IRQ_TYPE_EDGE_FALLING: + sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTBOTH, 0); + sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTMODE, 0); + sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTPOL, 0); + irq_set_handler_locked(data, handle_edge_irq); + break; + case IRQ_TYPE_EDGE_BOTH: + sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTBOTH, 1); + irq_set_handler_locked(data, handle_edge_irq); + break; + case IRQ_TYPE_LEVEL_HIGH: + sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTBOTH, 0); + sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTMODE, 1); + sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTPOL, 1); + irq_set_handler_locked(data, handle_level_irq); + break; + case IRQ_TYPE_LEVEL_LOW: + sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTBOTH, 0); + sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTMODE, 1); + sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTPOL, 0); + irq_set_handler_locked(data, handle_level_irq); + break; + default: + return -ENOTSUPP; + } + default: + dev_err(chip->parent, "Unsupported EIC type.\n"); + return -ENOTSUPP; + } + + return 0; +} + +static int sprd_eic_match_chip_by_type(struct gpio_chip *chip, void *data) +{ + enum sprd_eic_type type = *(enum sprd_eic_type *)data; + + return !strcmp(chip->label, sprd_eic_label_name[type]); +} + +static void sprd_eic_handle_one_type(struct gpio_chip *chip) +{ + struct sprd_eic *sprd_eic = gpiochip_get_data(chip); + u32 bank, n, girq; + + for (bank = 0; bank * SPRD_EIC_PER_BANK_NR < chip->ngpio; bank++) { + void __iomem *base = sprd_eic_offset_base(sprd_eic, bank); + unsigned long reg; + + switch (sprd_eic->type) { + case SPRD_EIC_DEBOUNCE: + reg = readl_relaxed(base + SPRD_EIC_DBNC_MIS) & + SPRD_EIC_DATA_MASK; + break; + case SPRD_EIC_LATCH: + reg = readl_relaxed(base + SPRD_EIC_LATCH_INTMSK) & + SPRD_EIC_DATA_MASK; + break; + case SPRD_EIC_ASYNC: + reg = readl_relaxed(base + SPRD_EIC_ASYNC_INTMSK) & + SPRD_EIC_DATA_MASK; + break; + case SPRD_EIC_SYNC: + reg = readl_relaxed(base + SPRD_EIC_SYNC_INTMSK) & + SPRD_EIC_DATA_MASK; + break; + default: + dev_err(chip->parent, "Unsupported EIC type.\n"); + return; + } + + for_each_set_bit(n, ®, SPRD_EIC_PER_BANK_NR) { + girq = irq_find_mapping(chip->irq.domain, + bank * SPRD_EIC_PER_BANK_NR + n); + + generic_handle_irq(girq); + } + } +} + +static void sprd_eic_irq_handler(struct irq_desc *desc) +{ + struct irq_chip *ic = irq_desc_get_chip(desc); + struct gpio_chip *chip; + enum sprd_eic_type type; + + chained_irq_enter(ic, desc); + + /* + * Since the digital-chip EIC 4 sub-modules (debounce, latch, async + * and sync) share one same interrupt line, we should iterate each + * EIC module to check if there are EIC interrupts were triggered. + */ + for (type = SPRD_EIC_DEBOUNCE; type < SPRD_EIC_MAX; type++) { + chip = gpiochip_find(&type, sprd_eic_match_chip_by_type); + if (!chip) + continue; + + sprd_eic_handle_one_type(chip); + } + + chained_irq_exit(ic, desc); +} + +static int sprd_eic_probe(struct platform_device *pdev) +{ + const struct sprd_eic_variant_data *pdata; + struct gpio_irq_chip *irq; + struct sprd_eic *sprd_eic; + struct resource *res; + int ret, i; + + pdata = of_device_get_match_data(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "No matching driver data found.\n"); + return -EINVAL; + } + + sprd_eic = devm_kzalloc(&pdev->dev, sizeof(*sprd_eic), GFP_KERNEL); + if (!sprd_eic) + return -ENOMEM; + + spin_lock_init(&sprd_eic->lock); + sprd_eic->type = pdata->type; + + sprd_eic->irq = platform_get_irq(pdev, 0); + if (sprd_eic->irq < 0) { + dev_err(&pdev->dev, "Failed to get EIC interrupt.\n"); + return sprd_eic->irq; + } + + for (i = 0; i < SPRD_EIC_MAX_BANK; i++) { + /* + * We can have maximum 3 banks EICs, and each EIC has + * its own base address. But some platform maybe only + * have one bank EIC, thus base[1] and base[2] can be + * optional. + */ + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) + continue; + + sprd_eic->base[i] = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(sprd_eic->base[i])) + return PTR_ERR(sprd_eic->base[i]); + } + + sprd_eic->chip.label = sprd_eic_label_name[sprd_eic->type]; + sprd_eic->chip.ngpio = pdata->num_eics; + sprd_eic->chip.base = -1; + sprd_eic->chip.parent = &pdev->dev; + sprd_eic->chip.of_node = pdev->dev.of_node; + sprd_eic->chip.direction_input = sprd_eic_direction_input; + switch (sprd_eic->type) { + case SPRD_EIC_DEBOUNCE: + sprd_eic->chip.request = sprd_eic_request; + sprd_eic->chip.free = sprd_eic_free; + sprd_eic->chip.set_config = sprd_eic_set_config; + sprd_eic->chip.set = sprd_eic_set; + /* fall-through */ + case SPRD_EIC_ASYNC: + /* fall-through */ + case SPRD_EIC_SYNC: + sprd_eic->chip.get = sprd_eic_get; + break; + case SPRD_EIC_LATCH: + /* fall-through */ + default: + break; + } + + sprd_eic->intc.name = dev_name(&pdev->dev); + sprd_eic->intc.irq_ack = sprd_eic_irq_ack; + sprd_eic->intc.irq_mask = sprd_eic_irq_mask; + sprd_eic->intc.irq_unmask = sprd_eic_irq_unmask; + sprd_eic->intc.irq_set_type = sprd_eic_irq_set_type; + sprd_eic->intc.flags = IRQCHIP_SKIP_SET_WAKE; + + irq = &sprd_eic->chip.irq; + irq->chip = &sprd_eic->intc; + irq->handler = handle_bad_irq; + irq->default_type = IRQ_TYPE_NONE; + irq->parent_handler = sprd_eic_irq_handler; + irq->parent_handler_data = sprd_eic; + irq->num_parents = 1; + irq->parents = &sprd_eic->irq; + + ret = devm_gpiochip_add_data(&pdev->dev, &sprd_eic->chip, sprd_eic); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip %d.\n", ret); + return ret; + } + + platform_set_drvdata(pdev, sprd_eic); + return 0; +} + +static const struct of_device_id sprd_eic_of_match[] = { + { + .compatible = "sprd,sc9860-eic-debounce", + .data = &sc9860_eic_dbnc_data, + }, + { + .compatible = "sprd,sc9860-eic-latch", + .data = &sc9860_eic_latch_data, + }, + { + .compatible = "sprd,sc9860-eic-async", + .data = &sc9860_eic_async_data, + }, + { + .compatible = "sprd,sc9860-eic-sync", + .data = &sc9860_eic_sync_data, + }, + { + /* end of list */ + } +}; +MODULE_DEVICE_TABLE(of, sprd_eic_of_match); + +static struct platform_driver sprd_eic_driver = { + .probe = sprd_eic_probe, + .driver = { + .name = "sprd-eic", + .of_match_table = sprd_eic_of_match, + }, +}; + +module_platform_driver(sprd_eic_driver); + +MODULE_DESCRIPTION("Spreadtrum EIC driver"); +MODULE_LICENSE("GPL v2"); |