162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// Copyright (C) 2008-2009 The GameCube Linux Team 362306a36Sopenharmony_ci// Copyright (C) 2008,2009 Albert Herranz 462306a36Sopenharmony_ci// Copyright (C) 2017-2018 Jonathan Neuschäfer 562306a36Sopenharmony_ci// 662306a36Sopenharmony_ci// Nintendo Wii (Hollywood) GPIO driver 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/gpio/driver.h> 962306a36Sopenharmony_ci#include <linux/io.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/seq_file.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* 1862306a36Sopenharmony_ci * Register names and offsets courtesy of WiiBrew: 1962306a36Sopenharmony_ci * https://wiibrew.org/wiki/Hardware/Hollywood_GPIOs 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * Note that for most registers, there are two versions: 2262306a36Sopenharmony_ci * - HW_GPIOB_* Is always accessible by the Broadway PowerPC core, but does 2362306a36Sopenharmony_ci * always give access to all GPIO lines 2462306a36Sopenharmony_ci * - HW_GPIO_* Is only accessible by the Broadway PowerPC code if the memory 2562306a36Sopenharmony_ci * firewall (AHBPROT) in the Hollywood chipset has been configured to allow 2662306a36Sopenharmony_ci * such access. 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * The ownership of each GPIO line can be configured in the HW_GPIO_OWNER 2962306a36Sopenharmony_ci * register: A one bit configures the line for access via the HW_GPIOB_* 3062306a36Sopenharmony_ci * registers, a zero bit indicates access via HW_GPIO_*. This driver uses 3162306a36Sopenharmony_ci * HW_GPIOB_*. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci#define HW_GPIOB_OUT 0x00 3462306a36Sopenharmony_ci#define HW_GPIOB_DIR 0x04 3562306a36Sopenharmony_ci#define HW_GPIOB_IN 0x08 3662306a36Sopenharmony_ci#define HW_GPIOB_INTLVL 0x0c 3762306a36Sopenharmony_ci#define HW_GPIOB_INTFLAG 0x10 3862306a36Sopenharmony_ci#define HW_GPIOB_INTMASK 0x14 3962306a36Sopenharmony_ci#define HW_GPIOB_INMIR 0x18 4062306a36Sopenharmony_ci#define HW_GPIO_ENABLE 0x1c 4162306a36Sopenharmony_ci#define HW_GPIO_OUT 0x20 4262306a36Sopenharmony_ci#define HW_GPIO_DIR 0x24 4362306a36Sopenharmony_ci#define HW_GPIO_IN 0x28 4462306a36Sopenharmony_ci#define HW_GPIO_INTLVL 0x2c 4562306a36Sopenharmony_ci#define HW_GPIO_INTFLAG 0x30 4662306a36Sopenharmony_ci#define HW_GPIO_INTMASK 0x34 4762306a36Sopenharmony_ci#define HW_GPIO_INMIR 0x38 4862306a36Sopenharmony_ci#define HW_GPIO_OWNER 0x3c 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistruct hlwd_gpio { 5162306a36Sopenharmony_ci struct gpio_chip gpioc; 5262306a36Sopenharmony_ci struct device *dev; 5362306a36Sopenharmony_ci void __iomem *regs; 5462306a36Sopenharmony_ci int irq; 5562306a36Sopenharmony_ci u32 edge_emulation; 5662306a36Sopenharmony_ci u32 rising_edge, falling_edge; 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void hlwd_gpio_irqhandler(struct irq_desc *desc) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct hlwd_gpio *hlwd = 6262306a36Sopenharmony_ci gpiochip_get_data(irq_desc_get_handler_data(desc)); 6362306a36Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 6462306a36Sopenharmony_ci unsigned long flags; 6562306a36Sopenharmony_ci unsigned long pending; 6662306a36Sopenharmony_ci int hwirq; 6762306a36Sopenharmony_ci u32 emulated_pending; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci raw_spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); 7062306a36Sopenharmony_ci pending = ioread32be(hlwd->regs + HW_GPIOB_INTFLAG); 7162306a36Sopenharmony_ci pending &= ioread32be(hlwd->regs + HW_GPIOB_INTMASK); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* Treat interrupts due to edge trigger emulation separately */ 7462306a36Sopenharmony_ci emulated_pending = hlwd->edge_emulation & pending; 7562306a36Sopenharmony_ci pending &= ~emulated_pending; 7662306a36Sopenharmony_ci if (emulated_pending) { 7762306a36Sopenharmony_ci u32 level, rising, falling; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); 8062306a36Sopenharmony_ci rising = level & emulated_pending; 8162306a36Sopenharmony_ci falling = ~level & emulated_pending; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* Invert the levels */ 8462306a36Sopenharmony_ci iowrite32be(level ^ emulated_pending, 8562306a36Sopenharmony_ci hlwd->regs + HW_GPIOB_INTLVL); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* Ack all emulated-edge interrupts */ 8862306a36Sopenharmony_ci iowrite32be(emulated_pending, hlwd->regs + HW_GPIOB_INTFLAG); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* Signal interrupts only on the correct edge */ 9162306a36Sopenharmony_ci rising &= hlwd->rising_edge; 9262306a36Sopenharmony_ci falling &= hlwd->falling_edge; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* Mark emulated interrupts as pending */ 9562306a36Sopenharmony_ci pending |= rising | falling; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci chained_irq_enter(chip, desc); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci for_each_set_bit(hwirq, &pending, 32) 10262306a36Sopenharmony_ci generic_handle_domain_irq(hlwd->gpioc.irq.domain, hwirq); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci chained_irq_exit(chip, desc); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic void hlwd_gpio_irq_ack(struct irq_data *data) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct hlwd_gpio *hlwd = 11062306a36Sopenharmony_ci gpiochip_get_data(irq_data_get_irq_chip_data(data)); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci iowrite32be(BIT(data->hwirq), hlwd->regs + HW_GPIOB_INTFLAG); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic void hlwd_gpio_irq_mask(struct irq_data *data) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct hlwd_gpio *hlwd = 11862306a36Sopenharmony_ci gpiochip_get_data(irq_data_get_irq_chip_data(data)); 11962306a36Sopenharmony_ci unsigned long flags; 12062306a36Sopenharmony_ci u32 mask; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci raw_spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); 12362306a36Sopenharmony_ci mask = ioread32be(hlwd->regs + HW_GPIOB_INTMASK); 12462306a36Sopenharmony_ci mask &= ~BIT(data->hwirq); 12562306a36Sopenharmony_ci iowrite32be(mask, hlwd->regs + HW_GPIOB_INTMASK); 12662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); 12762306a36Sopenharmony_ci gpiochip_disable_irq(&hlwd->gpioc, irqd_to_hwirq(data)); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void hlwd_gpio_irq_unmask(struct irq_data *data) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct hlwd_gpio *hlwd = 13362306a36Sopenharmony_ci gpiochip_get_data(irq_data_get_irq_chip_data(data)); 13462306a36Sopenharmony_ci unsigned long flags; 13562306a36Sopenharmony_ci u32 mask; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci gpiochip_enable_irq(&hlwd->gpioc, irqd_to_hwirq(data)); 13862306a36Sopenharmony_ci raw_spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); 13962306a36Sopenharmony_ci mask = ioread32be(hlwd->regs + HW_GPIOB_INTMASK); 14062306a36Sopenharmony_ci mask |= BIT(data->hwirq); 14162306a36Sopenharmony_ci iowrite32be(mask, hlwd->regs + HW_GPIOB_INTMASK); 14262306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic void hlwd_gpio_irq_enable(struct irq_data *data) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci hlwd_gpio_irq_ack(data); 14862306a36Sopenharmony_ci hlwd_gpio_irq_unmask(data); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic void hlwd_gpio_irq_setup_emulation(struct hlwd_gpio *hlwd, int hwirq, 15262306a36Sopenharmony_ci unsigned int flow_type) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci u32 level, state; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* Set the trigger level to the inactive level */ 15762306a36Sopenharmony_ci level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); 15862306a36Sopenharmony_ci state = ioread32be(hlwd->regs + HW_GPIOB_IN) & BIT(hwirq); 15962306a36Sopenharmony_ci level &= ~BIT(hwirq); 16062306a36Sopenharmony_ci level |= state ^ BIT(hwirq); 16162306a36Sopenharmony_ci iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci hlwd->edge_emulation |= BIT(hwirq); 16462306a36Sopenharmony_ci hlwd->rising_edge &= ~BIT(hwirq); 16562306a36Sopenharmony_ci hlwd->falling_edge &= ~BIT(hwirq); 16662306a36Sopenharmony_ci if (flow_type & IRQ_TYPE_EDGE_RISING) 16762306a36Sopenharmony_ci hlwd->rising_edge |= BIT(hwirq); 16862306a36Sopenharmony_ci if (flow_type & IRQ_TYPE_EDGE_FALLING) 16962306a36Sopenharmony_ci hlwd->falling_edge |= BIT(hwirq); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct hlwd_gpio *hlwd = 17562306a36Sopenharmony_ci gpiochip_get_data(irq_data_get_irq_chip_data(data)); 17662306a36Sopenharmony_ci unsigned long flags; 17762306a36Sopenharmony_ci u32 level; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci raw_spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci hlwd->edge_emulation &= ~BIT(data->hwirq); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci switch (flow_type) { 18462306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 18562306a36Sopenharmony_ci level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); 18662306a36Sopenharmony_ci level |= BIT(data->hwirq); 18762306a36Sopenharmony_ci iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); 18862306a36Sopenharmony_ci break; 18962306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 19062306a36Sopenharmony_ci level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); 19162306a36Sopenharmony_ci level &= ~BIT(data->hwirq); 19262306a36Sopenharmony_ci iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); 19362306a36Sopenharmony_ci break; 19462306a36Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 19562306a36Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 19662306a36Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 19762306a36Sopenharmony_ci hlwd_gpio_irq_setup_emulation(hlwd, data->hwirq, flow_type); 19862306a36Sopenharmony_ci break; 19962306a36Sopenharmony_ci default: 20062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); 20162306a36Sopenharmony_ci return -EINVAL; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); 20562306a36Sopenharmony_ci return 0; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic void hlwd_gpio_irq_print_chip(struct irq_data *data, struct seq_file *p) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct hlwd_gpio *hlwd = 21162306a36Sopenharmony_ci gpiochip_get_data(irq_data_get_irq_chip_data(data)); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci seq_printf(p, dev_name(hlwd->dev)); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic const struct irq_chip hlwd_gpio_irq_chip = { 21762306a36Sopenharmony_ci .irq_mask = hlwd_gpio_irq_mask, 21862306a36Sopenharmony_ci .irq_unmask = hlwd_gpio_irq_unmask, 21962306a36Sopenharmony_ci .irq_enable = hlwd_gpio_irq_enable, 22062306a36Sopenharmony_ci .irq_set_type = hlwd_gpio_irq_set_type, 22162306a36Sopenharmony_ci .irq_print_chip = hlwd_gpio_irq_print_chip, 22262306a36Sopenharmony_ci .flags = IRQCHIP_IMMUTABLE, 22362306a36Sopenharmony_ci GPIOCHIP_IRQ_RESOURCE_HELPERS, 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic int hlwd_gpio_probe(struct platform_device *pdev) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct hlwd_gpio *hlwd; 22962306a36Sopenharmony_ci u32 ngpios; 23062306a36Sopenharmony_ci int res; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci hlwd = devm_kzalloc(&pdev->dev, sizeof(*hlwd), GFP_KERNEL); 23362306a36Sopenharmony_ci if (!hlwd) 23462306a36Sopenharmony_ci return -ENOMEM; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci hlwd->regs = devm_platform_ioremap_resource(pdev, 0); 23762306a36Sopenharmony_ci if (IS_ERR(hlwd->regs)) 23862306a36Sopenharmony_ci return PTR_ERR(hlwd->regs); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci hlwd->dev = &pdev->dev; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* 24362306a36Sopenharmony_ci * Claim all GPIOs using the OWNER register. This will not work on 24462306a36Sopenharmony_ci * systems where the AHBPROT memory firewall hasn't been configured to 24562306a36Sopenharmony_ci * permit PPC access to HW_GPIO_*. 24662306a36Sopenharmony_ci * 24762306a36Sopenharmony_ci * Note that this has to happen before bgpio_init reads the 24862306a36Sopenharmony_ci * HW_GPIOB_OUT and HW_GPIOB_DIR, because otherwise it reads the wrong 24962306a36Sopenharmony_ci * values. 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ci iowrite32be(0xffffffff, hlwd->regs + HW_GPIO_OWNER); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci res = bgpio_init(&hlwd->gpioc, &pdev->dev, 4, 25462306a36Sopenharmony_ci hlwd->regs + HW_GPIOB_IN, hlwd->regs + HW_GPIOB_OUT, 25562306a36Sopenharmony_ci NULL, hlwd->regs + HW_GPIOB_DIR, NULL, 25662306a36Sopenharmony_ci BGPIOF_BIG_ENDIAN_BYTE_ORDER); 25762306a36Sopenharmony_ci if (res < 0) { 25862306a36Sopenharmony_ci dev_warn(&pdev->dev, "bgpio_init failed: %d\n", res); 25962306a36Sopenharmony_ci return res; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci res = of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpios); 26362306a36Sopenharmony_ci if (res) 26462306a36Sopenharmony_ci ngpios = 32; 26562306a36Sopenharmony_ci hlwd->gpioc.ngpio = ngpios; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* Mask and ack all interrupts */ 26862306a36Sopenharmony_ci iowrite32be(0, hlwd->regs + HW_GPIOB_INTMASK); 26962306a36Sopenharmony_ci iowrite32be(0xffffffff, hlwd->regs + HW_GPIOB_INTFLAG); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* 27262306a36Sopenharmony_ci * If this GPIO controller is not marked as an interrupt controller in 27362306a36Sopenharmony_ci * the DT, skip interrupt support. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_ci if (of_property_read_bool(pdev->dev.of_node, "interrupt-controller")) { 27662306a36Sopenharmony_ci struct gpio_irq_chip *girq; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci hlwd->irq = platform_get_irq(pdev, 0); 27962306a36Sopenharmony_ci if (hlwd->irq < 0) { 28062306a36Sopenharmony_ci dev_info(&pdev->dev, "platform_get_irq returned %d\n", 28162306a36Sopenharmony_ci hlwd->irq); 28262306a36Sopenharmony_ci return hlwd->irq; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci girq = &hlwd->gpioc.irq; 28662306a36Sopenharmony_ci gpio_irq_chip_set_chip(girq, &hlwd_gpio_irq_chip); 28762306a36Sopenharmony_ci girq->parent_handler = hlwd_gpio_irqhandler; 28862306a36Sopenharmony_ci girq->num_parents = 1; 28962306a36Sopenharmony_ci girq->parents = devm_kcalloc(&pdev->dev, 1, 29062306a36Sopenharmony_ci sizeof(*girq->parents), 29162306a36Sopenharmony_ci GFP_KERNEL); 29262306a36Sopenharmony_ci if (!girq->parents) 29362306a36Sopenharmony_ci return -ENOMEM; 29462306a36Sopenharmony_ci girq->parents[0] = hlwd->irq; 29562306a36Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 29662306a36Sopenharmony_ci girq->handler = handle_level_irq; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return devm_gpiochip_add_data(&pdev->dev, &hlwd->gpioc, hlwd); 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic const struct of_device_id hlwd_gpio_match[] = { 30362306a36Sopenharmony_ci { .compatible = "nintendo,hollywood-gpio", }, 30462306a36Sopenharmony_ci {}, 30562306a36Sopenharmony_ci}; 30662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, hlwd_gpio_match); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic struct platform_driver hlwd_gpio_driver = { 30962306a36Sopenharmony_ci .driver = { 31062306a36Sopenharmony_ci .name = "gpio-hlwd", 31162306a36Sopenharmony_ci .of_match_table = hlwd_gpio_match, 31262306a36Sopenharmony_ci }, 31362306a36Sopenharmony_ci .probe = hlwd_gpio_probe, 31462306a36Sopenharmony_ci}; 31562306a36Sopenharmony_cimodule_platform_driver(hlwd_gpio_driver); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ciMODULE_AUTHOR("Jonathan Neuschäfer <j.neuschaefer@gmx.net>"); 31862306a36Sopenharmony_ciMODULE_DESCRIPTION("Nintendo Wii GPIO driver"); 31962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 320