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