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