spear-shirq.c 8.5 KB
Newer Older
1 2 3
/*
 * SPEAr platform shared irq layer source file
 *
4
 * Copyright (C) 2009-2012 ST Microelectronics
V
Viresh Kumar 已提交
5
 * Viresh Kumar <viresh.linux@gmail.com>
6
 *
7
 * Copyright (C) 2012 ST Microelectronics
V
Viresh Kumar 已提交
8
 * Shiraz Hashim <shiraz.linux.kernel@gmail.com>
9
 *
10 11 12 13
 * This file is licensed under the terms of the GNU General Public
 * License version 2. This program is licensed "as is" without any
 * warranty of any kind, whether express or implied.
 */
14
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15 16

#include <linux/err.h>
17 18
#include <linux/export.h>
#include <linux/interrupt.h>
19 20
#include <linux/io.h>
#include <linux/irq.h>
21 22 23 24
#include <linux/irqdomain.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
25 26
#include <linux/spinlock.h>

27 28
#include "irqchip.h"

29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
/*
 * struct shirq_regs: shared irq register configuration
 *
 * enb_reg: enable register offset
 * reset_to_enb: val 1 indicates, we need to clear bit for enabling interrupt
 * status_reg: status register offset
 * status_reg_mask: status register valid mask
 * clear_reg: clear register offset
 * reset_to_clear: val 1 indicates, we need to clear bit for clearing interrupt
 */
struct shirq_regs {
	u32 enb_reg;
	u32 reset_to_enb;
	u32 status_reg;
	u32 clear_reg;
	u32 reset_to_clear;
};

/*
 * struct spear_shirq: shared irq structure
 *
 * irq: hardware irq number
 * irq_base: base irq in linux domain
 * irq_nr: no. of shared interrupts in a particular block
 * irq_bit_off: starting bit offset in the status register
 * invalid_irq: irq group is currently disabled
 * base: base address of shared irq register
 * regs: register configuration for shared irq block
 */
struct spear_shirq {
	u32 irq;
	u32 irq_base;
	u32 irq_nr;
	u32 irq_bit_off;
	int invalid_irq;
	void __iomem *base;
	struct shirq_regs regs;
};

68 69
static DEFINE_SPINLOCK(lock);

70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
/* spear300 shared irq registers offsets and masks */
#define SPEAR300_INT_ENB_MASK_REG	0x54
#define SPEAR300_INT_STS_MASK_REG	0x58

static struct spear_shirq spear300_shirq_ras1 = {
	.irq_nr = 9,
	.irq_bit_off = 0,
	.regs = {
		.enb_reg = SPEAR300_INT_ENB_MASK_REG,
		.status_reg = SPEAR300_INT_STS_MASK_REG,
		.clear_reg = -1,
	},
};

static struct spear_shirq *spear300_shirq_blocks[] = {
	&spear300_shirq_ras1,
};

/* spear310 shared irq registers offsets and masks */
#define SPEAR310_INT_STS_MASK_REG	0x04

static struct spear_shirq spear310_shirq_ras1 = {
	.irq_nr = 8,
	.irq_bit_off = 0,
	.regs = {
		.enb_reg = -1,
		.status_reg = SPEAR310_INT_STS_MASK_REG,
		.clear_reg = -1,
	},
};

static struct spear_shirq spear310_shirq_ras2 = {
	.irq_nr = 5,
	.irq_bit_off = 8,
	.regs = {
		.enb_reg = -1,
		.status_reg = SPEAR310_INT_STS_MASK_REG,
		.clear_reg = -1,
	},
};

static struct spear_shirq spear310_shirq_ras3 = {
	.irq_nr = 1,
	.irq_bit_off = 13,
	.regs = {
		.enb_reg = -1,
		.status_reg = SPEAR310_INT_STS_MASK_REG,
		.clear_reg = -1,
	},
};

static struct spear_shirq spear310_shirq_intrcomm_ras = {
	.irq_nr = 3,
	.irq_bit_off = 14,
	.regs = {
		.enb_reg = -1,
		.status_reg = SPEAR310_INT_STS_MASK_REG,
		.clear_reg = -1,
	},
};

static struct spear_shirq *spear310_shirq_blocks[] = {
	&spear310_shirq_ras1,
	&spear310_shirq_ras2,
	&spear310_shirq_ras3,
	&spear310_shirq_intrcomm_ras,
};

/* spear320 shared irq registers offsets and masks */
#define SPEAR320_INT_STS_MASK_REG		0x04
#define SPEAR320_INT_CLR_MASK_REG		0x04
#define SPEAR320_INT_ENB_MASK_REG		0x08

static struct spear_shirq spear320_shirq_ras1 = {
	.irq_nr = 3,
	.irq_bit_off = 7,
	.regs = {
		.enb_reg = -1,
		.status_reg = SPEAR320_INT_STS_MASK_REG,
		.clear_reg = SPEAR320_INT_CLR_MASK_REG,
		.reset_to_clear = 1,
	},
};

static struct spear_shirq spear320_shirq_ras2 = {
	.irq_nr = 1,
	.irq_bit_off = 10,
	.regs = {
		.enb_reg = -1,
		.status_reg = SPEAR320_INT_STS_MASK_REG,
		.clear_reg = SPEAR320_INT_CLR_MASK_REG,
		.reset_to_clear = 1,
	},
};

static struct spear_shirq spear320_shirq_ras3 = {
166
	.irq_nr = 7,
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
	.irq_bit_off = 0,
	.invalid_irq = 1,
	.regs = {
		.enb_reg = SPEAR320_INT_ENB_MASK_REG,
		.reset_to_enb = 1,
		.status_reg = SPEAR320_INT_STS_MASK_REG,
		.clear_reg = SPEAR320_INT_CLR_MASK_REG,
		.reset_to_clear = 1,
	},
};

static struct spear_shirq spear320_shirq_intrcomm_ras = {
	.irq_nr = 11,
	.irq_bit_off = 11,
	.regs = {
		.enb_reg = -1,
		.status_reg = SPEAR320_INT_STS_MASK_REG,
		.clear_reg = SPEAR320_INT_CLR_MASK_REG,
		.reset_to_clear = 1,
	},
};

static struct spear_shirq *spear320_shirq_blocks[] = {
	&spear320_shirq_ras3,
	&spear320_shirq_ras1,
	&spear320_shirq_ras2,
	&spear320_shirq_intrcomm_ras,
};

static void shirq_irq_mask_unmask(struct irq_data *d, bool mask)
197
{
198
	struct spear_shirq *shirq = irq_data_get_irq_chip_data(d);
199
	u32 val, offset = d->irq - shirq->irq_base;
200 201
	unsigned long flags;

202
	if (shirq->regs.enb_reg == -1)
203 204 205
		return;

	spin_lock_irqsave(&lock, flags);
206 207 208 209
	val = readl(shirq->base + shirq->regs.enb_reg);

	if (mask ^ shirq->regs.reset_to_enb)
		val &= ~(0x1 << shirq->irq_bit_off << offset);
210
	else
211 212 213
		val |= 0x1 << shirq->irq_bit_off << offset;

	writel(val, shirq->base + shirq->regs.enb_reg);
214
	spin_unlock_irqrestore(&lock, flags);
215

216 217
}

218
static void shirq_irq_mask(struct irq_data *d)
219
{
220 221
	shirq_irq_mask_unmask(d, 1);
}
222

223 224 225
static void shirq_irq_unmask(struct irq_data *d)
{
	shirq_irq_mask_unmask(d, 0);
226 227 228
}

static struct irq_chip shirq_chip = {
229
	.name		= "spear-shirq",
230 231 232
	.irq_ack	= shirq_irq_mask,
	.irq_mask	= shirq_irq_mask,
	.irq_unmask	= shirq_irq_unmask,
233 234 235 236
};

static void shirq_handler(unsigned irq, struct irq_desc *desc)
{
237 238
	u32 i, j, val, mask, tmp;
	struct irq_chip *chip;
T
Thomas Gleixner 已提交
239
	struct spear_shirq *shirq = irq_get_handler_data(irq);
240

241 242 243 244 245 246 247 248 249 250 251
	chip = irq_get_chip(irq);
	chip->irq_ack(&desc->irq_data);

	mask = ((0x1 << shirq->irq_nr) - 1) << shirq->irq_bit_off;
	while ((val = readl(shirq->base + shirq->regs.status_reg) &
				mask)) {

		val >>= shirq->irq_bit_off;
		for (i = 0, j = 1; i < shirq->irq_nr; i++, j <<= 1) {

			if (!(j & val))
252 253
				continue;

254
			generic_handle_irq(shirq->irq_base + i);
255 256

			/* clear interrupt */
257
			if (shirq->regs.clear_reg == -1)
258
				continue;
259 260

			tmp = readl(shirq->base + shirq->regs.clear_reg);
261
			if (shirq->regs.reset_to_clear)
262
				tmp &= ~(j << shirq->irq_bit_off);
263
			else
264 265
				tmp |= (j << shirq->irq_bit_off);
			writel(tmp, shirq->base + shirq->regs.clear_reg);
266 267
		}
	}
268
	chip->irq_unmask(&desc->irq_data);
269 270
}

271
static void __init spear_shirq_register(struct spear_shirq *shirq)
272 273 274
{
	int i;

275 276
	if (shirq->invalid_irq)
		return;
277

T
Thomas Gleixner 已提交
278
	irq_set_chained_handler(shirq->irq, shirq_handler);
279 280
	for (i = 0; i < shirq->irq_nr; i++) {
		irq_set_chip_and_handler(shirq->irq_base + i,
281
					 &shirq_chip, handle_simple_irq);
282 283
		set_irq_flags(shirq->irq_base + i, IRQF_VALID);
		irq_set_chip_data(shirq->irq_base + i, shirq);
284 285
	}

T
Thomas Gleixner 已提交
286
	irq_set_handler_data(shirq->irq, shirq);
287 288 289 290 291 292
}

static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr,
		struct device_node *np)
{
	int i, irq_base, hwirq = 0, irq_nr = 0;
293
	struct irq_domain *shirq_domain;
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
	void __iomem *base;

	base = of_iomap(np, 0);
	if (!base) {
		pr_err("%s: failed to map shirq registers\n", __func__);
		return -ENXIO;
	}

	for (i = 0; i < block_nr; i++)
		irq_nr += shirq_blocks[i]->irq_nr;

	irq_base = irq_alloc_descs(-1, 0, irq_nr, 0);
	if (IS_ERR_VALUE(irq_base)) {
		pr_err("%s: irq desc alloc failed\n", __func__);
		goto err_unmap;
	}

	shirq_domain = irq_domain_add_legacy(np, irq_nr, irq_base, 0,
			&irq_domain_simple_ops, NULL);
	if (WARN_ON(!shirq_domain)) {
		pr_warn("%s: irq domain init failed\n", __func__);
		goto err_free_desc;
	}

	for (i = 0; i < block_nr; i++) {
		shirq_blocks[i]->base = base;
		shirq_blocks[i]->irq_base = irq_find_mapping(shirq_domain,
				hwirq);
		shirq_blocks[i]->irq = irq_of_parse_and_map(np, i);

		spear_shirq_register(shirq_blocks[i]);
		hwirq += shirq_blocks[i]->irq_nr;
	}

328
	return 0;
329 330 331 332 333 334 335 336

err_free_desc:
	irq_free_descs(irq_base, irq_nr);
err_unmap:
	iounmap(base);
	return -ENXIO;
}

337 338
static int __init spear300_shirq_of_init(struct device_node *np,
					 struct device_node *parent)
339 340 341 342
{
	return shirq_init(spear300_shirq_blocks,
			ARRAY_SIZE(spear300_shirq_blocks), np);
}
343
IRQCHIP_DECLARE(spear300_shirq, "st,spear300-shirq", spear300_shirq_of_init);
344

345 346
static int __init spear310_shirq_of_init(struct device_node *np,
					 struct device_node *parent)
347 348 349 350
{
	return shirq_init(spear310_shirq_blocks,
			ARRAY_SIZE(spear310_shirq_blocks), np);
}
351
IRQCHIP_DECLARE(spear310_shirq, "st,spear310-shirq", spear310_shirq_of_init);
352

353 354
static int __init spear320_shirq_of_init(struct device_node *np,
					 struct device_node *parent)
355 356 357
{
	return shirq_init(spear320_shirq_blocks,
			ARRAY_SIZE(spear320_shirq_blocks), np);
358
}
359
IRQCHIP_DECLARE(spear320_shirq, "st,spear320-shirq", spear320_shirq_of_init);