FILE
irq-loongson-pch-pic.c
File preview with syntax highlighting.
linux-6.19.14/drivers/irqchip/irq-loongson-pch-pic.c
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.h> 16#include <linux/of_address.h> 17#include <linux/of_irq.h> 18#include <linux/syscore_ops.h> 19 20#include "irq-loongson.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 PIC_UNDEF_VECTOR 255 39 40static int nr_pics; 41 42struct pch_pic { 43 void __iomem *base; 44 struct irq_domain *pic_domain; 45 u32 ht_vec_base; 46 raw_spinlock_t pic_lock; 47 u32 vec_count; 48 u32 gsi_base; 49 u32 saved_vec_en[PIC_REG_COUNT]; 50 u32 saved_vec_pol[PIC_REG_COUNT]; 51 u32 saved_vec_edge[PIC_REG_COUNT]; 52 u8 table[PIC_COUNT]; 53 int inuse; 54}; 55 56static struct pch_pic *pch_pic_priv[MAX_IO_PICS]; 57 58struct fwnode_handle *pch_pic_handle[MAX_IO_PICS]; 59 60static inline u8 hwirq_to_bit(struct pch_pic *priv, int hirq) 61{ 62 return priv->table[hirq]; 63} 64 65static void pch_pic_bitset(struct pch_pic *priv, int offset, int bit) 66{ 67 u32 reg; 68 void __iomem *addr = priv->base + offset + PIC_REG_IDX(bit) * 4; 69 70 raw_spin_lock(&priv->pic_lock); 71 reg = readl(addr); 72 reg |= BIT(PIC_REG_BIT(bit)); 73 writel(reg, addr); 74 raw_spin_unlock(&priv->pic_lock); 75} 76 77static void pch_pic_bitclr(struct pch_pic *priv, int offset, int bit) 78{ 79 u32 reg; 80 void __iomem *addr = priv->base + offset + PIC_REG_IDX(bit) * 4; 81 82 raw_spin_lock(&priv->pic_lock); 83 reg = readl(addr); 84 reg &= ~BIT(PIC_REG_BIT(bit)); 85 writel(reg, addr); 86 raw_spin_unlock(&priv->pic_lock); 87} 88 89static void pch_pic_mask_irq(struct irq_data *d) 90{ 91 struct pch_pic *priv = irq_data_get_irq_chip_data(d); 92 93 pch_pic_bitset(priv, PCH_PIC_MASK, hwirq_to_bit(priv, d->hwirq)); 94 irq_chip_mask_parent(d); 95} 96 97static void pch_pic_unmask_irq(struct irq_data *d) 98{ 99 struct pch_pic *priv = irq_data_get_irq_chip_data(d); 100 int bit = hwirq_to_bit(priv, d->hwirq); 101 102 writel(BIT(PIC_REG_BIT(bit)), 103 priv->base + PCH_PIC_CLR + PIC_REG_IDX(bit) * 4); 104 105 irq_chip_unmask_parent(d); 106 pch_pic_bitclr(priv, PCH_PIC_MASK, bit); 107} 108 109static int pch_pic_set_type(struct irq_data *d, unsigned int type) 110{ 111 struct pch_pic *priv = irq_data_get_irq_chip_data(d); 112 int bit = hwirq_to_bit(priv, d->hwirq); 113 int ret = 0; 114 115 switch (type) { 116 case IRQ_TYPE_EDGE_RISING: 117 pch_pic_bitset(priv, PCH_PIC_EDGE, bit); 118 pch_pic_bitclr(priv, PCH_PIC_POL, bit); 119 irq_set_handler_locked(d, handle_edge_irq); 120 break; 121 case IRQ_TYPE_EDGE_FALLING: 122 pch_pic_bitset(priv, PCH_PIC_EDGE, bit); 123 pch_pic_bitset(priv, PCH_PIC_POL, bit); 124 irq_set_handler_locked(d, handle_edge_irq); 125 break; 126 case IRQ_TYPE_LEVEL_HIGH: 127 pch_pic_bitclr(priv, PCH_PIC_EDGE, bit); 128 pch_pic_bitclr(priv, PCH_PIC_POL, bit); 129 irq_set_handler_locked(d, handle_level_irq); 130 break; 131 case IRQ_TYPE_LEVEL_LOW: 132 pch_pic_bitclr(priv, PCH_PIC_EDGE, bit); 133 pch_pic_bitset(priv, PCH_PIC_POL, bit); 134 irq_set_handler_locked(d, handle_level_irq); 135 break; 136 default: 137 ret = -EINVAL; 138 break; 139 } 140 141 return ret; 142} 143 144static void pch_pic_ack_irq(struct irq_data *d) 145{ 146 unsigned int reg; 147 struct pch_pic *priv = irq_data_get_irq_chip_data(d); 148 int bit = hwirq_to_bit(priv, d->hwirq); 149 150 reg = readl(priv->base + PCH_PIC_EDGE + PIC_REG_IDX(bit) * 4); 151 if (reg & BIT(PIC_REG_BIT(bit))) { 152 writel(BIT(PIC_REG_BIT(bit)), 153 priv->base + PCH_PIC_CLR + PIC_REG_IDX(bit) * 4); 154 } 155 irq_chip_ack_parent(d); 156} 157 158static struct irq_chip pch_pic_irq_chip = { 159 .name = "PCH PIC", 160 .irq_mask = pch_pic_mask_irq, 161 .irq_unmask = pch_pic_unmask_irq, 162 .irq_ack = pch_pic_ack_irq, 163 .irq_set_affinity = irq_chip_set_affinity_parent, 164 .irq_set_type = pch_pic_set_type, 165 .flags = IRQCHIP_SKIP_SET_WAKE, 166}; 167 168static int pch_pic_domain_translate(struct irq_domain *d, 169 struct irq_fwspec *fwspec, 170 unsigned long *hwirq, 171 unsigned int *type) 172{ 173 struct pch_pic *priv = d->host_data; 174 struct device_node *of_node = to_of_node(fwspec->fwnode); 175 unsigned long flags; 176 int i; 177 178 if (of_node) { 179 if (fwspec->param_count < 2) 180 return -EINVAL; 181 182 *hwirq = fwspec->param[0]; 183 *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; 184 } else { 185 if (fwspec->param_count < 1) 186 return -EINVAL; 187 188 *hwirq = fwspec->param[0] - priv->gsi_base; 189 190 if (fwspec->param_count > 1) 191 *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; 192 else 193 *type = IRQ_TYPE_NONE; 194 } 195 196 raw_spin_lock_irqsave(&priv->pic_lock, flags); 197 /* Check pic-table to confirm if the hwirq has been assigned */ 198 for (i = 0; i < priv->inuse; i++) { 199 if (priv->table[i] == *hwirq) { 200 *hwirq = i; 201 break; 202 } 203 } 204 if (i == priv->inuse) { 205 /* Assign a new hwirq in pic-table */ 206 if (priv->inuse >= PIC_COUNT) { 207 pr_err("pch-pic domain has no free vectors\n"); 208 raw_spin_unlock_irqrestore(&priv->pic_lock, flags); 209 return -EINVAL; 210 } 211 priv->table[priv->inuse] = *hwirq; 212 *hwirq = priv->inuse++; 213 } 214 raw_spin_unlock_irqrestore(&priv->pic_lock, flags); 215 216 return 0; 217} 218 219static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq, 220 unsigned int nr_irqs, void *arg) 221{ 222 int err; 223 unsigned int type; 224 unsigned long hwirq; 225 struct irq_fwspec *fwspec = arg; 226 struct irq_fwspec parent_fwspec; 227 struct pch_pic *priv = domain->host_data; 228 229 err = pch_pic_domain_translate(domain, fwspec, &hwirq, &type); 230 if (err) 231 return err; 232 233 /* Write vector ID */ 234 writeb(priv->ht_vec_base + hwirq, priv->base + PCH_INT_HTVEC(hwirq_to_bit(priv, hwirq))); 235 236 parent_fwspec.fwnode = domain->parent->fwnode; 237 parent_fwspec.param_count = 1; 238 parent_fwspec.param[0] = hwirq + priv->ht_vec_base; 239 240 err = irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec); 241 if (err) 242 return err; 243 244 irq_domain_set_info(domain, virq, hwirq, 245 &pch_pic_irq_chip, priv, 246 handle_level_irq, NULL, NULL); 247 irq_set_probe(virq); 248 249 return 0; 250} 251 252static const struct irq_domain_ops pch_pic_domain_ops = { 253 .translate = pch_pic_domain_translate, 254 .alloc = pch_pic_alloc, 255 .free = irq_domain_free_irqs_parent, 256}; 257 258static void pch_pic_reset(struct pch_pic *priv) 259{ 260 int i; 261 262 for (i = 0; i < PIC_COUNT; i++) { 263 /* Write vector ID */ 264 writeb(priv->ht_vec_base + i, priv->base + PCH_INT_HTVEC(hwirq_to_bit(priv, i))); 265 /* Hardcode route to HT0 Lo */ 266 writeb(1, priv->base + PCH_INT_ROUTE(i)); 267 } 268 269 for (i = 0; i < PIC_REG_COUNT; i++) { 270 /* Clear IRQ cause registers, mask all interrupts */ 271 writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_MASK + 4 * i); 272 writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_CLR + 4 * i); 273 /* Clear auto bounce, we don't need that */ 274 writel_relaxed(0, priv->base + PCH_PIC_AUTO0 + 4 * i); 275 writel_relaxed(0, priv->base + PCH_PIC_AUTO1 + 4 * i); 276 /* Enable HTMSI transformer */ 277 writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_HTMSI_EN + 4 * i); 278 } 279} 280 281static int pch_pic_suspend(void *data) 282{ 283 int i, j; 284 285 for (i = 0; i < nr_pics; i++) { 286 for (j = 0; j < PIC_REG_COUNT; j++) { 287 pch_pic_priv[i]->saved_vec_pol[j] = 288 readl(pch_pic_priv[i]->base + PCH_PIC_POL + 4 * j); 289 pch_pic_priv[i]->saved_vec_edge[j] = 290 readl(pch_pic_priv[i]->base + PCH_PIC_EDGE + 4 * j); 291 pch_pic_priv[i]->saved_vec_en[j] = 292 readl(pch_pic_priv[i]->base + PCH_PIC_MASK + 4 * j); 293 } 294 } 295 296 return 0; 297} 298 299static void pch_pic_resume(void *data) 300{ 301 int i, j; 302 303 for (i = 0; i < nr_pics; i++) { 304 pch_pic_reset(pch_pic_priv[i]); 305 for (j = 0; j < PIC_REG_COUNT; j++) { 306 writel(pch_pic_priv[i]->saved_vec_pol[j], 307 pch_pic_priv[i]->base + PCH_PIC_POL + 4 * j); 308 writel(pch_pic_priv[i]->saved_vec_edge[j], 309 pch_pic_priv[i]->base + PCH_PIC_EDGE + 4 * j); 310 writel(pch_pic_priv[i]->saved_vec_en[j], 311 pch_pic_priv[i]->base + PCH_PIC_MASK + 4 * j); 312 } 313 } 314} 315 316static const struct syscore_ops pch_pic_syscore_ops = { 317 .suspend = pch_pic_suspend, 318 .resume = pch_pic_resume, 319}; 320 321static struct syscore pch_pic_syscore = { 322 .ops = &pch_pic_syscore_ops, 323}; 324 325static int pch_pic_init(phys_addr_t addr, unsigned long size, int vec_base, 326 struct irq_domain *parent_domain, struct fwnode_handle *domain_handle, 327 u32 gsi_base) 328{ 329 struct pch_pic *priv; 330 int i; 331 332 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 333 if (!priv) 334 return -ENOMEM; 335 336 raw_spin_lock_init(&priv->pic_lock); 337 priv->base = ioremap(addr, size); 338 if (!priv->base) 339 goto free_priv; 340 341 priv->inuse = 0; 342 for (i = 0; i < PIC_COUNT; i++) 343 priv->table[i] = PIC_UNDEF_VECTOR; 344 345 priv->ht_vec_base = vec_base; 346 priv->vec_count = ((readq(priv->base) >> 48) & 0xff) + 1; 347 priv->gsi_base = gsi_base; 348 349 priv->pic_domain = irq_domain_create_hierarchy(parent_domain, 0, 350 priv->vec_count, domain_handle, 351 &pch_pic_domain_ops, priv); 352 353 if (!priv->pic_domain) { 354 pr_err("Failed to create IRQ domain\n"); 355 goto iounmap_base; 356 } 357 358 pch_pic_reset(priv); 359 pch_pic_handle[nr_pics] = domain_handle; 360 pch_pic_priv[nr_pics++] = priv; 361 362 if (nr_pics == 1) 363 register_syscore(&pch_pic_syscore); 364 365 return 0; 366 367iounmap_base: 368 iounmap(priv->base); 369free_priv: 370 kfree(priv); 371 372 return -EINVAL; 373} 374 375#ifdef CONFIG_OF 376 377static int pch_pic_of_init(struct device_node *node, 378 struct device_node *parent) 379{ 380 int err, vec_base; 381 struct resource res; 382 struct irq_domain *parent_domain; 383 384 if (of_address_to_resource(node, 0, &res)) 385 return -EINVAL; 386 387 parent_domain = irq_find_host(parent); 388 if (!parent_domain) { 389 pr_err("Failed to find the parent domain\n"); 390 return -ENXIO; 391 } 392 393 if (of_property_read_u32(node, "loongson,pic-base-vec", &vec_base)) { 394 pr_err("Failed to determine pic-base-vec\n"); 395 return -EINVAL; 396 } 397 398 err = pch_pic_init(res.start, resource_size(&res), vec_base, 399 parent_domain, of_fwnode_handle(node), 0); 400 if (err < 0) 401 return err; 402 403 return 0; 404} 405 406IRQCHIP_DECLARE(pch_pic, "loongson,pch-pic-1.0", pch_pic_of_init); 407 408#endif 409 410#ifdef CONFIG_ACPI 411int find_pch_pic(u32 gsi) 412{ 413 int i; 414 415 /* Find the PCH_PIC that manages this GSI. */ 416 for (i = 0; i < MAX_IO_PICS; i++) { 417 struct pch_pic *priv = pch_pic_priv[i]; 418 419 if (!priv) 420 return -1; 421 422 if (gsi >= priv->gsi_base && gsi < (priv->gsi_base + priv->vec_count)) 423 return i; 424 } 425 426 pr_err("ERROR: Unable to locate PCH_PIC for GSI %d\n", gsi); 427 return -1; 428} 429 430static int __init pch_lpc_parse_madt(union acpi_subtable_headers *header, 431 const unsigned long end) 432{ 433 struct acpi_madt_lpc_pic *pchlpc_entry = (struct acpi_madt_lpc_pic *)header; 434 435 return pch_lpc_acpi_init(pch_pic_priv[0]->pic_domain, pchlpc_entry); 436} 437 438static int __init acpi_cascade_irqdomain_init(void) 439{ 440 int r; 441 442 r = acpi_table_parse_madt(ACPI_MADT_TYPE_LPC_PIC, pch_lpc_parse_madt, 0); 443 if (r < 0) 444 return r; 445 446 return 0; 447} 448 449int __init pch_pic_acpi_init(struct irq_domain *parent, 450 struct acpi_madt_bio_pic *acpi_pchpic) 451{ 452 int ret; 453 struct fwnode_handle *domain_handle; 454 455 if (find_pch_pic(acpi_pchpic->gsi_base) >= 0) 456 return 0; 457 458 domain_handle = irq_domain_alloc_fwnode(&acpi_pchpic->address); 459 if (!domain_handle) { 460 pr_err("Unable to allocate domain handle\n"); 461 return -ENOMEM; 462 } 463 464 ret = pch_pic_init(acpi_pchpic->address, acpi_pchpic->size, 465 0, parent, domain_handle, acpi_pchpic->gsi_base); 466 467 if (ret < 0) { 468 irq_domain_free_fwnode(domain_handle); 469 return ret; 470 } 471 472 if (acpi_pchpic->id == 0) 473 ret = acpi_cascade_irqdomain_init(); 474 475 return ret; 476} 477#endif
~/
Read-only file preview.