Path: blob/master/arch/powerpc/platforms/embedded6xx/flipper-pic.c
26489 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* arch/powerpc/platforms/embedded6xx/flipper-pic.c3*4* Nintendo GameCube/Wii "Flipper" interrupt controller support.5* Copyright (C) 2004-2009 The GameCube Linux Team6* Copyright (C) 2007,2008,2009 Albert Herranz7*/8#define DRV_MODULE_NAME "flipper-pic"9#define pr_fmt(fmt) DRV_MODULE_NAME ": " fmt1011#include <linux/kernel.h>12#include <linux/init.h>13#include <linux/irq.h>14#include <linux/irqdomain.h>15#include <linux/of.h>16#include <linux/of_address.h>17#include <asm/io.h>1819#include "flipper-pic.h"2021#define FLIPPER_NR_IRQS 322223/*24* Each interrupt has a corresponding bit in both25* the Interrupt Cause (ICR) and Interrupt Mask (IMR) registers.26*27* Enabling/disabling an interrupt line involves setting/clearing28* the corresponding bit in IMR.29* Except for the RSW interrupt, all interrupts get deasserted automatically30* when the source deasserts the interrupt.31*/32#define FLIPPER_ICR 0x0033#define FLIPPER_ICR_RSS (1<<16) /* reset switch state */3435#define FLIPPER_IMR 0x043637#define FLIPPER_RESET 0x24383940/*41* IRQ chip hooks.42*43*/4445static void flipper_pic_mask_and_ack(struct irq_data *d)46{47int irq = irqd_to_hwirq(d);48void __iomem *io_base = irq_data_get_irq_chip_data(d);49u32 mask = 1 << irq;5051clrbits32(io_base + FLIPPER_IMR, mask);52/* this is at least needed for RSW */53out_be32(io_base + FLIPPER_ICR, mask);54}5556static void flipper_pic_ack(struct irq_data *d)57{58int irq = irqd_to_hwirq(d);59void __iomem *io_base = irq_data_get_irq_chip_data(d);6061/* this is at least needed for RSW */62out_be32(io_base + FLIPPER_ICR, 1 << irq);63}6465static void flipper_pic_mask(struct irq_data *d)66{67int irq = irqd_to_hwirq(d);68void __iomem *io_base = irq_data_get_irq_chip_data(d);6970clrbits32(io_base + FLIPPER_IMR, 1 << irq);71}7273static void flipper_pic_unmask(struct irq_data *d)74{75int irq = irqd_to_hwirq(d);76void __iomem *io_base = irq_data_get_irq_chip_data(d);7778setbits32(io_base + FLIPPER_IMR, 1 << irq);79}808182static struct irq_chip flipper_pic = {83.name = "flipper-pic",84.irq_ack = flipper_pic_ack,85.irq_mask_ack = flipper_pic_mask_and_ack,86.irq_mask = flipper_pic_mask,87.irq_unmask = flipper_pic_unmask,88};8990/*91* IRQ host hooks.92*93*/9495static struct irq_domain *flipper_irq_host;9697static int flipper_pic_map(struct irq_domain *h, unsigned int virq,98irq_hw_number_t hwirq)99{100irq_set_chip_data(virq, h->host_data);101irq_set_status_flags(virq, IRQ_LEVEL);102irq_set_chip_and_handler(virq, &flipper_pic, handle_level_irq);103return 0;104}105106static const struct irq_domain_ops flipper_irq_domain_ops = {107.map = flipper_pic_map,108};109110/*111* Platform hooks.112*113*/114115static void __flipper_quiesce(void __iomem *io_base)116{117/* mask and ack all IRQs */118out_be32(io_base + FLIPPER_IMR, 0x00000000);119out_be32(io_base + FLIPPER_ICR, 0xffffffff);120}121122static struct irq_domain * __init flipper_pic_init(struct device_node *np)123{124struct device_node *pi;125struct irq_domain *irq_domain = NULL;126struct resource res;127void __iomem *io_base;128int retval;129130pi = of_get_parent(np);131if (!pi) {132pr_err("no parent found\n");133goto out;134}135if (!of_device_is_compatible(pi, "nintendo,flipper-pi")) {136pr_err("unexpected parent compatible\n");137goto out;138}139140retval = of_address_to_resource(pi, 0, &res);141if (retval) {142pr_err("no io memory range found\n");143goto out;144}145io_base = ioremap(res.start, resource_size(&res));146147pr_info("controller at 0x%pa mapped to 0x%p\n", &res.start, io_base);148149__flipper_quiesce(io_base);150151irq_domain = irq_domain_create_linear(of_fwnode_handle(np),152FLIPPER_NR_IRQS,153&flipper_irq_domain_ops, io_base);154if (!irq_domain) {155pr_err("failed to allocate irq_domain\n");156return NULL;157}158159out:160return irq_domain;161}162163unsigned int flipper_pic_get_irq(void)164{165void __iomem *io_base = flipper_irq_host->host_data;166int irq;167u32 irq_status;168169irq_status = in_be32(io_base + FLIPPER_ICR) &170in_be32(io_base + FLIPPER_IMR);171if (irq_status == 0)172return 0; /* no more IRQs pending */173174irq = __ffs(irq_status);175return irq_find_mapping(flipper_irq_host, irq);176}177178/*179* Probe function.180*181*/182183void __init flipper_pic_probe(void)184{185struct device_node *np;186187np = of_find_compatible_node(NULL, NULL, "nintendo,flipper-pic");188BUG_ON(!np);189190flipper_irq_host = flipper_pic_init(np);191BUG_ON(!flipper_irq_host);192193irq_set_default_domain(flipper_irq_host);194195of_node_put(np);196}197198/*199* Misc functions related to the flipper chipset.200*201*/202203/**204* flipper_quiesce() - quiesce flipper irq controller205*206* Mask and ack all interrupt sources.207*208*/209void flipper_quiesce(void)210{211void __iomem *io_base = flipper_irq_host->host_data;212213__flipper_quiesce(io_base);214}215216/*217* Resets the platform.218*/219void flipper_platform_reset(void)220{221void __iomem *io_base;222223if (flipper_irq_host && flipper_irq_host->host_data) {224io_base = flipper_irq_host->host_data;225out_8(io_base + FLIPPER_RESET, 0x00);226}227}228229/*230* Returns non-zero if the reset button is pressed.231*/232int flipper_is_reset_button_pressed(void)233{234void __iomem *io_base;235u32 icr;236237if (flipper_irq_host && flipper_irq_host->host_data) {238io_base = flipper_irq_host->host_data;239icr = in_be32(io_base + FLIPPER_ICR);240return !(icr & FLIPPER_ICR_RSS);241}242return 0;243}244245246247