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 36 struct 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 43 static 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 55 static 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 67 static 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 75 static 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 86 static 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 120 static 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 133 static 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 142 static 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 172 static 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 178 static 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 201 static 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 248 iounmap_base: 249 iounmap(priv->base); 250 free_priv: 251 kfree(priv); 252 253 return err; 254 } 255 256 IRQCHIP_DECLARE(pch_pic, "loongson,pch-pic-1.0", pch_pic_of_init); 257