From 3ea046564039b7f20b3974adbea9271af64a4295 Mon Sep 17 00:00:00 2001 From: Johan Jonker Date: Sat, 28 Aug 2021 14:10:07 +0200 Subject: dt-bindings: gpio: add gpio-line-names to rockchip,gpio-bank.yaml Some people and companies may want to add more description to there gpio pins. Add a gpio-line-names property to the rockchip,gpio-bank.yaml file to reduce the notifications from the existing mainline DT. Signed-off-by: Johan Jonker Acked-by: Rob Herring Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml b/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml index 0d62c28fb58d..d4e42c2b995b 100644 --- a/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml +++ b/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml @@ -29,6 +29,8 @@ properties: gpio-controller: true + gpio-line-names: true + "#gpio-cells": const: 2 -- cgit v1.2.3 From e1db0f55976f796d1e75aa48c06658488d407709 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Thu, 16 Sep 2021 20:19:36 +0900 Subject: gpio: uniphier: Use helper function to get IRQ hardware number Use helper function to get IRQ hardware number instead of direct access. No functional changes intended. Signed-off-by: Kunihiko Hayashi Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-uniphier.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c index f99f3c10bed0..954f2b537a75 100644 --- a/drivers/gpio/gpio-uniphier.c +++ b/drivers/gpio/gpio-uniphier.c @@ -180,7 +180,7 @@ static int uniphier_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) static void uniphier_gpio_irq_mask(struct irq_data *data) { struct uniphier_gpio_priv *priv = data->chip_data; - u32 mask = BIT(data->hwirq); + u32 mask = BIT(irqd_to_hwirq(data)); uniphier_gpio_reg_update(priv, UNIPHIER_GPIO_IRQ_EN, mask, 0); @@ -190,7 +190,7 @@ static void uniphier_gpio_irq_mask(struct irq_data *data) static void uniphier_gpio_irq_unmask(struct irq_data *data) { struct uniphier_gpio_priv *priv = data->chip_data; - u32 mask = BIT(data->hwirq); + u32 mask = BIT(irqd_to_hwirq(data)); uniphier_gpio_reg_update(priv, UNIPHIER_GPIO_IRQ_EN, mask, mask); @@ -200,7 +200,7 @@ static void uniphier_gpio_irq_unmask(struct irq_data *data) static int uniphier_gpio_irq_set_type(struct irq_data *data, unsigned int type) { struct uniphier_gpio_priv *priv = data->chip_data; - u32 mask = BIT(data->hwirq); + u32 mask = BIT(irqd_to_hwirq(data)); u32 val = 0; if (type == IRQ_TYPE_EDGE_BOTH) { @@ -297,7 +297,8 @@ static int uniphier_gpio_irq_domain_activate(struct irq_domain *domain, struct uniphier_gpio_priv *priv = domain->host_data; struct gpio_chip *chip = &priv->chip; - return gpiochip_lock_as_irq(chip, data->hwirq + UNIPHIER_GPIO_IRQ_OFFSET); + return gpiochip_lock_as_irq(chip, + irqd_to_hwirq(data) + UNIPHIER_GPIO_IRQ_OFFSET); } static void uniphier_gpio_irq_domain_deactivate(struct irq_domain *domain, @@ -306,7 +307,8 @@ static void uniphier_gpio_irq_domain_deactivate(struct irq_domain *domain, struct uniphier_gpio_priv *priv = domain->host_data; struct gpio_chip *chip = &priv->chip; - gpiochip_unlock_as_irq(chip, data->hwirq + UNIPHIER_GPIO_IRQ_OFFSET); + gpiochip_unlock_as_irq(chip, + irqd_to_hwirq(data) + UNIPHIER_GPIO_IRQ_OFFSET); } static const struct irq_domain_ops uniphier_gpio_irq_domain_ops = { -- cgit v1.2.3 From dcfd2a2975f3eaa29c5a96d150a9364f51a5302c Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Thu, 16 Sep 2021 20:19:37 +0900 Subject: gpio: uniphier: Use helper functions to get private data from IRQ data Use helper functions to get private data from IRQ data instead of direct access. No functional changes intended. Signed-off-by: Kunihiko Hayashi Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-uniphier.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c index 954f2b537a75..9c677a0e7025 100644 --- a/drivers/gpio/gpio-uniphier.c +++ b/drivers/gpio/gpio-uniphier.c @@ -179,7 +179,7 @@ static int uniphier_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) static void uniphier_gpio_irq_mask(struct irq_data *data) { - struct uniphier_gpio_priv *priv = data->chip_data; + struct uniphier_gpio_priv *priv = irq_data_get_irq_chip_data(data); u32 mask = BIT(irqd_to_hwirq(data)); uniphier_gpio_reg_update(priv, UNIPHIER_GPIO_IRQ_EN, mask, 0); @@ -189,7 +189,7 @@ static void uniphier_gpio_irq_mask(struct irq_data *data) static void uniphier_gpio_irq_unmask(struct irq_data *data) { - struct uniphier_gpio_priv *priv = data->chip_data; + struct uniphier_gpio_priv *priv = irq_data_get_irq_chip_data(data); u32 mask = BIT(irqd_to_hwirq(data)); uniphier_gpio_reg_update(priv, UNIPHIER_GPIO_IRQ_EN, mask, mask); @@ -199,7 +199,7 @@ static void uniphier_gpio_irq_unmask(struct irq_data *data) static int uniphier_gpio_irq_set_type(struct irq_data *data, unsigned int type) { - struct uniphier_gpio_priv *priv = data->chip_data; + struct uniphier_gpio_priv *priv = irq_data_get_irq_chip_data(data); u32 mask = BIT(irqd_to_hwirq(data)); u32 val = 0; -- cgit v1.2.3 From ca038748068f454d20ad1bb120cbe36599f81db6 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 17 Sep 2021 12:54:11 +0200 Subject: gpio: tegra186: Force one interrupt per bank Newer chips support up to 8 interrupts per bank, which can be useful to balance the load and decrease latency. However, it also required a very complicated interrupt routing to be set up. To keep things simple for now, ensure that a single interrupt per bank is enforced, even if all possible interrupts are described in device tree. Signed-off-by: Thierry Reding Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tegra186.c | 68 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index c99858f40a27..1bc4152e0275 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -81,6 +81,8 @@ struct tegra_gpio { unsigned int *irq; const struct tegra_gpio_soc *soc; + unsigned int num_irqs_per_bank; + unsigned int num_banks; void __iomem *secure; void __iomem *base; @@ -594,6 +596,28 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) } } +static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) +{ + struct device *dev = gpio->gpio.parent; + + if (gpio->num_irq > gpio->num_banks) { + if (gpio->num_irq % gpio->num_banks != 0) + goto error; + } + + if (gpio->num_irq < gpio->num_banks) + goto error; + + gpio->num_irqs_per_bank = gpio->num_irq / gpio->num_banks; + + return 0; + +error: + dev_err(dev, "invalid number of interrupts (%u) for %u banks\n", + gpio->num_irq, gpio->num_banks); + return -EINVAL; +} + static int tegra186_gpio_probe(struct platform_device *pdev) { unsigned int i, j, offset; @@ -608,7 +632,17 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -ENOMEM; gpio->soc = device_get_match_data(&pdev->dev); + gpio->gpio.label = gpio->soc->name; + gpio->gpio.parent = &pdev->dev; + + /* count the number of banks in the controller */ + for (i = 0; i < gpio->soc->num_ports; i++) + if (gpio->soc->ports[i].bank > gpio->num_banks) + gpio->num_banks = gpio->soc->ports[i].bank; + + gpio->num_banks++; + /* get register apertures */ gpio->secure = devm_platform_ioremap_resource_byname(pdev, "security"); if (IS_ERR(gpio->secure)) { gpio->secure = devm_platform_ioremap_resource(pdev, 0); @@ -629,6 +663,10 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->num_irq = err; + err = tegra186_gpio_irqs_per_bank(gpio); + if (err < 0) + return err; + gpio->irq = devm_kcalloc(&pdev->dev, gpio->num_irq, sizeof(*gpio->irq), GFP_KERNEL); if (!gpio->irq) @@ -642,9 +680,6 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->irq[i] = err; } - gpio->gpio.label = gpio->soc->name; - gpio->gpio.parent = &pdev->dev; - gpio->gpio.request = gpiochip_generic_request; gpio->gpio.free = gpiochip_generic_free; gpio->gpio.get_direction = tegra186_gpio_get_direction; @@ -708,7 +743,30 @@ static int tegra186_gpio_probe(struct platform_device *pdev) irq->parent_handler = tegra186_gpio_irq; irq->parent_handler_data = gpio; irq->num_parents = gpio->num_irq; - irq->parents = gpio->irq; + + /* + * To simplify things, use a single interrupt per bank for now. Some + * chips support up to 8 interrupts per bank, which can be useful to + * distribute the load and decrease the processing latency for GPIOs + * but it also requires a more complicated interrupt routing than we + * currently program. + */ + if (gpio->num_irqs_per_bank > 1) { + irq->parents = devm_kcalloc(&pdev->dev, gpio->num_banks, + sizeof(*irq->parents), GFP_KERNEL); + if (!irq->parents) + return -ENOMEM; + + for (i = 0; i < gpio->num_banks; i++) + irq->parents[i] = gpio->irq[i * gpio->num_irqs_per_bank]; + + irq->num_parents = gpio->num_banks; + } else { + irq->num_parents = gpio->num_irq; + irq->parents = gpio->irq; + } + + tegra186_gpio_init_route_mapping(gpio); np = of_find_matching_node(NULL, tegra186_pmc_of_match); if (np) { @@ -719,8 +777,6 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -EPROBE_DEFER; } - tegra186_gpio_init_route_mapping(gpio); - irq->map = devm_kcalloc(&pdev->dev, gpio->gpio.ngpio, sizeof(*irq->map), GFP_KERNEL); if (!irq->map) -- cgit v1.2.3 From 2103868047456e5f3e431ebb253d87e1fb806c76 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 17 Sep 2021 12:54:12 +0200 Subject: gpio: tegra186: Support multiple interrupts per bank Tegra194 and later support more than a single interrupt per bank. This is primarily useful for virtualization but can also be helpful for more fine-grained CPU affinity control. To keep things simple for now, route all pins to the first interrupt. For backwards-compatibility, support old device trees that specify only one interrupt per bank by counting the interrupts at probe time. Signed-off-by: Thierry Reding Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tegra186.c | 48 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index 1bc4152e0275..c026e7141e4e 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -69,6 +69,8 @@ struct tegra_gpio_soc { const char *name; unsigned int instance; + unsigned int num_irqs_per_bank; + const struct tegra186_pin_range *pin_ranges; unsigned int num_pin_ranges; const char *pinmux; @@ -452,7 +454,7 @@ static void tegra186_gpio_irq(struct irq_desc *desc) struct irq_domain *domain = gpio->gpio.irq.domain; struct irq_chip *chip = irq_desc_get_chip(desc); unsigned int parent = irq_desc_get_irq(desc); - unsigned int i, offset = 0; + unsigned int i, j, offset = 0; chained_irq_enter(chip, desc); @@ -465,7 +467,12 @@ static void tegra186_gpio_irq(struct irq_desc *desc) base = gpio->base + port->bank * 0x1000 + port->port * 0x200; /* skip ports that are not associated with this bank */ - if (parent != gpio->irq[port->bank]) + for (j = 0; j < gpio->num_irqs_per_bank; j++) { + if (parent == gpio->irq[port->bank * gpio->num_irqs_per_bank + j]) + break; + } + + if (j == gpio->num_irqs_per_bank) goto skip; value = readl(base + TEGRA186_GPIO_INTERRUPT_STATUS(1)); @@ -567,6 +574,7 @@ static const struct of_device_id tegra186_pmc_of_match[] = { static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) { + struct device *dev = gpio->gpio.parent; unsigned int i, j; u32 value; @@ -585,12 +593,30 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) */ if ((value & TEGRA186_GPIO_CTL_SCR_SEC_REN) == 0 && (value & TEGRA186_GPIO_CTL_SCR_SEC_WEN) == 0) { - for (j = 0; j < 8; j++) { + /* + * On Tegra194 and later, each pin can be routed to one or more + * interrupts. + */ + for (j = 0; j < gpio->num_irqs_per_bank; j++) { + dev_dbg(dev, "programming default interrupt routing for port %s\n", + port->name); + offset = TEGRA186_GPIO_INT_ROUTE_MAPPING(p, j); - value = readl(base + offset); - value = BIT(port->pins) - 1; - writel(value, base + offset); + /* + * By default we only want to route GPIO pins to IRQ 0. This works + * only under the assumption that we're running as the host kernel + * and hence all GPIO pins are owned by Linux. + * + * For cases where Linux is the guest OS, the hypervisor will have + * to configure the interrupt routing and pass only the valid + * interrupts via device tree. + */ + if (j == 0) { + value = readl(base + offset); + value = BIT(port->pins) - 1; + writel(value, base + offset); + } } } } @@ -610,6 +636,9 @@ static unsigned int tegra186_gpio_irqs_per_bank(struct tegra_gpio *gpio) gpio->num_irqs_per_bank = gpio->num_irq / gpio->num_banks; + if (gpio->num_irqs_per_bank > gpio->soc->num_irqs_per_bank) + goto error; + return 0; error: @@ -766,7 +795,8 @@ static int tegra186_gpio_probe(struct platform_device *pdev) irq->parents = gpio->irq; } - tegra186_gpio_init_route_mapping(gpio); + if (gpio->soc->num_irqs_per_bank > 1) + tegra186_gpio_init_route_mapping(gpio); np = of_find_matching_node(NULL, tegra186_pmc_of_match); if (np) { @@ -833,6 +863,7 @@ static const struct tegra_gpio_soc tegra186_main_soc = { .ports = tegra186_main_ports, .name = "tegra186-gpio", .instance = 0, + .num_irqs_per_bank = 1, }; #define TEGRA186_AON_GPIO_PORT(_name, _bank, _port, _pins) \ @@ -859,6 +890,7 @@ static const struct tegra_gpio_soc tegra186_aon_soc = { .ports = tegra186_aon_ports, .name = "tegra186-gpio-aon", .instance = 1, + .num_irqs_per_bank = 1, }; #define TEGRA194_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ @@ -910,6 +942,7 @@ static const struct tegra_gpio_soc tegra194_main_soc = { .ports = tegra194_main_ports, .name = "tegra194-gpio", .instance = 0, + .num_irqs_per_bank = 8, .num_pin_ranges = ARRAY_SIZE(tegra194_main_pin_ranges), .pin_ranges = tegra194_main_pin_ranges, .pinmux = "nvidia,tegra194-pinmux", @@ -936,6 +969,7 @@ static const struct tegra_gpio_soc tegra194_aon_soc = { .ports = tegra194_aon_ports, .name = "tegra194-gpio-aon", .instance = 1, + .num_irqs_per_bank = 8, }; static const struct of_device_id tegra186_gpio_of_match[] = { -- cgit v1.2.3 From e24b9fc109280a2420aabe352d38fad722f4ea37 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 20 Sep 2021 11:05:15 +0200 Subject: gpio: xilinx: simplify getting .driver_data We should get 'driver_data' from 'struct device' directly. Going via platform_device is an unneeded step back and forth. Signed-off-by: Wolfram Sang Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-xilinx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index a1b66338d077..b6d3a57e27ed 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -371,8 +371,7 @@ static int __maybe_unused xgpio_resume(struct device *dev) static int __maybe_unused xgpio_runtime_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct xgpio_instance *gpio = platform_get_drvdata(pdev); + struct xgpio_instance *gpio = dev_get_drvdata(dev); clk_disable(gpio->clk); @@ -381,8 +380,7 @@ static int __maybe_unused xgpio_runtime_suspend(struct device *dev) static int __maybe_unused xgpio_runtime_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct xgpio_instance *gpio = platform_get_drvdata(pdev); + struct xgpio_instance *gpio = dev_get_drvdata(dev); return clk_enable(gpio->clk); } -- cgit v1.2.3 From 3846a3607738ccbc763afe7af75b5bd25009b037 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 16 Sep 2021 18:46:40 +0200 Subject: gpio: max77620: drop unneeded MODULE_ALIAS The MODULE_DEVICE_TABLE already creates proper alias for platform driver. Having another MODULE_ALIAS causes the alias to be duplicated. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-max77620.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index 82b3a913005d..ebf9dea6546b 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -365,5 +365,4 @@ module_platform_driver(max77620_gpio_driver); MODULE_DESCRIPTION("GPIO interface for MAX77620 and MAX20024 PMIC"); MODULE_AUTHOR("Laxman Dewangan "); MODULE_AUTHOR("Chaitanya Bandi "); -MODULE_ALIAS("platform:max77620-gpio"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 03e2080defd2494bd2790d2ac1df9d7432671e85 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 16 Sep 2021 18:46:41 +0200 Subject: gpio: tps65218: drop unneeded MODULE_ALIAS The MODULE_DEVICE_TABLE already creates proper alias for platform driver. Having another MODULE_ALIAS causes the alias to be duplicated. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tps65218.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/gpio-tps65218.c b/drivers/gpio/gpio-tps65218.c index 3517debe2b0b..912382be48e1 100644 --- a/drivers/gpio/gpio-tps65218.c +++ b/drivers/gpio/gpio-tps65218.c @@ -230,4 +230,3 @@ module_platform_driver(tps65218_gpio_driver); MODULE_AUTHOR("Nicolas Saenz Julienne "); MODULE_DESCRIPTION("GPO interface for TPS65218 PMICs"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:tps65218-gpio"); -- cgit v1.2.3 From 23c64d7618a7e76e46db99444d184ef75498fb71 Mon Sep 17 00:00:00 2001 From: Piyush Mehta Date: Wed, 22 Sep 2021 19:23:17 +0530 Subject: firmware: zynqmp: Add MMIO read and write support for PS_MODE pin Add Xilinx ZynqMP firmware MMIO APIs support to set and get PS_MODE pins value and status. These APIs create an interface path between mode pin controller driver and low-level API to access GPIO pins. Signed-off-by: Piyush Mehta Acked-by: Michal Simek Acked-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/firmware/xilinx/zynqmp.c | 46 ++++++++++++++++++++++++++++++++++++ include/linux/firmware/xlnx-zynqmp.h | 14 +++++++++++ 2 files changed, 60 insertions(+) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index a3cadbaf3cba..7feba125b883 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -28,6 +28,13 @@ /* Max HashMap Order for PM API feature check (1<<7 = 128) */ #define PM_API_FEATURE_CHECK_MAX_ORDER 7 +/* CRL registers and bitfields */ +#define CRL_APB_BASE 0xFF5E0000U +/* BOOT_PIN_CTRL- Used to control the mode pins after boot */ +#define CRL_APB_BOOT_PIN_CTRL (CRL_APB_BASE + (0x250U)) +/* BOOT_PIN_CTRL_MASK- out_val[11:8], out_en[3:0] */ +#define CRL_APB_BOOTPIN_CTRL_MASK 0xF0FU + static bool feature_check_enabled; static DEFINE_HASHTABLE(pm_api_features_map, PM_API_FEATURE_CHECK_MAX_ORDER); @@ -925,6 +932,45 @@ int zynqmp_pm_pinctrl_set_config(const u32 pin, const u32 param, } EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_set_config); +/** + * zynqmp_pm_bootmode_read() - PM Config API for read bootpin status + * @ps_mode: Returned output value of ps_mode + * + * This API function is to be used for notify the power management controller + * to read bootpin status. + * + * Return: status, either success or error+reason + */ +unsigned int zynqmp_pm_bootmode_read(u32 *ps_mode) +{ + unsigned int ret; + u32 ret_payload[PAYLOAD_ARG_CNT]; + + ret = zynqmp_pm_invoke_fn(PM_MMIO_READ, CRL_APB_BOOT_PIN_CTRL, 0, + 0, 0, ret_payload); + + *ps_mode = ret_payload[1]; + + return ret; +} +EXPORT_SYMBOL_GPL(zynqmp_pm_bootmode_read); + +/** + * zynqmp_pm_bootmode_write() - PM Config API for Configure bootpin + * @ps_mode: Value to be written to the bootpin ctrl register + * + * This API function is to be used for notify the power management controller + * to configure bootpin. + * + * Return: Returns status, either success or error+reason + */ +int zynqmp_pm_bootmode_write(u32 ps_mode) +{ + return zynqmp_pm_invoke_fn(PM_MMIO_WRITE, CRL_APB_BOOT_PIN_CTRL, + CRL_APB_BOOTPIN_CTRL_MASK, ps_mode, 0, NULL); +} +EXPORT_SYMBOL_GPL(zynqmp_pm_bootmode_write); + /** * zynqmp_pm_init_finalize() - PM call to inform firmware that the caller * master has initialized its own power management diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index 56b426fe020c..3917f89b2b3c 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -72,6 +72,8 @@ enum pm_api_id { PM_SET_REQUIREMENT = 15, PM_RESET_ASSERT = 17, PM_RESET_GET_STATUS = 18, + PM_MMIO_WRITE = 19, + PM_MMIO_READ = 20, PM_PM_INIT_FINALIZE = 21, PM_FPGA_LOAD = 22, PM_FPGA_GET_STATUS = 23, @@ -390,6 +392,8 @@ int zynqmp_pm_sd_dll_reset(u32 node_id, u32 type); int zynqmp_pm_reset_assert(const enum zynqmp_pm_reset reset, const enum zynqmp_pm_reset_action assert_flag); int zynqmp_pm_reset_get_status(const enum zynqmp_pm_reset reset, u32 *status); +unsigned int zynqmp_pm_bootmode_read(u32 *ps_mode); +int zynqmp_pm_bootmode_write(u32 ps_mode); int zynqmp_pm_init_finalize(void); int zynqmp_pm_set_suspend_mode(u32 mode); int zynqmp_pm_request_node(const u32 node, const u32 capabilities, @@ -520,6 +524,16 @@ static inline int zynqmp_pm_reset_get_status(const enum zynqmp_pm_reset reset, return -ENODEV; } +static inline unsigned int zynqmp_pm_bootmode_read(u32 *ps_mode) +{ + return -ENODEV; +} + +static inline int zynqmp_pm_bootmode_write(u32 ps_mode) +{ + return -ENODEV; +} + static inline int zynqmp_pm_init_finalize(void) { return -ENODEV; -- cgit v1.2.3 From d7f4a65cdf4f671296fab1e5b331fd3f5ab776e5 Mon Sep 17 00:00:00 2001 From: Piyush Mehta Date: Wed, 22 Sep 2021 19:23:18 +0530 Subject: dt-bindings: gpio: zynqmp: Add binding documentation for modepin This patch adds DT binding document for zynqmp modepin GPIO controller. Modepin GPIO controller has four GPIO pins which can be configurable as input or output. Modepin driver is a bridge between the peripheral driver and GPIO pins. It has set and get APIs for accessing GPIO pins, based on the device-tree entry of reset-gpio property in the peripheral driver, every pin can be configured as input/output and trigger GPIO pin. For more information please refer zynqMp TRM link: Link: https://www.xilinx.com/support/documentation/user_guides/ug1085-zynq-ultrascale-trm.pdf Chapter 2: Signals, Interfaces, and Pins Table 2-2: Clock, Reset, and Configuration Pins - PS_MODE Signed-off-by: Piyush Mehta Acked-by: Michal Simek Reviewed-by: Rob Herring Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/xlnx,zynqmp-gpio-modepin.yaml | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/xlnx,zynqmp-gpio-modepin.yaml diff --git a/Documentation/devicetree/bindings/gpio/xlnx,zynqmp-gpio-modepin.yaml b/Documentation/devicetree/bindings/gpio/xlnx,zynqmp-gpio-modepin.yaml new file mode 100644 index 000000000000..31c0fc345903 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/xlnx,zynqmp-gpio-modepin.yaml @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/gpio/xlnx,zynqmp-gpio-modepin.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: ZynqMP Mode Pin GPIO controller + +description: + PS_MODE is 4-bits boot mode pins sampled on POR deassertion. Mode Pin + GPIO controller with configurable from numbers of pins (from 0 to 3 per + PS_MODE). Every pin can be configured as input/output. + +maintainers: + - Piyush Mehta + +properties: + compatible: + const: xlnx,zynqmp-gpio-modepin + + gpio-controller: true + + "#gpio-cells": + const: 2 + +required: + - compatible + - gpio-controller + - "#gpio-cells" + +additionalProperties: false + +examples: + - | + zynqmp-firmware { + gpio { + compatible = "xlnx,zynqmp-gpio-modepin"; + gpio-controller; + #gpio-cells = <2>; + }; + }; + +... -- cgit v1.2.3 From 7687a5b0ee9358c96d85db58bad2a3aebee562b7 Mon Sep 17 00:00:00 2001 From: Piyush Mehta Date: Wed, 22 Sep 2021 19:23:19 +0530 Subject: gpio: modepin: Add driver support for modepin GPIO controller This patch adds driver support for the zynqmp modepin GPIO controller. GPIO modepin driver set and get the value and status of the PS_MODE pin, based on device-tree pin configuration. These four mode pins are configurable as input/output. The mode pin has a control register, which have lower four-bits [0:3] are configurable as input/output, next four-bits can be used for reading the data as input[4:7], and next setting the output pin state output[8:11]. By default value of mode pin register is 0. Signed-off-by: Piyush Mehta Acked-by: Michal Simek Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 12 +++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-zynqmp-modepin.c | 162 +++++++++++++++++++++++++++++++++++++ 3 files changed, 175 insertions(+) create mode 100644 drivers/gpio/gpio-zynqmp-modepin.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index fae5141251e5..37a6f77c86fe 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -763,6 +763,18 @@ config GPIO_ZYNQ help Say yes here to support Xilinx Zynq GPIO controller. +config GPIO_ZYNQMP_MODEPIN + tristate "ZynqMP ps-mode pin gpio configuration driver" + depends on ZYNQMP_FIRMWARE + default ZYNQMP_FIRMWARE + help + Say yes here to support the ZynqMP ps-mode pin gpio configuration + driver. + + This ps-mode pin gpio driver is based on GPIO framework, PS_MODE + is 4-bits boot mode pins. It sets and gets the status of + the ps-mode pin. Every pin can be configured as input/output. + config GPIO_LOONGSON1 tristate "Loongson1 GPIO support" depends on MACH_LOONGSON32 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index fbcda637d5e1..71ee9fc2ff83 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -184,3 +184,4 @@ obj-$(CONFIG_GPIO_XRA1403) += gpio-xra1403.o obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o +obj-$(CONFIG_GPIO_ZYNQMP_MODEPIN) += gpio-zynqmp-modepin.o diff --git a/drivers/gpio/gpio-zynqmp-modepin.c b/drivers/gpio/gpio-zynqmp-modepin.c new file mode 100644 index 000000000000..a0d69387c153 --- /dev/null +++ b/drivers/gpio/gpio-zynqmp-modepin.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the ps-mode pin configuration. + * + * Copyright (c) 2021 Xilinx, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 4-bit boot mode pins */ +#define MODE_PINS 4 + +/** + * modepin_gpio_get_value - Get the state of the specified pin of GPIO device + * @chip: gpio_chip instance to be worked on + * @pin: gpio pin number within the device + * + * This function reads the state of the specified pin of the GPIO device. + * + * Return: 0 if the pin is low, 1 if pin is high, -EINVAL wrong pin configured + * or error value. + */ +static int modepin_gpio_get_value(struct gpio_chip *chip, unsigned int pin) +{ + u32 regval = 0; + int ret; + + ret = zynqmp_pm_bootmode_read(®val); + if (ret) + return ret; + + /* When [0:3] corresponding bit is set, then read output bit [8:11], + * if the bit is clear then read input bit [4:7] for status or value. + */ + if (regval & BIT(pin)) + return !!(regval & BIT(pin + 8)); + else + return !!(regval & BIT(pin + 4)); +} + +/** + * modepin_gpio_set_value - Modify the state of the pin with specified value + * @chip: gpio_chip instance to be worked on + * @pin: gpio pin number within the device + * @state: value used to modify the state of the specified pin + * + * This function reads the state of the specified pin of the GPIO device, mask + * with the capture state of GPIO pin, and update pin of GPIO device. + * + * Return: None. + */ +static void modepin_gpio_set_value(struct gpio_chip *chip, unsigned int pin, + int state) +{ + u32 bootpin_val = 0; + int ret; + + zynqmp_pm_bootmode_read(&bootpin_val); + + /* Configure pin as an output by set bit [0:3] */ + bootpin_val |= BIT(pin); + + if (state) + bootpin_val |= BIT(pin + 8); + else + bootpin_val &= ~BIT(pin + 8); + + /* Configure bootpin value */ + ret = zynqmp_pm_bootmode_write(bootpin_val); + if (ret) + pr_err("modepin: set value error %d for pin %d\n", ret, pin); +} + +/** + * modepin_gpio_dir_in - Set the direction of the specified GPIO pin as input + * @chip: gpio_chip instance to be worked on + * @pin: gpio pin number within the device + * + * Return: 0 always + */ +static int modepin_gpio_dir_in(struct gpio_chip *chip, unsigned int pin) +{ + return 0; +} + +/** + * modepin_gpio_dir_out - Set the direction of the specified GPIO pin as output + * @chip: gpio_chip instance to be worked on + * @pin: gpio pin number within the device + * @state: value to be written to specified pin + * + * Return: 0 always + */ +static int modepin_gpio_dir_out(struct gpio_chip *chip, unsigned int pin, + int state) +{ + return 0; +} + +/** + * modepin_gpio_probe - Initialization method for modepin_gpio + * @pdev: platform device instance + * + * Return: 0 on success, negative error otherwise. + */ +static int modepin_gpio_probe(struct platform_device *pdev) +{ + struct gpio_chip *chip; + int status; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + platform_set_drvdata(pdev, chip); + + /* configure the gpio chip */ + chip->base = -1; + chip->ngpio = MODE_PINS; + chip->owner = THIS_MODULE; + chip->parent = &pdev->dev; + chip->get = modepin_gpio_get_value; + chip->set = modepin_gpio_set_value; + chip->direction_input = modepin_gpio_dir_in; + chip->direction_output = modepin_gpio_dir_out; + chip->label = dev_name(&pdev->dev); + + /* modepin gpio registration */ + status = devm_gpiochip_add_data(&pdev->dev, chip, chip); + if (status) + return dev_err_probe(&pdev->dev, status, + "Failed to add GPIO chip\n"); + + return status; +} + +static const struct of_device_id modepin_platform_id[] = { + { .compatible = "xlnx,zynqmp-gpio-modepin", }, + { } +}; + +static struct platform_driver modepin_platform_driver = { + .driver = { + .name = "modepin-gpio", + .of_match_table = modepin_platform_id, + }, + .probe = modepin_gpio_probe, +}; + +module_platform_driver(modepin_platform_driver); + +MODULE_AUTHOR("Piyush Mehta "); +MODULE_DESCRIPTION("ZynqMP Boot PS_MODE Configuration"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From aa4858eb8264358e9c18c3ad79d6ab4fdc71e0c2 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 4 Oct 2021 14:45:45 +0200 Subject: gpio: aggregator: Wrap access to gpiochip_fwd.tmp[] The tmp[] member of the gpiochip_fwd structure is used to store both the temporary values bitmap and the desc pointers for operations on multiple GPIOs. As both are arrays with sizes unknown at compile-time, accessing them requires offset calculations, which are currently duplicated in gpio_fwd_get_multiple() and gpio_fwd_set_multiple(). Introduce (a) accessors for both arrays and (b) a macro to calculate the needed storage size. This confines the layout of the tmp[] member into a single spot, to ease maintenance. Signed-off-by: Geert Uytterhoeven Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-aggregator.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index 34e35b64dcdc..e9671d1660ef 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -247,6 +247,11 @@ struct gpiochip_fwd { unsigned long tmp[]; /* values and descs for multiple ops */ }; +#define fwd_tmp_values(fwd) &(fwd)->tmp[0] +#define fwd_tmp_descs(fwd) (void *)&(fwd)->tmp[BITS_TO_LONGS((fwd)->chip.ngpio)] + +#define fwd_tmp_size(ngpios) (BITS_TO_LONGS((ngpios)) + (ngpios)) + static int gpio_fwd_get_direction(struct gpio_chip *chip, unsigned int offset) { struct gpiochip_fwd *fwd = gpiochip_get_data(chip); @@ -279,15 +284,11 @@ static int gpio_fwd_get(struct gpio_chip *chip, unsigned int offset) static int gpio_fwd_get_multiple(struct gpiochip_fwd *fwd, unsigned long *mask, unsigned long *bits) { - struct gpio_desc **descs; - unsigned long *values; + struct gpio_desc **descs = fwd_tmp_descs(fwd); + unsigned long *values = fwd_tmp_values(fwd); unsigned int i, j = 0; int error; - /* Both values bitmap and desc pointers are stored in tmp[] */ - values = &fwd->tmp[0]; - descs = (void *)&fwd->tmp[BITS_TO_LONGS(fwd->chip.ngpio)]; - bitmap_clear(values, 0, fwd->chip.ngpio); for_each_set_bit(i, mask, fwd->chip.ngpio) descs[j++] = fwd->descs[i]; @@ -333,14 +334,10 @@ static void gpio_fwd_set(struct gpio_chip *chip, unsigned int offset, int value) static void gpio_fwd_set_multiple(struct gpiochip_fwd *fwd, unsigned long *mask, unsigned long *bits) { - struct gpio_desc **descs; - unsigned long *values; + struct gpio_desc **descs = fwd_tmp_descs(fwd); + unsigned long *values = fwd_tmp_values(fwd); unsigned int i, j = 0; - /* Both values bitmap and desc pointers are stored in tmp[] */ - values = &fwd->tmp[0]; - descs = (void *)&fwd->tmp[BITS_TO_LONGS(fwd->chip.ngpio)]; - for_each_set_bit(i, mask, fwd->chip.ngpio) { __assign_bit(j, values, test_bit(i, bits)); descs[j++] = fwd->descs[i]; @@ -398,8 +395,8 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev, unsigned int i; int error; - fwd = devm_kzalloc(dev, struct_size(fwd, tmp, - BITS_TO_LONGS(ngpios) + ngpios), GFP_KERNEL); + fwd = devm_kzalloc(dev, struct_size(fwd, tmp, fwd_tmp_size(ngpios)), + GFP_KERNEL); if (!fwd) return ERR_PTR(-ENOMEM); -- cgit v1.2.3 From 06de2cd788bfa3b51137e9bd471d68029ab68103 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 12 Oct 2021 17:39:27 +0200 Subject: gpio: max730x: Make __max730x_remove() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An spi or i2c remove callback is only called for devices that probed successfully. In this case this implies that __max730x_probe() set a non-NULL driver data. So the check ts == NULL is never true. With this check dropped, __max730x_remove() returns zero unconditionally. Make it return void instead which makes it easier to see in the callers that there is no error to handle. Also the return value of i2c and spi remove callbacks is ignored anyway. Signed-off-by: Uwe Kleine-König Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-max7300.c | 4 +++- drivers/gpio/gpio-max7301.c | 4 +++- drivers/gpio/gpio-max730x.c | 6 +----- include/linux/spi/max7301.h | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-max7300.c b/drivers/gpio/gpio-max7300.c index 19cc2ed6a3f5..b2b547dd6e84 100644 --- a/drivers/gpio/gpio-max7300.c +++ b/drivers/gpio/gpio-max7300.c @@ -50,7 +50,9 @@ static int max7300_probe(struct i2c_client *client, static int max7300_remove(struct i2c_client *client) { - return __max730x_remove(&client->dev); + __max730x_remove(&client->dev); + + return 0; } static const struct i2c_device_id max7300_id[] = { diff --git a/drivers/gpio/gpio-max7301.c b/drivers/gpio/gpio-max7301.c index 1307c243b4e9..5862d73bf325 100644 --- a/drivers/gpio/gpio-max7301.c +++ b/drivers/gpio/gpio-max7301.c @@ -66,7 +66,9 @@ static int max7301_probe(struct spi_device *spi) static int max7301_remove(struct spi_device *spi) { - return __max730x_remove(&spi->dev); + __max730x_remove(&spi->dev); + + return 0; } static const struct spi_device_id max7301_id[] = { diff --git a/drivers/gpio/gpio-max730x.c b/drivers/gpio/gpio-max730x.c index b8c1fe20f49a..bb5cf14ae4c8 100644 --- a/drivers/gpio/gpio-max730x.c +++ b/drivers/gpio/gpio-max730x.c @@ -220,18 +220,14 @@ exit_destroy: } EXPORT_SYMBOL_GPL(__max730x_probe); -int __max730x_remove(struct device *dev) +void __max730x_remove(struct device *dev) { struct max7301 *ts = dev_get_drvdata(dev); - if (ts == NULL) - return -ENODEV; - /* Power down the chip and disable IRQ output */ ts->write(dev, 0x04, 0x00); gpiochip_remove(&ts->chip); mutex_destroy(&ts->lock); - return 0; } EXPORT_SYMBOL_GPL(__max730x_remove); diff --git a/include/linux/spi/max7301.h b/include/linux/spi/max7301.h index 21449067aedb..e392c53758bc 100644 --- a/include/linux/spi/max7301.h +++ b/include/linux/spi/max7301.h @@ -31,6 +31,6 @@ struct max7301_platform_data { u32 input_pullup_active; }; -extern int __max730x_remove(struct device *dev); +extern void __max730x_remove(struct device *dev); extern int __max730x_probe(struct max7301 *ts); #endif -- cgit v1.2.3 From f4a20dfac88c06c9b529a41ff4cf9acba8f3fdff Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 12 Oct 2021 17:39:28 +0200 Subject: gpio: mc33880: Drop if with an always false condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An spi remove callback is only called for devices that probed successfully. In this case this implies that mc33880_probe() set a non-NULL driver data. So the check for mc being NULL is never true and the check can be dropped. Also the return value ofspi remove callbacks is ignored anyway. Signed-off-by: Uwe Kleine-König Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mc33880.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpio/gpio-mc33880.c b/drivers/gpio/gpio-mc33880.c index f8194f7c6186..31d2be1bebc8 100644 --- a/drivers/gpio/gpio-mc33880.c +++ b/drivers/gpio/gpio-mc33880.c @@ -139,8 +139,6 @@ static int mc33880_remove(struct spi_device *spi) struct mc33880 *mc; mc = spi_get_drvdata(spi); - if (!mc) - return -ENODEV; gpiochip_remove(&mc->chip); mutex_destroy(&mc->lock); -- cgit v1.2.3 From 2b725265cb08d6a0001bf81631ccb5728d095229 Mon Sep 17 00:00:00 2001 From: Asmaa Mnebhi Date: Fri, 15 Oct 2021 12:48:08 -0400 Subject: gpio: mlxbf2: Introduce IRQ support Introduce standard IRQ handling in the gpio-mlxbf2.c driver. Signed-off-by: Asmaa Mnebhi Acked-by: David S. Miller Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mlxbf2.c | 142 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 140 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-mlxbf2.c b/drivers/gpio/gpio-mlxbf2.c index 177d03ef4529..88ac2ade948d 100644 --- a/drivers/gpio/gpio-mlxbf2.c +++ b/drivers/gpio/gpio-mlxbf2.c @@ -1,9 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2021 NVIDIA CORPORATION & AFFILIATES + */ + #include #include #include #include +#include #include #include #include @@ -43,9 +48,14 @@ #define YU_GPIO_MODE0 0x0c #define YU_GPIO_DATASET 0x14 #define YU_GPIO_DATACLEAR 0x18 +#define YU_GPIO_CAUSE_RISE_EN 0x44 +#define YU_GPIO_CAUSE_FALL_EN 0x48 #define YU_GPIO_MODE1_CLEAR 0x50 #define YU_GPIO_MODE0_SET 0x54 #define YU_GPIO_MODE0_CLEAR 0x58 +#define YU_GPIO_CAUSE_OR_CAUSE_EVTEN0 0x80 +#define YU_GPIO_CAUSE_OR_EVTEN0 0x94 +#define YU_GPIO_CAUSE_OR_CLRCAUSE 0x98 struct mlxbf2_gpio_context_save_regs { u32 gpio_mode0; @@ -55,6 +65,7 @@ struct mlxbf2_gpio_context_save_regs { /* BlueField-2 gpio block context structure. */ struct mlxbf2_gpio_context { struct gpio_chip gc; + struct irq_chip irq_chip; /* YU GPIO blocks address */ void __iomem *gpio_io; @@ -218,15 +229,114 @@ static int mlxbf2_gpio_direction_output(struct gpio_chip *chip, return ret; } +static void mlxbf2_gpio_irq_enable(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc); + int offset = irqd_to_hwirq(irqd); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&gs->gc.bgpio_lock, flags); + val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE); + val |= BIT(offset); + writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE); + + val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0); + val |= BIT(offset); + writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0); + spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); +} + +static void mlxbf2_gpio_irq_disable(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc); + int offset = irqd_to_hwirq(irqd); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&gs->gc.bgpio_lock, flags); + val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0); + val &= ~BIT(offset); + writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0); + spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); +} + +static irqreturn_t mlxbf2_gpio_irq_handler(int irq, void *ptr) +{ + struct mlxbf2_gpio_context *gs = ptr; + struct gpio_chip *gc = &gs->gc; + unsigned long pending; + u32 level; + + pending = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_CAUSE_EVTEN0); + writel(pending, gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE); + + for_each_set_bit(level, &pending, gc->ngpio) { + int gpio_irq = irq_find_mapping(gc->irq.domain, level); + generic_handle_irq(gpio_irq); + } + + return IRQ_RETVAL(pending); +} + +static int +mlxbf2_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc); + int offset = irqd_to_hwirq(irqd); + unsigned long flags; + bool fall = false; + bool rise = false; + u32 val; + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_BOTH: + fall = true; + rise = true; + break; + case IRQ_TYPE_EDGE_RISING: + rise = true; + break; + case IRQ_TYPE_EDGE_FALLING: + fall = true; + break; + default: + return -EINVAL; + } + + spin_lock_irqsave(&gs->gc.bgpio_lock, flags); + if (fall) { + val = readl(gs->gpio_io + YU_GPIO_CAUSE_FALL_EN); + val |= BIT(offset); + writel(val, gs->gpio_io + YU_GPIO_CAUSE_FALL_EN); + } + + if (rise) { + val = readl(gs->gpio_io + YU_GPIO_CAUSE_RISE_EN); + val |= BIT(offset); + writel(val, gs->gpio_io + YU_GPIO_CAUSE_RISE_EN); + } + spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); + + return 0; +} + /* BlueField-2 GPIO driver initialization routine. */ static int mlxbf2_gpio_probe(struct platform_device *pdev) { struct mlxbf2_gpio_context *gs; struct device *dev = &pdev->dev; + struct gpio_irq_chip *girq; struct gpio_chip *gc; unsigned int npins; - int ret; + const char *name; + int ret, irq; + + name = dev_name(dev); gs = devm_kzalloc(dev, sizeof(*gs), GFP_KERNEL); if (!gs) @@ -261,6 +371,34 @@ mlxbf2_gpio_probe(struct platform_device *pdev) gc->ngpio = npins; gc->owner = THIS_MODULE; + irq = platform_get_irq(pdev, 0); + if (irq >= 0) { + gs->irq_chip.name = name; + gs->irq_chip.irq_set_type = mlxbf2_gpio_irq_set_type; + gs->irq_chip.irq_enable = mlxbf2_gpio_irq_enable; + gs->irq_chip.irq_disable = mlxbf2_gpio_irq_disable; + + girq = &gs->gc.irq; + girq->chip = &gs->irq_chip; + girq->handler = handle_simple_irq; + girq->default_type = IRQ_TYPE_NONE; + /* This will let us handle the parent IRQ in the driver */ + girq->num_parents = 0; + girq->parents = NULL; + girq->parent_handler = NULL; + + /* + * Directly request the irq here instead of passing + * a flow-handler because the irq is shared. + */ + ret = devm_request_irq(dev, irq, mlxbf2_gpio_irq_handler, + IRQF_SHARED, name, gs); + if (ret) { + dev_err(dev, "failed to request IRQ"); + return ret; + } + } + platform_set_drvdata(pdev, gs); ret = devm_gpiochip_add_data(dev, &gs->gc, gs); @@ -315,5 +453,5 @@ static struct platform_driver mlxbf2_gpio_driver = { module_platform_driver(mlxbf2_gpio_driver); MODULE_DESCRIPTION("Mellanox BlueField-2 GPIO Driver"); -MODULE_AUTHOR("Mellanox Technologies"); +MODULE_AUTHOR("Asmaa Mnebhi "); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 6c2a6ddca763271fa583e22bce10c2805c1ea9f6 Mon Sep 17 00:00:00 2001 From: Asmaa Mnebhi Date: Fri, 15 Oct 2021 12:48:09 -0400 Subject: net: mellanox: mlxbf_gige: Replace non-standard interrupt handling Since the GPIO driver (gpio-mlxbf2.c) supports interrupt handling, replace the custom routine with simple IRQ request. Signed-off-by: Asmaa Mnebhi Acked-by: David S. Miller Signed-off-by: Bartosz Golaszewski --- drivers/net/ethernet/mellanox/mlxbf_gige/Makefile | 1 - .../net/ethernet/mellanox/mlxbf_gige/mlxbf_gige.h | 12 -- .../ethernet/mellanox/mlxbf_gige/mlxbf_gige_gpio.c | 212 --------------------- .../ethernet/mellanox/mlxbf_gige/mlxbf_gige_main.c | 22 +-- 4 files changed, 9 insertions(+), 238 deletions(-) delete mode 100644 drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_gpio.c diff --git a/drivers/net/ethernet/mellanox/mlxbf_gige/Makefile b/drivers/net/ethernet/mellanox/mlxbf_gige/Makefile index e57c1375f236..a97c2bef846b 100644 --- a/drivers/net/ethernet/mellanox/mlxbf_gige/Makefile +++ b/drivers/net/ethernet/mellanox/mlxbf_gige/Makefile @@ -3,7 +3,6 @@ obj-$(CONFIG_MLXBF_GIGE) += mlxbf_gige.o mlxbf_gige-y := mlxbf_gige_ethtool.o \ - mlxbf_gige_gpio.o \ mlxbf_gige_intr.o \ mlxbf_gige_main.o \ mlxbf_gige_mdio.o \ diff --git a/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige.h b/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige.h index e3509e69ed1c..86826a70f9dd 100644 --- a/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige.h +++ b/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige.h @@ -51,11 +51,6 @@ #define MLXBF_GIGE_ERROR_INTR_IDX 0 #define MLXBF_GIGE_RECEIVE_PKT_INTR_IDX 1 #define MLXBF_GIGE_LLU_PLU_INTR_IDX 2 -#define MLXBF_GIGE_PHY_INT_N 3 - -#define MLXBF_GIGE_MDIO_DEFAULT_PHY_ADDR 0x3 - -#define MLXBF_GIGE_DEFAULT_PHY_INT_GPIO 12 struct mlxbf_gige_stats { u64 hw_access_errors; @@ -81,11 +76,7 @@ struct mlxbf_gige { struct platform_device *pdev; void __iomem *mdio_io; struct mii_bus *mdiobus; - void __iomem *gpio_io; - struct irq_domain *irqdomain; - u32 phy_int_gpio_mask; spinlock_t lock; /* for packet processing indices */ - spinlock_t gpio_lock; /* for GPIO bus access */ u16 rx_q_entries; u16 tx_q_entries; u64 *tx_wqe_base; @@ -184,7 +175,4 @@ int mlxbf_gige_poll(struct napi_struct *napi, int budget); extern const struct ethtool_ops mlxbf_gige_ethtool_ops; void mlxbf_gige_update_tx_wqe_next(struct mlxbf_gige *priv); -int mlxbf_gige_gpio_init(struct platform_device *pdev, struct mlxbf_gige *priv); -void mlxbf_gige_gpio_free(struct mlxbf_gige *priv); - #endif /* !defined(__MLXBF_GIGE_H__) */ diff --git a/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_gpio.c b/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_gpio.c deleted file mode 100644 index a8d966db5715..000000000000 --- a/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_gpio.c +++ /dev/null @@ -1,212 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause - -/* Initialize and handle GPIO interrupt triggered by INT_N PHY signal. - * This GPIO interrupt triggers the PHY state machine to bring the link - * up/down. - * - * Copyright (C) 2021 NVIDIA CORPORATION & AFFILIATES - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mlxbf_gige.h" -#include "mlxbf_gige_regs.h" - -#define MLXBF_GIGE_GPIO_CAUSE_FALL_EN 0x48 -#define MLXBF_GIGE_GPIO_CAUSE_OR_CAUSE_EVTEN0 0x80 -#define MLXBF_GIGE_GPIO_CAUSE_OR_EVTEN0 0x94 -#define MLXBF_GIGE_GPIO_CAUSE_OR_CLRCAUSE 0x98 - -static void mlxbf_gige_gpio_enable(struct mlxbf_gige *priv) -{ - unsigned long flags; - u32 val; - - spin_lock_irqsave(&priv->gpio_lock, flags); - val = readl(priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_CLRCAUSE); - val |= priv->phy_int_gpio_mask; - writel(val, priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_CLRCAUSE); - - /* The INT_N interrupt level is active low. - * So enable cause fall bit to detect when GPIO - * state goes low. - */ - val = readl(priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_FALL_EN); - val |= priv->phy_int_gpio_mask; - writel(val, priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_FALL_EN); - - /* Enable PHY interrupt by setting the priority level */ - val = readl(priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_EVTEN0); - val |= priv->phy_int_gpio_mask; - writel(val, priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_EVTEN0); - spin_unlock_irqrestore(&priv->gpio_lock, flags); -} - -static void mlxbf_gige_gpio_disable(struct mlxbf_gige *priv) -{ - unsigned long flags; - u32 val; - - spin_lock_irqsave(&priv->gpio_lock, flags); - val = readl(priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_EVTEN0); - val &= ~priv->phy_int_gpio_mask; - writel(val, priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_EVTEN0); - spin_unlock_irqrestore(&priv->gpio_lock, flags); -} - -static irqreturn_t mlxbf_gige_gpio_handler(int irq, void *ptr) -{ - struct mlxbf_gige *priv; - u32 val; - - priv = ptr; - - /* Check if this interrupt is from PHY device. - * Return if it is not. - */ - val = readl(priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_CAUSE_EVTEN0); - if (!(val & priv->phy_int_gpio_mask)) - return IRQ_NONE; - - /* Clear interrupt when done, otherwise, no further interrupt - * will be triggered. - */ - val = readl(priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_CLRCAUSE); - val |= priv->phy_int_gpio_mask; - writel(val, priv->gpio_io + MLXBF_GIGE_GPIO_CAUSE_OR_CLRCAUSE); - - generic_handle_irq(priv->phy_irq); - - return IRQ_HANDLED; -} - -static void mlxbf_gige_gpio_mask(struct irq_data *irqd) -{ - struct mlxbf_gige *priv = irq_data_get_irq_chip_data(irqd); - - mlxbf_gige_gpio_disable(priv); -} - -static void mlxbf_gige_gpio_unmask(struct irq_data *irqd) -{ - struct mlxbf_gige *priv = irq_data_get_irq_chip_data(irqd); - - mlxbf_gige_gpio_enable(priv); -} - -static struct irq_chip mlxbf_gige_gpio_chip = { - .name = "mlxbf_gige_phy", - .irq_mask = mlxbf_gige_gpio_mask, - .irq_unmask = mlxbf_gige_gpio_unmask, -}; - -static int mlxbf_gige_gpio_domain_map(struct irq_domain *d, - unsigned int irq, - irq_hw_number_t hwirq) -{ - irq_set_chip_data(irq, d->host_data); - irq_set_chip_and_handler(irq, &mlxbf_gige_gpio_chip, handle_simple_irq); - irq_set_noprobe(irq); - - return 0; -} - -static const struct irq_domain_ops mlxbf_gige_gpio_domain_ops = { - .map = mlxbf_gige_gpio_domain_map, - .xlate = irq_domain_xlate_twocell, -}; - -#ifdef CONFIG_ACPI -static int mlxbf_gige_gpio_resources(struct acpi_resource *ares, - void *data) -{ - struct acpi_resource_gpio *gpio; - u32 *phy_int_gpio = data; - - if (ares->type == ACPI_RESOURCE_TYPE_GPIO) { - gpio = &ares->data.gpio; - *phy_int_gpio = gpio->pin_table[0]; - } - - return 1; -} -#endif - -void mlxbf_gige_gpio_free(struct mlxbf_gige *priv) -{ - irq_dispose_mapping(priv->phy_irq); - irq_domain_remove(priv->irqdomain); -} - -int mlxbf_gige_gpio_init(struct platform_device *pdev, - struct mlxbf_gige *priv) -{ - struct device *dev = &pdev->dev; - struct resource *res; - u32 phy_int_gpio = 0; - int ret; - - LIST_HEAD(resources); - - res = platform_get_resource(pdev, IORESOURCE_MEM, MLXBF_GIGE_RES_GPIO0); - if (!res) - return -ENODEV; - - priv->gpio_io = devm_ioremap(dev, res->start, resource_size(res)); - if (!priv->gpio_io) - return -ENOMEM; - -#ifdef CONFIG_ACPI - ret = acpi_dev_get_resources(ACPI_COMPANION(dev), - &resources, mlxbf_gige_gpio_resources, - &phy_int_gpio); - acpi_dev_free_resource_list(&resources); - if (ret < 0 || !phy_int_gpio) { - dev_err(dev, "Error retrieving the gpio phy pin"); - return -EINVAL; - } -#endif - - priv->phy_int_gpio_mask = BIT(phy_int_gpio); - - mlxbf_gige_gpio_disable(priv); - - priv->hw_phy_irq = platform_get_irq(pdev, MLXBF_GIGE_PHY_INT_N); - - priv->irqdomain = irq_domain_add_simple(NULL, 1, 0, - &mlxbf_gige_gpio_domain_ops, - priv); - if (!priv->irqdomain) { - dev_err(dev, "Failed to add IRQ domain\n"); - return -ENOMEM; - } - - priv->phy_irq = irq_create_mapping(priv->irqdomain, 0); - if (!priv->phy_irq) { - irq_domain_remove(priv->irqdomain); - priv->irqdomain = NULL; - dev_err(dev, "Error mapping PHY IRQ\n"); - return -EINVAL; - } - - ret = devm_request_irq(dev, priv->hw_phy_irq, mlxbf_gige_gpio_handler, - IRQF_ONESHOT | IRQF_SHARED, "mlxbf_gige_phy", priv); - if (ret) { - dev_err(dev, "Failed to request PHY IRQ"); - mlxbf_gige_gpio_free(priv); - return ret; - } - - return ret; -} diff --git a/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_main.c b/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_main.c index 3e85b17f5857..4382ec8f7d64 100644 --- a/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_main.c +++ b/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_main.c @@ -273,8 +273,8 @@ static int mlxbf_gige_probe(struct platform_device *pdev) void __iomem *llu_base; void __iomem *plu_base; void __iomem *base; + int addr, phy_irq; u64 control; - int addr; int err; base = devm_platform_ioremap_resource(pdev, MLXBF_GIGE_RES_MAC); @@ -309,20 +309,12 @@ static int mlxbf_gige_probe(struct platform_device *pdev) priv->pdev = pdev; spin_lock_init(&priv->lock); - spin_lock_init(&priv->gpio_lock); /* Attach MDIO device */ err = mlxbf_gige_mdio_probe(pdev, priv); if (err) return err; - err = mlxbf_gige_gpio_init(pdev, priv); - if (err) { - dev_err(&pdev->dev, "PHY IRQ initialization failed\n"); - mlxbf_gige_mdio_remove(priv); - return -ENODEV; - } - priv->base = base; priv->llu_base = llu_base; priv->plu_base = plu_base; @@ -343,6 +335,12 @@ static int mlxbf_gige_probe(struct platform_device *pdev) priv->rx_irq = platform_get_irq(pdev, MLXBF_GIGE_RECEIVE_PKT_INTR_IDX); priv->llu_plu_irq = platform_get_irq(pdev, MLXBF_GIGE_LLU_PLU_INTR_IDX); + phy_irq = acpi_dev_gpio_irq_get_by(ACPI_COMPANION(&pdev->dev), "phy-gpios", 0); + if (phy_irq < 0) { + dev_err(&pdev->dev, "Error getting PHY irq. Use polling instead"); + phy_irq = PHY_POLL; + } + phydev = phy_find_first(priv->mdiobus); if (!phydev) { err = -ENODEV; @@ -350,8 +348,8 @@ static int mlxbf_gige_probe(struct platform_device *pdev) } addr = phydev->mdio.addr; - priv->mdiobus->irq[addr] = priv->phy_irq; - phydev->irq = priv->phy_irq; + priv->mdiobus->irq[addr] = phy_irq; + phydev->irq = phy_irq; err = phy_connect_direct(netdev, phydev, mlxbf_gige_adjust_link, @@ -387,7 +385,6 @@ static int mlxbf_gige_probe(struct platform_device *pdev) return 0; out: - mlxbf_gige_gpio_free(priv); mlxbf_gige_mdio_remove(priv); return err; } @@ -398,7 +395,6 @@ static int mlxbf_gige_remove(struct platform_device *pdev) unregister_netdev(priv->netdev); phy_disconnect(priv->netdev->phydev); - mlxbf_gige_gpio_free(priv); mlxbf_gige_mdio_remove(priv); platform_set_drvdata(pdev, NULL); -- cgit v1.2.3 From dd1695a221e0eb60e345095deca28a45c3284428 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 23 Oct 2021 23:00:58 -0700 Subject: gpio: clean up Kconfig file Fix multiple problems in punctuation, capitalization, grammar, wording, and typos in the GPIO Kconfig file. Signed-off-by: Randy Dunlap Cc: Linus Walleij Cc: Bartosz Golaszewski Cc: linux-gpio@vger.kernel.org Cc: Mika Westerberg Cc: Andy Shevchenko Cc: Geert Uytterhoeven Cc: Sean Young Cc: Bamvor Jian Zhang Cc: Michael Walle Acked-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 116 +++++++++++++++++++++++++-------------------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 37a6f77c86fe..02eb9f647926 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -15,7 +15,7 @@ menuconfig GPIOLIB bool "GPIO Support" help This enables GPIO support through the generic GPIO library. - You only need to enable this, if you also want to enable + You only need to enable this if you also want to enable one or more of the GPIO drivers below. If unsure, say N. @@ -140,8 +140,8 @@ config GPIO_AMDPT depends on ACPI select GPIO_GENERIC help - driver for GPIO functionality on Promontory IOHub - Require ACPI ASL code to enumerate as a platform device. + Driver for GPIO functionality on Promontory IOHub. + Requires ACPI ASL code to enumerate as a platform device. config GPIO_ASPEED tristate "Aspeed GPIO support" @@ -306,7 +306,7 @@ config GPIO_HISI help Say Y or M here to build support for the HiSilicon GPIO controller driver GPIO block. - This GPIO controller support double-edge interrupt and multi-core + This GPIO controller supports double-edge interrupt and multi-core concurrent access. config GPIO_HLWD @@ -326,7 +326,7 @@ config GPIO_ICH help Say yes here to support the GPIO functionality of a number of Intel ICH-based chipsets. Currently supported devices: ICH6, ICH7, ICH8 - ICH9, ICH10, Series 5/3400 (eg Ibex Peak), Series 6/C200 (eg + ICH9, ICH10, Series 5/3400 (e.g. Ibex Peak), Series 6/C200 (e.g. Cougar Point), NM10 (Tiger Point), and 3100 (Whitmore Lake). If unsure, say N. @@ -337,7 +337,7 @@ config GPIO_IOP select GPIO_GENERIC help Say yes here to support the GPIO functionality of a number of Intel - IOP32X or IOP33X. + IOP32X or IOP33X series of chips. If unsure, say N. @@ -364,7 +364,7 @@ config GPIO_LOONGSON bool "Loongson-2/3 GPIO support" depends on CPU_LOONGSON2EF || CPU_LOONGSON64 help - driver for GPIO functionality on Loongson-2F/3A/3B processors. + Driver for GPIO functionality on Loongson-2F/3A/3B processors. config GPIO_LPC18XX tristate "NXP LPC18XX/43XX GPIO support" @@ -392,15 +392,15 @@ config GPIO_MENZ127 depends on MCB select GPIO_GENERIC help - Say yes here to support the MEN 16Z127 GPIO Controller + Say yes here to support the MEN 16Z127 GPIO Controller. config GPIO_MM_LANTIQ bool "Lantiq Memory mapped GPIOs" depends on LANTIQ && SOC_XWAY help This enables support for memory mapped GPIOs on the External Bus Unit - (EBU) found on Lantiq SoCs. The gpios are output only as they are - created by attaching a 16bit latch to the bus. + (EBU) found on Lantiq SoCs. The GPIOs are output only as they are + created by attaching a 16-bit latch to the bus. config GPIO_MPC5200 def_bool y @@ -424,7 +424,7 @@ config GPIO_MT7621 select GPIO_GENERIC select GPIOLIB_IRQCHIP help - Say yes here to support the Mediatek MT7621 SoC GPIO device + Say yes here to support the Mediatek MT7621 SoC GPIO device. config GPIO_MVEBU def_bool y @@ -469,7 +469,7 @@ config GPIO_PL061 select IRQ_DOMAIN select GPIOLIB_IRQCHIP help - Say yes here to support the PrimeCell PL061 GPIO device + Say yes here to support the PrimeCell PL061 GPIO device. config GPIO_PMIC_EIC_SPRD tristate "Spreadtrum PMIC EIC support" @@ -483,7 +483,7 @@ config GPIO_PXA bool "PXA GPIO support" depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST help - Say yes here to support the PXA GPIO device + Say yes here to support the PXA GPIO device. config GPIO_RCAR tristate "Renesas R-Car and RZ/G GPIO support" @@ -573,7 +573,7 @@ config GPIO_SPEAR_SPICS depends on PLAT_SPEAR select GENERIC_IRQ_CHIP help - Say yes here to support ST SPEAr SPI Chip Select as GPIO device + Say yes here to support ST SPEAr SPI Chip Select as GPIO device. config GPIO_SPRD tristate "Spreadtrum GPIO support" @@ -598,8 +598,8 @@ config GPIO_STP_XWAY help This enables support for the Serial To Parallel (STP) unit found on XWAY SoC. The STP allows the SoC to drive a shift registers cascade, - that can be up to 24 bit. This peripheral is aimed at driving leds. - Some of the gpios/leds can be auto updated by the soc with dsl and + that can be up to 24 bits. This peripheral is aimed at driving LEDs. + Some of the GPIOs/LEDs can be auto updated by the SoC with DSL and phy status. config GPIO_SYSCON @@ -679,10 +679,10 @@ config GPIO_VISCONTI Say yes here to support GPIO on Tohisba Visconti. config GPIO_VR41XX - tristate "NEC VR4100 series General-purpose I/O Uint support" + tristate "NEC VR4100 series General-purpose I/O Unit support" depends on CPU_VR41XX help - Say yes here to support the NEC VR4100 series General-purpose I/O Uint + Say yes here to support the NEC VR4100 series General-purpose I/O Unit. config GPIO_VX855 tristate "VIA VX855/VX875 GPIO" @@ -690,14 +690,14 @@ config GPIO_VX855 select MFD_CORE select MFD_VX855 help - Support access to the VX855/VX875 GPIO lines through the gpio library. + Support access to the VX855/VX875 GPIO lines through the GPIO library. - This driver provides common support for accessing the device, - additional drivers must be enabled in order to use the + This driver provides common support for accessing the device. + Additional drivers must be enabled in order to use the functionality of the device. config GPIO_WCD934X - tristate "Qualcomm Technologies Inc WCD9340/WCD9341 gpio controller driver" + tristate "Qualcomm Technologies Inc WCD9340/WCD9341 GPIO controller driver" depends on MFD_WCD934X && OF_GPIO help This driver is to support GPIO block found on the Qualcomm Technologies @@ -727,7 +727,7 @@ config GPIO_XILINX select GPIOLIB_IRQCHIP depends on OF_GPIO help - Say yes here to support the Xilinx FPGA GPIO device + Say yes here to support the Xilinx FPGA GPIO device. config GPIO_XLP tristate "Netlogic XLP GPIO support" @@ -748,7 +748,7 @@ config GPIO_XTENSA depends on !SMP help Say yes here to support the Xtensa internal GPIO32 IMPWIRE (input) - and EXPSTATE (output) ports + and EXPSTATE (output) ports. config GPIO_ZEVIO bool "LSI ZEVIO SoC memory mapped GPIOs" @@ -764,14 +764,14 @@ config GPIO_ZYNQ Say yes here to support Xilinx Zynq GPIO controller. config GPIO_ZYNQMP_MODEPIN - tristate "ZynqMP ps-mode pin gpio configuration driver" + tristate "ZynqMP ps-mode pin GPIO configuration driver" depends on ZYNQMP_FIRMWARE default ZYNQMP_FIRMWARE help - Say yes here to support the ZynqMP ps-mode pin gpio configuration + Say yes here to support the ZynqMP ps-mode pin GPIO configuration driver. - This ps-mode pin gpio driver is based on GPIO framework, PS_MODE + This ps-mode pin GPIO driver is based on GPIO framework. PS_MODE is 4-bits boot mode pins. It sets and gets the status of the ps-mode pin. Every pin can be configured as input/output. @@ -785,12 +785,12 @@ config GPIO_LOONGSON1 config GPIO_AMD_FCH tristate "GPIO support for AMD Fusion Controller Hub (G-series SOCs)" help - This option enables driver for GPIO on AMDs Fusion Controller Hub, - as found on G-series SOCs (eg. GX-412TC) + This option enables driver for GPIO on AMD's Fusion Controller Hub, + as found on G-series SOCs (e.g. GX-412TC). - Note: This driver doesn't registers itself automatically, as it - needs to be provided with platform specific configuration. - (See eg. CONFIG_PCENGINES_APU2.) + Note: This driver doesn't register itself automatically, as it + needs to be provided with platform-specific configuration. + (See e.g. CONFIG_PCENGINES_APU2.) config GPIO_MSC313 bool "MStar MSC313 GPIO support" @@ -800,7 +800,7 @@ config GPIO_MSC313 select IRQ_DOMAIN_HIERARCHY help Say Y here to support the main GPIO block on MStar/SigmaStar - ARMv7 based SoCs. + ARMv7-based SoCs. config GPIO_IDT3243X tristate "IDT 79RC3243X GPIO support" @@ -809,7 +809,7 @@ config GPIO_IDT3243X select GPIOLIB_IRQCHIP help Select this option to enable GPIO driver for - IDT 79RC3243X based devices like Mikrotik RB532. + IDT 79RC3243X-based devices like Mikrotik RB532. To compile this driver as a module, choose M here: the module will be called gpio-idt3243x. @@ -887,7 +887,7 @@ config GPIO_IT87 well. To compile this driver as a module, choose M here: the module will - be called gpio_it87 + be called gpio_it87. config GPIO_SCH tristate "Intel SCH/TunnelCreek/Centerton/Quark X1000 GPIO" @@ -903,7 +903,7 @@ config GPIO_SCH powered by the core power rail and are turned off during sleep modes (S3 and higher). The remaining four GPIOs are powered by the Intel SCH suspend power supply. These GPIOs remain - active during S3. The suspend powered GPIOs can be used to wake the + active during S3. The suspend-powered GPIOs can be used to wake the system from the Suspend-to-RAM state. The Intel Tunnel Creek processor has 5 GPIOs powered by the @@ -1056,7 +1056,7 @@ config GPIO_PCA953X_IRQ select GPIOLIB_IRQCHIP help Say yes here to enable the pca953x to be used as an interrupt - controller. It requires the driver to be built in the kernel. + controller. config GPIO_PCA9570 tristate "PCA9570 4-Bit I2C GPO expander" @@ -1183,7 +1183,7 @@ config GPIO_CRYSTAL_COVE help Support for GPIO pins on Crystal Cove PMIC. - Say Yes if you have a Intel SoC based tablet with Crystal Cove PMIC + Say Yes if you have a Intel SoC-based tablet with Crystal Cove PMIC inside. This driver can also be built as a module. If so, the module will be @@ -1213,7 +1213,7 @@ config GPIO_DA9055 Say yes here to enable the GPIO driver for the DA9055 chip. The Dialog DA9055 PMIC chip has 3 GPIO pins that can be - be controller by this driver. + be controlled by this driver. If driver is built as a module it will be called gpio-da9055. @@ -1235,7 +1235,7 @@ config HTC_EGPIO help This driver supports the CPLD egpio chip present on several HTC phones. It provides basic support for input - pins, output pins, and irqs. + pins, output pins, and IRQs. config GPIO_JANZ_TTL tristate "Janz VMOD-TTL Digital IO Module" @@ -1296,8 +1296,8 @@ config GPIO_MAX77620 help GPIO driver for MAX77620 and MAX20024 PMIC from Maxim Semiconductor. MAX77620 PMIC has 8 pins that can be configured as GPIOs. The - driver also provides interrupt support for each of the gpios. - Say yes here to enable the max77620 to be used as gpio controller. + driver also provides interrupt support for each of the GPIOs. + Say yes here to enable the max77620 to be used as GPIO controller. config GPIO_MAX77650 tristate "Maxim MAX77650/77651 GPIO support" @@ -1319,8 +1319,8 @@ config GPIO_RC5T583 help Select this option to enable GPIO driver for the Ricoh RC5T583 chip family. - This driver provides the support for driving/reading the gpio pins - of RC5T583 device through standard gpio library. + This driver provides the support for driving/reading the GPIO pins + of RC5T583 device through standard GPIO library. config GPIO_SL28CPLD tristate "Kontron sl28cpld GPIO support" @@ -1389,7 +1389,7 @@ config GPIO_TPS65912 tristate "TI TPS65912 GPIO" depends on MFD_TPS65912 help - This driver supports TPS65912 gpio chip + This driver supports TPS65912 GPIO chip. config GPIO_TPS68470 bool "TPS68470 GPIO" @@ -1397,7 +1397,7 @@ config GPIO_TPS68470 help Select this option to enable GPIO driver for the TPS68470 chip family. - There are 7 GPIOs and few sensor related GPIOs supported + There are 7 GPIOs and few sensor-related GPIOs supported by the TPS68470. While the 7 GPIOs can be configured as input or output as appropriate, the sensor related GPIOs are "output only" GPIOs. @@ -1442,7 +1442,7 @@ config GPIO_WHISKEY_COVE help Support for GPIO pins on Whiskey Cove PMIC. - Say Yes if you have a Intel SoC based tablet with Whiskey Cove PMIC + Say Yes if you have an Intel SoC-based tablet with Whiskey Cove PMIC inside. This driver can also be built as a module. If so, the module will be @@ -1479,10 +1479,10 @@ config GPIO_AMD8111 depends on X86 || COMPILE_TEST depends on HAS_IOPORT_MAP help - The AMD 8111 south bridge contains 32 GPIO pins which can be used. + The AMD 8111 southbridge contains 32 GPIO pins which can be used. - Note, that usually system firmware/ACPI handles GPIO pins on their - own and users might easily break their systems with uncarefull usage + Note that usually system firmware/ACPI handles GPIO pins on their + own and users might easily break their systems with uncareful usage of this driver! If unsure, say N @@ -1530,22 +1530,22 @@ config GPIO_ML_IOH select GENERIC_IRQ_CHIP help ML7213 is companion chip for Intel Atom E6xx series. - This driver can be used for OKI SEMICONDUCTOR ML7213 IOH(Input/Output - Hub) which is for IVI(In-Vehicle Infotainment) use. + This driver can be used for OKI SEMICONDUCTOR ML7213 IOH (Input/Output + Hub) which is for IVI (In-Vehicle Infotainment) use. This driver can access the IOH's GPIO device. config GPIO_PCH - tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7223/ML7831) GPIO" + tristate "Intel EG20T PCH/LAPIS Semiconductor IOH (ML7223/ML7831) GPIO" depends on X86_32 || MIPS || COMPILE_TEST select GENERIC_IRQ_CHIP help - This driver is for PCH(Platform controller Hub) GPIO of Intel Topcliff - which is an IOH(Input/Output Hub) for x86 embedded processor. + This driver is for PCH (Platform Controller Hub) GPIO of Intel Topcliff, + which is an IOH (Input/Output Hub) for x86 embedded processor. This driver can access PCH GPIO device. - This driver also can be used for LAPIS Semiconductor IOH(Input/ + This driver also can be used for LAPIS Semiconductor IOH (Input/ Output Hub), ML7223 and ML7831. - ML7223 IOH is for MP(Media Phone) use. + ML7223 IOH is for MP (Media Phone) use. ML7831 IOH is for general purpose use. ML7223/ML7831 is companion chip for Intel Atom E6xx series. ML7223/ML7831 is completely compatible for Intel EG20T PCH. @@ -1596,7 +1596,7 @@ config GPIO_74X164 help Driver for 74x164 compatible serial-in/parallel-out 8-outputs shift registers. This driver can be used to provide access - to more gpio outputs. + to more GPIO outputs. config GPIO_MAX3191X tristate "Maxim MAX3191x industrial serializer" -- cgit v1.2.3 From 585a07079909ba9061ddd88214c36653e1aef71a Mon Sep 17 00:00:00 2001 From: Sander Vanheule Date: Thu, 28 Oct 2021 10:52:43 +0200 Subject: gpio: realtek-otto: fix GPIO line IRQ offset The irqchip uses one domain for all GPIO lines, so the line offset should be determined w.r.t. the first line of the first port, not the first line of the triggered port. Fixes: 0d82fb1127fb ("gpio: Add Realtek Otto GPIO support") Signed-off-by: Sander Vanheule Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-realtek-otto.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-realtek-otto.c b/drivers/gpio/gpio-realtek-otto.c index eeeb39bc171d..bd75401b549d 100644 --- a/drivers/gpio/gpio-realtek-otto.c +++ b/drivers/gpio/gpio-realtek-otto.c @@ -205,7 +205,7 @@ static void realtek_gpio_irq_handler(struct irq_desc *desc) status = realtek_gpio_read_isr(ctrl, lines_done / 8); port_pin_count = min(gc->ngpio - lines_done, 8U); for_each_set_bit(offset, &status, port_pin_count) - generic_handle_domain_irq(gc->irq.domain, offset); + generic_handle_domain_irq(gc->irq.domain, offset + lines_done); } chained_irq_exit(irq_chip, desc); -- cgit v1.2.3 From eff5cdd745a68863a73095b0b4d62d15e0d9d902 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 21 Oct 2021 16:34:19 +0530 Subject: gpio: virtio: Add IRQ support This patch adds IRQ support for the virtio GPIO driver. Note that this uses the irq_bus_lock/unlock() callbacks, since those operations over virtio may sleep. Reviewed-by: Linus Walleij Signed-off-by: Viresh Kumar Acked-by: Michael S. Tsirkin Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-virtio.c | 302 ++++++++++++++++++++++++++++++++++++++- include/uapi/linux/virtio_gpio.h | 25 ++++ 3 files changed, 324 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 02eb9f647926..072ed610f9c6 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1686,6 +1686,7 @@ config GPIO_MOCKUP config GPIO_VIRTIO tristate "VirtIO GPIO support" depends on VIRTIO + select GPIOLIB_IRQCHIP help Say Y here to enable guest support for virtio-based GPIO controllers. diff --git a/drivers/gpio/gpio-virtio.c b/drivers/gpio/gpio-virtio.c index d24f1c9264bc..aeec4bf0b625 100644 --- a/drivers/gpio/gpio-virtio.c +++ b/drivers/gpio/gpio-virtio.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -28,12 +29,30 @@ struct virtio_gpio_line { unsigned int rxlen; }; +struct vgpio_irq_line { + u8 type; + bool disabled; + bool masked; + bool queued; + bool update_pending; + bool queue_pending; + + struct virtio_gpio_irq_request ireq ____cacheline_aligned; + struct virtio_gpio_irq_response ires ____cacheline_aligned; +}; + struct virtio_gpio { struct virtio_device *vdev; struct mutex lock; /* Protects virtqueue operation */ struct gpio_chip gc; struct virtio_gpio_line *lines; struct virtqueue *request_vq; + + /* irq support */ + struct virtqueue *event_vq; + struct mutex irq_lock; /* Protects irq operation */ + raw_spinlock_t eventq_lock; /* Protects queuing of the buffer */ + struct vgpio_irq_line *irq_lines; }; static int _virtio_gpio_req(struct virtio_gpio *vgpio, u16 type, u16 gpio, @@ -186,6 +205,238 @@ static void virtio_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) virtio_gpio_req(vgpio, VIRTIO_GPIO_MSG_SET_VALUE, gpio, value, NULL); } +/* Interrupt handling */ +static void virtio_gpio_irq_prepare(struct virtio_gpio *vgpio, u16 gpio) +{ + struct vgpio_irq_line *irq_line = &vgpio->irq_lines[gpio]; + struct virtio_gpio_irq_request *ireq = &irq_line->ireq; + struct virtio_gpio_irq_response *ires = &irq_line->ires; + struct scatterlist *sgs[2], req_sg, res_sg; + int ret; + + if (WARN_ON(irq_line->queued || irq_line->masked || irq_line->disabled)) + return; + + ireq->gpio = cpu_to_le16(gpio); + sg_init_one(&req_sg, ireq, sizeof(*ireq)); + sg_init_one(&res_sg, ires, sizeof(*ires)); + sgs[0] = &req_sg; + sgs[1] = &res_sg; + + ret = virtqueue_add_sgs(vgpio->event_vq, sgs, 1, 1, irq_line, GFP_ATOMIC); + if (ret) { + dev_err(&vgpio->vdev->dev, "failed to add request to eventq\n"); + return; + } + + irq_line->queued = true; + virtqueue_kick(vgpio->event_vq); +} + +static void virtio_gpio_irq_enable(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct virtio_gpio *vgpio = gpiochip_get_data(gc); + struct vgpio_irq_line *irq_line = &vgpio->irq_lines[d->hwirq]; + + raw_spin_lock(&vgpio->eventq_lock); + irq_line->disabled = false; + irq_line->masked = false; + irq_line->queue_pending = true; + raw_spin_unlock(&vgpio->eventq_lock); + + irq_line->update_pending = true; +} + +static void virtio_gpio_irq_disable(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct virtio_gpio *vgpio = gpiochip_get_data(gc); + struct vgpio_irq_line *irq_line = &vgpio->irq_lines[d->hwirq]; + + raw_spin_lock(&vgpio->eventq_lock); + irq_line->disabled = true; + irq_line->masked = true; + irq_line->queue_pending = false; + raw_spin_unlock(&vgpio->eventq_lock); + + irq_line->update_pending = true; +} + +static void virtio_gpio_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct virtio_gpio *vgpio = gpiochip_get_data(gc); + struct vgpio_irq_line *irq_line = &vgpio->irq_lines[d->hwirq]; + + raw_spin_lock(&vgpio->eventq_lock); + irq_line->masked = true; + raw_spin_unlock(&vgpio->eventq_lock); +} + +static void virtio_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct virtio_gpio *vgpio = gpiochip_get_data(gc); + struct vgpio_irq_line *irq_line = &vgpio->irq_lines[d->hwirq]; + + raw_spin_lock(&vgpio->eventq_lock); + irq_line->masked = false; + + /* Queue the buffer unconditionally on unmask */ + virtio_gpio_irq_prepare(vgpio, d->hwirq); + raw_spin_unlock(&vgpio->eventq_lock); +} + +static int virtio_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct virtio_gpio *vgpio = gpiochip_get_data(gc); + struct vgpio_irq_line *irq_line = &vgpio->irq_lines[d->hwirq]; + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + type = VIRTIO_GPIO_IRQ_TYPE_EDGE_RISING; + break; + case IRQ_TYPE_EDGE_FALLING: + type = VIRTIO_GPIO_IRQ_TYPE_EDGE_FALLING; + break; + case IRQ_TYPE_EDGE_BOTH: + type = VIRTIO_GPIO_IRQ_TYPE_EDGE_BOTH; + break; + case IRQ_TYPE_LEVEL_LOW: + type = VIRTIO_GPIO_IRQ_TYPE_LEVEL_LOW; + break; + case IRQ_TYPE_LEVEL_HIGH: + type = VIRTIO_GPIO_IRQ_TYPE_LEVEL_HIGH; + break; + default: + dev_err(&vgpio->vdev->dev, "unsupported irq type: %u\n", type); + return -EINVAL; + } + + irq_line->type = type; + irq_line->update_pending = true; + + return 0; +} + +static void virtio_gpio_irq_bus_lock(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct virtio_gpio *vgpio = gpiochip_get_data(gc); + + mutex_lock(&vgpio->irq_lock); +} + +static void virtio_gpio_irq_bus_sync_unlock(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct virtio_gpio *vgpio = gpiochip_get_data(gc); + struct vgpio_irq_line *irq_line = &vgpio->irq_lines[d->hwirq]; + u8 type = irq_line->disabled ? VIRTIO_GPIO_IRQ_TYPE_NONE : irq_line->type; + unsigned long flags; + + if (irq_line->update_pending) { + irq_line->update_pending = false; + virtio_gpio_req(vgpio, VIRTIO_GPIO_MSG_IRQ_TYPE, d->hwirq, type, + NULL); + + /* Queue the buffer only after interrupt is enabled */ + raw_spin_lock_irqsave(&vgpio->eventq_lock, flags); + if (irq_line->queue_pending) { + irq_line->queue_pending = false; + virtio_gpio_irq_prepare(vgpio, d->hwirq); + } + raw_spin_unlock_irqrestore(&vgpio->eventq_lock, flags); + } + + mutex_unlock(&vgpio->irq_lock); +} + +static struct irq_chip vgpio_irq_chip = { + .name = "virtio-gpio", + .irq_enable = virtio_gpio_irq_enable, + .irq_disable = virtio_gpio_irq_disable, + .irq_mask = virtio_gpio_irq_mask, + .irq_unmask = virtio_gpio_irq_unmask, + .irq_set_type = virtio_gpio_irq_set_type, + + /* These are required to implement irqchip for slow busses */ + .irq_bus_lock = virtio_gpio_irq_bus_lock, + .irq_bus_sync_unlock = virtio_gpio_irq_bus_sync_unlock, +}; + +static bool ignore_irq(struct virtio_gpio *vgpio, int gpio, + struct vgpio_irq_line *irq_line) +{ + bool ignore = false; + + raw_spin_lock(&vgpio->eventq_lock); + irq_line->queued = false; + + /* Interrupt is disabled currently */ + if (irq_line->masked || irq_line->disabled) { + ignore = true; + goto unlock; + } + + /* + * Buffer is returned as the interrupt was disabled earlier, but is + * enabled again now. Requeue the buffers. + */ + if (irq_line->ires.status == VIRTIO_GPIO_IRQ_STATUS_INVALID) { + virtio_gpio_irq_prepare(vgpio, gpio); + ignore = true; + goto unlock; + } + + if (WARN_ON(irq_line->ires.status != VIRTIO_GPIO_IRQ_STATUS_VALID)) + ignore = true; + +unlock: + raw_spin_unlock(&vgpio->eventq_lock); + + return ignore; +} + +static void virtio_gpio_event_vq(struct virtqueue *vq) +{ + struct virtio_gpio *vgpio = vq->vdev->priv; + struct device *dev = &vgpio->vdev->dev; + struct vgpio_irq_line *irq_line; + int gpio, ret; + unsigned int len; + + while (true) { + irq_line = virtqueue_get_buf(vgpio->event_vq, &len); + if (!irq_line) + break; + + if (len != sizeof(irq_line->ires)) { + dev_err(dev, "irq with incorrect length (%u : %u)\n", + len, (unsigned int)sizeof(irq_line->ires)); + continue; + } + + /* + * Find GPIO line number from the offset of irq_line within the + * irq_lines block. We can also get GPIO number from + * irq-request, but better not to rely on a buffer returned by + * remote. + */ + gpio = irq_line - vgpio->irq_lines; + WARN_ON(gpio >= vgpio->gc.ngpio); + + if (unlikely(ignore_irq(vgpio, gpio, irq_line))) + continue; + + ret = generic_handle_domain_irq(vgpio->gc.irq.domain, gpio); + if (ret) + dev_err(dev, "failed to handle interrupt: %d\n", ret); + }; +} + static void virtio_gpio_request_vq(struct virtqueue *vq) { struct virtio_gpio_line *line; @@ -210,14 +461,15 @@ static void virtio_gpio_free_vqs(struct virtio_device *vdev) static int virtio_gpio_alloc_vqs(struct virtio_gpio *vgpio, struct virtio_device *vdev) { - const char * const names[] = { "requestq" }; + const char * const names[] = { "requestq", "eventq" }; vq_callback_t *cbs[] = { virtio_gpio_request_vq, + virtio_gpio_event_vq, }; - struct virtqueue *vqs[1] = { NULL }; + struct virtqueue *vqs[2] = { NULL, NULL }; int ret; - ret = virtio_find_vqs(vdev, 1, vqs, cbs, names, NULL); + ret = virtio_find_vqs(vdev, vgpio->irq_lines ? 2 : 1, vqs, cbs, names, NULL); if (ret) { dev_err(&vdev->dev, "failed to find vqs: %d\n", ret); return ret; @@ -225,11 +477,23 @@ static int virtio_gpio_alloc_vqs(struct virtio_gpio *vgpio, if (!vqs[0]) { dev_err(&vdev->dev, "failed to find requestq vq\n"); - return -ENODEV; + goto out; } vgpio->request_vq = vqs[0]; + if (vgpio->irq_lines && !vqs[1]) { + dev_err(&vdev->dev, "failed to find eventq vq\n"); + goto out; + } + vgpio->event_vq = vqs[1]; + return 0; + +out: + if (vqs[0] || vqs[1]) + virtio_gpio_free_vqs(vdev); + + return -ENODEV; } static const char **virtio_gpio_get_names(struct virtio_gpio *vgpio, @@ -325,6 +589,30 @@ static int virtio_gpio_probe(struct virtio_device *vdev) vgpio->gc.owner = THIS_MODULE; vgpio->gc.can_sleep = true; + /* Interrupt support */ + if (virtio_has_feature(vdev, VIRTIO_GPIO_F_IRQ)) { + vgpio->irq_lines = devm_kcalloc(dev, ngpio, sizeof(*vgpio->irq_lines), GFP_KERNEL); + if (!vgpio->irq_lines) + return -ENOMEM; + + /* The event comes from the outside so no parent handler */ + vgpio->gc.irq.parent_handler = NULL; + vgpio->gc.irq.num_parents = 0; + vgpio->gc.irq.parents = NULL; + vgpio->gc.irq.default_type = IRQ_TYPE_NONE; + vgpio->gc.irq.handler = handle_level_irq; + vgpio->gc.irq.chip = &vgpio_irq_chip; + + for (i = 0; i < ngpio; i++) { + vgpio->irq_lines[i].type = VIRTIO_GPIO_IRQ_TYPE_NONE; + vgpio->irq_lines[i].disabled = true; + vgpio->irq_lines[i].masked = true; + } + + mutex_init(&vgpio->irq_lock); + raw_spin_lock_init(&vgpio->eventq_lock); + } + ret = virtio_gpio_alloc_vqs(vgpio, vdev); if (ret) return ret; @@ -357,7 +645,13 @@ static const struct virtio_device_id id_table[] = { }; MODULE_DEVICE_TABLE(virtio, id_table); +static const unsigned int features[] = { + VIRTIO_GPIO_F_IRQ, +}; + static struct virtio_driver virtio_gpio_driver = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), .id_table = id_table, .probe = virtio_gpio_probe, .remove = virtio_gpio_remove, diff --git a/include/uapi/linux/virtio_gpio.h b/include/uapi/linux/virtio_gpio.h index 0445f905d8cc..d04af9c5f0de 100644 --- a/include/uapi/linux/virtio_gpio.h +++ b/include/uapi/linux/virtio_gpio.h @@ -5,12 +5,16 @@ #include +/* Virtio GPIO Feature bits */ +#define VIRTIO_GPIO_F_IRQ 0 + /* Virtio GPIO request types */ #define VIRTIO_GPIO_MSG_GET_NAMES 0x0001 #define VIRTIO_GPIO_MSG_GET_DIRECTION 0x0002 #define VIRTIO_GPIO_MSG_SET_DIRECTION 0x0003 #define VIRTIO_GPIO_MSG_GET_VALUE 0x0004 #define VIRTIO_GPIO_MSG_SET_VALUE 0x0005 +#define VIRTIO_GPIO_MSG_IRQ_TYPE 0x0006 /* Possible values of the status field */ #define VIRTIO_GPIO_STATUS_OK 0x0 @@ -21,6 +25,14 @@ #define VIRTIO_GPIO_DIRECTION_OUT 0x01 #define VIRTIO_GPIO_DIRECTION_IN 0x02 +/* Virtio GPIO IRQ types */ +#define VIRTIO_GPIO_IRQ_TYPE_NONE 0x00 +#define VIRTIO_GPIO_IRQ_TYPE_EDGE_RISING 0x01 +#define VIRTIO_GPIO_IRQ_TYPE_EDGE_FALLING 0x02 +#define VIRTIO_GPIO_IRQ_TYPE_EDGE_BOTH 0x03 +#define VIRTIO_GPIO_IRQ_TYPE_LEVEL_HIGH 0x04 +#define VIRTIO_GPIO_IRQ_TYPE_LEVEL_LOW 0x08 + struct virtio_gpio_config { __le16 ngpio; __u8 padding[2]; @@ -44,4 +56,17 @@ struct virtio_gpio_response_get_names { __u8 value[]; }; +/* Virtio GPIO IRQ Request / Response */ +struct virtio_gpio_irq_request { + __le16 gpio; +}; + +struct virtio_gpio_irq_response { + __u8 status; +}; + +/* Possible values of the interrupt status field */ +#define VIRTIO_GPIO_IRQ_STATUS_INVALID 0x0 +#define VIRTIO_GPIO_IRQ_STATUS_VALID 0x1 + #endif /* _LINUX_VIRTIO_GPIO_H */ -- cgit v1.2.3 From 7d0003da6297eb128f3490e396e6fc6df71557cd Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 1 Nov 2021 05:11:25 -0400 Subject: virtio_gpio: drop packed attribute Declaring the struct packed here is mostly harmless, but gives a bad example for people to copy. As the struct is packed and aligned manually, let's just drop the attribute. Signed-off-by: Michael S. Tsirkin Acked-by: Viresh Kumar Signed-off-by: Bartosz Golaszewski --- include/uapi/linux/virtio_gpio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/linux/virtio_gpio.h b/include/uapi/linux/virtio_gpio.h index d04af9c5f0de..d4b29d9a39dd 100644 --- a/include/uapi/linux/virtio_gpio.h +++ b/include/uapi/linux/virtio_gpio.h @@ -37,7 +37,7 @@ struct virtio_gpio_config { __le16 ngpio; __u8 padding[2]; __le32 gpio_names_size; -} __packed; +}; /* Virtio GPIO Request / Response */ struct virtio_gpio_request { -- cgit v1.2.3