Source
x
static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
// SPDX-License-Identifier: GPL-2.0+
// Copyright (C) 2008-2009 The GameCube Linux Team
// Copyright (C) 2008,2009 Albert Herranz
// Copyright (C) 2017-2018 Jonathan Neuschäfer
//
// Nintendo Wii (Hollywood) GPIO driver
/*
* Register names and offsets courtesy of WiiBrew:
* https://wiibrew.org/wiki/Hardware/Hollywood_GPIOs
*
* Note that for most registers, there are two versions:
* - HW_GPIOB_* Is always accessible by the Broadway PowerPC core, but does
* always give access to all GPIO lines
* - HW_GPIO_* Is only accessible by the Broadway PowerPC code if the memory
* firewall (AHBPROT) in the Hollywood chipset has been configured to allow
* such access.
*
* The ownership of each GPIO line can be configured in the HW_GPIO_OWNER
* register: A one bit configures the line for access via the HW_GPIOB_*
* registers, a zero bit indicates access via HW_GPIO_*. This driver uses
* HW_GPIOB_*.
*/
struct hlwd_gpio {
struct gpio_chip gpioc;
struct irq_chip irqc;
void __iomem *regs;
int irq;
u32 edge_emulation;
u32 rising_edge, falling_edge;
};
static void hlwd_gpio_irqhandler(struct irq_desc *desc)
{
struct hlwd_gpio *hlwd =
gpiochip_get_data(irq_desc_get_handler_data(desc));
struct irq_chip *chip = irq_desc_get_chip(desc);
unsigned long flags;
unsigned long pending;
int hwirq;
u32 emulated_pending;
spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
pending = ioread32be(hlwd->regs + HW_GPIOB_INTFLAG);
pending &= ioread32be(hlwd->regs + HW_GPIOB_INTMASK);
/* Treat interrupts due to edge trigger emulation separately */
emulated_pending = hlwd->edge_emulation & pending;
pending &= ~emulated_pending;
if (emulated_pending) {
u32 level, rising, falling;
level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL);
rising = level & emulated_pending;
falling = ~level & emulated_pending;
/* Invert the levels */
iowrite32be(level ^ emulated_pending,
hlwd->regs + HW_GPIOB_INTLVL);
/* Ack all emulated-edge interrupts */
iowrite32be(emulated_pending, hlwd->regs + HW_GPIOB_INTFLAG);
/* Signal interrupts only on the correct edge */
rising &= hlwd->rising_edge;
falling &= hlwd->falling_edge;