1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com> 4 * Jianmin Lv <lvjianmin@loongson.cn> 5 * Huacai Chen <chenhuacai@loongson.cn> 6 * Loongson PCH PIC support 7 */ 8 9#define pr_fmt(fmt) "pch-pic: " fmt 10 11#include <linux/interrupt.h> 12#include <linux/irq.h> 13#include <linux/irqchip.h> 14#include <linux/irqdomain.h> 15#include <linux/kernel.h> 16#include <linux/platform_device.h> 17#include <linux/of_address.h> 18#include <linux/of_irq.h> 19#include <linux/of_platform.h> 20#include <linux/syscore_ops.h> 21 22/* Registers */ 23#define PCH_PIC_MASK 0x20 24#define PCH_PIC_HTMSI_EN 0x40 25#define PCH_PIC_EDGE 0x60 26#define PCH_PIC_CLR 0x80 27#define PCH_PIC_AUTO0 0xc0 28#define PCH_PIC_AUTO1 0xe0 29#define PCH_INT_ROUTE(irq) (0x100 + irq) 30#define PCH_INT_HTVEC(irq) (0x200 + irq) 31#define PCH_PIC_POL 0x3e0 32 33#define PIC_COUNT_PER_REG 32 34#define PIC_REG_COUNT 2 35#define PIC_COUNT (PIC_COUNT_PER_REG * PIC_REG_COUNT) 36#define PIC_REG_IDX(irq_id) ((irq_id) / PIC_COUNT_PER_REG) 37#define PIC_REG_BIT(irq_id) ((irq_id) % PIC_COUNT_PER_REG) 38#define PCH_PIC_SIZE 0x400 39 40static int nr_pics; 41 42struct pch_pic { 43 void __iomem *base; 44 struct irq_domain *pic_domain; 45 struct fwnode_handle *domain_handle; 46 u32 ht_vec_base; 47 raw_spinlock_t pic_lock; 48 u32 saved_vec_en[PIC_REG_COUNT]; 49 u32 saved_vec_pol[PIC_REG_COUNT]; 50 u32 saved_vec_edge[PIC_REG_COUNT]; 51}; 52 53static struct pch_pic *pch_pic_priv[MAX_IO_PICS]; 54 55static void pch_pic_bitset(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 67static void pch_pic_bitclr(struct pch_pic *priv, int offset, int bit) 68{ 69 u32 reg; 70 void __iomem *addr = priv->base + offset + PIC_REG_IDX(bit) * 4; 71 72 raw_spin_lock(&priv->pic_lock); 73 reg = readl(addr); 74 reg &= ~BIT(PIC_REG_BIT(bit)); 75 writel(reg, addr); 76 raw_spin_unlock(&priv->pic_lock); 77} 78 79static void pch_pic_mask_irq(struct irq_data *d) 80{ 81 struct pch_pic *priv = irq_data_get_irq_chip_data(d); 82 83 pch_pic_bitset(priv, PCH_PIC_MASK, d->hwirq); 84 irq_chip_mask_parent(d); 85} 86 87static void pch_pic_unmask_irq(struct irq_data *d) 88{ 89 struct pch_pic *priv = irq_data_get_irq_chip_data(d); 90 91 writel(BIT(PIC_REG_BIT(d->hwirq)), 92 priv->base + PCH_PIC_CLR + PIC_REG_IDX(d->hwirq) * 4); 93 94 irq_chip_unmask_parent(d); 95 pch_pic_bitclr(priv, PCH_PIC_MASK, d->hwirq); 96} 97 98static int pch_pic_set_type(struct irq_data *d, unsigned int type) 99{ 100 struct pch_pic *priv = irq_data_get_irq_chip_data(d); 101 int ret = 0; 102 103 switch (type) { 104 case IRQ_TYPE_EDGE_RISING: 105 pch_pic_bitset(priv, PCH_PIC_EDGE, d->hwirq); 106 pch_pic_bitclr(priv, PCH_PIC_POL, d->hwirq); 107 irq_set_handler_locked(d, handle_edge_irq); 108 break; 109 case IRQ_TYPE_EDGE_FALLING: 110 pch_pic_bitset(priv, PCH_PIC_EDGE, d->hwirq); 111 pch_pic_bitset(priv, PCH_PIC_POL, d->hwirq); 112 irq_set_handler_locked(d, handle_edge_irq); 113 break; 114 case IRQ_TYPE_LEVEL_HIGH: 115 pch_pic_bitclr(priv, PCH_PIC_EDGE, d->hwirq); 116 pch_pic_bitclr(priv, PCH_PIC_POL, d->hwirq); 117 irq_set_handler_locked(d, handle_level_irq); 118 break; 119 case IRQ_TYPE_LEVEL_LOW: 120 pch_pic_bitclr(priv, PCH_PIC_EDGE, d->hwirq); 121 pch_pic_bitset(priv, PCH_PIC_POL, d->hwirq); 122 irq_set_handler_locked(d, handle_level_irq); 123 break; 124 default: 125 ret = -EINVAL; 126 break; 127 } 128 129 return ret; 130} 131 132static void pch_pic_ack_irq(struct irq_data *d) 133{ 134 unsigned int reg; 135 struct pch_pic *priv = irq_data_get_irq_chip_data(d); 136 137 reg = readl(priv->base + PCH_PIC_EDGE + PIC_REG_IDX(d->hwirq) * 4); 138 if (reg & BIT(PIC_REG_BIT(d->hwirq))) { 139 writel(BIT(PIC_REG_BIT(d->hwirq)), 140 priv->base + PCH_PIC_CLR + PIC_REG_IDX(d->hwirq) * 4); 141 } 142 irq_chip_ack_parent(d); 143} 144 145static struct irq_chip pch_pic_irq_chip = { 146 .name = "PCH PIC", 147 .irq_mask = pch_pic_mask_irq, 148 .irq_unmask = pch_pic_unmask_irq, 149 .irq_ack = pch_pic_ack_irq, 150 .irq_set_affinity = irq_chip_set_affinity_parent, 151 .irq_set_type = pch_pic_set_type, 152 .flags = IRQCHIP_SKIP_SET_WAKE, 153}; 154 155static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq, 156 unsigned int nr_irqs, void *arg) 157{ 158 int err; 159 unsigned int type; 160 unsigned long hwirq; 161 struct irq_fwspec *fwspec = arg; 162 struct irq_fwspec parent_fwspec; 163 struct pch_pic *priv = domain->host_data; 164 165 err = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type); 166 if (err) 167 return err; 168 169 parent_fwspec.fwnode = domain->parent->fwnode; 170 parent_fwspec.param_count = 1; 171 parent_fwspec.param[0] = hwirq + priv->ht_vec_base; 172 173 err = irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec); 174 if (err) 175 return err; 176 177 irq_domain_set_info(domain, virq, hwirq, 178 &pch_pic_irq_chip, priv, 179 handle_level_irq, NULL, NULL); 180 irq_set_probe(virq); 181 182 return 0; 183} 184 185static const struct irq_domain_ops pch_pic_domain_ops = { 186 .translate = irq_domain_translate_twocell, 187 .alloc = pch_pic_alloc, 188 .free = irq_domain_free_irqs_parent, 189}; 190 191static void pch_pic_reset(struct pch_pic *priv) 192{ 193 int i; 194 195 for (i = 0; i < PIC_COUNT; i++) { 196 /* Write vector ID */ 197 writeb(priv->ht_vec_base + i, priv->base + PCH_INT_HTVEC(i)); 198 /* Hardcode route to HT0 Lo */ 199 writeb(1, priv->base + PCH_INT_ROUTE(i)); 200 } 201 202 for (i = 0; i < PIC_REG_COUNT; i++) { 203 /* Clear IRQ cause registers, mask all interrupts */ 204 writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_MASK + 4 * i); 205 writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_CLR + 4 * i); 206 /* Clear auto bounce, we don't need that */ 207 writel_relaxed(0, priv->base + PCH_PIC_AUTO0 + 4 * i); 208 writel_relaxed(0, priv->base + PCH_PIC_AUTO1 + 4 * i); 209 /* Enable HTMSI transformer */ 210 writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_HTMSI_EN + 4 * i); 211 } 212} 213 214static int pch_pic_suspend(void) 215{ 216 int i, j; 217 218 for (i = 0; i < nr_pics; i++) { 219 for (j = 0; j < PIC_REG_COUNT; j++) { 220 pch_pic_priv[i]->saved_vec_pol[j] = 221 readl(pch_pic_priv[i]->base + PCH_PIC_POL + 4 * j); 222 pch_pic_priv[i]->saved_vec_edge[j] = 223 readl(pch_pic_priv[i]->base + PCH_PIC_EDGE + 4 * j); 224 pch_pic_priv[i]->saved_vec_en[j] = 225 readl(pch_pic_priv[i]->base + PCH_PIC_MASK + 4 * j); 226 } 227 } 228 229 return 0; 230} 231 232static void pch_pic_resume(void) 233{ 234 int i, j; 235 236 for (i = 0; i < nr_pics; i++) { 237 pch_pic_reset(pch_pic_priv[i]); 238 for (j = 0; j < PIC_REG_COUNT; j++) { 239 writel(pch_pic_priv[i]->saved_vec_pol[j], 240 pch_pic_priv[i]->base + PCH_PIC_POL + 4 * j); 241 writel(pch_pic_priv[i]->saved_vec_edge[j], 242 pch_pic_priv[i]->base + PCH_PIC_EDGE + 4 * j); 243 writel(pch_pic_priv[i]->saved_vec_en[j], 244 pch_pic_priv[i]->base + PCH_PIC_MASK + 4 * j); 245 } 246 } 247} 248 249static struct syscore_ops pch_pic_syscore_ops = { 250 .suspend = pch_pic_suspend, 251 .resume = pch_pic_resume, 252}; 253 254static int pch_pic_init(phys_addr_t addr, unsigned long size, int vec_base, 255 struct irq_domain *parent_domain, struct fwnode_handle *domain_handle) 256{ 257 int vec_count; 258 struct pch_pic *priv; 259 260 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 261 if (!priv) 262 return -ENOMEM; 263 264 raw_spin_lock_init(&priv->pic_lock); 265 priv->base = ioremap(addr, size); 266 if (!priv->base) 267 goto free_priv; 268 269 priv->domain_handle = domain_handle; 270 271 priv->ht_vec_base = vec_base; 272 vec_count = ((readq(priv->base) >> 48) & 0xff) + 1; 273 274 priv->pic_domain = irq_domain_create_hierarchy(parent_domain, 0, 275 vec_count, priv->domain_handle, 276 &pch_pic_domain_ops, priv); 277 278 if (!priv->pic_domain) { 279 pr_err("Failed to create IRQ domain\n"); 280 goto iounmap_base; 281 } 282 283 pch_pic_reset(priv); 284 pch_pic_priv[nr_pics++] = priv; 285 286 register_syscore_ops(&pch_pic_syscore_ops); 287 288 return 0; 289 290iounmap_base: 291 iounmap(priv->base); 292free_priv: 293 kfree(priv); 294 295 return -EINVAL; 296} 297 298#ifdef CONFIG_OF 299 300static int pch_pic_of_init(struct device_node *node, 301 struct device_node *parent) 302{ 303 int err, vec_base; 304 struct resource res; 305 struct irq_domain *parent_domain; 306 307 if (of_address_to_resource(node, 0, &res)) 308 return -EINVAL; 309 310 parent_domain = irq_find_host(parent); 311 if (!parent_domain) { 312 pr_err("Failed to find the parent domain\n"); 313 return -ENXIO; 314 } 315 316 if (of_property_read_u32(node, "loongson,pic-base-vec", &vec_base)) { 317 pr_err("Failed to determine pic-base-vec\n"); 318 return -EINVAL; 319 } 320 321 err = pch_pic_init(res.start, resource_size(&res), vec_base, 322 parent_domain, of_node_to_fwnode(node)); 323 if (err < 0) 324 return err; 325 326 return 0; 327} 328 329IRQCHIP_DECLARE(pch_pic, "loongson,pch-pic-1.0", pch_pic_of_init); 330 331#endif 332 333#ifdef CONFIG_ACPI 334 335struct irq_domain *pch_pic_acpi_init(struct irq_domain *parent, 336 struct acpi_madt_bio_pic *acpi_pchpic) 337{ 338 int ret, vec_base; 339 struct fwnode_handle *domain_handle; 340 341 if (!acpi_pchpic) 342 return NULL; 343 344 vec_base = acpi_pchpic->gsi_base - GSI_MIN_PCH_IRQ; 345 346 domain_handle = irq_domain_alloc_fwnode((phys_addr_t *)acpi_pchpic); 347 if (!domain_handle) { 348 pr_err("Unable to allocate domain handle\n"); 349 return NULL; 350 } 351 352 ret = pch_pic_init(acpi_pchpic->address, acpi_pchpic->size, 353 vec_base, parent, domain_handle); 354 if (ret < 0) 355 return NULL; 356 357 return irq_find_matching_fwnode(domain_handle, DOMAIN_BUS_ANY); 358} 359 360#endif 361