irq-ingenic-tcu.c (5119B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * JZ47xx SoCs TCU IRQ driver 4 * Copyright (C) 2019 Paul Cercueil <paul@crapouillou.net> 5 */ 6 7#include <linux/clk.h> 8#include <linux/interrupt.h> 9#include <linux/irqchip.h> 10#include <linux/irqchip/chained_irq.h> 11#include <linux/mfd/ingenic-tcu.h> 12#include <linux/mfd/syscon.h> 13#include <linux/of_irq.h> 14#include <linux/regmap.h> 15 16struct ingenic_tcu { 17 struct regmap *map; 18 struct clk *clk; 19 struct irq_domain *domain; 20 unsigned int nb_parent_irqs; 21 u32 parent_irqs[3]; 22}; 23 24static void ingenic_tcu_intc_cascade(struct irq_desc *desc) 25{ 26 struct irq_chip *irq_chip = irq_data_get_irq_chip(&desc->irq_data); 27 struct irq_domain *domain = irq_desc_get_handler_data(desc); 28 struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0); 29 struct regmap *map = gc->private; 30 uint32_t irq_reg, irq_mask; 31 unsigned long bits; 32 unsigned int i; 33 34 regmap_read(map, TCU_REG_TFR, &irq_reg); 35 regmap_read(map, TCU_REG_TMR, &irq_mask); 36 37 chained_irq_enter(irq_chip, desc); 38 39 irq_reg &= ~irq_mask; 40 bits = irq_reg; 41 42 for_each_set_bit(i, &bits, 32) 43 generic_handle_domain_irq(domain, i); 44 45 chained_irq_exit(irq_chip, desc); 46} 47 48static void ingenic_tcu_gc_unmask_enable_reg(struct irq_data *d) 49{ 50 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 51 struct irq_chip_type *ct = irq_data_get_chip_type(d); 52 struct regmap *map = gc->private; 53 u32 mask = d->mask; 54 55 irq_gc_lock(gc); 56 regmap_write(map, ct->regs.ack, mask); 57 regmap_write(map, ct->regs.enable, mask); 58 *ct->mask_cache |= mask; 59 irq_gc_unlock(gc); 60} 61 62static void ingenic_tcu_gc_mask_disable_reg(struct irq_data *d) 63{ 64 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 65 struct irq_chip_type *ct = irq_data_get_chip_type(d); 66 struct regmap *map = gc->private; 67 u32 mask = d->mask; 68 69 irq_gc_lock(gc); 70 regmap_write(map, ct->regs.disable, mask); 71 *ct->mask_cache &= ~mask; 72 irq_gc_unlock(gc); 73} 74 75static void ingenic_tcu_gc_mask_disable_reg_and_ack(struct irq_data *d) 76{ 77 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 78 struct irq_chip_type *ct = irq_data_get_chip_type(d); 79 struct regmap *map = gc->private; 80 u32 mask = d->mask; 81 82 irq_gc_lock(gc); 83 regmap_write(map, ct->regs.ack, mask); 84 regmap_write(map, ct->regs.disable, mask); 85 irq_gc_unlock(gc); 86} 87 88static int __init ingenic_tcu_irq_init(struct device_node *np, 89 struct device_node *parent) 90{ 91 struct irq_chip_generic *gc; 92 struct irq_chip_type *ct; 93 struct ingenic_tcu *tcu; 94 struct regmap *map; 95 unsigned int i; 96 int ret, irqs; 97 98 map = device_node_to_regmap(np); 99 if (IS_ERR(map)) 100 return PTR_ERR(map); 101 102 tcu = kzalloc(sizeof(*tcu), GFP_KERNEL); 103 if (!tcu) 104 return -ENOMEM; 105 106 tcu->map = map; 107 108 irqs = of_property_count_elems_of_size(np, "interrupts", sizeof(u32)); 109 if (irqs < 0 || irqs > ARRAY_SIZE(tcu->parent_irqs)) { 110 pr_crit("%s: Invalid 'interrupts' property\n", __func__); 111 ret = -EINVAL; 112 goto err_free_tcu; 113 } 114 115 tcu->nb_parent_irqs = irqs; 116 117 tcu->domain = irq_domain_add_linear(np, 32, &irq_generic_chip_ops, 118 NULL); 119 if (!tcu->domain) { 120 ret = -ENOMEM; 121 goto err_free_tcu; 122 } 123 124 ret = irq_alloc_domain_generic_chips(tcu->domain, 32, 1, "TCU", 125 handle_level_irq, 0, 126 IRQ_NOPROBE | IRQ_LEVEL, 0); 127 if (ret) { 128 pr_crit("%s: Invalid 'interrupts' property\n", __func__); 129 goto out_domain_remove; 130 } 131 132 gc = irq_get_domain_generic_chip(tcu->domain, 0); 133 ct = gc->chip_types; 134 135 gc->wake_enabled = IRQ_MSK(32); 136 gc->private = tcu->map; 137 138 ct->regs.disable = TCU_REG_TMSR; 139 ct->regs.enable = TCU_REG_TMCR; 140 ct->regs.ack = TCU_REG_TFCR; 141 ct->chip.irq_unmask = ingenic_tcu_gc_unmask_enable_reg; 142 ct->chip.irq_mask = ingenic_tcu_gc_mask_disable_reg; 143 ct->chip.irq_mask_ack = ingenic_tcu_gc_mask_disable_reg_and_ack; 144 ct->chip.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE; 145 146 /* Mask all IRQs by default */ 147 regmap_write(tcu->map, TCU_REG_TMSR, IRQ_MSK(32)); 148 149 /* 150 * On JZ4740, timer 0 and timer 1 have their own interrupt line; 151 * timers 2-7 share one interrupt. 152 * On SoCs >= JZ4770, timer 5 has its own interrupt line; 153 * timers 0-4 and 6-7 share one single interrupt. 154 * 155 * To keep things simple, we just register the same handler to 156 * all parent interrupts. The handler will properly detect which 157 * channel fired the interrupt. 158 */ 159 for (i = 0; i < irqs; i++) { 160 tcu->parent_irqs[i] = irq_of_parse_and_map(np, i); 161 if (!tcu->parent_irqs[i]) { 162 ret = -EINVAL; 163 goto out_unmap_irqs; 164 } 165 166 irq_set_chained_handler_and_data(tcu->parent_irqs[i], 167 ingenic_tcu_intc_cascade, 168 tcu->domain); 169 } 170 171 return 0; 172 173out_unmap_irqs: 174 for (; i > 0; i--) 175 irq_dispose_mapping(tcu->parent_irqs[i - 1]); 176out_domain_remove: 177 irq_domain_remove(tcu->domain); 178err_free_tcu: 179 kfree(tcu); 180 return ret; 181} 182IRQCHIP_DECLARE(jz4740_tcu_irq, "ingenic,jz4740-tcu", ingenic_tcu_irq_init); 183IRQCHIP_DECLARE(jz4725b_tcu_irq, "ingenic,jz4725b-tcu", ingenic_tcu_irq_init); 184IRQCHIP_DECLARE(jz4760_tcu_irq, "ingenic,jz4760-tcu", ingenic_tcu_irq_init); 185IRQCHIP_DECLARE(jz4770_tcu_irq, "ingenic,jz4770-tcu", ingenic_tcu_irq_init); 186IRQCHIP_DECLARE(x1000_tcu_irq, "ingenic,x1000-tcu", ingenic_tcu_irq_init);