18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * IMG PowerDown Controller (PDC)
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2010-2013 Imagination Technologies Ltd.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Exposes the syswake and PDC peripheral wake interrupts to the system.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/bitops.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/irqdomain.h>
148c2ecf20Sopenharmony_ci#include <linux/io.h>
158c2ecf20Sopenharmony_ci#include <linux/kernel.h>
168c2ecf20Sopenharmony_ci#include <linux/of.h>
178c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
188c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* PDC interrupt register numbers */
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define PDC_IRQ_STATUS			0x310
238c2ecf20Sopenharmony_ci#define PDC_IRQ_ENABLE			0x314
248c2ecf20Sopenharmony_ci#define PDC_IRQ_CLEAR			0x318
258c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE			0x31c
268c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_BASE		0x330
278c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_STRIDE		0x8
288c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_CONFIG_BASE	0x334
298c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_CONFIG_STRIDE	0x8
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/* PDC interrupt register field masks */
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define PDC_IRQ_SYS3			0x08
348c2ecf20Sopenharmony_ci#define PDC_IRQ_SYS2			0x04
358c2ecf20Sopenharmony_ci#define PDC_IRQ_SYS1			0x02
368c2ecf20Sopenharmony_ci#define PDC_IRQ_SYS0			0x01
378c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_WU_EN_SYS3	0x08000000
388c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_WU_EN_SYS2	0x04000000
398c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_WU_EN_SYS1	0x02000000
408c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_WU_EN_SYS0	0x01000000
418c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_WU_EN_WD		0x00040000
428c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_WU_EN_IR		0x00020000
438c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_WU_EN_RTC		0x00010000
448c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_EXT_EN_SYS3	0x00000800
458c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_EXT_EN_SYS2	0x00000400
468c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_EXT_EN_SYS1	0x00000200
478c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_EXT_EN_SYS0	0x00000100
488c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_EXT_EN_WD		0x00000004
498c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_EXT_EN_IR		0x00000002
508c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_EXT_EN_RTC	0x00000001
518c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_RESET		0x00000010
528c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_INT_MODE		0x0000000e
538c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_INT_MODE_SHIFT	1
548c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_PIN_VAL		0x00000001
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/* PDC interrupt constants */
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_INT_LOW		0x0
598c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_INT_HIGH		0x1
608c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_INT_DOWN		0x2
618c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_INT_UP		0x3
628c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_INT_CHANGE		0x6
638c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_INT_NONE		0x4
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/**
668c2ecf20Sopenharmony_ci * struct pdc_intc_priv - private pdc interrupt data.
678c2ecf20Sopenharmony_ci * @nr_perips:		Number of peripheral interrupt signals.
688c2ecf20Sopenharmony_ci * @nr_syswakes:	Number of syswake signals.
698c2ecf20Sopenharmony_ci * @perip_irqs:		List of peripheral IRQ numbers handled.
708c2ecf20Sopenharmony_ci * @syswake_irq:	Shared PDC syswake IRQ number.
718c2ecf20Sopenharmony_ci * @domain:		IRQ domain for PDC peripheral and syswake IRQs.
728c2ecf20Sopenharmony_ci * @pdc_base:		Base of PDC registers.
738c2ecf20Sopenharmony_ci * @irq_route:		Cached version of PDC_IRQ_ROUTE register.
748c2ecf20Sopenharmony_ci * @lock:		Lock to protect the PDC syswake registers and the cached
758c2ecf20Sopenharmony_ci *			values of those registers in this struct.
768c2ecf20Sopenharmony_ci */
778c2ecf20Sopenharmony_cistruct pdc_intc_priv {
788c2ecf20Sopenharmony_ci	unsigned int		nr_perips;
798c2ecf20Sopenharmony_ci	unsigned int		nr_syswakes;
808c2ecf20Sopenharmony_ci	unsigned int		*perip_irqs;
818c2ecf20Sopenharmony_ci	unsigned int		syswake_irq;
828c2ecf20Sopenharmony_ci	struct irq_domain	*domain;
838c2ecf20Sopenharmony_ci	void __iomem		*pdc_base;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	u32			irq_route;
868c2ecf20Sopenharmony_ci	raw_spinlock_t		lock;
878c2ecf20Sopenharmony_ci};
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic void pdc_write(struct pdc_intc_priv *priv, unsigned int reg_offs,
908c2ecf20Sopenharmony_ci		      unsigned int data)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	iowrite32(data, priv->pdc_base + reg_offs);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic unsigned int pdc_read(struct pdc_intc_priv *priv,
968c2ecf20Sopenharmony_ci			     unsigned int reg_offs)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	return ioread32(priv->pdc_base + reg_offs);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/* Generic IRQ callbacks */
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci#define SYS0_HWIRQ	8
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic unsigned int hwirq_is_syswake(irq_hw_number_t hw)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	return hw >= SYS0_HWIRQ;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic unsigned int hwirq_to_syswake(irq_hw_number_t hw)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	return hw - SYS0_HWIRQ;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic irq_hw_number_t syswake_to_hwirq(unsigned int syswake)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	return SYS0_HWIRQ + syswake;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic struct pdc_intc_priv *irqd_to_priv(struct irq_data *data)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	return (struct pdc_intc_priv *)data->domain->host_data;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci/*
1268c2ecf20Sopenharmony_ci * perip_irq_mask() and perip_irq_unmask() use IRQ_ROUTE which also contains
1278c2ecf20Sopenharmony_ci * wake bits, therefore we cannot use the generic irqchip mask callbacks as they
1288c2ecf20Sopenharmony_ci * cache the mask.
1298c2ecf20Sopenharmony_ci */
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic void perip_irq_mask(struct irq_data *data)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct pdc_intc_priv *priv = irqd_to_priv(data);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	raw_spin_lock(&priv->lock);
1368c2ecf20Sopenharmony_ci	priv->irq_route &= ~data->mask;
1378c2ecf20Sopenharmony_ci	pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route);
1388c2ecf20Sopenharmony_ci	raw_spin_unlock(&priv->lock);
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic void perip_irq_unmask(struct irq_data *data)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	struct pdc_intc_priv *priv = irqd_to_priv(data);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	raw_spin_lock(&priv->lock);
1468c2ecf20Sopenharmony_ci	priv->irq_route |= data->mask;
1478c2ecf20Sopenharmony_ci	pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route);
1488c2ecf20Sopenharmony_ci	raw_spin_unlock(&priv->lock);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic int syswake_irq_set_type(struct irq_data *data, unsigned int flow_type)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct pdc_intc_priv *priv = irqd_to_priv(data);
1548c2ecf20Sopenharmony_ci	unsigned int syswake = hwirq_to_syswake(data->hwirq);
1558c2ecf20Sopenharmony_ci	unsigned int irq_mode;
1568c2ecf20Sopenharmony_ci	unsigned int soc_sys_wake_regoff, soc_sys_wake;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	/* translate to syswake IRQ mode */
1598c2ecf20Sopenharmony_ci	switch (flow_type) {
1608c2ecf20Sopenharmony_ci	case IRQ_TYPE_EDGE_BOTH:
1618c2ecf20Sopenharmony_ci		irq_mode = PDC_SYS_WAKE_INT_CHANGE;
1628c2ecf20Sopenharmony_ci		break;
1638c2ecf20Sopenharmony_ci	case IRQ_TYPE_EDGE_RISING:
1648c2ecf20Sopenharmony_ci		irq_mode = PDC_SYS_WAKE_INT_UP;
1658c2ecf20Sopenharmony_ci		break;
1668c2ecf20Sopenharmony_ci	case IRQ_TYPE_EDGE_FALLING:
1678c2ecf20Sopenharmony_ci		irq_mode = PDC_SYS_WAKE_INT_DOWN;
1688c2ecf20Sopenharmony_ci		break;
1698c2ecf20Sopenharmony_ci	case IRQ_TYPE_LEVEL_HIGH:
1708c2ecf20Sopenharmony_ci		irq_mode = PDC_SYS_WAKE_INT_HIGH;
1718c2ecf20Sopenharmony_ci		break;
1728c2ecf20Sopenharmony_ci	case IRQ_TYPE_LEVEL_LOW:
1738c2ecf20Sopenharmony_ci		irq_mode = PDC_SYS_WAKE_INT_LOW;
1748c2ecf20Sopenharmony_ci		break;
1758c2ecf20Sopenharmony_ci	default:
1768c2ecf20Sopenharmony_ci		return -EINVAL;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	raw_spin_lock(&priv->lock);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	/* set the IRQ mode */
1828c2ecf20Sopenharmony_ci	soc_sys_wake_regoff = PDC_SYS_WAKE_BASE + syswake*PDC_SYS_WAKE_STRIDE;
1838c2ecf20Sopenharmony_ci	soc_sys_wake = pdc_read(priv, soc_sys_wake_regoff);
1848c2ecf20Sopenharmony_ci	soc_sys_wake &= ~PDC_SYS_WAKE_INT_MODE;
1858c2ecf20Sopenharmony_ci	soc_sys_wake |= irq_mode << PDC_SYS_WAKE_INT_MODE_SHIFT;
1868c2ecf20Sopenharmony_ci	pdc_write(priv, soc_sys_wake_regoff, soc_sys_wake);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/* and update the handler */
1898c2ecf20Sopenharmony_ci	irq_setup_alt_chip(data, flow_type);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	raw_spin_unlock(&priv->lock);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	return 0;
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci/* applies to both peripheral and syswake interrupts */
1978c2ecf20Sopenharmony_cistatic int pdc_irq_set_wake(struct irq_data *data, unsigned int on)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	struct pdc_intc_priv *priv = irqd_to_priv(data);
2008c2ecf20Sopenharmony_ci	irq_hw_number_t hw = data->hwirq;
2018c2ecf20Sopenharmony_ci	unsigned int mask = (1 << 16) << hw;
2028c2ecf20Sopenharmony_ci	unsigned int dst_irq;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	raw_spin_lock(&priv->lock);
2058c2ecf20Sopenharmony_ci	if (on)
2068c2ecf20Sopenharmony_ci		priv->irq_route |= mask;
2078c2ecf20Sopenharmony_ci	else
2088c2ecf20Sopenharmony_ci		priv->irq_route &= ~mask;
2098c2ecf20Sopenharmony_ci	pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route);
2108c2ecf20Sopenharmony_ci	raw_spin_unlock(&priv->lock);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/* control the destination IRQ wakeup too for standby mode */
2138c2ecf20Sopenharmony_ci	if (hwirq_is_syswake(hw))
2148c2ecf20Sopenharmony_ci		dst_irq = priv->syswake_irq;
2158c2ecf20Sopenharmony_ci	else
2168c2ecf20Sopenharmony_ci		dst_irq = priv->perip_irqs[hw];
2178c2ecf20Sopenharmony_ci	irq_set_irq_wake(dst_irq, on);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	return 0;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic void pdc_intc_perip_isr(struct irq_desc *desc)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	unsigned int irq = irq_desc_get_irq(desc);
2258c2ecf20Sopenharmony_ci	struct pdc_intc_priv *priv;
2268c2ecf20Sopenharmony_ci	unsigned int i, irq_no;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	priv = (struct pdc_intc_priv *)irq_desc_get_handler_data(desc);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	/* find the peripheral number */
2318c2ecf20Sopenharmony_ci	for (i = 0; i < priv->nr_perips; ++i)
2328c2ecf20Sopenharmony_ci		if (irq == priv->perip_irqs[i])
2338c2ecf20Sopenharmony_ci			goto found;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	/* should never get here */
2368c2ecf20Sopenharmony_ci	return;
2378c2ecf20Sopenharmony_cifound:
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/* pass on the interrupt */
2408c2ecf20Sopenharmony_ci	irq_no = irq_linear_revmap(priv->domain, i);
2418c2ecf20Sopenharmony_ci	generic_handle_irq(irq_no);
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic void pdc_intc_syswake_isr(struct irq_desc *desc)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct pdc_intc_priv *priv;
2478c2ecf20Sopenharmony_ci	unsigned int syswake, irq_no;
2488c2ecf20Sopenharmony_ci	unsigned int status;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	priv = (struct pdc_intc_priv *)irq_desc_get_handler_data(desc);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	status = pdc_read(priv, PDC_IRQ_STATUS) &
2538c2ecf20Sopenharmony_ci		 pdc_read(priv, PDC_IRQ_ENABLE);
2548c2ecf20Sopenharmony_ci	status &= (1 << priv->nr_syswakes) - 1;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	for (syswake = 0; status; status >>= 1, ++syswake) {
2578c2ecf20Sopenharmony_ci		/* Has this sys_wake triggered? */
2588c2ecf20Sopenharmony_ci		if (!(status & 1))
2598c2ecf20Sopenharmony_ci			continue;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci		irq_no = irq_linear_revmap(priv->domain,
2628c2ecf20Sopenharmony_ci					   syswake_to_hwirq(syswake));
2638c2ecf20Sopenharmony_ci		generic_handle_irq(irq_no);
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic void pdc_intc_setup(struct pdc_intc_priv *priv)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	int i;
2708c2ecf20Sopenharmony_ci	unsigned int soc_sys_wake_regoff;
2718c2ecf20Sopenharmony_ci	unsigned int soc_sys_wake;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/*
2748c2ecf20Sopenharmony_ci	 * Mask all syswake interrupts before routing, or we could receive an
2758c2ecf20Sopenharmony_ci	 * interrupt before we're ready to handle it.
2768c2ecf20Sopenharmony_ci	 */
2778c2ecf20Sopenharmony_ci	pdc_write(priv, PDC_IRQ_ENABLE, 0);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	/*
2808c2ecf20Sopenharmony_ci	 * Enable routing of all syswakes
2818c2ecf20Sopenharmony_ci	 * Disable all wake sources
2828c2ecf20Sopenharmony_ci	 */
2838c2ecf20Sopenharmony_ci	priv->irq_route = ((PDC_IRQ_ROUTE_EXT_EN_SYS0 << priv->nr_syswakes) -
2848c2ecf20Sopenharmony_ci				PDC_IRQ_ROUTE_EXT_EN_SYS0);
2858c2ecf20Sopenharmony_ci	pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	/* Initialise syswake IRQ */
2888c2ecf20Sopenharmony_ci	for (i = 0; i < priv->nr_syswakes; ++i) {
2898c2ecf20Sopenharmony_ci		/* set the IRQ mode to none */
2908c2ecf20Sopenharmony_ci		soc_sys_wake_regoff = PDC_SYS_WAKE_BASE + i*PDC_SYS_WAKE_STRIDE;
2918c2ecf20Sopenharmony_ci		soc_sys_wake = PDC_SYS_WAKE_INT_NONE
2928c2ecf20Sopenharmony_ci				<< PDC_SYS_WAKE_INT_MODE_SHIFT;
2938c2ecf20Sopenharmony_ci		pdc_write(priv, soc_sys_wake_regoff, soc_sys_wake);
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic int pdc_intc_probe(struct platform_device *pdev)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	struct pdc_intc_priv *priv;
3008c2ecf20Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
3018c2ecf20Sopenharmony_ci	struct resource *res_regs;
3028c2ecf20Sopenharmony_ci	struct irq_chip_generic *gc;
3038c2ecf20Sopenharmony_ci	unsigned int i;
3048c2ecf20Sopenharmony_ci	int irq, ret;
3058c2ecf20Sopenharmony_ci	u32 val;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (!node)
3088c2ecf20Sopenharmony_ci		return -ENOENT;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/* Get registers */
3118c2ecf20Sopenharmony_ci	res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
3128c2ecf20Sopenharmony_ci	if (res_regs == NULL) {
3138c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "cannot find registers resource\n");
3148c2ecf20Sopenharmony_ci		return -ENOENT;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	/* Allocate driver data */
3188c2ecf20Sopenharmony_ci	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
3198c2ecf20Sopenharmony_ci	if (!priv) {
3208c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "cannot allocate device data\n");
3218c2ecf20Sopenharmony_ci		return -ENOMEM;
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci	raw_spin_lock_init(&priv->lock);
3248c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, priv);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	/* Ioremap the registers */
3278c2ecf20Sopenharmony_ci	priv->pdc_base = devm_ioremap(&pdev->dev, res_regs->start,
3288c2ecf20Sopenharmony_ci				      resource_size(res_regs));
3298c2ecf20Sopenharmony_ci	if (!priv->pdc_base)
3308c2ecf20Sopenharmony_ci		return -EIO;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	/* Get number of peripherals */
3338c2ecf20Sopenharmony_ci	ret = of_property_read_u32(node, "num-perips", &val);
3348c2ecf20Sopenharmony_ci	if (ret) {
3358c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "No num-perips node property found\n");
3368c2ecf20Sopenharmony_ci		return -EINVAL;
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci	if (val > SYS0_HWIRQ) {
3398c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "num-perips (%u) out of range\n", val);
3408c2ecf20Sopenharmony_ci		return -EINVAL;
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci	priv->nr_perips = val;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	/* Get number of syswakes */
3458c2ecf20Sopenharmony_ci	ret = of_property_read_u32(node, "num-syswakes", &val);
3468c2ecf20Sopenharmony_ci	if (ret) {
3478c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "No num-syswakes node property found\n");
3488c2ecf20Sopenharmony_ci		return -EINVAL;
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci	if (val > SYS0_HWIRQ) {
3518c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "num-syswakes (%u) out of range\n", val);
3528c2ecf20Sopenharmony_ci		return -EINVAL;
3538c2ecf20Sopenharmony_ci	}
3548c2ecf20Sopenharmony_ci	priv->nr_syswakes = val;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	/* Get peripheral IRQ numbers */
3578c2ecf20Sopenharmony_ci	priv->perip_irqs = devm_kcalloc(&pdev->dev, 4, priv->nr_perips,
3588c2ecf20Sopenharmony_ci					GFP_KERNEL);
3598c2ecf20Sopenharmony_ci	if (!priv->perip_irqs) {
3608c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "cannot allocate perip IRQ list\n");
3618c2ecf20Sopenharmony_ci		return -ENOMEM;
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci	for (i = 0; i < priv->nr_perips; ++i) {
3648c2ecf20Sopenharmony_ci		irq = platform_get_irq(pdev, 1 + i);
3658c2ecf20Sopenharmony_ci		if (irq < 0)
3668c2ecf20Sopenharmony_ci			return irq;
3678c2ecf20Sopenharmony_ci		priv->perip_irqs[i] = irq;
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci	/* check if too many were provided */
3708c2ecf20Sopenharmony_ci	if (platform_get_irq(pdev, 1 + i) >= 0) {
3718c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "surplus perip IRQs detected\n");
3728c2ecf20Sopenharmony_ci		return -EINVAL;
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	/* Get syswake IRQ number */
3768c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
3778c2ecf20Sopenharmony_ci	if (irq < 0)
3788c2ecf20Sopenharmony_ci		return irq;
3798c2ecf20Sopenharmony_ci	priv->syswake_irq = irq;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	/* Set up an IRQ domain */
3828c2ecf20Sopenharmony_ci	priv->domain = irq_domain_add_linear(node, 16, &irq_generic_chip_ops,
3838c2ecf20Sopenharmony_ci					     priv);
3848c2ecf20Sopenharmony_ci	if (unlikely(!priv->domain)) {
3858c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "cannot add IRQ domain\n");
3868c2ecf20Sopenharmony_ci		return -ENOMEM;
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	/*
3908c2ecf20Sopenharmony_ci	 * Set up 2 generic irq chips with 2 chip types.
3918c2ecf20Sopenharmony_ci	 * The first one for peripheral irqs (only 1 chip type used)
3928c2ecf20Sopenharmony_ci	 * The second one for syswake irqs (edge and level chip types)
3938c2ecf20Sopenharmony_ci	 */
3948c2ecf20Sopenharmony_ci	ret = irq_alloc_domain_generic_chips(priv->domain, 8, 2, "pdc",
3958c2ecf20Sopenharmony_ci					     handle_level_irq, 0, 0,
3968c2ecf20Sopenharmony_ci					     IRQ_GC_INIT_NESTED_LOCK);
3978c2ecf20Sopenharmony_ci	if (ret)
3988c2ecf20Sopenharmony_ci		goto err_generic;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	/* peripheral interrupt chip */
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	gc = irq_get_domain_generic_chip(priv->domain, 0);
4038c2ecf20Sopenharmony_ci	gc->unused	= ~(BIT(priv->nr_perips) - 1);
4048c2ecf20Sopenharmony_ci	gc->reg_base	= priv->pdc_base;
4058c2ecf20Sopenharmony_ci	/*
4068c2ecf20Sopenharmony_ci	 * IRQ_ROUTE contains wake bits, so we can't use the generic versions as
4078c2ecf20Sopenharmony_ci	 * they cache the mask
4088c2ecf20Sopenharmony_ci	 */
4098c2ecf20Sopenharmony_ci	gc->chip_types[0].regs.mask		= PDC_IRQ_ROUTE;
4108c2ecf20Sopenharmony_ci	gc->chip_types[0].chip.irq_mask		= perip_irq_mask;
4118c2ecf20Sopenharmony_ci	gc->chip_types[0].chip.irq_unmask	= perip_irq_unmask;
4128c2ecf20Sopenharmony_ci	gc->chip_types[0].chip.irq_set_wake	= pdc_irq_set_wake;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	/* syswake interrupt chip */
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	gc = irq_get_domain_generic_chip(priv->domain, 8);
4178c2ecf20Sopenharmony_ci	gc->unused	= ~(BIT(priv->nr_syswakes) - 1);
4188c2ecf20Sopenharmony_ci	gc->reg_base	= priv->pdc_base;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	/* edge interrupts */
4218c2ecf20Sopenharmony_ci	gc->chip_types[0].type			= IRQ_TYPE_EDGE_BOTH;
4228c2ecf20Sopenharmony_ci	gc->chip_types[0].handler		= handle_edge_irq;
4238c2ecf20Sopenharmony_ci	gc->chip_types[0].regs.ack		= PDC_IRQ_CLEAR;
4248c2ecf20Sopenharmony_ci	gc->chip_types[0].regs.mask		= PDC_IRQ_ENABLE;
4258c2ecf20Sopenharmony_ci	gc->chip_types[0].chip.irq_ack		= irq_gc_ack_set_bit;
4268c2ecf20Sopenharmony_ci	gc->chip_types[0].chip.irq_mask		= irq_gc_mask_clr_bit;
4278c2ecf20Sopenharmony_ci	gc->chip_types[0].chip.irq_unmask	= irq_gc_mask_set_bit;
4288c2ecf20Sopenharmony_ci	gc->chip_types[0].chip.irq_set_type	= syswake_irq_set_type;
4298c2ecf20Sopenharmony_ci	gc->chip_types[0].chip.irq_set_wake	= pdc_irq_set_wake;
4308c2ecf20Sopenharmony_ci	/* for standby we pass on to the shared syswake IRQ */
4318c2ecf20Sopenharmony_ci	gc->chip_types[0].chip.flags		= IRQCHIP_MASK_ON_SUSPEND;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	/* level interrupts */
4348c2ecf20Sopenharmony_ci	gc->chip_types[1].type			= IRQ_TYPE_LEVEL_MASK;
4358c2ecf20Sopenharmony_ci	gc->chip_types[1].handler		= handle_level_irq;
4368c2ecf20Sopenharmony_ci	gc->chip_types[1].regs.ack		= PDC_IRQ_CLEAR;
4378c2ecf20Sopenharmony_ci	gc->chip_types[1].regs.mask		= PDC_IRQ_ENABLE;
4388c2ecf20Sopenharmony_ci	gc->chip_types[1].chip.irq_ack		= irq_gc_ack_set_bit;
4398c2ecf20Sopenharmony_ci	gc->chip_types[1].chip.irq_mask		= irq_gc_mask_clr_bit;
4408c2ecf20Sopenharmony_ci	gc->chip_types[1].chip.irq_unmask	= irq_gc_mask_set_bit;
4418c2ecf20Sopenharmony_ci	gc->chip_types[1].chip.irq_set_type	= syswake_irq_set_type;
4428c2ecf20Sopenharmony_ci	gc->chip_types[1].chip.irq_set_wake	= pdc_irq_set_wake;
4438c2ecf20Sopenharmony_ci	/* for standby we pass on to the shared syswake IRQ */
4448c2ecf20Sopenharmony_ci	gc->chip_types[1].chip.flags		= IRQCHIP_MASK_ON_SUSPEND;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	/* Set up the hardware to enable interrupt routing */
4478c2ecf20Sopenharmony_ci	pdc_intc_setup(priv);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	/* Setup chained handlers for the peripheral IRQs */
4508c2ecf20Sopenharmony_ci	for (i = 0; i < priv->nr_perips; ++i) {
4518c2ecf20Sopenharmony_ci		irq = priv->perip_irqs[i];
4528c2ecf20Sopenharmony_ci		irq_set_chained_handler_and_data(irq, pdc_intc_perip_isr,
4538c2ecf20Sopenharmony_ci						 priv);
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	/* Setup chained handler for the syswake IRQ */
4578c2ecf20Sopenharmony_ci	irq_set_chained_handler_and_data(priv->syswake_irq,
4588c2ecf20Sopenharmony_ci					 pdc_intc_syswake_isr, priv);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	dev_info(&pdev->dev,
4618c2ecf20Sopenharmony_ci		 "PDC IRQ controller initialised (%u perip IRQs, %u syswake IRQs)\n",
4628c2ecf20Sopenharmony_ci		 priv->nr_perips,
4638c2ecf20Sopenharmony_ci		 priv->nr_syswakes);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	return 0;
4668c2ecf20Sopenharmony_cierr_generic:
4678c2ecf20Sopenharmony_ci	irq_domain_remove(priv->domain);
4688c2ecf20Sopenharmony_ci	return ret;
4698c2ecf20Sopenharmony_ci}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cistatic int pdc_intc_remove(struct platform_device *pdev)
4728c2ecf20Sopenharmony_ci{
4738c2ecf20Sopenharmony_ci	struct pdc_intc_priv *priv = platform_get_drvdata(pdev);
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	irq_domain_remove(priv->domain);
4768c2ecf20Sopenharmony_ci	return 0;
4778c2ecf20Sopenharmony_ci}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_cistatic const struct of_device_id pdc_intc_match[] = {
4808c2ecf20Sopenharmony_ci	{ .compatible = "img,pdc-intc" },
4818c2ecf20Sopenharmony_ci	{}
4828c2ecf20Sopenharmony_ci};
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic struct platform_driver pdc_intc_driver = {
4858c2ecf20Sopenharmony_ci	.driver = {
4868c2ecf20Sopenharmony_ci		.name		= "pdc-intc",
4878c2ecf20Sopenharmony_ci		.of_match_table	= pdc_intc_match,
4888c2ecf20Sopenharmony_ci	},
4898c2ecf20Sopenharmony_ci	.probe = pdc_intc_probe,
4908c2ecf20Sopenharmony_ci	.remove = pdc_intc_remove,
4918c2ecf20Sopenharmony_ci};
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_cistatic int __init pdc_intc_init(void)
4948c2ecf20Sopenharmony_ci{
4958c2ecf20Sopenharmony_ci	return platform_driver_register(&pdc_intc_driver);
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_cicore_initcall(pdc_intc_init);
498