162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/err.h> 762306a36Sopenharmony_ci#include <linux/init.h> 862306a36Sopenharmony_ci#include <linux/interrupt.h> 962306a36Sopenharmony_ci#include <linux/irq.h> 1062306a36Sopenharmony_ci#include <linux/irqchip.h> 1162306a36Sopenharmony_ci#include <linux/irqdomain.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/of_address.h> 1762306a36Sopenharmony_ci#include <linux/of_irq.h> 1862306a36Sopenharmony_ci#include <linux/soc/qcom/irq.h> 1962306a36Sopenharmony_ci#include <linux/spinlock.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <linux/types.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define PDC_MAX_GPIO_IRQS 256 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* Valid only on HW version < 3.2 */ 2662306a36Sopenharmony_ci#define IRQ_ENABLE_BANK 0x10 2762306a36Sopenharmony_ci#define IRQ_i_CFG 0x110 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Valid only on HW version >= 3.2 */ 3062306a36Sopenharmony_ci#define IRQ_i_CFG_IRQ_ENABLE 3 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define IRQ_i_CFG_TYPE_MASK GENMASK(2, 0) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define PDC_VERSION_REG 0x1000 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* Notable PDC versions */ 3762306a36Sopenharmony_ci#define PDC_VERSION_3_2 0x30200 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct pdc_pin_region { 4062306a36Sopenharmony_ci u32 pin_base; 4162306a36Sopenharmony_ci u32 parent_base; 4262306a36Sopenharmony_ci u32 cnt; 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define pin_to_hwirq(r, p) ((r)->parent_base + (p) - (r)->pin_base) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(pdc_lock); 4862306a36Sopenharmony_cistatic void __iomem *pdc_base; 4962306a36Sopenharmony_cistatic struct pdc_pin_region *pdc_region; 5062306a36Sopenharmony_cistatic int pdc_region_cnt; 5162306a36Sopenharmony_cistatic unsigned int pdc_version; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void pdc_reg_write(int reg, u32 i, u32 val) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci writel_relaxed(val, pdc_base + reg + i * sizeof(u32)); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic u32 pdc_reg_read(int reg, u32 i) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci return readl_relaxed(pdc_base + reg + i * sizeof(u32)); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void __pdc_enable_intr(int pin_out, bool on) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci unsigned long enable; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (pdc_version < PDC_VERSION_3_2) { 6862306a36Sopenharmony_ci u32 index, mask; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci index = pin_out / 32; 7162306a36Sopenharmony_ci mask = pin_out % 32; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci enable = pdc_reg_read(IRQ_ENABLE_BANK, index); 7462306a36Sopenharmony_ci __assign_bit(mask, &enable, on); 7562306a36Sopenharmony_ci pdc_reg_write(IRQ_ENABLE_BANK, index, enable); 7662306a36Sopenharmony_ci } else { 7762306a36Sopenharmony_ci enable = pdc_reg_read(IRQ_i_CFG, pin_out); 7862306a36Sopenharmony_ci __assign_bit(IRQ_i_CFG_IRQ_ENABLE, &enable, on); 7962306a36Sopenharmony_ci pdc_reg_write(IRQ_i_CFG, pin_out, enable); 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void pdc_enable_intr(struct irq_data *d, bool on) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci unsigned long flags; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci raw_spin_lock_irqsave(&pdc_lock, flags); 8862306a36Sopenharmony_ci __pdc_enable_intr(d->hwirq, on); 8962306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&pdc_lock, flags); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic void qcom_pdc_gic_disable(struct irq_data *d) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci pdc_enable_intr(d, false); 9562306a36Sopenharmony_ci irq_chip_disable_parent(d); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic void qcom_pdc_gic_enable(struct irq_data *d) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci pdc_enable_intr(d, true); 10162306a36Sopenharmony_ci irq_chip_enable_parent(d); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* 10562306a36Sopenharmony_ci * GIC does not handle falling edge or active low. To allow falling edge and 10662306a36Sopenharmony_ci * active low interrupts to be handled at GIC, PDC has an inverter that inverts 10762306a36Sopenharmony_ci * falling edge into a rising edge and active low into an active high. 10862306a36Sopenharmony_ci * For the inverter to work, the polarity bit in the IRQ_CONFIG register has to 10962306a36Sopenharmony_ci * set as per the table below. 11062306a36Sopenharmony_ci * Level sensitive active low LOW 11162306a36Sopenharmony_ci * Rising edge sensitive NOT USED 11262306a36Sopenharmony_ci * Falling edge sensitive LOW 11362306a36Sopenharmony_ci * Dual Edge sensitive NOT USED 11462306a36Sopenharmony_ci * Level sensitive active High HIGH 11562306a36Sopenharmony_ci * Falling Edge sensitive NOT USED 11662306a36Sopenharmony_ci * Rising edge sensitive HIGH 11762306a36Sopenharmony_ci * Dual Edge sensitive HIGH 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_cienum pdc_irq_config_bits { 12062306a36Sopenharmony_ci PDC_LEVEL_LOW = 0b000, 12162306a36Sopenharmony_ci PDC_EDGE_FALLING = 0b010, 12262306a36Sopenharmony_ci PDC_LEVEL_HIGH = 0b100, 12362306a36Sopenharmony_ci PDC_EDGE_RISING = 0b110, 12462306a36Sopenharmony_ci PDC_EDGE_DUAL = 0b111, 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/** 12862306a36Sopenharmony_ci * qcom_pdc_gic_set_type: Configure PDC for the interrupt 12962306a36Sopenharmony_ci * 13062306a36Sopenharmony_ci * @d: the interrupt data 13162306a36Sopenharmony_ci * @type: the interrupt type 13262306a36Sopenharmony_ci * 13362306a36Sopenharmony_ci * If @type is edge triggered, forward that as Rising edge as PDC 13462306a36Sopenharmony_ci * takes care of converting falling edge to rising edge signal 13562306a36Sopenharmony_ci * If @type is level, then forward that as level high as PDC 13662306a36Sopenharmony_ci * takes care of converting falling edge to rising edge signal 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_cistatic int qcom_pdc_gic_set_type(struct irq_data *d, unsigned int type) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci enum pdc_irq_config_bits pdc_type; 14162306a36Sopenharmony_ci enum pdc_irq_config_bits old_pdc_type; 14262306a36Sopenharmony_ci int ret; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci switch (type) { 14562306a36Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 14662306a36Sopenharmony_ci pdc_type = PDC_EDGE_RISING; 14762306a36Sopenharmony_ci break; 14862306a36Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 14962306a36Sopenharmony_ci pdc_type = PDC_EDGE_FALLING; 15062306a36Sopenharmony_ci type = IRQ_TYPE_EDGE_RISING; 15162306a36Sopenharmony_ci break; 15262306a36Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 15362306a36Sopenharmony_ci pdc_type = PDC_EDGE_DUAL; 15462306a36Sopenharmony_ci type = IRQ_TYPE_EDGE_RISING; 15562306a36Sopenharmony_ci break; 15662306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 15762306a36Sopenharmony_ci pdc_type = PDC_LEVEL_HIGH; 15862306a36Sopenharmony_ci break; 15962306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 16062306a36Sopenharmony_ci pdc_type = PDC_LEVEL_LOW; 16162306a36Sopenharmony_ci type = IRQ_TYPE_LEVEL_HIGH; 16262306a36Sopenharmony_ci break; 16362306a36Sopenharmony_ci default: 16462306a36Sopenharmony_ci WARN_ON(1); 16562306a36Sopenharmony_ci return -EINVAL; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci old_pdc_type = pdc_reg_read(IRQ_i_CFG, d->hwirq); 16962306a36Sopenharmony_ci pdc_type |= (old_pdc_type & ~IRQ_i_CFG_TYPE_MASK); 17062306a36Sopenharmony_ci pdc_reg_write(IRQ_i_CFG, d->hwirq, pdc_type); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci ret = irq_chip_set_type_parent(d, type); 17362306a36Sopenharmony_ci if (ret) 17462306a36Sopenharmony_ci return ret; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* 17762306a36Sopenharmony_ci * When we change types the PDC can give a phantom interrupt. 17862306a36Sopenharmony_ci * Clear it. Specifically the phantom shows up when reconfiguring 17962306a36Sopenharmony_ci * polarity of interrupt without changing the state of the signal 18062306a36Sopenharmony_ci * but let's be consistent and clear it always. 18162306a36Sopenharmony_ci * 18262306a36Sopenharmony_ci * Doing this works because we have IRQCHIP_SET_TYPE_MASKED so the 18362306a36Sopenharmony_ci * interrupt will be cleared before the rest of the system sees it. 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ci if (old_pdc_type != pdc_type) 18662306a36Sopenharmony_ci irq_chip_set_parent_state(d, IRQCHIP_STATE_PENDING, false); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return 0; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic struct irq_chip qcom_pdc_gic_chip = { 19262306a36Sopenharmony_ci .name = "PDC", 19362306a36Sopenharmony_ci .irq_eoi = irq_chip_eoi_parent, 19462306a36Sopenharmony_ci .irq_mask = irq_chip_mask_parent, 19562306a36Sopenharmony_ci .irq_unmask = irq_chip_unmask_parent, 19662306a36Sopenharmony_ci .irq_disable = qcom_pdc_gic_disable, 19762306a36Sopenharmony_ci .irq_enable = qcom_pdc_gic_enable, 19862306a36Sopenharmony_ci .irq_get_irqchip_state = irq_chip_get_parent_state, 19962306a36Sopenharmony_ci .irq_set_irqchip_state = irq_chip_set_parent_state, 20062306a36Sopenharmony_ci .irq_retrigger = irq_chip_retrigger_hierarchy, 20162306a36Sopenharmony_ci .irq_set_type = qcom_pdc_gic_set_type, 20262306a36Sopenharmony_ci .flags = IRQCHIP_MASK_ON_SUSPEND | 20362306a36Sopenharmony_ci IRQCHIP_SET_TYPE_MASKED | 20462306a36Sopenharmony_ci IRQCHIP_SKIP_SET_WAKE | 20562306a36Sopenharmony_ci IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND, 20662306a36Sopenharmony_ci .irq_set_vcpu_affinity = irq_chip_set_vcpu_affinity_parent, 20762306a36Sopenharmony_ci .irq_set_affinity = irq_chip_set_affinity_parent, 20862306a36Sopenharmony_ci}; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic struct pdc_pin_region *get_pin_region(int pin) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci int i; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci for (i = 0; i < pdc_region_cnt; i++) { 21562306a36Sopenharmony_ci if (pin >= pdc_region[i].pin_base && 21662306a36Sopenharmony_ci pin < pdc_region[i].pin_base + pdc_region[i].cnt) 21762306a36Sopenharmony_ci return &pdc_region[i]; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return NULL; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic int qcom_pdc_alloc(struct irq_domain *domain, unsigned int virq, 22462306a36Sopenharmony_ci unsigned int nr_irqs, void *data) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct irq_fwspec *fwspec = data; 22762306a36Sopenharmony_ci struct irq_fwspec parent_fwspec; 22862306a36Sopenharmony_ci struct pdc_pin_region *region; 22962306a36Sopenharmony_ci irq_hw_number_t hwirq; 23062306a36Sopenharmony_ci unsigned int type; 23162306a36Sopenharmony_ci int ret; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci ret = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type); 23462306a36Sopenharmony_ci if (ret) 23562306a36Sopenharmony_ci return ret; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (hwirq == GPIO_NO_WAKE_IRQ) 23862306a36Sopenharmony_ci return irq_domain_disconnect_hierarchy(domain, virq); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, 24162306a36Sopenharmony_ci &qcom_pdc_gic_chip, NULL); 24262306a36Sopenharmony_ci if (ret) 24362306a36Sopenharmony_ci return ret; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci region = get_pin_region(hwirq); 24662306a36Sopenharmony_ci if (!region) 24762306a36Sopenharmony_ci return irq_domain_disconnect_hierarchy(domain->parent, virq); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (type & IRQ_TYPE_EDGE_BOTH) 25062306a36Sopenharmony_ci type = IRQ_TYPE_EDGE_RISING; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (type & IRQ_TYPE_LEVEL_MASK) 25362306a36Sopenharmony_ci type = IRQ_TYPE_LEVEL_HIGH; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci parent_fwspec.fwnode = domain->parent->fwnode; 25662306a36Sopenharmony_ci parent_fwspec.param_count = 3; 25762306a36Sopenharmony_ci parent_fwspec.param[0] = 0; 25862306a36Sopenharmony_ci parent_fwspec.param[1] = pin_to_hwirq(region, hwirq); 25962306a36Sopenharmony_ci parent_fwspec.param[2] = type; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, 26262306a36Sopenharmony_ci &parent_fwspec); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic const struct irq_domain_ops qcom_pdc_ops = { 26662306a36Sopenharmony_ci .translate = irq_domain_translate_twocell, 26762306a36Sopenharmony_ci .alloc = qcom_pdc_alloc, 26862306a36Sopenharmony_ci .free = irq_domain_free_irqs_common, 26962306a36Sopenharmony_ci}; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic int pdc_setup_pin_mapping(struct device_node *np) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci int ret, n, i; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci n = of_property_count_elems_of_size(np, "qcom,pdc-ranges", sizeof(u32)); 27662306a36Sopenharmony_ci if (n <= 0 || n % 3) 27762306a36Sopenharmony_ci return -EINVAL; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci pdc_region_cnt = n / 3; 28062306a36Sopenharmony_ci pdc_region = kcalloc(pdc_region_cnt, sizeof(*pdc_region), GFP_KERNEL); 28162306a36Sopenharmony_ci if (!pdc_region) { 28262306a36Sopenharmony_ci pdc_region_cnt = 0; 28362306a36Sopenharmony_ci return -ENOMEM; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci for (n = 0; n < pdc_region_cnt; n++) { 28762306a36Sopenharmony_ci ret = of_property_read_u32_index(np, "qcom,pdc-ranges", 28862306a36Sopenharmony_ci n * 3 + 0, 28962306a36Sopenharmony_ci &pdc_region[n].pin_base); 29062306a36Sopenharmony_ci if (ret) 29162306a36Sopenharmony_ci return ret; 29262306a36Sopenharmony_ci ret = of_property_read_u32_index(np, "qcom,pdc-ranges", 29362306a36Sopenharmony_ci n * 3 + 1, 29462306a36Sopenharmony_ci &pdc_region[n].parent_base); 29562306a36Sopenharmony_ci if (ret) 29662306a36Sopenharmony_ci return ret; 29762306a36Sopenharmony_ci ret = of_property_read_u32_index(np, "qcom,pdc-ranges", 29862306a36Sopenharmony_ci n * 3 + 2, 29962306a36Sopenharmony_ci &pdc_region[n].cnt); 30062306a36Sopenharmony_ci if (ret) 30162306a36Sopenharmony_ci return ret; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci for (i = 0; i < pdc_region[n].cnt; i++) 30462306a36Sopenharmony_ci __pdc_enable_intr(i + pdc_region[n].pin_base, 0); 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return 0; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci#define QCOM_PDC_SIZE 0x30000 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic int qcom_pdc_init(struct device_node *node, struct device_node *parent) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct irq_domain *parent_domain, *pdc_domain; 31562306a36Sopenharmony_ci resource_size_t res_size; 31662306a36Sopenharmony_ci struct resource res; 31762306a36Sopenharmony_ci int ret; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* compat with old sm8150 DT which had very small region for PDC */ 32062306a36Sopenharmony_ci if (of_address_to_resource(node, 0, &res)) 32162306a36Sopenharmony_ci return -EINVAL; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci res_size = max_t(resource_size_t, resource_size(&res), QCOM_PDC_SIZE); 32462306a36Sopenharmony_ci if (res_size > resource_size(&res)) 32562306a36Sopenharmony_ci pr_warn("%pOF: invalid reg size, please fix DT\n", node); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci pdc_base = ioremap(res.start, res_size); 32862306a36Sopenharmony_ci if (!pdc_base) { 32962306a36Sopenharmony_ci pr_err("%pOF: unable to map PDC registers\n", node); 33062306a36Sopenharmony_ci return -ENXIO; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci pdc_version = pdc_reg_read(PDC_VERSION_REG, 0); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci parent_domain = irq_find_host(parent); 33662306a36Sopenharmony_ci if (!parent_domain) { 33762306a36Sopenharmony_ci pr_err("%pOF: unable to find PDC's parent domain\n", node); 33862306a36Sopenharmony_ci ret = -ENXIO; 33962306a36Sopenharmony_ci goto fail; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci ret = pdc_setup_pin_mapping(node); 34362306a36Sopenharmony_ci if (ret) { 34462306a36Sopenharmony_ci pr_err("%pOF: failed to init PDC pin-hwirq mapping\n", node); 34562306a36Sopenharmony_ci goto fail; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci pdc_domain = irq_domain_create_hierarchy(parent_domain, 34962306a36Sopenharmony_ci IRQ_DOMAIN_FLAG_QCOM_PDC_WAKEUP, 35062306a36Sopenharmony_ci PDC_MAX_GPIO_IRQS, 35162306a36Sopenharmony_ci of_fwnode_handle(node), 35262306a36Sopenharmony_ci &qcom_pdc_ops, NULL); 35362306a36Sopenharmony_ci if (!pdc_domain) { 35462306a36Sopenharmony_ci pr_err("%pOF: PDC domain add failed\n", node); 35562306a36Sopenharmony_ci ret = -ENOMEM; 35662306a36Sopenharmony_ci goto fail; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci irq_domain_update_bus_token(pdc_domain, DOMAIN_BUS_WAKEUP); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci return 0; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cifail: 36462306a36Sopenharmony_ci kfree(pdc_region); 36562306a36Sopenharmony_ci iounmap(pdc_base); 36662306a36Sopenharmony_ci return ret; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ciIRQCHIP_PLATFORM_DRIVER_BEGIN(qcom_pdc) 37062306a36Sopenharmony_ciIRQCHIP_MATCH("qcom,pdc", qcom_pdc_init) 37162306a36Sopenharmony_ciIRQCHIP_PLATFORM_DRIVER_END(qcom_pdc) 37262306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm Technologies, Inc. Power Domain Controller"); 37362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 374