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_eoi_irq(struct irq_data *d) 68 { 69 u32 idx = PIC_REG_IDX(d->hwirq); 70 struct pch_pic *priv = irq_data_get_irq_chip_data(d); 71 72 writel(BIT(PIC_REG_BIT(d->hwirq)), 73 priv->base + PCH_PIC_CLR + idx * 4); 74 } 75 76 static void pch_pic_mask_irq(struct irq_data *d) 77 { 78 struct pch_pic *priv = irq_data_get_irq_chip_data(d); 79 80 pch_pic_bitset(priv, PCH_PIC_MASK, d->hwirq); 81 irq_chip_mask_parent(d); 82 } 83 84 static void pch_pic_unmask_irq(struct irq_data *d) 85 { 86 struct pch_pic *priv = irq_data_get_irq_chip_data(d); 87 88 irq_chip_unmask_parent(d); 89 pch_pic_bitclr(priv, PCH_PIC_MASK, d->hwirq); 90 } 91 92 static int pch_pic_set_type(struct irq_data *d, unsigned int type) 93 { 94 struct pch_pic *priv = irq_data_get_irq_chip_data(d); 95 int ret = 0; 96 97 switch (type) { 98 case IRQ_TYPE_EDGE_RISING: 99 pch_pic_bitset(priv, PCH_PIC_EDGE, d->hwirq); 100 pch_pic_bitclr(priv, PCH_PIC_POL, d->hwirq); 101 break; 102 case IRQ_TYPE_EDGE_FALLING: 103 pch_pic_bitset(priv, PCH_PIC_EDGE, d->hwirq); 104 pch_pic_bitset(priv, PCH_PIC_POL, d->hwirq); 105 break; 106 case IRQ_TYPE_LEVEL_HIGH: 107 pch_pic_bitclr(priv, PCH_PIC_EDGE, d->hwirq); 108 pch_pic_bitclr(priv, PCH_PIC_POL, d->hwirq); 109 break; 110 case IRQ_TYPE_LEVEL_LOW: 111 pch_pic_bitclr(priv, PCH_PIC_EDGE, d->hwirq); 112 pch_pic_bitset(priv, PCH_PIC_POL, d->hwirq); 113 break; 114 default: 115 ret = -EINVAL; 116 break; 117 } 118 119 return ret; 120 } 121 122 static struct irq_chip pch_pic_irq_chip = { 123 .name = "PCH PIC", 124 .irq_mask = pch_pic_mask_irq, 125 .irq_unmask = pch_pic_unmask_irq, 126 .irq_ack = irq_chip_ack_parent, 127 .irq_eoi = pch_pic_eoi_irq, 128 .irq_set_affinity = irq_chip_set_affinity_parent, 129 .irq_set_type = pch_pic_set_type, 130 }; 131 132 static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq, 133 unsigned int nr_irqs, void *arg) 134 { 135 int err; 136 unsigned int type; 137 unsigned long hwirq; 138 struct irq_fwspec fwspec; 139 struct pch_pic *priv = domain->host_data; 140 141 irq_domain_translate_twocell(domain, arg, &hwirq, &type); 142 143 fwspec.fwnode = domain->parent->fwnode; 144 fwspec.param_count = 1; 145 fwspec.param[0] = hwirq + priv->ht_vec_base; 146 147 err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec); 148 if (err) 149 return err; 150 151 irq_domain_set_info(domain, virq, hwirq, 152 &pch_pic_irq_chip, priv, 153 handle_fasteoi_ack_irq, NULL, NULL); 154 irq_set_probe(virq); 155 156 return 0; 157 } 158 159 static const struct irq_domain_ops pch_pic_domain_ops = { 160 .translate = irq_domain_translate_twocell, 161 .alloc = pch_pic_alloc, 162 .free = irq_domain_free_irqs_parent, 163 }; 164 165 static void pch_pic_reset(struct pch_pic *priv) 166 { 167 int i; 168 169 for (i = 0; i < PIC_COUNT; i++) { 170 /* Write vectore ID */ 171 writeb(priv->ht_vec_base + i, priv->base + PCH_INT_HTVEC(i)); 172 /* Hardcode route to HT0 Lo */ 173 writeb(1, priv->base + PCH_INT_ROUTE(i)); 174 } 175 176 for (i = 0; i < PIC_REG_COUNT; i++) { 177 /* Clear IRQ cause registers, mask all interrupts */ 178 writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_MASK + 4 * i); 179 writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_CLR + 4 * i); 180 /* Clear auto bounce, we don't need that */ 181 writel_relaxed(0, priv->base + PCH_PIC_AUTO0 + 4 * i); 182 writel_relaxed(0, priv->base + PCH_PIC_AUTO1 + 4 * i); 183 /* Enable HTMSI transformer */ 184 writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_HTMSI_EN + 4 * i); 185 } 186 } 187 188 static int pch_pic_of_init(struct device_node *node, 189 struct device_node *parent) 190 { 191 struct pch_pic *priv; 192 struct irq_domain *parent_domain; 193 int err; 194 195 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 196 if (!priv) 197 return -ENOMEM; 198 199 raw_spin_lock_init(&priv->pic_lock); 200 priv->base = of_iomap(node, 0); 201 if (!priv->base) { 202 err = -ENOMEM; 203 goto free_priv; 204 } 205 206 parent_domain = irq_find_host(parent); 207 if (!parent_domain) { 208 pr_err("Failed to find the parent domain\n"); 209 err = -ENXIO; 210 goto iounmap_base; 211 } 212 213 if (of_property_read_u32(node, "loongson,pic-base-vec", 214 &priv->ht_vec_base)) { 215 pr_err("Failed to determine pic-base-vec\n"); 216 err = -EINVAL; 217 goto iounmap_base; 218 } 219 220 priv->pic_domain = irq_domain_create_hierarchy(parent_domain, 0, 221 PIC_COUNT, 222 of_node_to_fwnode(node), 223 &pch_pic_domain_ops, 224 priv); 225 if (!priv->pic_domain) { 226 pr_err("Failed to create IRQ domain\n"); 227 err = -ENOMEM; 228 goto iounmap_base; 229 } 230 231 pch_pic_reset(priv); 232 233 return 0; 234 235 iounmap_base: 236 iounmap(priv->base); 237 free_priv: 238 kfree(priv); 239 240 return err; 241 } 242 243 IRQCHIP_DECLARE(pch_pic, "loongson,pch-pic-1.0", pch_pic_of_init); 244