18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// Copyright (C) 2008-2009 The GameCube Linux Team 38c2ecf20Sopenharmony_ci// Copyright (C) 2008,2009 Albert Herranz 48c2ecf20Sopenharmony_ci// Copyright (C) 2017-2018 Jonathan Neuschäfer 58c2ecf20Sopenharmony_ci// 68c2ecf20Sopenharmony_ci// Nintendo Wii (Hollywood) GPIO driver 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/of.h> 138c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* 178c2ecf20Sopenharmony_ci * Register names and offsets courtesy of WiiBrew: 188c2ecf20Sopenharmony_ci * https://wiibrew.org/wiki/Hardware/Hollywood_GPIOs 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Note that for most registers, there are two versions: 218c2ecf20Sopenharmony_ci * - HW_GPIOB_* Is always accessible by the Broadway PowerPC core, but does 228c2ecf20Sopenharmony_ci * always give access to all GPIO lines 238c2ecf20Sopenharmony_ci * - HW_GPIO_* Is only accessible by the Broadway PowerPC code if the memory 248c2ecf20Sopenharmony_ci * firewall (AHBPROT) in the Hollywood chipset has been configured to allow 258c2ecf20Sopenharmony_ci * such access. 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * The ownership of each GPIO line can be configured in the HW_GPIO_OWNER 288c2ecf20Sopenharmony_ci * register: A one bit configures the line for access via the HW_GPIOB_* 298c2ecf20Sopenharmony_ci * registers, a zero bit indicates access via HW_GPIO_*. This driver uses 308c2ecf20Sopenharmony_ci * HW_GPIOB_*. 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci#define HW_GPIOB_OUT 0x00 338c2ecf20Sopenharmony_ci#define HW_GPIOB_DIR 0x04 348c2ecf20Sopenharmony_ci#define HW_GPIOB_IN 0x08 358c2ecf20Sopenharmony_ci#define HW_GPIOB_INTLVL 0x0c 368c2ecf20Sopenharmony_ci#define HW_GPIOB_INTFLAG 0x10 378c2ecf20Sopenharmony_ci#define HW_GPIOB_INTMASK 0x14 388c2ecf20Sopenharmony_ci#define HW_GPIOB_INMIR 0x18 398c2ecf20Sopenharmony_ci#define HW_GPIO_ENABLE 0x1c 408c2ecf20Sopenharmony_ci#define HW_GPIO_OUT 0x20 418c2ecf20Sopenharmony_ci#define HW_GPIO_DIR 0x24 428c2ecf20Sopenharmony_ci#define HW_GPIO_IN 0x28 438c2ecf20Sopenharmony_ci#define HW_GPIO_INTLVL 0x2c 448c2ecf20Sopenharmony_ci#define HW_GPIO_INTFLAG 0x30 458c2ecf20Sopenharmony_ci#define HW_GPIO_INTMASK 0x34 468c2ecf20Sopenharmony_ci#define HW_GPIO_INMIR 0x38 478c2ecf20Sopenharmony_ci#define HW_GPIO_OWNER 0x3c 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistruct hlwd_gpio { 508c2ecf20Sopenharmony_ci struct gpio_chip gpioc; 518c2ecf20Sopenharmony_ci struct irq_chip irqc; 528c2ecf20Sopenharmony_ci void __iomem *regs; 538c2ecf20Sopenharmony_ci int irq; 548c2ecf20Sopenharmony_ci u32 edge_emulation; 558c2ecf20Sopenharmony_ci u32 rising_edge, falling_edge; 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic void hlwd_gpio_irqhandler(struct irq_desc *desc) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct hlwd_gpio *hlwd = 618c2ecf20Sopenharmony_ci gpiochip_get_data(irq_desc_get_handler_data(desc)); 628c2ecf20Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 638c2ecf20Sopenharmony_ci unsigned long flags; 648c2ecf20Sopenharmony_ci unsigned long pending; 658c2ecf20Sopenharmony_ci int hwirq; 668c2ecf20Sopenharmony_ci u32 emulated_pending; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); 698c2ecf20Sopenharmony_ci pending = ioread32be(hlwd->regs + HW_GPIOB_INTFLAG); 708c2ecf20Sopenharmony_ci pending &= ioread32be(hlwd->regs + HW_GPIOB_INTMASK); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* Treat interrupts due to edge trigger emulation separately */ 738c2ecf20Sopenharmony_ci emulated_pending = hlwd->edge_emulation & pending; 748c2ecf20Sopenharmony_ci pending &= ~emulated_pending; 758c2ecf20Sopenharmony_ci if (emulated_pending) { 768c2ecf20Sopenharmony_ci u32 level, rising, falling; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); 798c2ecf20Sopenharmony_ci rising = level & emulated_pending; 808c2ecf20Sopenharmony_ci falling = ~level & emulated_pending; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* Invert the levels */ 838c2ecf20Sopenharmony_ci iowrite32be(level ^ emulated_pending, 848c2ecf20Sopenharmony_ci hlwd->regs + HW_GPIOB_INTLVL); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* Ack all emulated-edge interrupts */ 878c2ecf20Sopenharmony_ci iowrite32be(emulated_pending, hlwd->regs + HW_GPIOB_INTFLAG); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* Signal interrupts only on the correct edge */ 908c2ecf20Sopenharmony_ci rising &= hlwd->rising_edge; 918c2ecf20Sopenharmony_ci falling &= hlwd->falling_edge; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* Mark emulated interrupts as pending */ 948c2ecf20Sopenharmony_ci pending |= rising | falling; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci chained_irq_enter(chip, desc); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci for_each_set_bit(hwirq, &pending, 32) { 1018c2ecf20Sopenharmony_ci int irq = irq_find_mapping(hlwd->gpioc.irq.domain, hwirq); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci generic_handle_irq(irq); 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci chained_irq_exit(chip, desc); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void hlwd_gpio_irq_ack(struct irq_data *data) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct hlwd_gpio *hlwd = 1128c2ecf20Sopenharmony_ci gpiochip_get_data(irq_data_get_irq_chip_data(data)); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci iowrite32be(BIT(data->hwirq), hlwd->regs + HW_GPIOB_INTFLAG); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic void hlwd_gpio_irq_mask(struct irq_data *data) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct hlwd_gpio *hlwd = 1208c2ecf20Sopenharmony_ci gpiochip_get_data(irq_data_get_irq_chip_data(data)); 1218c2ecf20Sopenharmony_ci unsigned long flags; 1228c2ecf20Sopenharmony_ci u32 mask; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); 1258c2ecf20Sopenharmony_ci mask = ioread32be(hlwd->regs + HW_GPIOB_INTMASK); 1268c2ecf20Sopenharmony_ci mask &= ~BIT(data->hwirq); 1278c2ecf20Sopenharmony_ci iowrite32be(mask, hlwd->regs + HW_GPIOB_INTMASK); 1288c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic void hlwd_gpio_irq_unmask(struct irq_data *data) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct hlwd_gpio *hlwd = 1348c2ecf20Sopenharmony_ci gpiochip_get_data(irq_data_get_irq_chip_data(data)); 1358c2ecf20Sopenharmony_ci unsigned long flags; 1368c2ecf20Sopenharmony_ci u32 mask; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); 1398c2ecf20Sopenharmony_ci mask = ioread32be(hlwd->regs + HW_GPIOB_INTMASK); 1408c2ecf20Sopenharmony_ci mask |= BIT(data->hwirq); 1418c2ecf20Sopenharmony_ci iowrite32be(mask, hlwd->regs + HW_GPIOB_INTMASK); 1428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic void hlwd_gpio_irq_enable(struct irq_data *data) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci hlwd_gpio_irq_ack(data); 1488c2ecf20Sopenharmony_ci hlwd_gpio_irq_unmask(data); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic void hlwd_gpio_irq_setup_emulation(struct hlwd_gpio *hlwd, int hwirq, 1528c2ecf20Sopenharmony_ci unsigned int flow_type) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci u32 level, state; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* Set the trigger level to the inactive level */ 1578c2ecf20Sopenharmony_ci level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); 1588c2ecf20Sopenharmony_ci state = ioread32be(hlwd->regs + HW_GPIOB_IN) & BIT(hwirq); 1598c2ecf20Sopenharmony_ci level &= ~BIT(hwirq); 1608c2ecf20Sopenharmony_ci level |= state ^ BIT(hwirq); 1618c2ecf20Sopenharmony_ci iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci hlwd->edge_emulation |= BIT(hwirq); 1648c2ecf20Sopenharmony_ci hlwd->rising_edge &= ~BIT(hwirq); 1658c2ecf20Sopenharmony_ci hlwd->falling_edge &= ~BIT(hwirq); 1668c2ecf20Sopenharmony_ci if (flow_type & IRQ_TYPE_EDGE_RISING) 1678c2ecf20Sopenharmony_ci hlwd->rising_edge |= BIT(hwirq); 1688c2ecf20Sopenharmony_ci if (flow_type & IRQ_TYPE_EDGE_FALLING) 1698c2ecf20Sopenharmony_ci hlwd->falling_edge |= BIT(hwirq); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct hlwd_gpio *hlwd = 1758c2ecf20Sopenharmony_ci gpiochip_get_data(irq_data_get_irq_chip_data(data)); 1768c2ecf20Sopenharmony_ci unsigned long flags; 1778c2ecf20Sopenharmony_ci u32 level; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci hlwd->edge_emulation &= ~BIT(data->hwirq); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci switch (flow_type) { 1848c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 1858c2ecf20Sopenharmony_ci level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); 1868c2ecf20Sopenharmony_ci level |= BIT(data->hwirq); 1878c2ecf20Sopenharmony_ci iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); 1888c2ecf20Sopenharmony_ci break; 1898c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 1908c2ecf20Sopenharmony_ci level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); 1918c2ecf20Sopenharmony_ci level &= ~BIT(data->hwirq); 1928c2ecf20Sopenharmony_ci iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); 1938c2ecf20Sopenharmony_ci break; 1948c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 1958c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 1968c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 1978c2ecf20Sopenharmony_ci hlwd_gpio_irq_setup_emulation(hlwd, data->hwirq, flow_type); 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci default: 2008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); 2018c2ecf20Sopenharmony_ci return -EINVAL; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int hlwd_gpio_probe(struct platform_device *pdev) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct hlwd_gpio *hlwd; 2118c2ecf20Sopenharmony_ci u32 ngpios; 2128c2ecf20Sopenharmony_ci int res; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci hlwd = devm_kzalloc(&pdev->dev, sizeof(*hlwd), GFP_KERNEL); 2158c2ecf20Sopenharmony_ci if (!hlwd) 2168c2ecf20Sopenharmony_ci return -ENOMEM; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci hlwd->regs = devm_platform_ioremap_resource(pdev, 0); 2198c2ecf20Sopenharmony_ci if (IS_ERR(hlwd->regs)) 2208c2ecf20Sopenharmony_ci return PTR_ERR(hlwd->regs); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* 2238c2ecf20Sopenharmony_ci * Claim all GPIOs using the OWNER register. This will not work on 2248c2ecf20Sopenharmony_ci * systems where the AHBPROT memory firewall hasn't been configured to 2258c2ecf20Sopenharmony_ci * permit PPC access to HW_GPIO_*. 2268c2ecf20Sopenharmony_ci * 2278c2ecf20Sopenharmony_ci * Note that this has to happen before bgpio_init reads the 2288c2ecf20Sopenharmony_ci * HW_GPIOB_OUT and HW_GPIOB_DIR, because otherwise it reads the wrong 2298c2ecf20Sopenharmony_ci * values. 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci iowrite32be(0xffffffff, hlwd->regs + HW_GPIO_OWNER); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci res = bgpio_init(&hlwd->gpioc, &pdev->dev, 4, 2348c2ecf20Sopenharmony_ci hlwd->regs + HW_GPIOB_IN, hlwd->regs + HW_GPIOB_OUT, 2358c2ecf20Sopenharmony_ci NULL, hlwd->regs + HW_GPIOB_DIR, NULL, 2368c2ecf20Sopenharmony_ci BGPIOF_BIG_ENDIAN_BYTE_ORDER); 2378c2ecf20Sopenharmony_ci if (res < 0) { 2388c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "bgpio_init failed: %d\n", res); 2398c2ecf20Sopenharmony_ci return res; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci res = of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpios); 2438c2ecf20Sopenharmony_ci if (res) 2448c2ecf20Sopenharmony_ci ngpios = 32; 2458c2ecf20Sopenharmony_ci hlwd->gpioc.ngpio = ngpios; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* Mask and ack all interrupts */ 2488c2ecf20Sopenharmony_ci iowrite32be(0, hlwd->regs + HW_GPIOB_INTMASK); 2498c2ecf20Sopenharmony_ci iowrite32be(0xffffffff, hlwd->regs + HW_GPIOB_INTFLAG); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* 2528c2ecf20Sopenharmony_ci * If this GPIO controller is not marked as an interrupt controller in 2538c2ecf20Sopenharmony_ci * the DT, skip interrupt support. 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_ci if (of_property_read_bool(pdev->dev.of_node, "interrupt-controller")) { 2568c2ecf20Sopenharmony_ci struct gpio_irq_chip *girq; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci hlwd->irq = platform_get_irq(pdev, 0); 2598c2ecf20Sopenharmony_ci if (hlwd->irq < 0) { 2608c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "platform_get_irq returned %d\n", 2618c2ecf20Sopenharmony_ci hlwd->irq); 2628c2ecf20Sopenharmony_ci return hlwd->irq; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci hlwd->irqc.name = dev_name(&pdev->dev); 2668c2ecf20Sopenharmony_ci hlwd->irqc.irq_mask = hlwd_gpio_irq_mask; 2678c2ecf20Sopenharmony_ci hlwd->irqc.irq_unmask = hlwd_gpio_irq_unmask; 2688c2ecf20Sopenharmony_ci hlwd->irqc.irq_enable = hlwd_gpio_irq_enable; 2698c2ecf20Sopenharmony_ci hlwd->irqc.irq_set_type = hlwd_gpio_irq_set_type; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci girq = &hlwd->gpioc.irq; 2728c2ecf20Sopenharmony_ci girq->chip = &hlwd->irqc; 2738c2ecf20Sopenharmony_ci girq->parent_handler = hlwd_gpio_irqhandler; 2748c2ecf20Sopenharmony_ci girq->num_parents = 1; 2758c2ecf20Sopenharmony_ci girq->parents = devm_kcalloc(&pdev->dev, 1, 2768c2ecf20Sopenharmony_ci sizeof(*girq->parents), 2778c2ecf20Sopenharmony_ci GFP_KERNEL); 2788c2ecf20Sopenharmony_ci if (!girq->parents) 2798c2ecf20Sopenharmony_ci return -ENOMEM; 2808c2ecf20Sopenharmony_ci girq->parents[0] = hlwd->irq; 2818c2ecf20Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 2828c2ecf20Sopenharmony_ci girq->handler = handle_level_irq; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return devm_gpiochip_add_data(&pdev->dev, &hlwd->gpioc, hlwd); 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic const struct of_device_id hlwd_gpio_match[] = { 2898c2ecf20Sopenharmony_ci { .compatible = "nintendo,hollywood-gpio", }, 2908c2ecf20Sopenharmony_ci {}, 2918c2ecf20Sopenharmony_ci}; 2928c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, hlwd_gpio_match); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic struct platform_driver hlwd_gpio_driver = { 2958c2ecf20Sopenharmony_ci .driver = { 2968c2ecf20Sopenharmony_ci .name = "gpio-hlwd", 2978c2ecf20Sopenharmony_ci .of_match_table = hlwd_gpio_match, 2988c2ecf20Sopenharmony_ci }, 2998c2ecf20Sopenharmony_ci .probe = hlwd_gpio_probe, 3008c2ecf20Sopenharmony_ci}; 3018c2ecf20Sopenharmony_cimodule_platform_driver(hlwd_gpio_driver); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jonathan Neuschäfer <j.neuschaefer@gmx.net>"); 3048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Nintendo Wii GPIO driver"); 3058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 306