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