Path: blob/master/arch/powerpc/platforms/embedded6xx/hlwd-pic.c
26489 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* arch/powerpc/platforms/embedded6xx/hlwd-pic.c3*4* Nintendo Wii "Hollywood" interrupt controller support.5* Copyright (C) 2009 The GameCube Linux Team6* Copyright (C) 2009 Albert Herranz7*/8#define DRV_MODULE_NAME "hlwd-pic"9#define pr_fmt(fmt) DRV_MODULE_NAME ": " fmt1011#include <linux/kernel.h>12#include <linux/irq.h>13#include <linux/of.h>14#include <linux/of_address.h>15#include <linux/of_irq.h>16#include <asm/io.h>1718#include "hlwd-pic.h"1920#define HLWD_NR_IRQS 322122/*23* Each interrupt has a corresponding bit in both24* the Interrupt Cause (ICR) and Interrupt Mask (IMR) registers.25*26* Enabling/disabling an interrupt line involves asserting/clearing27* the corresponding bit in IMR. ACK'ing a request simply involves28* asserting the corresponding bit in ICR.29*/30#define HW_BROADWAY_ICR 0x0031#define HW_BROADWAY_IMR 0x0432#define HW_STARLET_ICR 0x0833#define HW_STARLET_IMR 0x0c343536/*37* IRQ chip hooks.38*39*/4041static void hlwd_pic_mask_and_ack(struct irq_data *d)42{43int irq = irqd_to_hwirq(d);44void __iomem *io_base = irq_data_get_irq_chip_data(d);45u32 mask = 1 << irq;4647clrbits32(io_base + HW_BROADWAY_IMR, mask);48out_be32(io_base + HW_BROADWAY_ICR, mask);49}5051static void hlwd_pic_ack(struct irq_data *d)52{53int irq = irqd_to_hwirq(d);54void __iomem *io_base = irq_data_get_irq_chip_data(d);5556out_be32(io_base + HW_BROADWAY_ICR, 1 << irq);57}5859static void hlwd_pic_mask(struct irq_data *d)60{61int irq = irqd_to_hwirq(d);62void __iomem *io_base = irq_data_get_irq_chip_data(d);6364clrbits32(io_base + HW_BROADWAY_IMR, 1 << irq);65}6667static void hlwd_pic_unmask(struct irq_data *d)68{69int irq = irqd_to_hwirq(d);70void __iomem *io_base = irq_data_get_irq_chip_data(d);7172setbits32(io_base + HW_BROADWAY_IMR, 1 << irq);7374/* Make sure the ARM (aka. Starlet) doesn't handle this interrupt. */75clrbits32(io_base + HW_STARLET_IMR, 1 << irq);76}777879static struct irq_chip hlwd_pic = {80.name = "hlwd-pic",81.irq_ack = hlwd_pic_ack,82.irq_mask_ack = hlwd_pic_mask_and_ack,83.irq_mask = hlwd_pic_mask,84.irq_unmask = hlwd_pic_unmask,85};8687/*88* IRQ host hooks.89*90*/9192static struct irq_domain *hlwd_irq_host;9394static int hlwd_pic_map(struct irq_domain *h, unsigned int virq,95irq_hw_number_t hwirq)96{97irq_set_chip_data(virq, h->host_data);98irq_set_status_flags(virq, IRQ_LEVEL);99irq_set_chip_and_handler(virq, &hlwd_pic, handle_level_irq);100return 0;101}102103static const struct irq_domain_ops hlwd_irq_domain_ops = {104.map = hlwd_pic_map,105};106107static unsigned int __hlwd_pic_get_irq(struct irq_domain *h)108{109void __iomem *io_base = h->host_data;110u32 irq_status;111112irq_status = in_be32(io_base + HW_BROADWAY_ICR) &113in_be32(io_base + HW_BROADWAY_IMR);114if (irq_status == 0)115return 0; /* no more IRQs pending */116117return __ffs(irq_status);118}119120static void hlwd_pic_irq_cascade(struct irq_desc *desc)121{122struct irq_chip *chip = irq_desc_get_chip(desc);123struct irq_domain *irq_domain = irq_desc_get_handler_data(desc);124unsigned int hwirq;125126raw_spin_lock(&desc->lock);127chip->irq_mask(&desc->irq_data); /* IRQ_LEVEL */128raw_spin_unlock(&desc->lock);129130hwirq = __hlwd_pic_get_irq(irq_domain);131if (hwirq)132generic_handle_domain_irq(irq_domain, hwirq);133else134pr_err("spurious interrupt!\n");135136raw_spin_lock(&desc->lock);137chip->irq_ack(&desc->irq_data); /* IRQ_LEVEL */138if (!irqd_irq_disabled(&desc->irq_data) && chip->irq_unmask)139chip->irq_unmask(&desc->irq_data);140raw_spin_unlock(&desc->lock);141}142143/*144* Platform hooks.145*146*/147148static void __hlwd_quiesce(void __iomem *io_base)149{150/* mask and ack all IRQs */151out_be32(io_base + HW_BROADWAY_IMR, 0);152out_be32(io_base + HW_BROADWAY_ICR, 0xffffffff);153}154155static struct irq_domain *__init hlwd_pic_init(struct device_node *np)156{157struct irq_domain *irq_domain;158struct resource res;159void __iomem *io_base;160int retval;161162retval = of_address_to_resource(np, 0, &res);163if (retval) {164pr_err("no io memory range found\n");165return NULL;166}167io_base = ioremap(res.start, resource_size(&res));168if (!io_base) {169pr_err("ioremap failed\n");170return NULL;171}172173pr_info("controller at 0x%pa mapped to 0x%p\n", &res.start, io_base);174175__hlwd_quiesce(io_base);176177irq_domain = irq_domain_create_linear(of_fwnode_handle(np),178HLWD_NR_IRQS,179&hlwd_irq_domain_ops, io_base);180if (!irq_domain) {181pr_err("failed to allocate irq_domain\n");182iounmap(io_base);183return NULL;184}185186return irq_domain;187}188189unsigned int hlwd_pic_get_irq(void)190{191unsigned int hwirq = __hlwd_pic_get_irq(hlwd_irq_host);192return hwirq ? irq_find_mapping(hlwd_irq_host, hwirq) : 0;193}194195/*196* Probe function.197*198*/199200void __init hlwd_pic_probe(void)201{202struct irq_domain *host;203struct device_node *np;204const u32 *interrupts;205int cascade_virq;206207for_each_compatible_node(np, NULL, "nintendo,hollywood-pic") {208interrupts = of_get_property(np, "interrupts", NULL);209if (interrupts) {210host = hlwd_pic_init(np);211BUG_ON(!host);212cascade_virq = irq_of_parse_and_map(np, 0);213irq_set_handler_data(cascade_virq, host);214irq_set_chained_handler(cascade_virq,215hlwd_pic_irq_cascade);216hlwd_irq_host = host;217of_node_put(np);218break;219}220}221}222223/**224* hlwd_quiesce() - quiesce hollywood irq controller225*226* Mask and ack all interrupt sources.227*228*/229void hlwd_quiesce(void)230{231void __iomem *io_base = hlwd_irq_host->host_data;232233__hlwd_quiesce(io_base);234}235236237238