Merge tag 'gpio-updates-for-v5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux
Pull gpio updates from Bartosz Golaszewski:
"We have a single new driver, new features in others and some cleanups
all over the place.
Nothing really stands out and it is all relatively small.
- new driver: gpio-modepin (plus relevant change in zynqmp firmware)
- add interrupt support to gpio-virtio
- enable the 'gpio-line-names' property in the DT bindings for
gpio-rockchip
- use the subsystem helpers where applicable in gpio-uniphier instead
of accessing IRQ structures directly
- code shrink in gpio-xilinx
- add interrupt to gpio-mlxbf2 (and include the removal of custom
interrupt code from the mellanox ethernet driver)
- support multiple interrupts per bank in gpio-tegra186 (and force
one interrupt per bank in older models)
- fix GPIO line IRQ offset calculation in gpio-realtek-otto
- drop unneeded MODULE_ALIAS expansions in multiple drivers
- code cleanup in gpio-aggregator
- minor improvements in gpio-max730x and gpio-mc33880
- Kconfig cleanups"
* tag 'gpio-updates-for-v5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux:
virtio_gpio: drop packed attribute
gpio: virtio: Add IRQ support
gpio: realtek-otto: fix GPIO line IRQ offset
gpio: clean up Kconfig file
net: mellanox: mlxbf_gige: Replace non-standard interrupt handling
gpio: mlxbf2: Introduce IRQ support
gpio: mc33880: Drop if with an always false condition
gpio: max730x: Make __max730x_remove() return void
gpio: aggregator: Wrap access to gpiochip_fwd.tmp[]
gpio: modepin: Add driver support for modepin GPIO controller
dt-bindings: gpio: zynqmp: Add binding documentation for modepin
firmware: zynqmp: Add MMIO read and write support for PS_MODE pin
gpio: tps65218: drop unneeded MODULE_ALIAS
gpio: max77620: drop unneeded MODULE_ALIAS
gpio: xilinx: simplify getting .driver_data
gpio: tegra186: Support multiple interrupts per bank
gpio: tegra186: Force one interrupt per bank
gpio: uniphier: Use helper functions to get private data from IRQ data
gpio: uniphier: Use helper function to get IRQ hardware number
dt-bindings: gpio: add gpio-line-names to rockchip,gpio-bank.yaml
This commit is contained in:
@@ -29,6 +29,8 @@ properties:
|
|||||||
|
|
||||||
gpio-controller: true
|
gpio-controller: true
|
||||||
|
|
||||||
|
gpio-line-names: true
|
||||||
|
|
||||||
"#gpio-cells":
|
"#gpio-cells":
|
||||||
const: 2
|
const: 2
|
||||||
|
|
||||||
|
|||||||
@@ -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 <piyush.mehta@xilinx.com>
|
||||||
|
|
||||||
|
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>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
...
|
||||||
@@ -28,6 +28,13 @@
|
|||||||
/* Max HashMap Order for PM API feature check (1<<7 = 128) */
|
/* Max HashMap Order for PM API feature check (1<<7 = 128) */
|
||||||
#define PM_API_FEATURE_CHECK_MAX_ORDER 7
|
#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 bool feature_check_enabled;
|
||||||
static DEFINE_HASHTABLE(pm_api_features_map, PM_API_FEATURE_CHECK_MAX_ORDER);
|
static DEFINE_HASHTABLE(pm_api_features_map, PM_API_FEATURE_CHECK_MAX_ORDER);
|
||||||
|
|
||||||
@@ -942,6 +949,45 @@ int zynqmp_pm_pinctrl_set_config(const u32 pin, const u32 param,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_set_config);
|
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
|
* zynqmp_pm_init_finalize() - PM call to inform firmware that the caller
|
||||||
* master has initialized its own power management
|
* master has initialized its own power management
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ menuconfig GPIOLIB
|
|||||||
bool "GPIO Support"
|
bool "GPIO Support"
|
||||||
help
|
help
|
||||||
This enables GPIO support through the generic GPIO library.
|
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.
|
one or more of the GPIO drivers below.
|
||||||
|
|
||||||
If unsure, say N.
|
If unsure, say N.
|
||||||
@@ -140,8 +140,8 @@ config GPIO_AMDPT
|
|||||||
depends on ACPI
|
depends on ACPI
|
||||||
select GPIO_GENERIC
|
select GPIO_GENERIC
|
||||||
help
|
help
|
||||||
driver for GPIO functionality on Promontory IOHub
|
Driver for GPIO functionality on Promontory IOHub.
|
||||||
Require ACPI ASL code to enumerate as a platform device.
|
Requires ACPI ASL code to enumerate as a platform device.
|
||||||
|
|
||||||
config GPIO_ASPEED
|
config GPIO_ASPEED
|
||||||
tristate "Aspeed GPIO support"
|
tristate "Aspeed GPIO support"
|
||||||
@@ -306,7 +306,7 @@ config GPIO_HISI
|
|||||||
help
|
help
|
||||||
Say Y or M here to build support for the HiSilicon GPIO controller
|
Say Y or M here to build support for the HiSilicon GPIO controller
|
||||||
driver GPIO block.
|
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.
|
concurrent access.
|
||||||
|
|
||||||
config GPIO_HLWD
|
config GPIO_HLWD
|
||||||
@@ -326,7 +326,7 @@ config GPIO_ICH
|
|||||||
help
|
help
|
||||||
Say yes here to support the GPIO functionality of a number of Intel
|
Say yes here to support the GPIO functionality of a number of Intel
|
||||||
ICH-based chipsets. Currently supported devices: ICH6, ICH7, ICH8
|
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).
|
Cougar Point), NM10 (Tiger Point), and 3100 (Whitmore Lake).
|
||||||
|
|
||||||
If unsure, say N.
|
If unsure, say N.
|
||||||
@@ -337,7 +337,7 @@ config GPIO_IOP
|
|||||||
select GPIO_GENERIC
|
select GPIO_GENERIC
|
||||||
help
|
help
|
||||||
Say yes here to support the GPIO functionality of a number of Intel
|
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.
|
If unsure, say N.
|
||||||
|
|
||||||
@@ -364,7 +364,7 @@ config GPIO_LOONGSON
|
|||||||
bool "Loongson-2/3 GPIO support"
|
bool "Loongson-2/3 GPIO support"
|
||||||
depends on CPU_LOONGSON2EF || CPU_LOONGSON64
|
depends on CPU_LOONGSON2EF || CPU_LOONGSON64
|
||||||
help
|
help
|
||||||
driver for GPIO functionality on Loongson-2F/3A/3B processors.
|
Driver for GPIO functionality on Loongson-2F/3A/3B processors.
|
||||||
|
|
||||||
config GPIO_LPC18XX
|
config GPIO_LPC18XX
|
||||||
tristate "NXP LPC18XX/43XX GPIO support"
|
tristate "NXP LPC18XX/43XX GPIO support"
|
||||||
@@ -392,15 +392,15 @@ config GPIO_MENZ127
|
|||||||
depends on MCB
|
depends on MCB
|
||||||
select GPIO_GENERIC
|
select GPIO_GENERIC
|
||||||
help
|
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
|
config GPIO_MM_LANTIQ
|
||||||
bool "Lantiq Memory mapped GPIOs"
|
bool "Lantiq Memory mapped GPIOs"
|
||||||
depends on LANTIQ && SOC_XWAY
|
depends on LANTIQ && SOC_XWAY
|
||||||
help
|
help
|
||||||
This enables support for memory mapped GPIOs on the External Bus Unit
|
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
|
(EBU) found on Lantiq SoCs. The GPIOs are output only as they are
|
||||||
created by attaching a 16bit latch to the bus.
|
created by attaching a 16-bit latch to the bus.
|
||||||
|
|
||||||
config GPIO_MPC5200
|
config GPIO_MPC5200
|
||||||
def_bool y
|
def_bool y
|
||||||
@@ -424,7 +424,7 @@ config GPIO_MT7621
|
|||||||
select GPIO_GENERIC
|
select GPIO_GENERIC
|
||||||
select GPIOLIB_IRQCHIP
|
select GPIOLIB_IRQCHIP
|
||||||
help
|
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
|
config GPIO_MVEBU
|
||||||
def_bool y
|
def_bool y
|
||||||
@@ -469,7 +469,7 @@ config GPIO_PL061
|
|||||||
select IRQ_DOMAIN
|
select IRQ_DOMAIN
|
||||||
select GPIOLIB_IRQCHIP
|
select GPIOLIB_IRQCHIP
|
||||||
help
|
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
|
config GPIO_PMIC_EIC_SPRD
|
||||||
tristate "Spreadtrum PMIC EIC support"
|
tristate "Spreadtrum PMIC EIC support"
|
||||||
@@ -483,7 +483,7 @@ config GPIO_PXA
|
|||||||
bool "PXA GPIO support"
|
bool "PXA GPIO support"
|
||||||
depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST
|
depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST
|
||||||
help
|
help
|
||||||
Say yes here to support the PXA GPIO device
|
Say yes here to support the PXA GPIO device.
|
||||||
|
|
||||||
config GPIO_RCAR
|
config GPIO_RCAR
|
||||||
tristate "Renesas R-Car and RZ/G GPIO support"
|
tristate "Renesas R-Car and RZ/G GPIO support"
|
||||||
@@ -573,7 +573,7 @@ config GPIO_SPEAR_SPICS
|
|||||||
depends on PLAT_SPEAR
|
depends on PLAT_SPEAR
|
||||||
select GENERIC_IRQ_CHIP
|
select GENERIC_IRQ_CHIP
|
||||||
help
|
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
|
config GPIO_SPRD
|
||||||
tristate "Spreadtrum GPIO support"
|
tristate "Spreadtrum GPIO support"
|
||||||
@@ -598,8 +598,8 @@ config GPIO_STP_XWAY
|
|||||||
help
|
help
|
||||||
This enables support for the Serial To Parallel (STP) unit found on
|
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,
|
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.
|
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
|
Some of the GPIOs/LEDs can be auto updated by the SoC with DSL and
|
||||||
phy status.
|
phy status.
|
||||||
|
|
||||||
config GPIO_SYSCON
|
config GPIO_SYSCON
|
||||||
@@ -679,10 +679,10 @@ config GPIO_VISCONTI
|
|||||||
Say yes here to support GPIO on Tohisba Visconti.
|
Say yes here to support GPIO on Tohisba Visconti.
|
||||||
|
|
||||||
config GPIO_VR41XX
|
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
|
depends on CPU_VR41XX
|
||||||
help
|
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
|
config GPIO_VX855
|
||||||
tristate "VIA VX855/VX875 GPIO"
|
tristate "VIA VX855/VX875 GPIO"
|
||||||
@@ -690,14 +690,14 @@ config GPIO_VX855
|
|||||||
select MFD_CORE
|
select MFD_CORE
|
||||||
select MFD_VX855
|
select MFD_VX855
|
||||||
help
|
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,
|
This driver provides common support for accessing the device.
|
||||||
additional drivers must be enabled in order to use the
|
Additional drivers must be enabled in order to use the
|
||||||
functionality of the device.
|
functionality of the device.
|
||||||
|
|
||||||
config GPIO_WCD934X
|
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
|
depends on MFD_WCD934X && OF_GPIO
|
||||||
help
|
help
|
||||||
This driver is to support GPIO block found on the Qualcomm Technologies
|
This driver is to support GPIO block found on the Qualcomm Technologies
|
||||||
@@ -727,7 +727,7 @@ config GPIO_XILINX
|
|||||||
select GPIOLIB_IRQCHIP
|
select GPIOLIB_IRQCHIP
|
||||||
depends on OF_GPIO
|
depends on OF_GPIO
|
||||||
help
|
help
|
||||||
Say yes here to support the Xilinx FPGA GPIO device
|
Say yes here to support the Xilinx FPGA GPIO device.
|
||||||
|
|
||||||
config GPIO_XLP
|
config GPIO_XLP
|
||||||
tristate "Netlogic XLP GPIO support"
|
tristate "Netlogic XLP GPIO support"
|
||||||
@@ -748,7 +748,7 @@ config GPIO_XTENSA
|
|||||||
depends on !SMP
|
depends on !SMP
|
||||||
help
|
help
|
||||||
Say yes here to support the Xtensa internal GPIO32 IMPWIRE (input)
|
Say yes here to support the Xtensa internal GPIO32 IMPWIRE (input)
|
||||||
and EXPSTATE (output) ports
|
and EXPSTATE (output) ports.
|
||||||
|
|
||||||
config GPIO_ZEVIO
|
config GPIO_ZEVIO
|
||||||
bool "LSI ZEVIO SoC memory mapped GPIOs"
|
bool "LSI ZEVIO SoC memory mapped GPIOs"
|
||||||
@@ -763,6 +763,18 @@ config GPIO_ZYNQ
|
|||||||
help
|
help
|
||||||
Say yes here to support Xilinx Zynq GPIO controller.
|
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
|
config GPIO_LOONGSON1
|
||||||
tristate "Loongson1 GPIO support"
|
tristate "Loongson1 GPIO support"
|
||||||
depends on MACH_LOONGSON32
|
depends on MACH_LOONGSON32
|
||||||
@@ -773,12 +785,12 @@ config GPIO_LOONGSON1
|
|||||||
config GPIO_AMD_FCH
|
config GPIO_AMD_FCH
|
||||||
tristate "GPIO support for AMD Fusion Controller Hub (G-series SOCs)"
|
tristate "GPIO support for AMD Fusion Controller Hub (G-series SOCs)"
|
||||||
help
|
help
|
||||||
This option enables driver for GPIO on AMDs Fusion Controller Hub,
|
This option enables driver for GPIO on AMD's Fusion Controller Hub,
|
||||||
as found on G-series SOCs (eg. GX-412TC)
|
as found on G-series SOCs (e.g. GX-412TC).
|
||||||
|
|
||||||
Note: This driver doesn't registers itself automatically, as it
|
Note: This driver doesn't register itself automatically, as it
|
||||||
needs to be provided with platform specific configuration.
|
needs to be provided with platform-specific configuration.
|
||||||
(See eg. CONFIG_PCENGINES_APU2.)
|
(See e.g. CONFIG_PCENGINES_APU2.)
|
||||||
|
|
||||||
config GPIO_MSC313
|
config GPIO_MSC313
|
||||||
bool "MStar MSC313 GPIO support"
|
bool "MStar MSC313 GPIO support"
|
||||||
@@ -788,7 +800,7 @@ config GPIO_MSC313
|
|||||||
select IRQ_DOMAIN_HIERARCHY
|
select IRQ_DOMAIN_HIERARCHY
|
||||||
help
|
help
|
||||||
Say Y here to support the main GPIO block on MStar/SigmaStar
|
Say Y here to support the main GPIO block on MStar/SigmaStar
|
||||||
ARMv7 based SoCs.
|
ARMv7-based SoCs.
|
||||||
|
|
||||||
config GPIO_IDT3243X
|
config GPIO_IDT3243X
|
||||||
tristate "IDT 79RC3243X GPIO support"
|
tristate "IDT 79RC3243X GPIO support"
|
||||||
@@ -797,7 +809,7 @@ config GPIO_IDT3243X
|
|||||||
select GPIOLIB_IRQCHIP
|
select GPIOLIB_IRQCHIP
|
||||||
help
|
help
|
||||||
Select this option to enable GPIO driver for
|
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
|
To compile this driver as a module, choose M here: the module will
|
||||||
be called gpio-idt3243x.
|
be called gpio-idt3243x.
|
||||||
@@ -875,7 +887,7 @@ config GPIO_IT87
|
|||||||
well.
|
well.
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the module will
|
To compile this driver as a module, choose M here: the module will
|
||||||
be called gpio_it87
|
be called gpio_it87.
|
||||||
|
|
||||||
config GPIO_SCH
|
config GPIO_SCH
|
||||||
tristate "Intel SCH/TunnelCreek/Centerton/Quark X1000 GPIO"
|
tristate "Intel SCH/TunnelCreek/Centerton/Quark X1000 GPIO"
|
||||||
@@ -891,7 +903,7 @@ config GPIO_SCH
|
|||||||
powered by the core power rail and are turned off during sleep
|
powered by the core power rail and are turned off during sleep
|
||||||
modes (S3 and higher). The remaining four GPIOs are powered by
|
modes (S3 and higher). The remaining four GPIOs are powered by
|
||||||
the Intel SCH suspend power supply. These GPIOs remain
|
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.
|
system from the Suspend-to-RAM state.
|
||||||
|
|
||||||
The Intel Tunnel Creek processor has 5 GPIOs powered by the
|
The Intel Tunnel Creek processor has 5 GPIOs powered by the
|
||||||
@@ -1044,7 +1056,7 @@ config GPIO_PCA953X_IRQ
|
|||||||
select GPIOLIB_IRQCHIP
|
select GPIOLIB_IRQCHIP
|
||||||
help
|
help
|
||||||
Say yes here to enable the pca953x to be used as an interrupt
|
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
|
config GPIO_PCA9570
|
||||||
tristate "PCA9570 4-Bit I2C GPO expander"
|
tristate "PCA9570 4-Bit I2C GPO expander"
|
||||||
@@ -1171,7 +1183,7 @@ config GPIO_CRYSTAL_COVE
|
|||||||
help
|
help
|
||||||
Support for GPIO pins on Crystal Cove PMIC.
|
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.
|
inside.
|
||||||
|
|
||||||
This driver can also be built as a module. If so, the module will be
|
This driver can also be built as a module. If so, the module will be
|
||||||
@@ -1201,7 +1213,7 @@ config GPIO_DA9055
|
|||||||
Say yes here to enable the GPIO driver for the DA9055 chip.
|
Say yes here to enable the GPIO driver for the DA9055 chip.
|
||||||
|
|
||||||
The Dialog DA9055 PMIC chip has 3 GPIO pins that can be
|
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.
|
If driver is built as a module it will be called gpio-da9055.
|
||||||
|
|
||||||
@@ -1223,7 +1235,7 @@ config HTC_EGPIO
|
|||||||
help
|
help
|
||||||
This driver supports the CPLD egpio chip present on
|
This driver supports the CPLD egpio chip present on
|
||||||
several HTC phones. It provides basic support for input
|
several HTC phones. It provides basic support for input
|
||||||
pins, output pins, and irqs.
|
pins, output pins, and IRQs.
|
||||||
|
|
||||||
config GPIO_JANZ_TTL
|
config GPIO_JANZ_TTL
|
||||||
tristate "Janz VMOD-TTL Digital IO Module"
|
tristate "Janz VMOD-TTL Digital IO Module"
|
||||||
@@ -1284,8 +1296,8 @@ config GPIO_MAX77620
|
|||||||
help
|
help
|
||||||
GPIO driver for MAX77620 and MAX20024 PMIC from Maxim Semiconductor.
|
GPIO driver for MAX77620 and MAX20024 PMIC from Maxim Semiconductor.
|
||||||
MAX77620 PMIC has 8 pins that can be configured as GPIOs. The
|
MAX77620 PMIC has 8 pins that can be configured as GPIOs. The
|
||||||
driver also provides interrupt support for each of the gpios.
|
driver also provides interrupt support for each of the GPIOs.
|
||||||
Say yes here to enable the max77620 to be used as gpio controller.
|
Say yes here to enable the max77620 to be used as GPIO controller.
|
||||||
|
|
||||||
config GPIO_MAX77650
|
config GPIO_MAX77650
|
||||||
tristate "Maxim MAX77650/77651 GPIO support"
|
tristate "Maxim MAX77650/77651 GPIO support"
|
||||||
@@ -1307,8 +1319,8 @@ config GPIO_RC5T583
|
|||||||
help
|
help
|
||||||
Select this option to enable GPIO driver for the Ricoh RC5T583
|
Select this option to enable GPIO driver for the Ricoh RC5T583
|
||||||
chip family.
|
chip family.
|
||||||
This driver provides the support for driving/reading the gpio pins
|
This driver provides the support for driving/reading the GPIO pins
|
||||||
of RC5T583 device through standard gpio library.
|
of RC5T583 device through standard GPIO library.
|
||||||
|
|
||||||
config GPIO_SL28CPLD
|
config GPIO_SL28CPLD
|
||||||
tristate "Kontron sl28cpld GPIO support"
|
tristate "Kontron sl28cpld GPIO support"
|
||||||
@@ -1377,7 +1389,7 @@ config GPIO_TPS65912
|
|||||||
tristate "TI TPS65912 GPIO"
|
tristate "TI TPS65912 GPIO"
|
||||||
depends on MFD_TPS65912
|
depends on MFD_TPS65912
|
||||||
help
|
help
|
||||||
This driver supports TPS65912 gpio chip
|
This driver supports TPS65912 GPIO chip.
|
||||||
|
|
||||||
config GPIO_TPS68470
|
config GPIO_TPS68470
|
||||||
bool "TPS68470 GPIO"
|
bool "TPS68470 GPIO"
|
||||||
@@ -1385,7 +1397,7 @@ config GPIO_TPS68470
|
|||||||
help
|
help
|
||||||
Select this option to enable GPIO driver for the TPS68470
|
Select this option to enable GPIO driver for the TPS68470
|
||||||
chip family.
|
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
|
by the TPS68470. While the 7 GPIOs can be configured as
|
||||||
input or output as appropriate, the sensor related GPIOs
|
input or output as appropriate, the sensor related GPIOs
|
||||||
are "output only" GPIOs.
|
are "output only" GPIOs.
|
||||||
@@ -1430,7 +1442,7 @@ config GPIO_WHISKEY_COVE
|
|||||||
help
|
help
|
||||||
Support for GPIO pins on Whiskey Cove PMIC.
|
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.
|
inside.
|
||||||
|
|
||||||
This driver can also be built as a module. If so, the module will be
|
This driver can also be built as a module. If so, the module will be
|
||||||
@@ -1469,8 +1481,8 @@ config GPIO_AMD8111
|
|||||||
help
|
help
|
||||||
The AMD 8111 southbridge 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
|
Note that usually system firmware/ACPI handles GPIO pins on their
|
||||||
own and users might easily break their systems with uncarefull usage
|
own and users might easily break their systems with uncareful usage
|
||||||
of this driver!
|
of this driver!
|
||||||
|
|
||||||
If unsure, say N
|
If unsure, say N
|
||||||
@@ -1527,7 +1539,7 @@ config GPIO_PCH
|
|||||||
depends on X86_32 || MIPS || COMPILE_TEST
|
depends on X86_32 || MIPS || COMPILE_TEST
|
||||||
select GENERIC_IRQ_CHIP
|
select GENERIC_IRQ_CHIP
|
||||||
help
|
help
|
||||||
This driver is for PCH(Platform controller Hub) GPIO of Intel Topcliff
|
This driver is for PCH (Platform Controller Hub) GPIO of Intel Topcliff,
|
||||||
which is an IOH (Input/Output Hub) for x86 embedded processor.
|
which is an IOH (Input/Output Hub) for x86 embedded processor.
|
||||||
This driver can access PCH GPIO device.
|
This driver can access PCH GPIO device.
|
||||||
|
|
||||||
@@ -1584,7 +1596,7 @@ config GPIO_74X164
|
|||||||
help
|
help
|
||||||
Driver for 74x164 compatible serial-in/parallel-out 8-outputs
|
Driver for 74x164 compatible serial-in/parallel-out 8-outputs
|
||||||
shift registers. This driver can be used to provide access
|
shift registers. This driver can be used to provide access
|
||||||
to more gpio outputs.
|
to more GPIO outputs.
|
||||||
|
|
||||||
config GPIO_MAX3191X
|
config GPIO_MAX3191X
|
||||||
tristate "Maxim MAX3191x industrial serializer"
|
tristate "Maxim MAX3191x industrial serializer"
|
||||||
@@ -1674,6 +1686,7 @@ config GPIO_MOCKUP
|
|||||||
config GPIO_VIRTIO
|
config GPIO_VIRTIO
|
||||||
tristate "VirtIO GPIO support"
|
tristate "VirtIO GPIO support"
|
||||||
depends on VIRTIO
|
depends on VIRTIO
|
||||||
|
select GPIOLIB_IRQCHIP
|
||||||
help
|
help
|
||||||
Say Y here to enable guest support for virtio-based GPIO controllers.
|
Say Y here to enable guest support for virtio-based GPIO controllers.
|
||||||
|
|
||||||
|
|||||||
@@ -184,3 +184,4 @@ obj-$(CONFIG_GPIO_XRA1403) += gpio-xra1403.o
|
|||||||
obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o
|
obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o
|
||||||
obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o
|
obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o
|
||||||
obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o
|
obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o
|
||||||
|
obj-$(CONFIG_GPIO_ZYNQMP_MODEPIN) += gpio-zynqmp-modepin.o
|
||||||
|
|||||||
@@ -247,6 +247,11 @@ struct gpiochip_fwd {
|
|||||||
unsigned long tmp[]; /* values and descs for multiple ops */
|
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)
|
static int gpio_fwd_get_direction(struct gpio_chip *chip, unsigned int offset)
|
||||||
{
|
{
|
||||||
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
|
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,
|
static int gpio_fwd_get_multiple(struct gpiochip_fwd *fwd, unsigned long *mask,
|
||||||
unsigned long *bits)
|
unsigned long *bits)
|
||||||
{
|
{
|
||||||
struct gpio_desc **descs;
|
struct gpio_desc **descs = fwd_tmp_descs(fwd);
|
||||||
unsigned long *values;
|
unsigned long *values = fwd_tmp_values(fwd);
|
||||||
unsigned int i, j = 0;
|
unsigned int i, j = 0;
|
||||||
int error;
|
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);
|
bitmap_clear(values, 0, fwd->chip.ngpio);
|
||||||
for_each_set_bit(i, mask, fwd->chip.ngpio)
|
for_each_set_bit(i, mask, fwd->chip.ngpio)
|
||||||
descs[j++] = fwd->descs[i];
|
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,
|
static void gpio_fwd_set_multiple(struct gpiochip_fwd *fwd, unsigned long *mask,
|
||||||
unsigned long *bits)
|
unsigned long *bits)
|
||||||
{
|
{
|
||||||
struct gpio_desc **descs;
|
struct gpio_desc **descs = fwd_tmp_descs(fwd);
|
||||||
unsigned long *values;
|
unsigned long *values = fwd_tmp_values(fwd);
|
||||||
unsigned int i, j = 0;
|
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) {
|
for_each_set_bit(i, mask, fwd->chip.ngpio) {
|
||||||
__assign_bit(j, values, test_bit(i, bits));
|
__assign_bit(j, values, test_bit(i, bits));
|
||||||
descs[j++] = fwd->descs[i];
|
descs[j++] = fwd->descs[i];
|
||||||
@@ -398,8 +395,8 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev,
|
|||||||
unsigned int i;
|
unsigned int i;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
fwd = devm_kzalloc(dev, struct_size(fwd, tmp,
|
fwd = devm_kzalloc(dev, struct_size(fwd, tmp, fwd_tmp_size(ngpios)),
|
||||||
BITS_TO_LONGS(ngpios) + ngpios), GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!fwd)
|
if (!fwd)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,9 @@ static int max7300_probe(struct i2c_client *client,
|
|||||||
|
|
||||||
static int max7300_remove(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[] = {
|
static const struct i2c_device_id max7300_id[] = {
|
||||||
|
|||||||
@@ -66,7 +66,9 @@ static int max7301_probe(struct spi_device *spi)
|
|||||||
|
|
||||||
static int max7301_remove(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[] = {
|
static const struct spi_device_id max7301_id[] = {
|
||||||
|
|||||||
@@ -220,18 +220,14 @@ exit_destroy:
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(__max730x_probe);
|
EXPORT_SYMBOL_GPL(__max730x_probe);
|
||||||
|
|
||||||
int __max730x_remove(struct device *dev)
|
void __max730x_remove(struct device *dev)
|
||||||
{
|
{
|
||||||
struct max7301 *ts = dev_get_drvdata(dev);
|
struct max7301 *ts = dev_get_drvdata(dev);
|
||||||
|
|
||||||
if (ts == NULL)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
/* Power down the chip and disable IRQ output */
|
/* Power down the chip and disable IRQ output */
|
||||||
ts->write(dev, 0x04, 0x00);
|
ts->write(dev, 0x04, 0x00);
|
||||||
gpiochip_remove(&ts->chip);
|
gpiochip_remove(&ts->chip);
|
||||||
mutex_destroy(&ts->lock);
|
mutex_destroy(&ts->lock);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(__max730x_remove);
|
EXPORT_SYMBOL_GPL(__max730x_remove);
|
||||||
|
|
||||||
|
|||||||
@@ -365,5 +365,4 @@ module_platform_driver(max77620_gpio_driver);
|
|||||||
MODULE_DESCRIPTION("GPIO interface for MAX77620 and MAX20024 PMIC");
|
MODULE_DESCRIPTION("GPIO interface for MAX77620 and MAX20024 PMIC");
|
||||||
MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
|
MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
|
||||||
MODULE_AUTHOR("Chaitanya Bandi <bandik@nvidia.com>");
|
MODULE_AUTHOR("Chaitanya Bandi <bandik@nvidia.com>");
|
||||||
MODULE_ALIAS("platform:max77620-gpio");
|
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
|
|||||||
@@ -139,8 +139,6 @@ static int mc33880_remove(struct spi_device *spi)
|
|||||||
struct mc33880 *mc;
|
struct mc33880 *mc;
|
||||||
|
|
||||||
mc = spi_get_drvdata(spi);
|
mc = spi_get_drvdata(spi);
|
||||||
if (!mc)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
gpiochip_remove(&mc->chip);
|
gpiochip_remove(&mc->chip);
|
||||||
mutex_destroy(&mc->lock);
|
mutex_destroy(&mc->lock);
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020-2021 NVIDIA CORPORATION & AFFILIATES
|
||||||
|
*/
|
||||||
|
|
||||||
#include <linux/bitfield.h>
|
#include <linux/bitfield.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/gpio/driver.h>
|
#include <linux/gpio/driver.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
@@ -43,9 +48,14 @@
|
|||||||
#define YU_GPIO_MODE0 0x0c
|
#define YU_GPIO_MODE0 0x0c
|
||||||
#define YU_GPIO_DATASET 0x14
|
#define YU_GPIO_DATASET 0x14
|
||||||
#define YU_GPIO_DATACLEAR 0x18
|
#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_MODE1_CLEAR 0x50
|
||||||
#define YU_GPIO_MODE0_SET 0x54
|
#define YU_GPIO_MODE0_SET 0x54
|
||||||
#define YU_GPIO_MODE0_CLEAR 0x58
|
#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 {
|
struct mlxbf2_gpio_context_save_regs {
|
||||||
u32 gpio_mode0;
|
u32 gpio_mode0;
|
||||||
@@ -55,6 +65,7 @@ struct mlxbf2_gpio_context_save_regs {
|
|||||||
/* BlueField-2 gpio block context structure. */
|
/* BlueField-2 gpio block context structure. */
|
||||||
struct mlxbf2_gpio_context {
|
struct mlxbf2_gpio_context {
|
||||||
struct gpio_chip gc;
|
struct gpio_chip gc;
|
||||||
|
struct irq_chip irq_chip;
|
||||||
|
|
||||||
/* YU GPIO blocks address */
|
/* YU GPIO blocks address */
|
||||||
void __iomem *gpio_io;
|
void __iomem *gpio_io;
|
||||||
@@ -218,15 +229,114 @@ static int mlxbf2_gpio_direction_output(struct gpio_chip *chip,
|
|||||||
return ret;
|
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. */
|
/* BlueField-2 GPIO driver initialization routine. */
|
||||||
static int
|
static int
|
||||||
mlxbf2_gpio_probe(struct platform_device *pdev)
|
mlxbf2_gpio_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct mlxbf2_gpio_context *gs;
|
struct mlxbf2_gpio_context *gs;
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
|
struct gpio_irq_chip *girq;
|
||||||
struct gpio_chip *gc;
|
struct gpio_chip *gc;
|
||||||
unsigned int npins;
|
unsigned int npins;
|
||||||
int ret;
|
const char *name;
|
||||||
|
int ret, irq;
|
||||||
|
|
||||||
|
name = dev_name(dev);
|
||||||
|
|
||||||
gs = devm_kzalloc(dev, sizeof(*gs), GFP_KERNEL);
|
gs = devm_kzalloc(dev, sizeof(*gs), GFP_KERNEL);
|
||||||
if (!gs)
|
if (!gs)
|
||||||
@@ -266,6 +376,34 @@ mlxbf2_gpio_probe(struct platform_device *pdev)
|
|||||||
gc->ngpio = npins;
|
gc->ngpio = npins;
|
||||||
gc->owner = THIS_MODULE;
|
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);
|
platform_set_drvdata(pdev, gs);
|
||||||
|
|
||||||
ret = devm_gpiochip_add_data(dev, &gs->gc, gs);
|
ret = devm_gpiochip_add_data(dev, &gs->gc, gs);
|
||||||
@@ -320,5 +458,5 @@ static struct platform_driver mlxbf2_gpio_driver = {
|
|||||||
module_platform_driver(mlxbf2_gpio_driver);
|
module_platform_driver(mlxbf2_gpio_driver);
|
||||||
|
|
||||||
MODULE_DESCRIPTION("Mellanox BlueField-2 GPIO Driver");
|
MODULE_DESCRIPTION("Mellanox BlueField-2 GPIO Driver");
|
||||||
MODULE_AUTHOR("Mellanox Technologies");
|
MODULE_AUTHOR("Asmaa Mnebhi <asmaa@nvidia.com>");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ static void realtek_gpio_irq_handler(struct irq_desc *desc)
|
|||||||
status = realtek_gpio_read_isr(ctrl, lines_done / 8);
|
status = realtek_gpio_read_isr(ctrl, lines_done / 8);
|
||||||
port_pin_count = min(gc->ngpio - lines_done, 8U);
|
port_pin_count = min(gc->ngpio - lines_done, 8U);
|
||||||
for_each_set_bit(offset, &status, port_pin_count)
|
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);
|
chained_irq_exit(irq_chip, desc);
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ struct tegra_gpio_soc {
|
|||||||
const char *name;
|
const char *name;
|
||||||
unsigned int instance;
|
unsigned int instance;
|
||||||
|
|
||||||
|
unsigned int num_irqs_per_bank;
|
||||||
|
|
||||||
const struct tegra186_pin_range *pin_ranges;
|
const struct tegra186_pin_range *pin_ranges;
|
||||||
unsigned int num_pin_ranges;
|
unsigned int num_pin_ranges;
|
||||||
const char *pinmux;
|
const char *pinmux;
|
||||||
@@ -81,6 +83,8 @@ struct tegra_gpio {
|
|||||||
unsigned int *irq;
|
unsigned int *irq;
|
||||||
|
|
||||||
const struct tegra_gpio_soc *soc;
|
const struct tegra_gpio_soc *soc;
|
||||||
|
unsigned int num_irqs_per_bank;
|
||||||
|
unsigned int num_banks;
|
||||||
|
|
||||||
void __iomem *secure;
|
void __iomem *secure;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
@@ -450,7 +454,7 @@ static void tegra186_gpio_irq(struct irq_desc *desc)
|
|||||||
struct irq_domain *domain = gpio->gpio.irq.domain;
|
struct irq_domain *domain = gpio->gpio.irq.domain;
|
||||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||||
unsigned int parent = irq_desc_get_irq(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);
|
chained_irq_enter(chip, desc);
|
||||||
|
|
||||||
@@ -463,7 +467,12 @@ static void tegra186_gpio_irq(struct irq_desc *desc)
|
|||||||
base = gpio->base + port->bank * 0x1000 + port->port * 0x200;
|
base = gpio->base + port->bank * 0x1000 + port->port * 0x200;
|
||||||
|
|
||||||
/* skip ports that are not associated with this bank */
|
/* 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;
|
goto skip;
|
||||||
|
|
||||||
value = readl(base + TEGRA186_GPIO_INTERRUPT_STATUS(1));
|
value = readl(base + TEGRA186_GPIO_INTERRUPT_STATUS(1));
|
||||||
@@ -565,6 +574,7 @@ static const struct of_device_id tegra186_pmc_of_match[] = {
|
|||||||
|
|
||||||
static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio)
|
static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio)
|
||||||
{
|
{
|
||||||
|
struct device *dev = gpio->gpio.parent;
|
||||||
unsigned int i, j;
|
unsigned int i, j;
|
||||||
u32 value;
|
u32 value;
|
||||||
|
|
||||||
@@ -583,9 +593,26 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio)
|
|||||||
*/
|
*/
|
||||||
if ((value & TEGRA186_GPIO_CTL_SCR_SEC_REN) == 0 &&
|
if ((value & TEGRA186_GPIO_CTL_SCR_SEC_REN) == 0 &&
|
||||||
(value & TEGRA186_GPIO_CTL_SCR_SEC_WEN) == 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);
|
offset = TEGRA186_GPIO_INT_ROUTE_MAPPING(p, j);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 = readl(base + offset);
|
||||||
value = BIT(port->pins) - 1;
|
value = BIT(port->pins) - 1;
|
||||||
writel(value, base + offset);
|
writel(value, base + offset);
|
||||||
@@ -593,6 +620,32 @@ 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;
|
||||||
|
|
||||||
|
if (gpio->num_irqs_per_bank > gpio->soc->num_irqs_per_bank)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
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)
|
static int tegra186_gpio_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
@@ -608,7 +661,17 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
gpio->soc = device_get_match_data(&pdev->dev);
|
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");
|
gpio->secure = devm_platform_ioremap_resource_byname(pdev, "security");
|
||||||
if (IS_ERR(gpio->secure)) {
|
if (IS_ERR(gpio->secure)) {
|
||||||
gpio->secure = devm_platform_ioremap_resource(pdev, 0);
|
gpio->secure = devm_platform_ioremap_resource(pdev, 0);
|
||||||
@@ -629,6 +692,10 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
gpio->num_irq = err;
|
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),
|
gpio->irq = devm_kcalloc(&pdev->dev, gpio->num_irq, sizeof(*gpio->irq),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!gpio->irq)
|
if (!gpio->irq)
|
||||||
@@ -642,9 +709,6 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
|
|||||||
gpio->irq[i] = err;
|
gpio->irq[i] = err;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpio->gpio.label = gpio->soc->name;
|
|
||||||
gpio->gpio.parent = &pdev->dev;
|
|
||||||
|
|
||||||
gpio->gpio.request = gpiochip_generic_request;
|
gpio->gpio.request = gpiochip_generic_request;
|
||||||
gpio->gpio.free = gpiochip_generic_free;
|
gpio->gpio.free = gpiochip_generic_free;
|
||||||
gpio->gpio.get_direction = tegra186_gpio_get_direction;
|
gpio->gpio.get_direction = tegra186_gpio_get_direction;
|
||||||
@@ -708,7 +772,31 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
|
|||||||
irq->parent_handler = tegra186_gpio_irq;
|
irq->parent_handler = tegra186_gpio_irq;
|
||||||
irq->parent_handler_data = gpio;
|
irq->parent_handler_data = gpio;
|
||||||
irq->num_parents = gpio->num_irq;
|
irq->num_parents = gpio->num_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;
|
irq->parents = gpio->irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gpio->soc->num_irqs_per_bank > 1)
|
||||||
|
tegra186_gpio_init_route_mapping(gpio);
|
||||||
|
|
||||||
np = of_find_matching_node(NULL, tegra186_pmc_of_match);
|
np = of_find_matching_node(NULL, tegra186_pmc_of_match);
|
||||||
if (np) {
|
if (np) {
|
||||||
@@ -719,8 +807,6 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
|
|||||||
return -EPROBE_DEFER;
|
return -EPROBE_DEFER;
|
||||||
}
|
}
|
||||||
|
|
||||||
tegra186_gpio_init_route_mapping(gpio);
|
|
||||||
|
|
||||||
irq->map = devm_kcalloc(&pdev->dev, gpio->gpio.ngpio,
|
irq->map = devm_kcalloc(&pdev->dev, gpio->gpio.ngpio,
|
||||||
sizeof(*irq->map), GFP_KERNEL);
|
sizeof(*irq->map), GFP_KERNEL);
|
||||||
if (!irq->map)
|
if (!irq->map)
|
||||||
@@ -777,6 +863,7 @@ static const struct tegra_gpio_soc tegra186_main_soc = {
|
|||||||
.ports = tegra186_main_ports,
|
.ports = tegra186_main_ports,
|
||||||
.name = "tegra186-gpio",
|
.name = "tegra186-gpio",
|
||||||
.instance = 0,
|
.instance = 0,
|
||||||
|
.num_irqs_per_bank = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TEGRA186_AON_GPIO_PORT(_name, _bank, _port, _pins) \
|
#define TEGRA186_AON_GPIO_PORT(_name, _bank, _port, _pins) \
|
||||||
@@ -803,6 +890,7 @@ static const struct tegra_gpio_soc tegra186_aon_soc = {
|
|||||||
.ports = tegra186_aon_ports,
|
.ports = tegra186_aon_ports,
|
||||||
.name = "tegra186-gpio-aon",
|
.name = "tegra186-gpio-aon",
|
||||||
.instance = 1,
|
.instance = 1,
|
||||||
|
.num_irqs_per_bank = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TEGRA194_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
|
#define TEGRA194_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
|
||||||
@@ -854,6 +942,7 @@ static const struct tegra_gpio_soc tegra194_main_soc = {
|
|||||||
.ports = tegra194_main_ports,
|
.ports = tegra194_main_ports,
|
||||||
.name = "tegra194-gpio",
|
.name = "tegra194-gpio",
|
||||||
.instance = 0,
|
.instance = 0,
|
||||||
|
.num_irqs_per_bank = 8,
|
||||||
.num_pin_ranges = ARRAY_SIZE(tegra194_main_pin_ranges),
|
.num_pin_ranges = ARRAY_SIZE(tegra194_main_pin_ranges),
|
||||||
.pin_ranges = tegra194_main_pin_ranges,
|
.pin_ranges = tegra194_main_pin_ranges,
|
||||||
.pinmux = "nvidia,tegra194-pinmux",
|
.pinmux = "nvidia,tegra194-pinmux",
|
||||||
@@ -880,6 +969,7 @@ static const struct tegra_gpio_soc tegra194_aon_soc = {
|
|||||||
.ports = tegra194_aon_ports,
|
.ports = tegra194_aon_ports,
|
||||||
.name = "tegra194-gpio-aon",
|
.name = "tegra194-gpio-aon",
|
||||||
.instance = 1,
|
.instance = 1,
|
||||||
|
.num_irqs_per_bank = 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id tegra186_gpio_of_match[] = {
|
static const struct of_device_id tegra186_gpio_of_match[] = {
|
||||||
|
|||||||
@@ -230,4 +230,3 @@ module_platform_driver(tps65218_gpio_driver);
|
|||||||
MODULE_AUTHOR("Nicolas Saenz Julienne <nicolassaenzj@gmail.com>");
|
MODULE_AUTHOR("Nicolas Saenz Julienne <nicolassaenzj@gmail.com>");
|
||||||
MODULE_DESCRIPTION("GPO interface for TPS65218 PMICs");
|
MODULE_DESCRIPTION("GPO interface for TPS65218 PMICs");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
MODULE_ALIAS("platform:tps65218-gpio");
|
|
||||||
|
|||||||
@@ -179,8 +179,8 @@ static int uniphier_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
|
|||||||
|
|
||||||
static void uniphier_gpio_irq_mask(struct irq_data *data)
|
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(data->hwirq);
|
u32 mask = BIT(irqd_to_hwirq(data));
|
||||||
|
|
||||||
uniphier_gpio_reg_update(priv, UNIPHIER_GPIO_IRQ_EN, mask, 0);
|
uniphier_gpio_reg_update(priv, UNIPHIER_GPIO_IRQ_EN, mask, 0);
|
||||||
|
|
||||||
@@ -189,8 +189,8 @@ static void uniphier_gpio_irq_mask(struct irq_data *data)
|
|||||||
|
|
||||||
static void uniphier_gpio_irq_unmask(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(data->hwirq);
|
u32 mask = BIT(irqd_to_hwirq(data));
|
||||||
|
|
||||||
uniphier_gpio_reg_update(priv, UNIPHIER_GPIO_IRQ_EN, mask, mask);
|
uniphier_gpio_reg_update(priv, UNIPHIER_GPIO_IRQ_EN, mask, mask);
|
||||||
|
|
||||||
@@ -199,8 +199,8 @@ static void uniphier_gpio_irq_unmask(struct irq_data *data)
|
|||||||
|
|
||||||
static int uniphier_gpio_irq_set_type(struct irq_data *data, unsigned int type)
|
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(data->hwirq);
|
u32 mask = BIT(irqd_to_hwirq(data));
|
||||||
u32 val = 0;
|
u32 val = 0;
|
||||||
|
|
||||||
if (type == IRQ_TYPE_EDGE_BOTH) {
|
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 uniphier_gpio_priv *priv = domain->host_data;
|
||||||
struct gpio_chip *chip = &priv->chip;
|
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,
|
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 uniphier_gpio_priv *priv = domain->host_data;
|
||||||
struct gpio_chip *chip = &priv->chip;
|
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 = {
|
static const struct irq_domain_ops uniphier_gpio_irq_domain_ops = {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
#include <linux/virtio_config.h>
|
#include <linux/virtio_config.h>
|
||||||
#include <uapi/linux/virtio_gpio.h>
|
#include <uapi/linux/virtio_gpio.h>
|
||||||
#include <uapi/linux/virtio_ids.h>
|
#include <uapi/linux/virtio_ids.h>
|
||||||
@@ -28,12 +29,30 @@ struct virtio_gpio_line {
|
|||||||
unsigned int rxlen;
|
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_gpio {
|
||||||
struct virtio_device *vdev;
|
struct virtio_device *vdev;
|
||||||
struct mutex lock; /* Protects virtqueue operation */
|
struct mutex lock; /* Protects virtqueue operation */
|
||||||
struct gpio_chip gc;
|
struct gpio_chip gc;
|
||||||
struct virtio_gpio_line *lines;
|
struct virtio_gpio_line *lines;
|
||||||
struct virtqueue *request_vq;
|
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,
|
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);
|
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)
|
static void virtio_gpio_request_vq(struct virtqueue *vq)
|
||||||
{
|
{
|
||||||
struct virtio_gpio_line *line;
|
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,
|
static int virtio_gpio_alloc_vqs(struct virtio_gpio *vgpio,
|
||||||
struct virtio_device *vdev)
|
struct virtio_device *vdev)
|
||||||
{
|
{
|
||||||
const char * const names[] = { "requestq" };
|
const char * const names[] = { "requestq", "eventq" };
|
||||||
vq_callback_t *cbs[] = {
|
vq_callback_t *cbs[] = {
|
||||||
virtio_gpio_request_vq,
|
virtio_gpio_request_vq,
|
||||||
|
virtio_gpio_event_vq,
|
||||||
};
|
};
|
||||||
struct virtqueue *vqs[1] = { NULL };
|
struct virtqueue *vqs[2] = { NULL, NULL };
|
||||||
int ret;
|
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) {
|
if (ret) {
|
||||||
dev_err(&vdev->dev, "failed to find vqs: %d\n", ret);
|
dev_err(&vdev->dev, "failed to find vqs: %d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -225,11 +477,23 @@ static int virtio_gpio_alloc_vqs(struct virtio_gpio *vgpio,
|
|||||||
|
|
||||||
if (!vqs[0]) {
|
if (!vqs[0]) {
|
||||||
dev_err(&vdev->dev, "failed to find requestq vq\n");
|
dev_err(&vdev->dev, "failed to find requestq vq\n");
|
||||||
return -ENODEV;
|
goto out;
|
||||||
}
|
}
|
||||||
vgpio->request_vq = vqs[0];
|
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;
|
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,
|
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.owner = THIS_MODULE;
|
||||||
vgpio->gc.can_sleep = true;
|
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);
|
ret = virtio_gpio_alloc_vqs(vgpio, vdev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
@@ -357,7 +645,13 @@ static const struct virtio_device_id id_table[] = {
|
|||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(virtio, id_table);
|
MODULE_DEVICE_TABLE(virtio, id_table);
|
||||||
|
|
||||||
|
static const unsigned int features[] = {
|
||||||
|
VIRTIO_GPIO_F_IRQ,
|
||||||
|
};
|
||||||
|
|
||||||
static struct virtio_driver virtio_gpio_driver = {
|
static struct virtio_driver virtio_gpio_driver = {
|
||||||
|
.feature_table = features,
|
||||||
|
.feature_table_size = ARRAY_SIZE(features),
|
||||||
.id_table = id_table,
|
.id_table = id_table,
|
||||||
.probe = virtio_gpio_probe,
|
.probe = virtio_gpio_probe,
|
||||||
.remove = virtio_gpio_remove,
|
.remove = virtio_gpio_remove,
|
||||||
|
|||||||
@@ -371,8 +371,7 @@ static int __maybe_unused xgpio_resume(struct device *dev)
|
|||||||
|
|
||||||
static int __maybe_unused xgpio_runtime_suspend(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 = dev_get_drvdata(dev);
|
||||||
struct xgpio_instance *gpio = platform_get_drvdata(pdev);
|
|
||||||
|
|
||||||
clk_disable(gpio->clk);
|
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)
|
static int __maybe_unused xgpio_runtime_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
struct xgpio_instance *gpio = dev_get_drvdata(dev);
|
||||||
struct xgpio_instance *gpio = platform_get_drvdata(pdev);
|
|
||||||
|
|
||||||
return clk_enable(gpio->clk);
|
return clk_enable(gpio->clk);
|
||||||
}
|
}
|
||||||
|
|||||||
162
drivers/gpio/gpio-zynqmp-modepin.c
Normal file
162
drivers/gpio/gpio-zynqmp-modepin.c
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Driver for the ps-mode pin configuration.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 Xilinx, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/gpio/driver.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/firmware/xlnx-zynqmp.h>
|
||||||
|
|
||||||
|
/* 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 <piyush.mehta@xilinx.com>");
|
||||||
|
MODULE_DESCRIPTION("ZynqMP Boot PS_MODE Configuration");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
obj-$(CONFIG_MLXBF_GIGE) += mlxbf_gige.o
|
obj-$(CONFIG_MLXBF_GIGE) += mlxbf_gige.o
|
||||||
|
|
||||||
mlxbf_gige-y := mlxbf_gige_ethtool.o \
|
mlxbf_gige-y := mlxbf_gige_ethtool.o \
|
||||||
mlxbf_gige_gpio.o \
|
|
||||||
mlxbf_gige_intr.o \
|
mlxbf_gige_intr.o \
|
||||||
mlxbf_gige_main.o \
|
mlxbf_gige_main.o \
|
||||||
mlxbf_gige_mdio.o \
|
mlxbf_gige_mdio.o \
|
||||||
|
|||||||
@@ -51,11 +51,6 @@
|
|||||||
#define MLXBF_GIGE_ERROR_INTR_IDX 0
|
#define MLXBF_GIGE_ERROR_INTR_IDX 0
|
||||||
#define MLXBF_GIGE_RECEIVE_PKT_INTR_IDX 1
|
#define MLXBF_GIGE_RECEIVE_PKT_INTR_IDX 1
|
||||||
#define MLXBF_GIGE_LLU_PLU_INTR_IDX 2
|
#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 {
|
struct mlxbf_gige_stats {
|
||||||
u64 hw_access_errors;
|
u64 hw_access_errors;
|
||||||
@@ -81,11 +76,7 @@ struct mlxbf_gige {
|
|||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
void __iomem *mdio_io;
|
void __iomem *mdio_io;
|
||||||
struct mii_bus *mdiobus;
|
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 lock; /* for packet processing indices */
|
||||||
spinlock_t gpio_lock; /* for GPIO bus access */
|
|
||||||
u16 rx_q_entries;
|
u16 rx_q_entries;
|
||||||
u16 tx_q_entries;
|
u16 tx_q_entries;
|
||||||
u64 *tx_wqe_base;
|
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;
|
extern const struct ethtool_ops mlxbf_gige_ethtool_ops;
|
||||||
void mlxbf_gige_update_tx_wqe_next(struct mlxbf_gige *priv);
|
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__) */
|
#endif /* !defined(__MLXBF_GIGE_H__) */
|
||||||
|
|||||||
@@ -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 <linux/acpi.h>
|
|
||||||
#include <linux/bitfield.h>
|
|
||||||
#include <linux/device.h>
|
|
||||||
#include <linux/err.h>
|
|
||||||
#include <linux/gpio/driver.h>
|
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/irq.h>
|
|
||||||
#include <linux/irqdomain.h>
|
|
||||||
#include <linux/irqreturn.h>
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/property.h>
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
@@ -280,8 +280,8 @@ static int mlxbf_gige_probe(struct platform_device *pdev)
|
|||||||
void __iomem *llu_base;
|
void __iomem *llu_base;
|
||||||
void __iomem *plu_base;
|
void __iomem *plu_base;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
int addr, phy_irq;
|
||||||
u64 control;
|
u64 control;
|
||||||
int addr;
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
base = devm_platform_ioremap_resource(pdev, MLXBF_GIGE_RES_MAC);
|
base = devm_platform_ioremap_resource(pdev, MLXBF_GIGE_RES_MAC);
|
||||||
@@ -316,20 +316,12 @@ static int mlxbf_gige_probe(struct platform_device *pdev)
|
|||||||
priv->pdev = pdev;
|
priv->pdev = pdev;
|
||||||
|
|
||||||
spin_lock_init(&priv->lock);
|
spin_lock_init(&priv->lock);
|
||||||
spin_lock_init(&priv->gpio_lock);
|
|
||||||
|
|
||||||
/* Attach MDIO device */
|
/* Attach MDIO device */
|
||||||
err = mlxbf_gige_mdio_probe(pdev, priv);
|
err = mlxbf_gige_mdio_probe(pdev, priv);
|
||||||
if (err)
|
if (err)
|
||||||
return 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->base = base;
|
||||||
priv->llu_base = llu_base;
|
priv->llu_base = llu_base;
|
||||||
priv->plu_base = plu_base;
|
priv->plu_base = plu_base;
|
||||||
@@ -350,6 +342,12 @@ static int mlxbf_gige_probe(struct platform_device *pdev)
|
|||||||
priv->rx_irq = platform_get_irq(pdev, MLXBF_GIGE_RECEIVE_PKT_INTR_IDX);
|
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);
|
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);
|
phydev = phy_find_first(priv->mdiobus);
|
||||||
if (!phydev) {
|
if (!phydev) {
|
||||||
err = -ENODEV;
|
err = -ENODEV;
|
||||||
@@ -357,8 +355,8 @@ static int mlxbf_gige_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
addr = phydev->mdio.addr;
|
addr = phydev->mdio.addr;
|
||||||
priv->mdiobus->irq[addr] = priv->phy_irq;
|
priv->mdiobus->irq[addr] = phy_irq;
|
||||||
phydev->irq = priv->phy_irq;
|
phydev->irq = phy_irq;
|
||||||
|
|
||||||
err = phy_connect_direct(netdev, phydev,
|
err = phy_connect_direct(netdev, phydev,
|
||||||
mlxbf_gige_adjust_link,
|
mlxbf_gige_adjust_link,
|
||||||
@@ -394,7 +392,6 @@ static int mlxbf_gige_probe(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
mlxbf_gige_gpio_free(priv);
|
|
||||||
mlxbf_gige_mdio_remove(priv);
|
mlxbf_gige_mdio_remove(priv);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -405,7 +402,6 @@ static int mlxbf_gige_remove(struct platform_device *pdev)
|
|||||||
|
|
||||||
unregister_netdev(priv->netdev);
|
unregister_netdev(priv->netdev);
|
||||||
phy_disconnect(priv->netdev->phydev);
|
phy_disconnect(priv->netdev->phydev);
|
||||||
mlxbf_gige_gpio_free(priv);
|
|
||||||
mlxbf_gige_mdio_remove(priv);
|
mlxbf_gige_mdio_remove(priv);
|
||||||
platform_set_drvdata(pdev, NULL);
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ enum pm_api_id {
|
|||||||
PM_SET_REQUIREMENT = 15,
|
PM_SET_REQUIREMENT = 15,
|
||||||
PM_RESET_ASSERT = 17,
|
PM_RESET_ASSERT = 17,
|
||||||
PM_RESET_GET_STATUS = 18,
|
PM_RESET_GET_STATUS = 18,
|
||||||
|
PM_MMIO_WRITE = 19,
|
||||||
|
PM_MMIO_READ = 20,
|
||||||
PM_PM_INIT_FINALIZE = 21,
|
PM_PM_INIT_FINALIZE = 21,
|
||||||
PM_FPGA_LOAD = 22,
|
PM_FPGA_LOAD = 22,
|
||||||
PM_FPGA_GET_STATUS = 23,
|
PM_FPGA_GET_STATUS = 23,
|
||||||
@@ -397,6 +399,8 @@ int zynqmp_pm_ospi_mux_select(u32 dev_id, u32 select);
|
|||||||
int zynqmp_pm_reset_assert(const enum zynqmp_pm_reset reset,
|
int zynqmp_pm_reset_assert(const enum zynqmp_pm_reset reset,
|
||||||
const enum zynqmp_pm_reset_action assert_flag);
|
const enum zynqmp_pm_reset_action assert_flag);
|
||||||
int zynqmp_pm_reset_get_status(const enum zynqmp_pm_reset reset, u32 *status);
|
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_init_finalize(void);
|
||||||
int zynqmp_pm_set_suspend_mode(u32 mode);
|
int zynqmp_pm_set_suspend_mode(u32 mode);
|
||||||
int zynqmp_pm_request_node(const u32 node, const u32 capabilities,
|
int zynqmp_pm_request_node(const u32 node, const u32 capabilities,
|
||||||
@@ -532,6 +536,16 @@ static inline int zynqmp_pm_reset_get_status(const enum zynqmp_pm_reset reset,
|
|||||||
return -ENODEV;
|
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)
|
static inline int zynqmp_pm_init_finalize(void)
|
||||||
{
|
{
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|||||||
@@ -31,6 +31,6 @@ struct max7301_platform_data {
|
|||||||
u32 input_pullup_active;
|
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);
|
extern int __max730x_probe(struct max7301 *ts);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -5,12 +5,16 @@
|
|||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
/* Virtio GPIO Feature bits */
|
||||||
|
#define VIRTIO_GPIO_F_IRQ 0
|
||||||
|
|
||||||
/* Virtio GPIO request types */
|
/* Virtio GPIO request types */
|
||||||
#define VIRTIO_GPIO_MSG_GET_NAMES 0x0001
|
#define VIRTIO_GPIO_MSG_GET_NAMES 0x0001
|
||||||
#define VIRTIO_GPIO_MSG_GET_DIRECTION 0x0002
|
#define VIRTIO_GPIO_MSG_GET_DIRECTION 0x0002
|
||||||
#define VIRTIO_GPIO_MSG_SET_DIRECTION 0x0003
|
#define VIRTIO_GPIO_MSG_SET_DIRECTION 0x0003
|
||||||
#define VIRTIO_GPIO_MSG_GET_VALUE 0x0004
|
#define VIRTIO_GPIO_MSG_GET_VALUE 0x0004
|
||||||
#define VIRTIO_GPIO_MSG_SET_VALUE 0x0005
|
#define VIRTIO_GPIO_MSG_SET_VALUE 0x0005
|
||||||
|
#define VIRTIO_GPIO_MSG_IRQ_TYPE 0x0006
|
||||||
|
|
||||||
/* Possible values of the status field */
|
/* Possible values of the status field */
|
||||||
#define VIRTIO_GPIO_STATUS_OK 0x0
|
#define VIRTIO_GPIO_STATUS_OK 0x0
|
||||||
@@ -21,11 +25,19 @@
|
|||||||
#define VIRTIO_GPIO_DIRECTION_OUT 0x01
|
#define VIRTIO_GPIO_DIRECTION_OUT 0x01
|
||||||
#define VIRTIO_GPIO_DIRECTION_IN 0x02
|
#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 {
|
struct virtio_gpio_config {
|
||||||
__le16 ngpio;
|
__le16 ngpio;
|
||||||
__u8 padding[2];
|
__u8 padding[2];
|
||||||
__le32 gpio_names_size;
|
__le32 gpio_names_size;
|
||||||
} __packed;
|
};
|
||||||
|
|
||||||
/* Virtio GPIO Request / Response */
|
/* Virtio GPIO Request / Response */
|
||||||
struct virtio_gpio_request {
|
struct virtio_gpio_request {
|
||||||
@@ -44,4 +56,17 @@ struct virtio_gpio_response_get_names {
|
|||||||
__u8 value[];
|
__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 */
|
#endif /* _LINUX_VIRTIO_GPIO_H */
|
||||||
|
|||||||
Reference in New Issue
Block a user