18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Sonics Silicon Backplane 38c2ecf20Sopenharmony_ci * GPIO driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2011, Broadcom Corporation 68c2ecf20Sopenharmony_ci * Copyright 2012, Hauke Mehrtens <hauke@hauke-m.de> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Licensed under the GNU/GPL. See COPYING for details. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "ssb_private.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 148c2ecf20Sopenharmony_ci#include <linux/irq.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 178c2ecf20Sopenharmony_ci#include <linux/export.h> 188c2ecf20Sopenharmony_ci#include <linux/ssb/ssb.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/************************************************** 228c2ecf20Sopenharmony_ci * Shared 238c2ecf20Sopenharmony_ci **************************************************/ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SSB_EMBEDDED) 268c2ecf20Sopenharmony_cistatic int ssb_gpio_to_irq(struct gpio_chip *chip, unsigned int gpio) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci struct ssb_bus *bus = gpiochip_get_data(chip); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci if (bus->bustype == SSB_BUSTYPE_SSB) 318c2ecf20Sopenharmony_ci return irq_find_mapping(bus->irq_domain, gpio); 328c2ecf20Sopenharmony_ci else 338c2ecf20Sopenharmony_ci return -EINVAL; 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci#endif 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/************************************************** 388c2ecf20Sopenharmony_ci * ChipCommon 398c2ecf20Sopenharmony_ci **************************************************/ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int ssb_gpio_chipco_get_value(struct gpio_chip *chip, unsigned int gpio) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct ssb_bus *bus = gpiochip_get_data(chip); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci return !!ssb_chipco_gpio_in(&bus->chipco, 1 << gpio); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic void ssb_gpio_chipco_set_value(struct gpio_chip *chip, unsigned int gpio, 498c2ecf20Sopenharmony_ci int value) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct ssb_bus *bus = gpiochip_get_data(chip); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci ssb_chipco_gpio_out(&bus->chipco, 1 << gpio, value ? 1 << gpio : 0); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int ssb_gpio_chipco_direction_input(struct gpio_chip *chip, 578c2ecf20Sopenharmony_ci unsigned int gpio) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci struct ssb_bus *bus = gpiochip_get_data(chip); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci ssb_chipco_gpio_outen(&bus->chipco, 1 << gpio, 0); 628c2ecf20Sopenharmony_ci return 0; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int ssb_gpio_chipco_direction_output(struct gpio_chip *chip, 668c2ecf20Sopenharmony_ci unsigned int gpio, int value) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct ssb_bus *bus = gpiochip_get_data(chip); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci ssb_chipco_gpio_outen(&bus->chipco, 1 << gpio, 1 << gpio); 718c2ecf20Sopenharmony_ci ssb_chipco_gpio_out(&bus->chipco, 1 << gpio, value ? 1 << gpio : 0); 728c2ecf20Sopenharmony_ci return 0; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int ssb_gpio_chipco_request(struct gpio_chip *chip, unsigned int gpio) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct ssb_bus *bus = gpiochip_get_data(chip); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci ssb_chipco_gpio_control(&bus->chipco, 1 << gpio, 0); 808c2ecf20Sopenharmony_ci /* clear pulldown */ 818c2ecf20Sopenharmony_ci ssb_chipco_gpio_pulldown(&bus->chipco, 1 << gpio, 0); 828c2ecf20Sopenharmony_ci /* Set pullup */ 838c2ecf20Sopenharmony_ci ssb_chipco_gpio_pullup(&bus->chipco, 1 << gpio, 1 << gpio); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return 0; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic void ssb_gpio_chipco_free(struct gpio_chip *chip, unsigned int gpio) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct ssb_bus *bus = gpiochip_get_data(chip); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* clear pullup */ 938c2ecf20Sopenharmony_ci ssb_chipco_gpio_pullup(&bus->chipco, 1 << gpio, 0); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SSB_EMBEDDED) 978c2ecf20Sopenharmony_cistatic void ssb_gpio_irq_chipco_mask(struct irq_data *d) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct ssb_bus *bus = irq_data_get_irq_chip_data(d); 1008c2ecf20Sopenharmony_ci int gpio = irqd_to_hwirq(d); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci ssb_chipco_gpio_intmask(&bus->chipco, BIT(gpio), 0); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic void ssb_gpio_irq_chipco_unmask(struct irq_data *d) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct ssb_bus *bus = irq_data_get_irq_chip_data(d); 1088c2ecf20Sopenharmony_ci int gpio = irqd_to_hwirq(d); 1098c2ecf20Sopenharmony_ci u32 val = ssb_chipco_gpio_in(&bus->chipco, BIT(gpio)); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci ssb_chipco_gpio_polarity(&bus->chipco, BIT(gpio), val); 1128c2ecf20Sopenharmony_ci ssb_chipco_gpio_intmask(&bus->chipco, BIT(gpio), BIT(gpio)); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic struct irq_chip ssb_gpio_irq_chipco_chip = { 1168c2ecf20Sopenharmony_ci .name = "SSB-GPIO-CC", 1178c2ecf20Sopenharmony_ci .irq_mask = ssb_gpio_irq_chipco_mask, 1188c2ecf20Sopenharmony_ci .irq_unmask = ssb_gpio_irq_chipco_unmask, 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic irqreturn_t ssb_gpio_irq_chipco_handler(int irq, void *dev_id) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct ssb_bus *bus = dev_id; 1248c2ecf20Sopenharmony_ci struct ssb_chipcommon *chipco = &bus->chipco; 1258c2ecf20Sopenharmony_ci u32 val = chipco_read32(chipco, SSB_CHIPCO_GPIOIN); 1268c2ecf20Sopenharmony_ci u32 mask = chipco_read32(chipco, SSB_CHIPCO_GPIOIRQ); 1278c2ecf20Sopenharmony_ci u32 pol = chipco_read32(chipco, SSB_CHIPCO_GPIOPOL); 1288c2ecf20Sopenharmony_ci unsigned long irqs = (val ^ pol) & mask; 1298c2ecf20Sopenharmony_ci int gpio; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (!irqs) 1328c2ecf20Sopenharmony_ci return IRQ_NONE; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci for_each_set_bit(gpio, &irqs, bus->gpio.ngpio) 1358c2ecf20Sopenharmony_ci generic_handle_irq(ssb_gpio_to_irq(&bus->gpio, gpio)); 1368c2ecf20Sopenharmony_ci ssb_chipco_gpio_polarity(chipco, irqs, val & irqs); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int ssb_gpio_irq_chipco_domain_init(struct ssb_bus *bus) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct ssb_chipcommon *chipco = &bus->chipco; 1448c2ecf20Sopenharmony_ci struct gpio_chip *chip = &bus->gpio; 1458c2ecf20Sopenharmony_ci int gpio, hwirq, err; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (bus->bustype != SSB_BUSTYPE_SSB) 1488c2ecf20Sopenharmony_ci return 0; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci bus->irq_domain = irq_domain_add_linear(NULL, chip->ngpio, 1518c2ecf20Sopenharmony_ci &irq_domain_simple_ops, chipco); 1528c2ecf20Sopenharmony_ci if (!bus->irq_domain) { 1538c2ecf20Sopenharmony_ci err = -ENODEV; 1548c2ecf20Sopenharmony_ci goto err_irq_domain; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci for (gpio = 0; gpio < chip->ngpio; gpio++) { 1578c2ecf20Sopenharmony_ci int irq = irq_create_mapping(bus->irq_domain, gpio); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci irq_set_chip_data(irq, bus); 1608c2ecf20Sopenharmony_ci irq_set_chip_and_handler(irq, &ssb_gpio_irq_chipco_chip, 1618c2ecf20Sopenharmony_ci handle_simple_irq); 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci hwirq = ssb_mips_irq(bus->chipco.dev) + 2; 1658c2ecf20Sopenharmony_ci err = request_irq(hwirq, ssb_gpio_irq_chipco_handler, IRQF_SHARED, 1668c2ecf20Sopenharmony_ci "gpio", bus); 1678c2ecf20Sopenharmony_ci if (err) 1688c2ecf20Sopenharmony_ci goto err_req_irq; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci ssb_chipco_gpio_intmask(&bus->chipco, ~0, 0); 1718c2ecf20Sopenharmony_ci chipco_set32(chipco, SSB_CHIPCO_IRQMASK, SSB_CHIPCO_IRQ_GPIO); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return 0; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cierr_req_irq: 1768c2ecf20Sopenharmony_ci for (gpio = 0; gpio < chip->ngpio; gpio++) { 1778c2ecf20Sopenharmony_ci int irq = irq_find_mapping(bus->irq_domain, gpio); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci irq_dispose_mapping(irq); 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci irq_domain_remove(bus->irq_domain); 1828c2ecf20Sopenharmony_cierr_irq_domain: 1838c2ecf20Sopenharmony_ci return err; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic void ssb_gpio_irq_chipco_domain_exit(struct ssb_bus *bus) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct ssb_chipcommon *chipco = &bus->chipco; 1898c2ecf20Sopenharmony_ci struct gpio_chip *chip = &bus->gpio; 1908c2ecf20Sopenharmony_ci int gpio; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (bus->bustype != SSB_BUSTYPE_SSB) 1938c2ecf20Sopenharmony_ci return; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci chipco_mask32(chipco, SSB_CHIPCO_IRQMASK, ~SSB_CHIPCO_IRQ_GPIO); 1968c2ecf20Sopenharmony_ci free_irq(ssb_mips_irq(bus->chipco.dev) + 2, chipco); 1978c2ecf20Sopenharmony_ci for (gpio = 0; gpio < chip->ngpio; gpio++) { 1988c2ecf20Sopenharmony_ci int irq = irq_find_mapping(bus->irq_domain, gpio); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci irq_dispose_mapping(irq); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci irq_domain_remove(bus->irq_domain); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci#else 2058c2ecf20Sopenharmony_cistatic int ssb_gpio_irq_chipco_domain_init(struct ssb_bus *bus) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci return 0; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic void ssb_gpio_irq_chipco_domain_exit(struct ssb_bus *bus) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci#endif 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int ssb_gpio_chipco_init(struct ssb_bus *bus) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct gpio_chip *chip = &bus->gpio; 2188c2ecf20Sopenharmony_ci int err; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci chip->label = "ssb_chipco_gpio"; 2218c2ecf20Sopenharmony_ci chip->owner = THIS_MODULE; 2228c2ecf20Sopenharmony_ci chip->request = ssb_gpio_chipco_request; 2238c2ecf20Sopenharmony_ci chip->free = ssb_gpio_chipco_free; 2248c2ecf20Sopenharmony_ci chip->get = ssb_gpio_chipco_get_value; 2258c2ecf20Sopenharmony_ci chip->set = ssb_gpio_chipco_set_value; 2268c2ecf20Sopenharmony_ci chip->direction_input = ssb_gpio_chipco_direction_input; 2278c2ecf20Sopenharmony_ci chip->direction_output = ssb_gpio_chipco_direction_output; 2288c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SSB_EMBEDDED) 2298c2ecf20Sopenharmony_ci chip->to_irq = ssb_gpio_to_irq; 2308c2ecf20Sopenharmony_ci#endif 2318c2ecf20Sopenharmony_ci chip->ngpio = 16; 2328c2ecf20Sopenharmony_ci /* There is just one SoC in one device and its GPIO addresses should be 2338c2ecf20Sopenharmony_ci * deterministic to address them more easily. The other buses could get 2348c2ecf20Sopenharmony_ci * a random base number. */ 2358c2ecf20Sopenharmony_ci if (bus->bustype == SSB_BUSTYPE_SSB) 2368c2ecf20Sopenharmony_ci chip->base = 0; 2378c2ecf20Sopenharmony_ci else 2388c2ecf20Sopenharmony_ci chip->base = -1; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci err = ssb_gpio_irq_chipco_domain_init(bus); 2418c2ecf20Sopenharmony_ci if (err) 2428c2ecf20Sopenharmony_ci return err; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci err = gpiochip_add_data(chip, bus); 2458c2ecf20Sopenharmony_ci if (err) { 2468c2ecf20Sopenharmony_ci ssb_gpio_irq_chipco_domain_exit(bus); 2478c2ecf20Sopenharmony_ci return err; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci return 0; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci/************************************************** 2548c2ecf20Sopenharmony_ci * EXTIF 2558c2ecf20Sopenharmony_ci **************************************************/ 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_DRIVER_EXTIF 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int ssb_gpio_extif_get_value(struct gpio_chip *chip, unsigned int gpio) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct ssb_bus *bus = gpiochip_get_data(chip); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return !!ssb_extif_gpio_in(&bus->extif, 1 << gpio); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic void ssb_gpio_extif_set_value(struct gpio_chip *chip, unsigned int gpio, 2678c2ecf20Sopenharmony_ci int value) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct ssb_bus *bus = gpiochip_get_data(chip); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci ssb_extif_gpio_out(&bus->extif, 1 << gpio, value ? 1 << gpio : 0); 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic int ssb_gpio_extif_direction_input(struct gpio_chip *chip, 2758c2ecf20Sopenharmony_ci unsigned int gpio) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct ssb_bus *bus = gpiochip_get_data(chip); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci ssb_extif_gpio_outen(&bus->extif, 1 << gpio, 0); 2808c2ecf20Sopenharmony_ci return 0; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int ssb_gpio_extif_direction_output(struct gpio_chip *chip, 2848c2ecf20Sopenharmony_ci unsigned int gpio, int value) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct ssb_bus *bus = gpiochip_get_data(chip); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ssb_extif_gpio_outen(&bus->extif, 1 << gpio, 1 << gpio); 2898c2ecf20Sopenharmony_ci ssb_extif_gpio_out(&bus->extif, 1 << gpio, value ? 1 << gpio : 0); 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SSB_EMBEDDED) 2948c2ecf20Sopenharmony_cistatic void ssb_gpio_irq_extif_mask(struct irq_data *d) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct ssb_bus *bus = irq_data_get_irq_chip_data(d); 2978c2ecf20Sopenharmony_ci int gpio = irqd_to_hwirq(d); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci ssb_extif_gpio_intmask(&bus->extif, BIT(gpio), 0); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic void ssb_gpio_irq_extif_unmask(struct irq_data *d) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct ssb_bus *bus = irq_data_get_irq_chip_data(d); 3058c2ecf20Sopenharmony_ci int gpio = irqd_to_hwirq(d); 3068c2ecf20Sopenharmony_ci u32 val = ssb_extif_gpio_in(&bus->extif, BIT(gpio)); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci ssb_extif_gpio_polarity(&bus->extif, BIT(gpio), val); 3098c2ecf20Sopenharmony_ci ssb_extif_gpio_intmask(&bus->extif, BIT(gpio), BIT(gpio)); 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic struct irq_chip ssb_gpio_irq_extif_chip = { 3138c2ecf20Sopenharmony_ci .name = "SSB-GPIO-EXTIF", 3148c2ecf20Sopenharmony_ci .irq_mask = ssb_gpio_irq_extif_mask, 3158c2ecf20Sopenharmony_ci .irq_unmask = ssb_gpio_irq_extif_unmask, 3168c2ecf20Sopenharmony_ci}; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic irqreturn_t ssb_gpio_irq_extif_handler(int irq, void *dev_id) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct ssb_bus *bus = dev_id; 3218c2ecf20Sopenharmony_ci struct ssb_extif *extif = &bus->extif; 3228c2ecf20Sopenharmony_ci u32 val = ssb_read32(extif->dev, SSB_EXTIF_GPIO_IN); 3238c2ecf20Sopenharmony_ci u32 mask = ssb_read32(extif->dev, SSB_EXTIF_GPIO_INTMASK); 3248c2ecf20Sopenharmony_ci u32 pol = ssb_read32(extif->dev, SSB_EXTIF_GPIO_INTPOL); 3258c2ecf20Sopenharmony_ci unsigned long irqs = (val ^ pol) & mask; 3268c2ecf20Sopenharmony_ci int gpio; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (!irqs) 3298c2ecf20Sopenharmony_ci return IRQ_NONE; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci for_each_set_bit(gpio, &irqs, bus->gpio.ngpio) 3328c2ecf20Sopenharmony_ci generic_handle_irq(ssb_gpio_to_irq(&bus->gpio, gpio)); 3338c2ecf20Sopenharmony_ci ssb_extif_gpio_polarity(extif, irqs, val & irqs); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int ssb_gpio_irq_extif_domain_init(struct ssb_bus *bus) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct ssb_extif *extif = &bus->extif; 3418c2ecf20Sopenharmony_ci struct gpio_chip *chip = &bus->gpio; 3428c2ecf20Sopenharmony_ci int gpio, hwirq, err; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (bus->bustype != SSB_BUSTYPE_SSB) 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci bus->irq_domain = irq_domain_add_linear(NULL, chip->ngpio, 3488c2ecf20Sopenharmony_ci &irq_domain_simple_ops, extif); 3498c2ecf20Sopenharmony_ci if (!bus->irq_domain) { 3508c2ecf20Sopenharmony_ci err = -ENODEV; 3518c2ecf20Sopenharmony_ci goto err_irq_domain; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci for (gpio = 0; gpio < chip->ngpio; gpio++) { 3548c2ecf20Sopenharmony_ci int irq = irq_create_mapping(bus->irq_domain, gpio); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci irq_set_chip_data(irq, bus); 3578c2ecf20Sopenharmony_ci irq_set_chip_and_handler(irq, &ssb_gpio_irq_extif_chip, 3588c2ecf20Sopenharmony_ci handle_simple_irq); 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci hwirq = ssb_mips_irq(bus->extif.dev) + 2; 3628c2ecf20Sopenharmony_ci err = request_irq(hwirq, ssb_gpio_irq_extif_handler, IRQF_SHARED, 3638c2ecf20Sopenharmony_ci "gpio", bus); 3648c2ecf20Sopenharmony_ci if (err) 3658c2ecf20Sopenharmony_ci goto err_req_irq; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci ssb_extif_gpio_intmask(&bus->extif, ~0, 0); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cierr_req_irq: 3728c2ecf20Sopenharmony_ci for (gpio = 0; gpio < chip->ngpio; gpio++) { 3738c2ecf20Sopenharmony_ci int irq = irq_find_mapping(bus->irq_domain, gpio); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci irq_dispose_mapping(irq); 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci irq_domain_remove(bus->irq_domain); 3788c2ecf20Sopenharmony_cierr_irq_domain: 3798c2ecf20Sopenharmony_ci return err; 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic void ssb_gpio_irq_extif_domain_exit(struct ssb_bus *bus) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci struct ssb_extif *extif = &bus->extif; 3858c2ecf20Sopenharmony_ci struct gpio_chip *chip = &bus->gpio; 3868c2ecf20Sopenharmony_ci int gpio; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (bus->bustype != SSB_BUSTYPE_SSB) 3898c2ecf20Sopenharmony_ci return; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci free_irq(ssb_mips_irq(bus->extif.dev) + 2, extif); 3928c2ecf20Sopenharmony_ci for (gpio = 0; gpio < chip->ngpio; gpio++) { 3938c2ecf20Sopenharmony_ci int irq = irq_find_mapping(bus->irq_domain, gpio); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci irq_dispose_mapping(irq); 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci irq_domain_remove(bus->irq_domain); 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci#else 4008c2ecf20Sopenharmony_cistatic int ssb_gpio_irq_extif_domain_init(struct ssb_bus *bus) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci return 0; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic void ssb_gpio_irq_extif_domain_exit(struct ssb_bus *bus) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci#endif 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic int ssb_gpio_extif_init(struct ssb_bus *bus) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct gpio_chip *chip = &bus->gpio; 4138c2ecf20Sopenharmony_ci int err; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci chip->label = "ssb_extif_gpio"; 4168c2ecf20Sopenharmony_ci chip->owner = THIS_MODULE; 4178c2ecf20Sopenharmony_ci chip->get = ssb_gpio_extif_get_value; 4188c2ecf20Sopenharmony_ci chip->set = ssb_gpio_extif_set_value; 4198c2ecf20Sopenharmony_ci chip->direction_input = ssb_gpio_extif_direction_input; 4208c2ecf20Sopenharmony_ci chip->direction_output = ssb_gpio_extif_direction_output; 4218c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SSB_EMBEDDED) 4228c2ecf20Sopenharmony_ci chip->to_irq = ssb_gpio_to_irq; 4238c2ecf20Sopenharmony_ci#endif 4248c2ecf20Sopenharmony_ci chip->ngpio = 5; 4258c2ecf20Sopenharmony_ci /* There is just one SoC in one device and its GPIO addresses should be 4268c2ecf20Sopenharmony_ci * deterministic to address them more easily. The other buses could get 4278c2ecf20Sopenharmony_ci * a random base number. */ 4288c2ecf20Sopenharmony_ci if (bus->bustype == SSB_BUSTYPE_SSB) 4298c2ecf20Sopenharmony_ci chip->base = 0; 4308c2ecf20Sopenharmony_ci else 4318c2ecf20Sopenharmony_ci chip->base = -1; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci err = ssb_gpio_irq_extif_domain_init(bus); 4348c2ecf20Sopenharmony_ci if (err) 4358c2ecf20Sopenharmony_ci return err; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci err = gpiochip_add_data(chip, bus); 4388c2ecf20Sopenharmony_ci if (err) { 4398c2ecf20Sopenharmony_ci ssb_gpio_irq_extif_domain_exit(bus); 4408c2ecf20Sopenharmony_ci return err; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci return 0; 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci#else 4478c2ecf20Sopenharmony_cistatic int ssb_gpio_extif_init(struct ssb_bus *bus) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci return -ENOTSUPP; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci#endif 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci/************************************************** 4548c2ecf20Sopenharmony_ci * Init 4558c2ecf20Sopenharmony_ci **************************************************/ 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ciint ssb_gpio_init(struct ssb_bus *bus) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci if (ssb_chipco_available(&bus->chipco)) 4608c2ecf20Sopenharmony_ci return ssb_gpio_chipco_init(bus); 4618c2ecf20Sopenharmony_ci else if (ssb_extif_available(&bus->extif)) 4628c2ecf20Sopenharmony_ci return ssb_gpio_extif_init(bus); 4638c2ecf20Sopenharmony_ci return -1; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ciint ssb_gpio_unregister(struct ssb_bus *bus) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci if (ssb_chipco_available(&bus->chipco) || 4698c2ecf20Sopenharmony_ci ssb_extif_available(&bus->extif)) { 4708c2ecf20Sopenharmony_ci gpiochip_remove(&bus->gpio); 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci return -1; 4748c2ecf20Sopenharmony_ci} 475