File Index
Files and directories
Sign in
FILE

irq-davinci-cp-intc.c

File preview with syntax highlighting.

linux-6.19.14/drivers/irqchip/irq-davinci-cp-intc.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2//
  3// Author: Steve Chen <schen@mvista.com>
  4// Copyright (C) 2008-2009, MontaVista Software, Inc. <source@mvista.com>
  5// Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
  6// Copyright (C) 2019, Texas Instruments
  7//
  8// TI Common Platform Interrupt Controller (cp_intc) driver
  9
 10#include <linux/export.h>
 11#include <linux/init.h>
 12#include <linux/irq.h>
 13#include <linux/irqchip.h>
 14#include <linux/irqdomain.h>
 15#include <linux/io.h>
 16#include <linux/of.h>
 17#include <linux/of_address.h>
 18#include <linux/of_irq.h>
 19
 20#include <asm/exception.h>
 21
 22#define DAVINCI_CP_INTC_CTRL			0x04
 23#define DAVINCI_CP_INTC_HOST_CTRL		0x0c
 24#define DAVINCI_CP_INTC_GLOBAL_ENABLE		0x10
 25#define DAVINCI_CP_INTC_SYS_STAT_IDX_CLR	0x24
 26#define DAVINCI_CP_INTC_SYS_ENABLE_IDX_SET	0x28
 27#define DAVINCI_CP_INTC_SYS_ENABLE_IDX_CLR	0x2c
 28#define DAVINCI_CP_INTC_HOST_ENABLE_IDX_SET	0x34
 29#define DAVINCI_CP_INTC_HOST_ENABLE_IDX_CLR	0x38
 30#define DAVINCI_CP_INTC_PRIO_IDX		0x80
 31#define DAVINCI_CP_INTC_SYS_STAT_CLR(n)		(0x0280 + (n << 2))
 32#define DAVINCI_CP_INTC_SYS_ENABLE_CLR(n)	(0x0380 + (n << 2))
 33#define DAVINCI_CP_INTC_CHAN_MAP(n)		(0x0400 + (n << 2))
 34#define DAVINCI_CP_INTC_SYS_POLARITY(n)		(0x0d00 + (n << 2))
 35#define DAVINCI_CP_INTC_SYS_TYPE(n)		(0x0d80 + (n << 2))
 36#define DAVINCI_CP_INTC_HOST_ENABLE(n)		(0x1500 + (n << 2))
 37#define DAVINCI_CP_INTC_PRI_INDX_MASK		GENMASK(9, 0)
 38#define DAVINCI_CP_INTC_GPIR_NONE		BIT(31)
 39
 40static void __iomem *davinci_cp_intc_base;
 41static struct irq_domain *davinci_cp_intc_irq_domain;
 42
 43static inline unsigned int davinci_cp_intc_read(unsigned int offset)
 44{
 45	return readl_relaxed(davinci_cp_intc_base + offset);
 46}
 47
 48static inline void davinci_cp_intc_write(unsigned long value,
 49					 unsigned int offset)
 50{
 51	writel_relaxed(value, davinci_cp_intc_base + offset);
 52}
 53
 54static void davinci_cp_intc_ack_irq(struct irq_data *d)
 55{
 56	davinci_cp_intc_write(d->hwirq, DAVINCI_CP_INTC_SYS_STAT_IDX_CLR);
 57}
 58
 59static void davinci_cp_intc_mask_irq(struct irq_data *d)
 60{
 61	/* XXX don't know why we need to disable nIRQ here... */
 62	davinci_cp_intc_write(1, DAVINCI_CP_INTC_HOST_ENABLE_IDX_CLR);
 63	davinci_cp_intc_write(d->hwirq, DAVINCI_CP_INTC_SYS_ENABLE_IDX_CLR);
 64	davinci_cp_intc_write(1, DAVINCI_CP_INTC_HOST_ENABLE_IDX_SET);
 65}
 66
 67static void davinci_cp_intc_unmask_irq(struct irq_data *d)
 68{
 69	davinci_cp_intc_write(d->hwirq, DAVINCI_CP_INTC_SYS_ENABLE_IDX_SET);
 70}
 71
 72static int davinci_cp_intc_set_irq_type(struct irq_data *d,
 73					unsigned int flow_type)
 74{
 75	unsigned int reg, mask, polarity, type;
 76
 77	reg = BIT_WORD(d->hwirq);
 78	mask = BIT_MASK(d->hwirq);
 79	polarity = davinci_cp_intc_read(DAVINCI_CP_INTC_SYS_POLARITY(reg));
 80	type = davinci_cp_intc_read(DAVINCI_CP_INTC_SYS_TYPE(reg));
 81
 82	switch (flow_type) {
 83	case IRQ_TYPE_EDGE_RISING:
 84		polarity |= mask;
 85		type |= mask;
 86		break;
 87	case IRQ_TYPE_EDGE_FALLING:
 88		polarity &= ~mask;
 89		type |= mask;
 90		break;
 91	case IRQ_TYPE_LEVEL_HIGH:
 92		polarity |= mask;
 93		type &= ~mask;
 94		break;
 95	case IRQ_TYPE_LEVEL_LOW:
 96		polarity &= ~mask;
 97		type &= ~mask;
 98		break;
 99	default:
100		return -EINVAL;
101	}
102
103	davinci_cp_intc_write(polarity, DAVINCI_CP_INTC_SYS_POLARITY(reg));
104	davinci_cp_intc_write(type, DAVINCI_CP_INTC_SYS_TYPE(reg));
105
106	return 0;
107}
108
109static struct irq_chip davinci_cp_intc_irq_chip = {
110	.name		= "cp_intc",
111	.irq_ack	= davinci_cp_intc_ack_irq,
112	.irq_mask	= davinci_cp_intc_mask_irq,
113	.irq_unmask	= davinci_cp_intc_unmask_irq,
114	.irq_set_type	= davinci_cp_intc_set_irq_type,
115	.flags		= IRQCHIP_SKIP_SET_WAKE,
116};
117
118static void __exception_irq_entry davinci_cp_intc_handle_irq(struct pt_regs *regs)
119{
120	int gpir, irqnr, none;
121
122	/*
123	 * The interrupt number is in first ten bits. The NONE field set to 1
124	 * indicates a spurious irq.
125	 */
126
127	gpir = davinci_cp_intc_read(DAVINCI_CP_INTC_PRIO_IDX);
128	irqnr = gpir & DAVINCI_CP_INTC_PRI_INDX_MASK;
129	none = gpir & DAVINCI_CP_INTC_GPIR_NONE;
130
131	if (unlikely(none)) {
132		pr_err_once("%s: spurious irq!\n", __func__);
133		return;
134	}
135
136	generic_handle_domain_irq(davinci_cp_intc_irq_domain, irqnr);
137}
138
139static int davinci_cp_intc_host_map(struct irq_domain *h, unsigned int virq,
140			  irq_hw_number_t hw)
141{
142	pr_debug("cp_intc_host_map(%d, 0x%lx)\n", virq, hw);
143
144	irq_set_chip(virq, &davinci_cp_intc_irq_chip);
145	irq_set_probe(virq);
146	irq_set_handler(virq, handle_edge_irq);
147
148	return 0;
149}
150
151static const struct irq_domain_ops davinci_cp_intc_irq_domain_ops = {
152	.map = davinci_cp_intc_host_map,
153	.xlate = irq_domain_xlate_onetwocell,
154};
155
156static int __init davinci_cp_intc_do_init(struct resource *res, unsigned int num_irqs,
157					  struct device_node *node)
158{
159	unsigned int num_regs = BITS_TO_LONGS(num_irqs);
160	int offset, irq_base;
161	void __iomem *req;
162
163	req = request_mem_region(res->start, resource_size(res), "davinci-cp-intc");
164	if (!req) {
165		pr_err("%s: register range busy\n", __func__);
166		return -EBUSY;
167	}
168
169	davinci_cp_intc_base = ioremap(res->start, resource_size(res));
170	if (!davinci_cp_intc_base) {
171		pr_err("%s: unable to ioremap register range\n", __func__);
172		return -EINVAL;
173	}
174
175	davinci_cp_intc_write(0, DAVINCI_CP_INTC_GLOBAL_ENABLE);
176
177	/* Disable all host interrupts */
178	davinci_cp_intc_write(0, DAVINCI_CP_INTC_HOST_ENABLE(0));
179
180	/* Disable system interrupts */
181	for (offset = 0; offset < num_regs; offset++)
182		davinci_cp_intc_write(~0, DAVINCI_CP_INTC_SYS_ENABLE_CLR(offset));
183
184	/* Set to normal mode, no nesting, no priority hold */
185	davinci_cp_intc_write(0, DAVINCI_CP_INTC_CTRL);
186	davinci_cp_intc_write(0, DAVINCI_CP_INTC_HOST_CTRL);
187
188	/* Clear system interrupt status */
189	for (offset = 0; offset < num_regs; offset++)
190		davinci_cp_intc_write(~0, DAVINCI_CP_INTC_SYS_STAT_CLR(offset));
191
192	/* Enable nIRQ (what about nFIQ?) */
193	davinci_cp_intc_write(1, DAVINCI_CP_INTC_HOST_ENABLE_IDX_SET);
194
195	/* 4 channels per register */
196	num_regs = (num_irqs + 3) >> 2;
197	/* Default all priorities to channel 7. */
198	for (offset = 0; offset < num_regs; offset++)
199		davinci_cp_intc_write(0x07070707, DAVINCI_CP_INTC_CHAN_MAP(offset));
200
201	irq_base = irq_alloc_descs(-1, 0, num_irqs, 0);
202	if (irq_base < 0) {
203		pr_err("%s: unable to allocate interrupt descriptors: %d\n", __func__, irq_base);
204		return irq_base;
205	}
206
207	davinci_cp_intc_irq_domain = irq_domain_create_legacy(of_fwnode_handle(node), num_irqs,
208							      irq_base, 0,
209							      &davinci_cp_intc_irq_domain_ops,
210							      NULL);
211
212	if (!davinci_cp_intc_irq_domain) {
213		pr_err("%s: unable to create an interrupt domain\n", __func__);
214		return -EINVAL;
215	}
216
217	set_handle_irq(davinci_cp_intc_handle_irq);
218
219	/* Enable global interrupt */
220	davinci_cp_intc_write(1, DAVINCI_CP_INTC_GLOBAL_ENABLE);
221
222	return 0;
223}
224
225static int __init davinci_cp_intc_of_init(struct device_node *node,
226					  struct device_node *parent)
227{
228	unsigned int num_irqs;
229	struct resource res;
230	int ret;
231
232	ret = of_address_to_resource(node, 0, &res);
233	if (ret) {
234		pr_err("%s: unable to get the register range from device-tree\n", __func__);
235		return ret;
236	}
237
238	ret = of_property_read_u32(node, "ti,intc-size", &num_irqs);
239	if (ret) {
240		pr_err("%s: unable to read the 'ti,intc-size' property\n", __func__);
241		return ret;
242	}
243
244	return davinci_cp_intc_do_init(&res, num_irqs, node);
245}
246IRQCHIP_DECLARE(cp_intc, "ti,cp-intc", davinci_cp_intc_of_init);
~/ Read-only file preview.