Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 *  Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
  4 *  Loongson PCH PIC support
  5 */
  6
  7#define pr_fmt(fmt) "pch-pic: " fmt
  8
  9#include <linux/interrupt.h>
 10#include <linux/irq.h>
 11#include <linux/irqchip.h>
 12#include <linux/irqdomain.h>
 13#include <linux/kernel.h>
 14#include <linux/platform_device.h>
 15#include <linux/of_address.h>
 16#include <linux/of_irq.h>
 17#include <linux/of_platform.h>
 18
 19/* Registers */
 20#define PCH_PIC_MASK		0x20
 21#define PCH_PIC_HTMSI_EN	0x40
 22#define PCH_PIC_EDGE		0x60
 23#define PCH_PIC_CLR		0x80
 24#define PCH_PIC_AUTO0		0xc0
 25#define PCH_PIC_AUTO1		0xe0
 26#define PCH_INT_ROUTE(irq)	(0x100 + irq)
 27#define PCH_INT_HTVEC(irq)	(0x200 + irq)
 28#define PCH_PIC_POL		0x3e0
 29
 30#define PIC_COUNT_PER_REG	32
 31#define PIC_REG_COUNT		2
 32#define PIC_COUNT		(PIC_COUNT_PER_REG * PIC_REG_COUNT)
 33#define PIC_REG_IDX(irq_id)	((irq_id) / PIC_COUNT_PER_REG)
 34#define PIC_REG_BIT(irq_id)	((irq_id) % PIC_COUNT_PER_REG)
 35
 36struct pch_pic {
 37	void __iomem		*base;
 38	struct irq_domain	*pic_domain;
 39	u32			ht_vec_base;
 40	raw_spinlock_t		pic_lock;
 41};
 42
 43static void pch_pic_bitset(struct pch_pic *priv, int offset, int bit)
 44{
 45	u32 reg;
 46	void __iomem *addr = priv->base + offset + PIC_REG_IDX(bit) * 4;
 47
 48	raw_spin_lock(&priv->pic_lock);
 49	reg = readl(addr);
 50	reg |= BIT(PIC_REG_BIT(bit));
 51	writel(reg, addr);
 52	raw_spin_unlock(&priv->pic_lock);
 53}
 54
 55static void pch_pic_bitclr(struct pch_pic *priv, int offset, int bit)
 56{
 57	u32 reg;
 58	void __iomem *addr = priv->base + offset + PIC_REG_IDX(bit) * 4;
 59
 60	raw_spin_lock(&priv->pic_lock);
 61	reg = readl(addr);
 62	reg &= ~BIT(PIC_REG_BIT(bit));
 63	writel(reg, addr);
 64	raw_spin_unlock(&priv->pic_lock);
 65}
 66
 67static void pch_pic_mask_irq(struct irq_data *d)
 68{
 69	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
 70
 71	pch_pic_bitset(priv, PCH_PIC_MASK, d->hwirq);
 72	irq_chip_mask_parent(d);
 73}
 74
 75static void pch_pic_unmask_irq(struct irq_data *d)
 76{
 77	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
 78
 79	writel(BIT(PIC_REG_BIT(d->hwirq)),
 80			priv->base + PCH_PIC_CLR + PIC_REG_IDX(d->hwirq) * 4);
 81
 82	irq_chip_unmask_parent(d);
 83	pch_pic_bitclr(priv, PCH_PIC_MASK, d->hwirq);
 84}
 85
 86static int pch_pic_set_type(struct irq_data *d, unsigned int type)
 87{
 88	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
 89	int ret = 0;
 90
 91	switch (type) {
 92	case IRQ_TYPE_EDGE_RISING:
 93		pch_pic_bitset(priv, PCH_PIC_EDGE, d->hwirq);
 94		pch_pic_bitclr(priv, PCH_PIC_POL, d->hwirq);
 95		irq_set_handler_locked(d, handle_edge_irq);
 96		break;
 97	case IRQ_TYPE_EDGE_FALLING:
 98		pch_pic_bitset(priv, PCH_PIC_EDGE, d->hwirq);
 99		pch_pic_bitset(priv, PCH_PIC_POL, d->hwirq);
100		irq_set_handler_locked(d, handle_edge_irq);
101		break;
102	case IRQ_TYPE_LEVEL_HIGH:
103		pch_pic_bitclr(priv, PCH_PIC_EDGE, d->hwirq);
104		pch_pic_bitclr(priv, PCH_PIC_POL, d->hwirq);
105		irq_set_handler_locked(d, handle_level_irq);
106		break;
107	case IRQ_TYPE_LEVEL_LOW:
108		pch_pic_bitclr(priv, PCH_PIC_EDGE, d->hwirq);
109		pch_pic_bitset(priv, PCH_PIC_POL, d->hwirq);
110		irq_set_handler_locked(d, handle_level_irq);
111		break;
112	default:
113		ret = -EINVAL;
114		break;
115	}
116
117	return ret;
118}
119
120static void pch_pic_ack_irq(struct irq_data *d)
121{
122	unsigned int reg;
123	struct pch_pic *priv = irq_data_get_irq_chip_data(d);
124
125	reg = readl(priv->base + PCH_PIC_EDGE + PIC_REG_IDX(d->hwirq) * 4);
126	if (reg & BIT(PIC_REG_BIT(d->hwirq))) {
127		writel(BIT(PIC_REG_BIT(d->hwirq)),
128			priv->base + PCH_PIC_CLR + PIC_REG_IDX(d->hwirq) * 4);
129	}
130	irq_chip_ack_parent(d);
131}
132
133static struct irq_chip pch_pic_irq_chip = {
134	.name			= "PCH PIC",
135	.irq_mask		= pch_pic_mask_irq,
136	.irq_unmask		= pch_pic_unmask_irq,
137	.irq_ack		= pch_pic_ack_irq,
138	.irq_set_affinity	= irq_chip_set_affinity_parent,
139	.irq_set_type		= pch_pic_set_type,
140};
141
142static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq,
143			      unsigned int nr_irqs, void *arg)
144{
145	int err;
146	unsigned int type;
147	unsigned long hwirq;
148	struct irq_fwspec *fwspec = arg;
149	struct irq_fwspec parent_fwspec;
150	struct pch_pic *priv = domain->host_data;
151
152	err = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type);
153	if (err)
154		return err;
155
156	parent_fwspec.fwnode = domain->parent->fwnode;
157	parent_fwspec.param_count = 1;
158	parent_fwspec.param[0] = hwirq + priv->ht_vec_base;
159
160	err = irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
161	if (err)
162		return err;
163
164	irq_domain_set_info(domain, virq, hwirq,
165			    &pch_pic_irq_chip, priv,
166			    handle_level_irq, NULL, NULL);
167	irq_set_probe(virq);
168
169	return 0;
170}
171
172static const struct irq_domain_ops pch_pic_domain_ops = {
173	.translate	= irq_domain_translate_twocell,
174	.alloc		= pch_pic_alloc,
175	.free		= irq_domain_free_irqs_parent,
176};
177
178static void pch_pic_reset(struct pch_pic *priv)
179{
180	int i;
181
182	for (i = 0; i < PIC_COUNT; i++) {
183		/* Write vectored ID */
184		writeb(priv->ht_vec_base + i, priv->base + PCH_INT_HTVEC(i));
185		/* Hardcode route to HT0 Lo */
186		writeb(1, priv->base + PCH_INT_ROUTE(i));
187	}
188
189	for (i = 0; i < PIC_REG_COUNT; i++) {
190		/* Clear IRQ cause registers, mask all interrupts */
191		writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_MASK + 4 * i);
192		writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_CLR + 4 * i);
193		/* Clear auto bounce, we don't need that */
194		writel_relaxed(0, priv->base + PCH_PIC_AUTO0 + 4 * i);
195		writel_relaxed(0, priv->base + PCH_PIC_AUTO1 + 4 * i);
196		/* Enable HTMSI transformer */
197		writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_HTMSI_EN + 4 * i);
198	}
199}
200
201static int pch_pic_of_init(struct device_node *node,
202				struct device_node *parent)
203{
204	struct pch_pic *priv;
205	struct irq_domain *parent_domain;
206	int err;
207
208	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
209	if (!priv)
210		return -ENOMEM;
211
212	raw_spin_lock_init(&priv->pic_lock);
213	priv->base = of_iomap(node, 0);
214	if (!priv->base) {
215		err = -ENOMEM;
216		goto free_priv;
217	}
218
219	parent_domain = irq_find_host(parent);
220	if (!parent_domain) {
221		pr_err("Failed to find the parent domain\n");
222		err = -ENXIO;
223		goto iounmap_base;
224	}
225
226	if (of_property_read_u32(node, "loongson,pic-base-vec",
227				&priv->ht_vec_base)) {
228		pr_err("Failed to determine pic-base-vec\n");
229		err = -EINVAL;
230		goto iounmap_base;
231	}
232
233	priv->pic_domain = irq_domain_create_hierarchy(parent_domain, 0,
234						       PIC_COUNT,
235						       of_node_to_fwnode(node),
236						       &pch_pic_domain_ops,
237						       priv);
238	if (!priv->pic_domain) {
239		pr_err("Failed to create IRQ domain\n");
240		err = -ENOMEM;
241		goto iounmap_base;
242	}
243
244	pch_pic_reset(priv);
245
246	return 0;
247
248iounmap_base:
249	iounmap(priv->base);
250free_priv:
251	kfree(priv);
252
253	return err;
254}
255
256IRQCHIP_DECLARE(pch_pic, "loongson,pch-pic-1.0", pch_pic_of_init);