162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Generic EP93xx GPIO handling 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2008 Ryan Mallon 662306a36Sopenharmony_ci * Copyright (c) 2011 H Hartley Sweeten <hsweeten@visionengravers.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Based on code originally from: 962306a36Sopenharmony_ci * linux/arch/arm/mach-ep93xx/core.c 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/io.h> 1662306a36Sopenharmony_ci#include <linux/irq.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/gpio/driver.h> 1962306a36Sopenharmony_ci#include <linux/bitops.h> 2062306a36Sopenharmony_ci#include <linux/seq_file.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define EP93XX_GPIO_F_INT_STATUS 0x5c 2362306a36Sopenharmony_ci#define EP93XX_GPIO_A_INT_STATUS 0xa0 2462306a36Sopenharmony_ci#define EP93XX_GPIO_B_INT_STATUS 0xbc 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* Maximum value for gpio line identifiers */ 2762306a36Sopenharmony_ci#define EP93XX_GPIO_LINE_MAX 63 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Number of GPIO chips in EP93XX */ 3062306a36Sopenharmony_ci#define EP93XX_GPIO_CHIP_NUM 8 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* Maximum value for irq capable line identifiers */ 3362306a36Sopenharmony_ci#define EP93XX_GPIO_LINE_MAX_IRQ 23 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define EP93XX_GPIO_A_IRQ_BASE 64 3662306a36Sopenharmony_ci#define EP93XX_GPIO_B_IRQ_BASE 72 3762306a36Sopenharmony_ci/* 3862306a36Sopenharmony_ci * Static mapping of GPIO bank F IRQS: 3962306a36Sopenharmony_ci * F0..F7 (16..24) to irq 80..87. 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci#define EP93XX_GPIO_F_IRQ_BASE 80 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct ep93xx_gpio_irq_chip { 4462306a36Sopenharmony_ci u8 irq_offset; 4562306a36Sopenharmony_ci u8 int_unmasked; 4662306a36Sopenharmony_ci u8 int_enabled; 4762306a36Sopenharmony_ci u8 int_type1; 4862306a36Sopenharmony_ci u8 int_type2; 4962306a36Sopenharmony_ci u8 int_debounce; 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistruct ep93xx_gpio_chip { 5362306a36Sopenharmony_ci struct gpio_chip gc; 5462306a36Sopenharmony_ci struct ep93xx_gpio_irq_chip *eic; 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistruct ep93xx_gpio { 5862306a36Sopenharmony_ci void __iomem *base; 5962306a36Sopenharmony_ci struct ep93xx_gpio_chip gc[EP93XX_GPIO_CHIP_NUM]; 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define to_ep93xx_gpio_chip(x) container_of(x, struct ep93xx_gpio_chip, gc) 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic struct ep93xx_gpio_irq_chip *to_ep93xx_gpio_irq_chip(struct gpio_chip *gc) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct ep93xx_gpio_chip *egc = to_ep93xx_gpio_chip(gc); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return egc->eic; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/************************************************************************* 7262306a36Sopenharmony_ci * Interrupt handling for EP93xx on-chip GPIOs 7362306a36Sopenharmony_ci *************************************************************************/ 7462306a36Sopenharmony_ci#define EP93XX_INT_TYPE1_OFFSET 0x00 7562306a36Sopenharmony_ci#define EP93XX_INT_TYPE2_OFFSET 0x04 7662306a36Sopenharmony_ci#define EP93XX_INT_EOI_OFFSET 0x08 7762306a36Sopenharmony_ci#define EP93XX_INT_EN_OFFSET 0x0c 7862306a36Sopenharmony_ci#define EP93XX_INT_STATUS_OFFSET 0x10 7962306a36Sopenharmony_ci#define EP93XX_INT_RAW_STATUS_OFFSET 0x14 8062306a36Sopenharmony_ci#define EP93XX_INT_DEBOUNCE_OFFSET 0x18 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void ep93xx_gpio_update_int_params(struct ep93xx_gpio *epg, 8362306a36Sopenharmony_ci struct ep93xx_gpio_irq_chip *eic) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci writeb_relaxed(0, epg->base + eic->irq_offset + EP93XX_INT_EN_OFFSET); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci writeb_relaxed(eic->int_type2, 8862306a36Sopenharmony_ci epg->base + eic->irq_offset + EP93XX_INT_TYPE2_OFFSET); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci writeb_relaxed(eic->int_type1, 9162306a36Sopenharmony_ci epg->base + eic->irq_offset + EP93XX_INT_TYPE1_OFFSET); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci writeb_relaxed(eic->int_unmasked & eic->int_enabled, 9462306a36Sopenharmony_ci epg->base + eic->irq_offset + EP93XX_INT_EN_OFFSET); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void ep93xx_gpio_int_debounce(struct gpio_chip *gc, 9862306a36Sopenharmony_ci unsigned int offset, bool enable) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct ep93xx_gpio *epg = gpiochip_get_data(gc); 10162306a36Sopenharmony_ci struct ep93xx_gpio_irq_chip *eic = to_ep93xx_gpio_irq_chip(gc); 10262306a36Sopenharmony_ci int port_mask = BIT(offset); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (enable) 10562306a36Sopenharmony_ci eic->int_debounce |= port_mask; 10662306a36Sopenharmony_ci else 10762306a36Sopenharmony_ci eic->int_debounce &= ~port_mask; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci writeb(eic->int_debounce, 11062306a36Sopenharmony_ci epg->base + eic->irq_offset + EP93XX_INT_DEBOUNCE_OFFSET); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic void ep93xx_gpio_ab_irq_handler(struct irq_desc *desc) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct gpio_chip *gc = irq_desc_get_handler_data(desc); 11662306a36Sopenharmony_ci struct ep93xx_gpio *epg = gpiochip_get_data(gc); 11762306a36Sopenharmony_ci struct irq_chip *irqchip = irq_desc_get_chip(desc); 11862306a36Sopenharmony_ci unsigned long stat; 11962306a36Sopenharmony_ci int offset; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci chained_irq_enter(irqchip, desc); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* 12462306a36Sopenharmony_ci * Dispatch the IRQs to the irqdomain of each A and B 12562306a36Sopenharmony_ci * gpiochip irqdomains depending on what has fired. 12662306a36Sopenharmony_ci * The tricky part is that the IRQ line is shared 12762306a36Sopenharmony_ci * between bank A and B and each has their own gpiochip. 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ci stat = readb(epg->base + EP93XX_GPIO_A_INT_STATUS); 13062306a36Sopenharmony_ci for_each_set_bit(offset, &stat, 8) 13162306a36Sopenharmony_ci generic_handle_domain_irq(epg->gc[0].gc.irq.domain, 13262306a36Sopenharmony_ci offset); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci stat = readb(epg->base + EP93XX_GPIO_B_INT_STATUS); 13562306a36Sopenharmony_ci for_each_set_bit(offset, &stat, 8) 13662306a36Sopenharmony_ci generic_handle_domain_irq(epg->gc[1].gc.irq.domain, 13762306a36Sopenharmony_ci offset); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci chained_irq_exit(irqchip, desc); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic void ep93xx_gpio_f_irq_handler(struct irq_desc *desc) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci * map discontiguous hw irq range to continuous sw irq range: 14662306a36Sopenharmony_ci * 14762306a36Sopenharmony_ci * IRQ_EP93XX_GPIO{0..7}MUX -> EP93XX_GPIO_LINE_F{0..7} 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci struct irq_chip *irqchip = irq_desc_get_chip(desc); 15062306a36Sopenharmony_ci unsigned int irq = irq_desc_get_irq(desc); 15162306a36Sopenharmony_ci int port_f_idx = (irq & 7) ^ 4; /* {20..23,48..51} -> {0..7} */ 15262306a36Sopenharmony_ci int gpio_irq = EP93XX_GPIO_F_IRQ_BASE + port_f_idx; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci chained_irq_enter(irqchip, desc); 15562306a36Sopenharmony_ci generic_handle_irq(gpio_irq); 15662306a36Sopenharmony_ci chained_irq_exit(irqchip, desc); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic void ep93xx_gpio_irq_ack(struct irq_data *d) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 16262306a36Sopenharmony_ci struct ep93xx_gpio_irq_chip *eic = to_ep93xx_gpio_irq_chip(gc); 16362306a36Sopenharmony_ci struct ep93xx_gpio *epg = gpiochip_get_data(gc); 16462306a36Sopenharmony_ci int port_mask = BIT(d->irq & 7); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) { 16762306a36Sopenharmony_ci eic->int_type2 ^= port_mask; /* switch edge direction */ 16862306a36Sopenharmony_ci ep93xx_gpio_update_int_params(epg, eic); 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci writeb(port_mask, epg->base + eic->irq_offset + EP93XX_INT_EOI_OFFSET); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic void ep93xx_gpio_irq_mask_ack(struct irq_data *d) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 17762306a36Sopenharmony_ci struct ep93xx_gpio_irq_chip *eic = to_ep93xx_gpio_irq_chip(gc); 17862306a36Sopenharmony_ci struct ep93xx_gpio *epg = gpiochip_get_data(gc); 17962306a36Sopenharmony_ci int port_mask = BIT(d->irq & 7); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) 18262306a36Sopenharmony_ci eic->int_type2 ^= port_mask; /* switch edge direction */ 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci eic->int_unmasked &= ~port_mask; 18562306a36Sopenharmony_ci ep93xx_gpio_update_int_params(epg, eic); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci writeb(port_mask, epg->base + eic->irq_offset + EP93XX_INT_EOI_OFFSET); 18862306a36Sopenharmony_ci gpiochip_disable_irq(gc, irqd_to_hwirq(d)); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic void ep93xx_gpio_irq_mask(struct irq_data *d) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 19462306a36Sopenharmony_ci struct ep93xx_gpio_irq_chip *eic = to_ep93xx_gpio_irq_chip(gc); 19562306a36Sopenharmony_ci struct ep93xx_gpio *epg = gpiochip_get_data(gc); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci eic->int_unmasked &= ~BIT(d->irq & 7); 19862306a36Sopenharmony_ci ep93xx_gpio_update_int_params(epg, eic); 19962306a36Sopenharmony_ci gpiochip_disable_irq(gc, irqd_to_hwirq(d)); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic void ep93xx_gpio_irq_unmask(struct irq_data *d) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 20562306a36Sopenharmony_ci struct ep93xx_gpio_irq_chip *eic = to_ep93xx_gpio_irq_chip(gc); 20662306a36Sopenharmony_ci struct ep93xx_gpio *epg = gpiochip_get_data(gc); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci gpiochip_enable_irq(gc, irqd_to_hwirq(d)); 20962306a36Sopenharmony_ci eic->int_unmasked |= BIT(d->irq & 7); 21062306a36Sopenharmony_ci ep93xx_gpio_update_int_params(epg, eic); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/* 21462306a36Sopenharmony_ci * gpio_int_type1 controls whether the interrupt is level (0) or 21562306a36Sopenharmony_ci * edge (1) triggered, while gpio_int_type2 controls whether it 21662306a36Sopenharmony_ci * triggers on low/falling (0) or high/rising (1). 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_cistatic int ep93xx_gpio_irq_type(struct irq_data *d, unsigned int type) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 22162306a36Sopenharmony_ci struct ep93xx_gpio_irq_chip *eic = to_ep93xx_gpio_irq_chip(gc); 22262306a36Sopenharmony_ci struct ep93xx_gpio *epg = gpiochip_get_data(gc); 22362306a36Sopenharmony_ci int offset = d->irq & 7; 22462306a36Sopenharmony_ci int port_mask = BIT(offset); 22562306a36Sopenharmony_ci irq_flow_handler_t handler; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci gc->direction_input(gc, offset); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci switch (type) { 23062306a36Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 23162306a36Sopenharmony_ci eic->int_type1 |= port_mask; 23262306a36Sopenharmony_ci eic->int_type2 |= port_mask; 23362306a36Sopenharmony_ci handler = handle_edge_irq; 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 23662306a36Sopenharmony_ci eic->int_type1 |= port_mask; 23762306a36Sopenharmony_ci eic->int_type2 &= ~port_mask; 23862306a36Sopenharmony_ci handler = handle_edge_irq; 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 24162306a36Sopenharmony_ci eic->int_type1 &= ~port_mask; 24262306a36Sopenharmony_ci eic->int_type2 |= port_mask; 24362306a36Sopenharmony_ci handler = handle_level_irq; 24462306a36Sopenharmony_ci break; 24562306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 24662306a36Sopenharmony_ci eic->int_type1 &= ~port_mask; 24762306a36Sopenharmony_ci eic->int_type2 &= ~port_mask; 24862306a36Sopenharmony_ci handler = handle_level_irq; 24962306a36Sopenharmony_ci break; 25062306a36Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 25162306a36Sopenharmony_ci eic->int_type1 |= port_mask; 25262306a36Sopenharmony_ci /* set initial polarity based on current input level */ 25362306a36Sopenharmony_ci if (gc->get(gc, offset)) 25462306a36Sopenharmony_ci eic->int_type2 &= ~port_mask; /* falling */ 25562306a36Sopenharmony_ci else 25662306a36Sopenharmony_ci eic->int_type2 |= port_mask; /* rising */ 25762306a36Sopenharmony_ci handler = handle_edge_irq; 25862306a36Sopenharmony_ci break; 25962306a36Sopenharmony_ci default: 26062306a36Sopenharmony_ci return -EINVAL; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci irq_set_handler_locked(d, handler); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci eic->int_enabled |= port_mask; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci ep93xx_gpio_update_int_params(epg, eic); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci/************************************************************************* 27362306a36Sopenharmony_ci * gpiolib interface for EP93xx on-chip GPIOs 27462306a36Sopenharmony_ci *************************************************************************/ 27562306a36Sopenharmony_cistruct ep93xx_gpio_bank { 27662306a36Sopenharmony_ci const char *label; 27762306a36Sopenharmony_ci int data; 27862306a36Sopenharmony_ci int dir; 27962306a36Sopenharmony_ci int irq; 28062306a36Sopenharmony_ci int base; 28162306a36Sopenharmony_ci bool has_irq; 28262306a36Sopenharmony_ci bool has_hierarchical_irq; 28362306a36Sopenharmony_ci unsigned int irq_base; 28462306a36Sopenharmony_ci}; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci#define EP93XX_GPIO_BANK(_label, _data, _dir, _irq, _base, _has_irq, _has_hier, _irq_base) \ 28762306a36Sopenharmony_ci { \ 28862306a36Sopenharmony_ci .label = _label, \ 28962306a36Sopenharmony_ci .data = _data, \ 29062306a36Sopenharmony_ci .dir = _dir, \ 29162306a36Sopenharmony_ci .irq = _irq, \ 29262306a36Sopenharmony_ci .base = _base, \ 29362306a36Sopenharmony_ci .has_irq = _has_irq, \ 29462306a36Sopenharmony_ci .has_hierarchical_irq = _has_hier, \ 29562306a36Sopenharmony_ci .irq_base = _irq_base, \ 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic struct ep93xx_gpio_bank ep93xx_gpio_banks[] = { 29962306a36Sopenharmony_ci /* Bank A has 8 IRQs */ 30062306a36Sopenharmony_ci EP93XX_GPIO_BANK("A", 0x00, 0x10, 0x90, 0, true, false, EP93XX_GPIO_A_IRQ_BASE), 30162306a36Sopenharmony_ci /* Bank B has 8 IRQs */ 30262306a36Sopenharmony_ci EP93XX_GPIO_BANK("B", 0x04, 0x14, 0xac, 8, true, false, EP93XX_GPIO_B_IRQ_BASE), 30362306a36Sopenharmony_ci EP93XX_GPIO_BANK("C", 0x08, 0x18, 0x00, 40, false, false, 0), 30462306a36Sopenharmony_ci EP93XX_GPIO_BANK("D", 0x0c, 0x1c, 0x00, 24, false, false, 0), 30562306a36Sopenharmony_ci EP93XX_GPIO_BANK("E", 0x20, 0x24, 0x00, 32, false, false, 0), 30662306a36Sopenharmony_ci /* Bank F has 8 IRQs */ 30762306a36Sopenharmony_ci EP93XX_GPIO_BANK("F", 0x30, 0x34, 0x4c, 16, false, true, EP93XX_GPIO_F_IRQ_BASE), 30862306a36Sopenharmony_ci EP93XX_GPIO_BANK("G", 0x38, 0x3c, 0x00, 48, false, false, 0), 30962306a36Sopenharmony_ci EP93XX_GPIO_BANK("H", 0x40, 0x44, 0x00, 56, false, false, 0), 31062306a36Sopenharmony_ci}; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic int ep93xx_gpio_set_config(struct gpio_chip *gc, unsigned offset, 31362306a36Sopenharmony_ci unsigned long config) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci u32 debounce; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE) 31862306a36Sopenharmony_ci return -ENOTSUPP; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci debounce = pinconf_to_config_argument(config); 32162306a36Sopenharmony_ci ep93xx_gpio_int_debounce(gc, offset, debounce ? true : false); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci return 0; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic void ep93xx_irq_print_chip(struct irq_data *data, struct seq_file *p) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(data); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci seq_printf(p, dev_name(gc->parent)); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic const struct irq_chip gpio_eic_irq_chip = { 33462306a36Sopenharmony_ci .name = "ep93xx-gpio-eic", 33562306a36Sopenharmony_ci .irq_ack = ep93xx_gpio_irq_ack, 33662306a36Sopenharmony_ci .irq_mask = ep93xx_gpio_irq_mask, 33762306a36Sopenharmony_ci .irq_unmask = ep93xx_gpio_irq_unmask, 33862306a36Sopenharmony_ci .irq_mask_ack = ep93xx_gpio_irq_mask_ack, 33962306a36Sopenharmony_ci .irq_set_type = ep93xx_gpio_irq_type, 34062306a36Sopenharmony_ci .irq_print_chip = ep93xx_irq_print_chip, 34162306a36Sopenharmony_ci .flags = IRQCHIP_IMMUTABLE, 34262306a36Sopenharmony_ci GPIOCHIP_IRQ_RESOURCE_HELPERS, 34362306a36Sopenharmony_ci}; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic int ep93xx_gpio_add_bank(struct ep93xx_gpio_chip *egc, 34662306a36Sopenharmony_ci struct platform_device *pdev, 34762306a36Sopenharmony_ci struct ep93xx_gpio *epg, 34862306a36Sopenharmony_ci struct ep93xx_gpio_bank *bank) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci void __iomem *data = epg->base + bank->data; 35162306a36Sopenharmony_ci void __iomem *dir = epg->base + bank->dir; 35262306a36Sopenharmony_ci struct gpio_chip *gc = &egc->gc; 35362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 35462306a36Sopenharmony_ci struct gpio_irq_chip *girq; 35562306a36Sopenharmony_ci int err; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci err = bgpio_init(gc, dev, 1, data, NULL, NULL, dir, NULL, 0); 35862306a36Sopenharmony_ci if (err) 35962306a36Sopenharmony_ci return err; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci gc->label = bank->label; 36262306a36Sopenharmony_ci gc->base = bank->base; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci girq = &gc->irq; 36562306a36Sopenharmony_ci if (bank->has_irq || bank->has_hierarchical_irq) { 36662306a36Sopenharmony_ci gc->set_config = ep93xx_gpio_set_config; 36762306a36Sopenharmony_ci egc->eic = devm_kcalloc(dev, 1, 36862306a36Sopenharmony_ci sizeof(*egc->eic), 36962306a36Sopenharmony_ci GFP_KERNEL); 37062306a36Sopenharmony_ci if (!egc->eic) 37162306a36Sopenharmony_ci return -ENOMEM; 37262306a36Sopenharmony_ci egc->eic->irq_offset = bank->irq; 37362306a36Sopenharmony_ci gpio_irq_chip_set_chip(girq, &gpio_eic_irq_chip); 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (bank->has_irq) { 37762306a36Sopenharmony_ci int ab_parent_irq = platform_get_irq(pdev, 0); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci girq->parent_handler = ep93xx_gpio_ab_irq_handler; 38062306a36Sopenharmony_ci girq->num_parents = 1; 38162306a36Sopenharmony_ci girq->parents = devm_kcalloc(dev, girq->num_parents, 38262306a36Sopenharmony_ci sizeof(*girq->parents), 38362306a36Sopenharmony_ci GFP_KERNEL); 38462306a36Sopenharmony_ci if (!girq->parents) 38562306a36Sopenharmony_ci return -ENOMEM; 38662306a36Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 38762306a36Sopenharmony_ci girq->handler = handle_level_irq; 38862306a36Sopenharmony_ci girq->parents[0] = ab_parent_irq; 38962306a36Sopenharmony_ci girq->first = bank->irq_base; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* Only bank F has especially funky IRQ handling */ 39362306a36Sopenharmony_ci if (bank->has_hierarchical_irq) { 39462306a36Sopenharmony_ci int gpio_irq; 39562306a36Sopenharmony_ci int i; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* 39862306a36Sopenharmony_ci * FIXME: convert this to use hierarchical IRQ support! 39962306a36Sopenharmony_ci * this requires fixing the root irqchip to be hierarchical. 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_ci girq->parent_handler = ep93xx_gpio_f_irq_handler; 40262306a36Sopenharmony_ci girq->num_parents = 8; 40362306a36Sopenharmony_ci girq->parents = devm_kcalloc(dev, girq->num_parents, 40462306a36Sopenharmony_ci sizeof(*girq->parents), 40562306a36Sopenharmony_ci GFP_KERNEL); 40662306a36Sopenharmony_ci if (!girq->parents) 40762306a36Sopenharmony_ci return -ENOMEM; 40862306a36Sopenharmony_ci /* Pick resources 1..8 for these IRQs */ 40962306a36Sopenharmony_ci for (i = 0; i < girq->num_parents; i++) { 41062306a36Sopenharmony_ci girq->parents[i] = platform_get_irq(pdev, i + 1); 41162306a36Sopenharmony_ci gpio_irq = bank->irq_base + i; 41262306a36Sopenharmony_ci irq_set_chip_data(gpio_irq, &epg->gc[5]); 41362306a36Sopenharmony_ci irq_set_chip_and_handler(gpio_irq, 41462306a36Sopenharmony_ci girq->chip, 41562306a36Sopenharmony_ci handle_level_irq); 41662306a36Sopenharmony_ci irq_clear_status_flags(gpio_irq, IRQ_NOREQUEST); 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 41962306a36Sopenharmony_ci girq->handler = handle_level_irq; 42062306a36Sopenharmony_ci girq->first = bank->irq_base; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return devm_gpiochip_add_data(dev, gc, epg); 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic int ep93xx_gpio_probe(struct platform_device *pdev) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct ep93xx_gpio *epg; 42962306a36Sopenharmony_ci int i; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci epg = devm_kzalloc(&pdev->dev, sizeof(*epg), GFP_KERNEL); 43262306a36Sopenharmony_ci if (!epg) 43362306a36Sopenharmony_ci return -ENOMEM; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci epg->base = devm_platform_ioremap_resource(pdev, 0); 43662306a36Sopenharmony_ci if (IS_ERR(epg->base)) 43762306a36Sopenharmony_ci return PTR_ERR(epg->base); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ep93xx_gpio_banks); i++) { 44062306a36Sopenharmony_ci struct ep93xx_gpio_chip *gc = &epg->gc[i]; 44162306a36Sopenharmony_ci struct ep93xx_gpio_bank *bank = &ep93xx_gpio_banks[i]; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if (ep93xx_gpio_add_bank(gc, pdev, epg, bank)) 44462306a36Sopenharmony_ci dev_warn(&pdev->dev, "Unable to add gpio bank %s\n", 44562306a36Sopenharmony_ci bank->label); 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci return 0; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic struct platform_driver ep93xx_gpio_driver = { 45262306a36Sopenharmony_ci .driver = { 45362306a36Sopenharmony_ci .name = "gpio-ep93xx", 45462306a36Sopenharmony_ci }, 45562306a36Sopenharmony_ci .probe = ep93xx_gpio_probe, 45662306a36Sopenharmony_ci}; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic int __init ep93xx_gpio_init(void) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci return platform_driver_register(&ep93xx_gpio_driver); 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_cipostcore_initcall(ep93xx_gpio_init); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ciMODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com> " 46562306a36Sopenharmony_ci "H Hartley Sweeten <hsweeten@visionengravers.com>"); 46662306a36Sopenharmony_ciMODULE_DESCRIPTION("EP93XX GPIO driver"); 46762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 468