Blame view
drivers/irqchip/irq-renesas-irqc.c
7.94 KB
fbc83b7f5 irqchip: Renesas ... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/* * Renesas IRQC Driver * * Copyright (C) 2013 Magnus Damm * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ |
6f46aedb9 irqchip: renesas-... |
19 |
#include <linux/clk.h> |
fbc83b7f5 irqchip: Renesas ... |
20 21 22 23 24 25 26 27 28 29 30 |
#include <linux/init.h> #include <linux/platform_device.h> #include <linux/spinlock.h> #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/io.h> #include <linux/irq.h> #include <linux/irqdomain.h> #include <linux/err.h> #include <linux/slab.h> #include <linux/module.h> |
51b05f6b8 irqchip: renesas-... |
31 |
#include <linux/pm_runtime.h> |
fbc83b7f5 irqchip: Renesas ... |
32 |
|
1cd5ec733 irqchip: renesas-... |
33 |
#define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */ |
fbc83b7f5 irqchip: Renesas ... |
34 |
|
1cd5ec733 irqchip: renesas-... |
35 36 37 |
#define IRQC_REQ_STS 0x00 /* Interrupt Request Status Register */ #define IRQC_EN_STS 0x04 /* Interrupt Enable Status Register */ #define IRQC_EN_SET 0x08 /* Interrupt Enable Set Register */ |
fbc83b7f5 irqchip: Renesas ... |
38 |
#define IRQC_INT_CPU_BASE(n) (0x000 + ((n) * 0x10)) |
1cd5ec733 irqchip: renesas-... |
39 40 41 42 43 44 45 46 47 48 |
/* SYS-CPU vs. RT-CPU */ #define DETECT_STATUS 0x100 /* IRQn Detect Status Register */ #define MONITOR 0x104 /* IRQn Signal Level Monitor Register */ #define HLVL_STS 0x108 /* IRQn High Level Detect Status Register */ #define LLVL_STS 0x10c /* IRQn Low Level Detect Status Register */ #define S_R_EDGE_STS 0x110 /* IRQn Sync Rising Edge Detect Status Reg. */ #define S_F_EDGE_STS 0x114 /* IRQn Sync Falling Edge Detect Status Reg. */ #define A_R_EDGE_STS 0x118 /* IRQn Async Rising Edge Detect Status Reg. */ #define A_F_EDGE_STS 0x11c /* IRQn Async Falling Edge Detect Status Reg. */ #define CHTEN_STS 0x120 /* Chattering Reduction Status Register */ |
fbc83b7f5 irqchip: Renesas ... |
49 |
#define IRQC_CONFIG(n) (0x180 + ((n) * 0x04)) |
1cd5ec733 irqchip: renesas-... |
50 |
/* IRQn Configuration Register */ |
fbc83b7f5 irqchip: Renesas ... |
51 52 53 54 |
struct irqc_irq { int hw_irq; int requested_irq; |
fbc83b7f5 irqchip: Renesas ... |
55 56 57 58 59 60 61 |
struct irqc_priv *p; }; struct irqc_priv { void __iomem *iomem; void __iomem *cpu_int_base; struct irqc_irq irq[IRQC_IRQ_MAX]; |
fbc83b7f5 irqchip: Renesas ... |
62 63 |
unsigned int number_of_irqs; struct platform_device *pdev; |
99c221df3 irqchip/renesas-i... |
64 |
struct irq_chip_generic *gc; |
fbc83b7f5 irqchip: Renesas ... |
65 |
struct irq_domain *irq_domain; |
6f46aedb9 irqchip: renesas-... |
66 |
struct clk *clk; |
fbc83b7f5 irqchip: Renesas ... |
67 |
}; |
99c221df3 irqchip/renesas-i... |
68 |
static struct irqc_priv *irq_data_to_priv(struct irq_data *data) |
fbc83b7f5 irqchip: Renesas ... |
69 |
{ |
99c221df3 irqchip/renesas-i... |
70 |
return data->domain->host_data; |
fbc83b7f5 irqchip: Renesas ... |
71 |
} |
99c221df3 irqchip/renesas-i... |
72 |
static void irqc_dbg(struct irqc_irq *i, char *str) |
fbc83b7f5 irqchip: Renesas ... |
73 |
{ |
99c221df3 irqchip/renesas-i... |
74 75 76 |
dev_dbg(&i->p->pdev->dev, "%s (%d:%d) ", str, i->requested_irq, i->hw_irq); |
fbc83b7f5 irqchip: Renesas ... |
77 |
} |
fbc83b7f5 irqchip: Renesas ... |
78 |
static unsigned char irqc_sense[IRQ_TYPE_SENSE_MASK + 1] = { |
ce70af188 irq-renesas-irqc:... |
79 80 81 82 83 |
[IRQ_TYPE_LEVEL_LOW] = 0x01, [IRQ_TYPE_LEVEL_HIGH] = 0x02, [IRQ_TYPE_EDGE_FALLING] = 0x04, /* Synchronous */ [IRQ_TYPE_EDGE_RISING] = 0x08, /* Synchronous */ [IRQ_TYPE_EDGE_BOTH] = 0x0c, /* Synchronous */ |
fbc83b7f5 irqchip: Renesas ... |
84 85 86 87 |
}; static int irqc_irq_set_type(struct irq_data *d, unsigned int type) { |
99c221df3 irqchip/renesas-i... |
88 |
struct irqc_priv *p = irq_data_to_priv(d); |
fbc83b7f5 irqchip: Renesas ... |
89 90 |
int hw_irq = irqd_to_hwirq(d); unsigned char value = irqc_sense[type & IRQ_TYPE_SENSE_MASK]; |
f791e3c10 irqchip: renesas-... |
91 |
u32 tmp; |
fbc83b7f5 irqchip: Renesas ... |
92 93 |
irqc_dbg(&p->irq[hw_irq], "sense"); |
ce70af188 irq-renesas-irqc:... |
94 |
if (!value) |
fbc83b7f5 irqchip: Renesas ... |
95 96 97 98 |
return -EINVAL; tmp = ioread32(p->iomem + IRQC_CONFIG(hw_irq)); tmp &= ~0x3f; |
ce70af188 irq-renesas-irqc:... |
99 |
tmp |= value; |
fbc83b7f5 irqchip: Renesas ... |
100 101 102 |
iowrite32(tmp, p->iomem + IRQC_CONFIG(hw_irq)); return 0; } |
6f46aedb9 irqchip: renesas-... |
103 104 |
static int irqc_irq_set_wake(struct irq_data *d, unsigned int on) { |
99c221df3 irqchip/renesas-i... |
105 |
struct irqc_priv *p = irq_data_to_priv(d); |
4cd7863ec irqchip/renesas-i... |
106 107 108 |
int hw_irq = irqd_to_hwirq(d); irq_set_irq_wake(p->irq[hw_irq].requested_irq, on); |
6f46aedb9 irqchip: renesas-... |
109 110 111 112 113 114 115 116 117 118 119 |
if (!p->clk) return 0; if (on) clk_enable(p->clk); else clk_disable(p->clk); return 0; } |
fbc83b7f5 irqchip: Renesas ... |
120 121 122 123 |
static irqreturn_t irqc_irq_handler(int irq, void *dev_id) { struct irqc_irq *i = dev_id; struct irqc_priv *p = i->p; |
f791e3c10 irqchip: renesas-... |
124 |
u32 bit = BIT(i->hw_irq); |
fbc83b7f5 irqchip: Renesas ... |
125 126 127 128 129 130 |
irqc_dbg(i, "demux1"); if (ioread32(p->iomem + DETECT_STATUS) & bit) { iowrite32(bit, p->iomem + DETECT_STATUS); irqc_dbg(i, "demux2"); |
e10fc03c4 irqchip/renesas-i... |
131 |
generic_handle_irq(irq_find_mapping(p->irq_domain, i->hw_irq)); |
fbc83b7f5 irqchip: Renesas ... |
132 133 134 135 |
return IRQ_HANDLED; } return IRQ_NONE; } |
fbc83b7f5 irqchip: Renesas ... |
136 137 |
static int irqc_probe(struct platform_device *pdev) { |
fbc83b7f5 irqchip: Renesas ... |
138 139 140 |
struct irqc_priv *p; struct resource *io; struct resource *irq; |
fbc83b7f5 irqchip: Renesas ... |
141 142 143 144 145 146 147 148 149 150 151 |
const char *name = dev_name(&pdev->dev); int ret; int k; p = kzalloc(sizeof(*p), GFP_KERNEL); if (!p) { dev_err(&pdev->dev, "failed to allocate driver data "); ret = -ENOMEM; goto err0; } |
fbc83b7f5 irqchip: Renesas ... |
152 153 |
p->pdev = pdev; platform_set_drvdata(pdev, p); |
6f46aedb9 irqchip: renesas-... |
154 155 156 157 158 159 |
p->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(p->clk)) { dev_warn(&pdev->dev, "unable to get clock "); p->clk = NULL; } |
51b05f6b8 irqchip: renesas-... |
160 161 |
pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); |
fbc83b7f5 irqchip: Renesas ... |
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
/* get hold of manadatory IOMEM */ io = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!io) { dev_err(&pdev->dev, "not enough IOMEM resources "); ret = -EINVAL; goto err1; } /* allow any number of IRQs between 1 and IRQC_IRQ_MAX */ for (k = 0; k < IRQC_IRQ_MAX; k++) { irq = platform_get_resource(pdev, IORESOURCE_IRQ, k); if (!irq) break; p->irq[k].p = p; |
e10fc03c4 irqchip/renesas-i... |
178 |
p->irq[k].hw_irq = k; |
fbc83b7f5 irqchip: Renesas ... |
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
p->irq[k].requested_irq = irq->start; } p->number_of_irqs = k; if (p->number_of_irqs < 1) { dev_err(&pdev->dev, "not enough IRQ resources "); ret = -EINVAL; goto err1; } /* ioremap IOMEM and setup read/write callbacks */ p->iomem = ioremap_nocache(io->start, resource_size(io)); if (!p->iomem) { dev_err(&pdev->dev, "failed to remap IOMEM "); ret = -ENXIO; goto err2; } p->cpu_int_base = p->iomem + IRQC_INT_CPU_BASE(0); /* SYS-SPI */ |
7d153751c irqchip/renesas-i... |
200 201 |
p->irq_domain = irq_domain_add_linear(pdev->dev.of_node, p->number_of_irqs, |
99c221df3 irqchip/renesas-i... |
202 |
&irq_generic_chip_ops, p); |
fbc83b7f5 irqchip: Renesas ... |
203 204 205 206 207 208 |
if (!p->irq_domain) { ret = -ENXIO; dev_err(&pdev->dev, "cannot initialize irq domain "); goto err2; } |
99c221df3 irqchip/renesas-i... |
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
ret = irq_alloc_domain_generic_chips(p->irq_domain, p->number_of_irqs, 1, name, handle_level_irq, 0, 0, IRQ_GC_INIT_NESTED_LOCK); if (ret) { dev_err(&pdev->dev, "cannot allocate generic chip "); goto err3; } p->gc = irq_get_domain_generic_chip(p->irq_domain, 0); p->gc->reg_base = p->cpu_int_base; p->gc->chip_types[0].regs.enable = IRQC_EN_SET; p->gc->chip_types[0].regs.disable = IRQC_EN_STS; p->gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg; p->gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg; p->gc->chip_types[0].chip.irq_set_type = irqc_irq_set_type; p->gc->chip_types[0].chip.irq_set_wake = irqc_irq_set_wake; p->gc->chip_types[0].chip.flags = IRQCHIP_MASK_ON_SUSPEND; |
fbc83b7f5 irqchip: Renesas ... |
227 228 229 230 231 232 233 |
/* request interrupts one by one */ for (k = 0; k < p->number_of_irqs; k++) { if (request_irq(p->irq[k].requested_irq, irqc_irq_handler, 0, name, &p->irq[k])) { dev_err(&pdev->dev, "failed to request IRQ "); ret = -ENOENT; |
99c221df3 irqchip/renesas-i... |
234 |
goto err4; |
fbc83b7f5 irqchip: Renesas ... |
235 236 237 238 239 |
} } dev_info(&pdev->dev, "driving %d irqs ", p->number_of_irqs); |
fbc83b7f5 irqchip: Renesas ... |
240 |
return 0; |
99c221df3 irqchip/renesas-i... |
241 |
err4: |
dfaf820a1 irqchip: renesas-... |
242 243 |
while (--k >= 0) free_irq(p->irq[k].requested_irq, &p->irq[k]); |
fbc83b7f5 irqchip: Renesas ... |
244 |
|
99c221df3 irqchip/renesas-i... |
245 |
err3: |
fbc83b7f5 irqchip: Renesas ... |
246 247 248 249 |
irq_domain_remove(p->irq_domain); err2: iounmap(p->iomem); err1: |
51b05f6b8 irqchip: renesas-... |
250 251 |
pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); |
fbc83b7f5 irqchip: Renesas ... |
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
kfree(p); err0: return ret; } static int irqc_remove(struct platform_device *pdev) { struct irqc_priv *p = platform_get_drvdata(pdev); int k; for (k = 0; k < p->number_of_irqs; k++) free_irq(p->irq[k].requested_irq, &p->irq[k]); irq_domain_remove(p->irq_domain); iounmap(p->iomem); |
51b05f6b8 irqchip: renesas-... |
267 268 |
pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); |
fbc83b7f5 irqchip: Renesas ... |
269 270 271 |
kfree(p); return 0; } |
3b8dfa7c2 irqchip: irqc: Ad... |
272 273 274 275 276 |
static const struct of_device_id irqc_dt_ids[] = { { .compatible = "renesas,irqc", }, {}, }; MODULE_DEVICE_TABLE(of, irqc_dt_ids); |
fbc83b7f5 irqchip: Renesas ... |
277 278 279 280 281 |
static struct platform_driver irqc_device_driver = { .probe = irqc_probe, .remove = irqc_remove, .driver = { .name = "renesas_irqc", |
3b8dfa7c2 irqchip: irqc: Ad... |
282 |
.of_match_table = irqc_dt_ids, |
fbc83b7f5 irqchip: Renesas ... |
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
} }; static int __init irqc_init(void) { return platform_driver_register(&irqc_device_driver); } postcore_initcall(irqc_init); static void __exit irqc_exit(void) { platform_driver_unregister(&irqc_device_driver); } module_exit(irqc_exit); MODULE_AUTHOR("Magnus Damm"); MODULE_DESCRIPTION("Renesas IRQC Driver"); MODULE_LICENSE("GPL v2"); |