gpio updates for v5.16
- 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 -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEFp3rbAvDxGAT0sefEacuoBRx13IFAmGJH+MACgkQEacuoBRx 13JjGg//YKoAby35hcclft5xJYDwnx2or001TJ+nFLOQ9d24gUI7+UMFVnZ2ifMP 9F/yO0934pkHNUqcTuIMbeALW6Cg2DglXJFCQSXT6Ng5ETjENOhLHRa4bRdoIEim HC+YJg/DS6CpSkMAH09ljZfkTPz3B2DHt+Y4phIDg2PIrt+UUsN/RC685B4jSwds pcAwdP1mqBV1da8uRjtfc7UcFSxR6/lvBBWQSo9RAEse5R6Rmm7XIOKBlQ+Exdts jAmVqxz7BnfuYug9Y3y3fOiAQYoRvZBaSjkeMJALub3GEd3W30vAKoQhq7gC9ea3 XvCj7V6C59JVp9aQUXo1cL6VqcesSGMWoRi2vll8/7gluXV2gCoNPVb+9KwxG9Ed +xwXBpCsOVQCXl8jgIHBWNfxOS31wafWlK8Ng42Cjov+uQfsccgufTvhQXBumKbM oECSOgjg9k3/AMpym3buOgAzqSec5R1iiSWHNPT+P6cSFXa2Bl3A8X0RgF/TwXPC Bn9BDfSiYfOcD6y6yx2GPMCrCXNJRWgfJ/2j3Jsb66XtGwtBDhmK7rcqcg9qIws9 mWWyISiJgO0gfk24hIIw7OxeKCidm3iVskryJAQUfTX+BBcR5nZfiuF3EeDXZv95 whkSoN7J5fmMYDDJ7wd0pFwbW5fe5C/i/2tjOlNBgdXsNgAv/r4= =mKh0 -----END PGP SIGNATURE----- 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:
commit
d20f7a09e5
@ -29,6 +29,8 @@ properties:
|
||||
|
||||
gpio-controller: true
|
||||
|
||||
gpio-line-names: true
|
||||
|
||||
"#gpio-cells":
|
||||
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) */
|
||||
#define PM_API_FEATURE_CHECK_MAX_ORDER 7
|
||||
|
||||
/* CRL registers and bitfields */
|
||||
#define CRL_APB_BASE 0xFF5E0000U
|
||||
/* BOOT_PIN_CTRL- Used to control the mode pins after boot */
|
||||
#define CRL_APB_BOOT_PIN_CTRL (CRL_APB_BASE + (0x250U))
|
||||
/* BOOT_PIN_CTRL_MASK- out_val[11:8], out_en[3:0] */
|
||||
#define CRL_APB_BOOTPIN_CTRL_MASK 0xF0FU
|
||||
|
||||
static bool feature_check_enabled;
|
||||
static DEFINE_HASHTABLE(pm_api_features_map, PM_API_FEATURE_CHECK_MAX_ORDER);
|
||||
|
||||
@ -942,6 +949,45 @@ int zynqmp_pm_pinctrl_set_config(const u32 pin, const u32 param,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_set_config);
|
||||
|
||||
/**
|
||||
* zynqmp_pm_bootmode_read() - PM Config API for read bootpin status
|
||||
* @ps_mode: Returned output value of ps_mode
|
||||
*
|
||||
* This API function is to be used for notify the power management controller
|
||||
* to read bootpin status.
|
||||
*
|
||||
* Return: status, either success or error+reason
|
||||
*/
|
||||
unsigned int zynqmp_pm_bootmode_read(u32 *ps_mode)
|
||||
{
|
||||
unsigned int ret;
|
||||
u32 ret_payload[PAYLOAD_ARG_CNT];
|
||||
|
||||
ret = zynqmp_pm_invoke_fn(PM_MMIO_READ, CRL_APB_BOOT_PIN_CTRL, 0,
|
||||
0, 0, ret_payload);
|
||||
|
||||
*ps_mode = ret_payload[1];
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(zynqmp_pm_bootmode_read);
|
||||
|
||||
/**
|
||||
* zynqmp_pm_bootmode_write() - PM Config API for Configure bootpin
|
||||
* @ps_mode: Value to be written to the bootpin ctrl register
|
||||
*
|
||||
* This API function is to be used for notify the power management controller
|
||||
* to configure bootpin.
|
||||
*
|
||||
* Return: Returns status, either success or error+reason
|
||||
*/
|
||||
int zynqmp_pm_bootmode_write(u32 ps_mode)
|
||||
{
|
||||
return zynqmp_pm_invoke_fn(PM_MMIO_WRITE, CRL_APB_BOOT_PIN_CTRL,
|
||||
CRL_APB_BOOTPIN_CTRL_MASK, ps_mode, 0, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(zynqmp_pm_bootmode_write);
|
||||
|
||||
/**
|
||||
* zynqmp_pm_init_finalize() - PM call to inform firmware that the caller
|
||||
* master has initialized its own power management
|
||||
|
@ -15,7 +15,7 @@ menuconfig GPIOLIB
|
||||
bool "GPIO Support"
|
||||
help
|
||||
This enables GPIO support through the generic GPIO library.
|
||||
You only need to enable this, if you also want to enable
|
||||
You only need to enable this if you also want to enable
|
||||
one or more of the GPIO drivers below.
|
||||
|
||||
If unsure, say N.
|
||||
@ -140,8 +140,8 @@ config GPIO_AMDPT
|
||||
depends on ACPI
|
||||
select GPIO_GENERIC
|
||||
help
|
||||
driver for GPIO functionality on Promontory IOHub
|
||||
Require ACPI ASL code to enumerate as a platform device.
|
||||
Driver for GPIO functionality on Promontory IOHub.
|
||||
Requires ACPI ASL code to enumerate as a platform device.
|
||||
|
||||
config GPIO_ASPEED
|
||||
tristate "Aspeed GPIO support"
|
||||
@ -306,7 +306,7 @@ config GPIO_HISI
|
||||
help
|
||||
Say Y or M here to build support for the HiSilicon GPIO controller
|
||||
driver GPIO block.
|
||||
This GPIO controller support double-edge interrupt and multi-core
|
||||
This GPIO controller supports double-edge interrupt and multi-core
|
||||
concurrent access.
|
||||
|
||||
config GPIO_HLWD
|
||||
@ -326,7 +326,7 @@ config GPIO_ICH
|
||||
help
|
||||
Say yes here to support the GPIO functionality of a number of Intel
|
||||
ICH-based chipsets. Currently supported devices: ICH6, ICH7, ICH8
|
||||
ICH9, ICH10, Series 5/3400 (eg Ibex Peak), Series 6/C200 (eg
|
||||
ICH9, ICH10, Series 5/3400 (e.g. Ibex Peak), Series 6/C200 (e.g.
|
||||
Cougar Point), NM10 (Tiger Point), and 3100 (Whitmore Lake).
|
||||
|
||||
If unsure, say N.
|
||||
@ -337,7 +337,7 @@ config GPIO_IOP
|
||||
select GPIO_GENERIC
|
||||
help
|
||||
Say yes here to support the GPIO functionality of a number of Intel
|
||||
IOP32X or IOP33X.
|
||||
IOP32X or IOP33X series of chips.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
@ -364,7 +364,7 @@ config GPIO_LOONGSON
|
||||
bool "Loongson-2/3 GPIO support"
|
||||
depends on CPU_LOONGSON2EF || CPU_LOONGSON64
|
||||
help
|
||||
driver for GPIO functionality on Loongson-2F/3A/3B processors.
|
||||
Driver for GPIO functionality on Loongson-2F/3A/3B processors.
|
||||
|
||||
config GPIO_LPC18XX
|
||||
tristate "NXP LPC18XX/43XX GPIO support"
|
||||
@ -392,15 +392,15 @@ config GPIO_MENZ127
|
||||
depends on MCB
|
||||
select GPIO_GENERIC
|
||||
help
|
||||
Say yes here to support the MEN 16Z127 GPIO Controller
|
||||
Say yes here to support the MEN 16Z127 GPIO Controller.
|
||||
|
||||
config GPIO_MM_LANTIQ
|
||||
bool "Lantiq Memory mapped GPIOs"
|
||||
depends on LANTIQ && SOC_XWAY
|
||||
help
|
||||
This enables support for memory mapped GPIOs on the External Bus Unit
|
||||
(EBU) found on Lantiq SoCs. The gpios are output only as they are
|
||||
created by attaching a 16bit latch to the bus.
|
||||
(EBU) found on Lantiq SoCs. The GPIOs are output only as they are
|
||||
created by attaching a 16-bit latch to the bus.
|
||||
|
||||
config GPIO_MPC5200
|
||||
def_bool y
|
||||
@ -424,7 +424,7 @@ config GPIO_MT7621
|
||||
select GPIO_GENERIC
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Say yes here to support the Mediatek MT7621 SoC GPIO device
|
||||
Say yes here to support the Mediatek MT7621 SoC GPIO device.
|
||||
|
||||
config GPIO_MVEBU
|
||||
def_bool y
|
||||
@ -469,7 +469,7 @@ config GPIO_PL061
|
||||
select IRQ_DOMAIN
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Say yes here to support the PrimeCell PL061 GPIO device
|
||||
Say yes here to support the PrimeCell PL061 GPIO device.
|
||||
|
||||
config GPIO_PMIC_EIC_SPRD
|
||||
tristate "Spreadtrum PMIC EIC support"
|
||||
@ -483,7 +483,7 @@ config GPIO_PXA
|
||||
bool "PXA GPIO support"
|
||||
depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST
|
||||
help
|
||||
Say yes here to support the PXA GPIO device
|
||||
Say yes here to support the PXA GPIO device.
|
||||
|
||||
config GPIO_RCAR
|
||||
tristate "Renesas R-Car and RZ/G GPIO support"
|
||||
@ -573,7 +573,7 @@ config GPIO_SPEAR_SPICS
|
||||
depends on PLAT_SPEAR
|
||||
select GENERIC_IRQ_CHIP
|
||||
help
|
||||
Say yes here to support ST SPEAr SPI Chip Select as GPIO device
|
||||
Say yes here to support ST SPEAr SPI Chip Select as GPIO device.
|
||||
|
||||
config GPIO_SPRD
|
||||
tristate "Spreadtrum GPIO support"
|
||||
@ -598,8 +598,8 @@ config GPIO_STP_XWAY
|
||||
help
|
||||
This enables support for the Serial To Parallel (STP) unit found on
|
||||
XWAY SoC. The STP allows the SoC to drive a shift registers cascade,
|
||||
that can be up to 24 bit. This peripheral is aimed at driving leds.
|
||||
Some of the gpios/leds can be auto updated by the soc with dsl and
|
||||
that can be up to 24 bits. This peripheral is aimed at driving LEDs.
|
||||
Some of the GPIOs/LEDs can be auto updated by the SoC with DSL and
|
||||
phy status.
|
||||
|
||||
config GPIO_SYSCON
|
||||
@ -679,10 +679,10 @@ config GPIO_VISCONTI
|
||||
Say yes here to support GPIO on Tohisba Visconti.
|
||||
|
||||
config GPIO_VR41XX
|
||||
tristate "NEC VR4100 series General-purpose I/O Uint support"
|
||||
tristate "NEC VR4100 series General-purpose I/O Unit support"
|
||||
depends on CPU_VR41XX
|
||||
help
|
||||
Say yes here to support the NEC VR4100 series General-purpose I/O Uint
|
||||
Say yes here to support the NEC VR4100 series General-purpose I/O Unit.
|
||||
|
||||
config GPIO_VX855
|
||||
tristate "VIA VX855/VX875 GPIO"
|
||||
@ -690,14 +690,14 @@ config GPIO_VX855
|
||||
select MFD_CORE
|
||||
select MFD_VX855
|
||||
help
|
||||
Support access to the VX855/VX875 GPIO lines through the gpio library.
|
||||
Support access to the VX855/VX875 GPIO lines through the GPIO library.
|
||||
|
||||
This driver provides common support for accessing the device,
|
||||
additional drivers must be enabled in order to use the
|
||||
This driver provides common support for accessing the device.
|
||||
Additional drivers must be enabled in order to use the
|
||||
functionality of the device.
|
||||
|
||||
config GPIO_WCD934X
|
||||
tristate "Qualcomm Technologies Inc WCD9340/WCD9341 gpio controller driver"
|
||||
tristate "Qualcomm Technologies Inc WCD9340/WCD9341 GPIO controller driver"
|
||||
depends on MFD_WCD934X && OF_GPIO
|
||||
help
|
||||
This driver is to support GPIO block found on the Qualcomm Technologies
|
||||
@ -727,7 +727,7 @@ config GPIO_XILINX
|
||||
select GPIOLIB_IRQCHIP
|
||||
depends on OF_GPIO
|
||||
help
|
||||
Say yes here to support the Xilinx FPGA GPIO device
|
||||
Say yes here to support the Xilinx FPGA GPIO device.
|
||||
|
||||
config GPIO_XLP
|
||||
tristate "Netlogic XLP GPIO support"
|
||||
@ -748,7 +748,7 @@ config GPIO_XTENSA
|
||||
depends on !SMP
|
||||
help
|
||||
Say yes here to support the Xtensa internal GPIO32 IMPWIRE (input)
|
||||
and EXPSTATE (output) ports
|
||||
and EXPSTATE (output) ports.
|
||||
|
||||
config GPIO_ZEVIO
|
||||
bool "LSI ZEVIO SoC memory mapped GPIOs"
|
||||
@ -763,6 +763,18 @@ config GPIO_ZYNQ
|
||||
help
|
||||
Say yes here to support Xilinx Zynq GPIO controller.
|
||||
|
||||
config GPIO_ZYNQMP_MODEPIN
|
||||
tristate "ZynqMP ps-mode pin GPIO configuration driver"
|
||||
depends on ZYNQMP_FIRMWARE
|
||||
default ZYNQMP_FIRMWARE
|
||||
help
|
||||
Say yes here to support the ZynqMP ps-mode pin GPIO configuration
|
||||
driver.
|
||||
|
||||
This ps-mode pin GPIO driver is based on GPIO framework. PS_MODE
|
||||
is 4-bits boot mode pins. It sets and gets the status of
|
||||
the ps-mode pin. Every pin can be configured as input/output.
|
||||
|
||||
config GPIO_LOONGSON1
|
||||
tristate "Loongson1 GPIO support"
|
||||
depends on MACH_LOONGSON32
|
||||
@ -773,12 +785,12 @@ config GPIO_LOONGSON1
|
||||
config GPIO_AMD_FCH
|
||||
tristate "GPIO support for AMD Fusion Controller Hub (G-series SOCs)"
|
||||
help
|
||||
This option enables driver for GPIO on AMDs Fusion Controller Hub,
|
||||
as found on G-series SOCs (eg. GX-412TC)
|
||||
This option enables driver for GPIO on AMD's Fusion Controller Hub,
|
||||
as found on G-series SOCs (e.g. GX-412TC).
|
||||
|
||||
Note: This driver doesn't registers itself automatically, as it
|
||||
needs to be provided with platform specific configuration.
|
||||
(See eg. CONFIG_PCENGINES_APU2.)
|
||||
Note: This driver doesn't register itself automatically, as it
|
||||
needs to be provided with platform-specific configuration.
|
||||
(See e.g. CONFIG_PCENGINES_APU2.)
|
||||
|
||||
config GPIO_MSC313
|
||||
bool "MStar MSC313 GPIO support"
|
||||
@ -788,7 +800,7 @@ config GPIO_MSC313
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
help
|
||||
Say Y here to support the main GPIO block on MStar/SigmaStar
|
||||
ARMv7 based SoCs.
|
||||
ARMv7-based SoCs.
|
||||
|
||||
config GPIO_IDT3243X
|
||||
tristate "IDT 79RC3243X GPIO support"
|
||||
@ -797,7 +809,7 @@ config GPIO_IDT3243X
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Select this option to enable GPIO driver for
|
||||
IDT 79RC3243X based devices like Mikrotik RB532.
|
||||
IDT 79RC3243X-based devices like Mikrotik RB532.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called gpio-idt3243x.
|
||||
@ -875,7 +887,7 @@ config GPIO_IT87
|
||||
well.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called gpio_it87
|
||||
be called gpio_it87.
|
||||
|
||||
config GPIO_SCH
|
||||
tristate "Intel SCH/TunnelCreek/Centerton/Quark X1000 GPIO"
|
||||
@ -891,7 +903,7 @@ config GPIO_SCH
|
||||
powered by the core power rail and are turned off during sleep
|
||||
modes (S3 and higher). The remaining four GPIOs are powered by
|
||||
the Intel SCH suspend power supply. These GPIOs remain
|
||||
active during S3. The suspend powered GPIOs can be used to wake the
|
||||
active during S3. The suspend-powered GPIOs can be used to wake the
|
||||
system from the Suspend-to-RAM state.
|
||||
|
||||
The Intel Tunnel Creek processor has 5 GPIOs powered by the
|
||||
@ -1044,7 +1056,7 @@ config GPIO_PCA953X_IRQ
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Say yes here to enable the pca953x to be used as an interrupt
|
||||
controller. It requires the driver to be built in the kernel.
|
||||
controller.
|
||||
|
||||
config GPIO_PCA9570
|
||||
tristate "PCA9570 4-Bit I2C GPO expander"
|
||||
@ -1171,7 +1183,7 @@ config GPIO_CRYSTAL_COVE
|
||||
help
|
||||
Support for GPIO pins on Crystal Cove PMIC.
|
||||
|
||||
Say Yes if you have a Intel SoC based tablet with Crystal Cove PMIC
|
||||
Say Yes if you have a Intel SoC-based tablet with Crystal Cove PMIC
|
||||
inside.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
@ -1201,7 +1213,7 @@ config GPIO_DA9055
|
||||
Say yes here to enable the GPIO driver for the DA9055 chip.
|
||||
|
||||
The Dialog DA9055 PMIC chip has 3 GPIO pins that can be
|
||||
be controller by this driver.
|
||||
be controlled by this driver.
|
||||
|
||||
If driver is built as a module it will be called gpio-da9055.
|
||||
|
||||
@ -1223,7 +1235,7 @@ config HTC_EGPIO
|
||||
help
|
||||
This driver supports the CPLD egpio chip present on
|
||||
several HTC phones. It provides basic support for input
|
||||
pins, output pins, and irqs.
|
||||
pins, output pins, and IRQs.
|
||||
|
||||
config GPIO_JANZ_TTL
|
||||
tristate "Janz VMOD-TTL Digital IO Module"
|
||||
@ -1284,8 +1296,8 @@ config GPIO_MAX77620
|
||||
help
|
||||
GPIO driver for MAX77620 and MAX20024 PMIC from Maxim Semiconductor.
|
||||
MAX77620 PMIC has 8 pins that can be configured as GPIOs. The
|
||||
driver also provides interrupt support for each of the gpios.
|
||||
Say yes here to enable the max77620 to be used as gpio controller.
|
||||
driver also provides interrupt support for each of the GPIOs.
|
||||
Say yes here to enable the max77620 to be used as GPIO controller.
|
||||
|
||||
config GPIO_MAX77650
|
||||
tristate "Maxim MAX77650/77651 GPIO support"
|
||||
@ -1307,8 +1319,8 @@ config GPIO_RC5T583
|
||||
help
|
||||
Select this option to enable GPIO driver for the Ricoh RC5T583
|
||||
chip family.
|
||||
This driver provides the support for driving/reading the gpio pins
|
||||
of RC5T583 device through standard gpio library.
|
||||
This driver provides the support for driving/reading the GPIO pins
|
||||
of RC5T583 device through standard GPIO library.
|
||||
|
||||
config GPIO_SL28CPLD
|
||||
tristate "Kontron sl28cpld GPIO support"
|
||||
@ -1377,7 +1389,7 @@ config GPIO_TPS65912
|
||||
tristate "TI TPS65912 GPIO"
|
||||
depends on MFD_TPS65912
|
||||
help
|
||||
This driver supports TPS65912 gpio chip
|
||||
This driver supports TPS65912 GPIO chip.
|
||||
|
||||
config GPIO_TPS68470
|
||||
bool "TPS68470 GPIO"
|
||||
@ -1385,7 +1397,7 @@ config GPIO_TPS68470
|
||||
help
|
||||
Select this option to enable GPIO driver for the TPS68470
|
||||
chip family.
|
||||
There are 7 GPIOs and few sensor related GPIOs supported
|
||||
There are 7 GPIOs and few sensor-related GPIOs supported
|
||||
by the TPS68470. While the 7 GPIOs can be configured as
|
||||
input or output as appropriate, the sensor related GPIOs
|
||||
are "output only" GPIOs.
|
||||
@ -1430,7 +1442,7 @@ config GPIO_WHISKEY_COVE
|
||||
help
|
||||
Support for GPIO pins on Whiskey Cove PMIC.
|
||||
|
||||
Say Yes if you have a Intel SoC based tablet with Whiskey Cove PMIC
|
||||
Say Yes if you have an Intel SoC-based tablet with Whiskey Cove PMIC
|
||||
inside.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
@ -1467,10 +1479,10 @@ config GPIO_AMD8111
|
||||
depends on X86 || COMPILE_TEST
|
||||
depends on HAS_IOPORT_MAP
|
||||
help
|
||||
The AMD 8111 south bridge contains 32 GPIO pins which can be used.
|
||||
The AMD 8111 southbridge contains 32 GPIO pins which can be used.
|
||||
|
||||
Note, that usually system firmware/ACPI handles GPIO pins on their
|
||||
own and users might easily break their systems with uncarefull usage
|
||||
Note that usually system firmware/ACPI handles GPIO pins on their
|
||||
own and users might easily break their systems with uncareful usage
|
||||
of this driver!
|
||||
|
||||
If unsure, say N
|
||||
@ -1518,22 +1530,22 @@ config GPIO_ML_IOH
|
||||
select GENERIC_IRQ_CHIP
|
||||
help
|
||||
ML7213 is companion chip for Intel Atom E6xx series.
|
||||
This driver can be used for OKI SEMICONDUCTOR ML7213 IOH(Input/Output
|
||||
Hub) which is for IVI(In-Vehicle Infotainment) use.
|
||||
This driver can be used for OKI SEMICONDUCTOR ML7213 IOH (Input/Output
|
||||
Hub) which is for IVI (In-Vehicle Infotainment) use.
|
||||
This driver can access the IOH's GPIO device.
|
||||
|
||||
config GPIO_PCH
|
||||
tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7223/ML7831) GPIO"
|
||||
tristate "Intel EG20T PCH/LAPIS Semiconductor IOH (ML7223/ML7831) GPIO"
|
||||
depends on X86_32 || MIPS || COMPILE_TEST
|
||||
select GENERIC_IRQ_CHIP
|
||||
help
|
||||
This driver is for PCH(Platform controller Hub) GPIO of Intel Topcliff
|
||||
which is an IOH(Input/Output Hub) for x86 embedded processor.
|
||||
This driver is for PCH (Platform Controller Hub) GPIO of Intel Topcliff,
|
||||
which is an IOH (Input/Output Hub) for x86 embedded processor.
|
||||
This driver can access PCH GPIO device.
|
||||
|
||||
This driver also can be used for LAPIS Semiconductor IOH(Input/
|
||||
This driver also can be used for LAPIS Semiconductor IOH (Input/
|
||||
Output Hub), ML7223 and ML7831.
|
||||
ML7223 IOH is for MP(Media Phone) use.
|
||||
ML7223 IOH is for MP (Media Phone) use.
|
||||
ML7831 IOH is for general purpose use.
|
||||
ML7223/ML7831 is companion chip for Intel Atom E6xx series.
|
||||
ML7223/ML7831 is completely compatible for Intel EG20T PCH.
|
||||
@ -1584,7 +1596,7 @@ config GPIO_74X164
|
||||
help
|
||||
Driver for 74x164 compatible serial-in/parallel-out 8-outputs
|
||||
shift registers. This driver can be used to provide access
|
||||
to more gpio outputs.
|
||||
to more GPIO outputs.
|
||||
|
||||
config GPIO_MAX3191X
|
||||
tristate "Maxim MAX3191x industrial serializer"
|
||||
@ -1674,6 +1686,7 @@ config GPIO_MOCKUP
|
||||
config GPIO_VIRTIO
|
||||
tristate "VirtIO GPIO support"
|
||||
depends on VIRTIO
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Say Y here to enable guest support for virtio-based GPIO controllers.
|
||||
|
||||
|
@ -184,3 +184,4 @@ obj-$(CONFIG_GPIO_XRA1403) += gpio-xra1403.o
|
||||
obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o
|
||||
obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o
|
||||
obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o
|
||||
obj-$(CONFIG_GPIO_ZYNQMP_MODEPIN) += gpio-zynqmp-modepin.o
|
||||
|
@ -247,6 +247,11 @@ struct gpiochip_fwd {
|
||||
unsigned long tmp[]; /* values and descs for multiple ops */
|
||||
};
|
||||
|
||||
#define fwd_tmp_values(fwd) &(fwd)->tmp[0]
|
||||
#define fwd_tmp_descs(fwd) (void *)&(fwd)->tmp[BITS_TO_LONGS((fwd)->chip.ngpio)]
|
||||
|
||||
#define fwd_tmp_size(ngpios) (BITS_TO_LONGS((ngpios)) + (ngpios))
|
||||
|
||||
static int gpio_fwd_get_direction(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
|
||||
@ -279,15 +284,11 @@ static int gpio_fwd_get(struct gpio_chip *chip, unsigned int offset)
|
||||
static int gpio_fwd_get_multiple(struct gpiochip_fwd *fwd, unsigned long *mask,
|
||||
unsigned long *bits)
|
||||
{
|
||||
struct gpio_desc **descs;
|
||||
unsigned long *values;
|
||||
struct gpio_desc **descs = fwd_tmp_descs(fwd);
|
||||
unsigned long *values = fwd_tmp_values(fwd);
|
||||
unsigned int i, j = 0;
|
||||
int error;
|
||||
|
||||
/* Both values bitmap and desc pointers are stored in tmp[] */
|
||||
values = &fwd->tmp[0];
|
||||
descs = (void *)&fwd->tmp[BITS_TO_LONGS(fwd->chip.ngpio)];
|
||||
|
||||
bitmap_clear(values, 0, fwd->chip.ngpio);
|
||||
for_each_set_bit(i, mask, fwd->chip.ngpio)
|
||||
descs[j++] = fwd->descs[i];
|
||||
@ -333,14 +334,10 @@ static void gpio_fwd_set(struct gpio_chip *chip, unsigned int offset, int value)
|
||||
static void gpio_fwd_set_multiple(struct gpiochip_fwd *fwd, unsigned long *mask,
|
||||
unsigned long *bits)
|
||||
{
|
||||
struct gpio_desc **descs;
|
||||
unsigned long *values;
|
||||
struct gpio_desc **descs = fwd_tmp_descs(fwd);
|
||||
unsigned long *values = fwd_tmp_values(fwd);
|
||||
unsigned int i, j = 0;
|
||||
|
||||
/* Both values bitmap and desc pointers are stored in tmp[] */
|
||||
values = &fwd->tmp[0];
|
||||
descs = (void *)&fwd->tmp[BITS_TO_LONGS(fwd->chip.ngpio)];
|
||||
|
||||
for_each_set_bit(i, mask, fwd->chip.ngpio) {
|
||||
__assign_bit(j, values, test_bit(i, bits));
|
||||
descs[j++] = fwd->descs[i];
|
||||
@ -398,8 +395,8 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev,
|
||||
unsigned int i;
|
||||
int error;
|
||||
|
||||
fwd = devm_kzalloc(dev, struct_size(fwd, tmp,
|
||||
BITS_TO_LONGS(ngpios) + ngpios), GFP_KERNEL);
|
||||
fwd = devm_kzalloc(dev, struct_size(fwd, tmp, fwd_tmp_size(ngpios)),
|
||||
GFP_KERNEL);
|
||||
if (!fwd)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
@ -50,7 +50,9 @@ static int max7300_probe(struct i2c_client *client,
|
||||
|
||||
static int max7300_remove(struct i2c_client *client)
|
||||
{
|
||||
return __max730x_remove(&client->dev);
|
||||
__max730x_remove(&client->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max7300_id[] = {
|
||||
|
@ -66,7 +66,9 @@ static int max7301_probe(struct spi_device *spi)
|
||||
|
||||
static int max7301_remove(struct spi_device *spi)
|
||||
{
|
||||
return __max730x_remove(&spi->dev);
|
||||
__max730x_remove(&spi->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id max7301_id[] = {
|
||||
|
@ -220,18 +220,14 @@ exit_destroy:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__max730x_probe);
|
||||
|
||||
int __max730x_remove(struct device *dev)
|
||||
void __max730x_remove(struct device *dev)
|
||||
{
|
||||
struct max7301 *ts = dev_get_drvdata(dev);
|
||||
|
||||
if (ts == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
/* Power down the chip and disable IRQ output */
|
||||
ts->write(dev, 0x04, 0x00);
|
||||
gpiochip_remove(&ts->chip);
|
||||
mutex_destroy(&ts->lock);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__max730x_remove);
|
||||
|
||||
|
@ -365,5 +365,4 @@ module_platform_driver(max77620_gpio_driver);
|
||||
MODULE_DESCRIPTION("GPIO interface for MAX77620 and MAX20024 PMIC");
|
||||
MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
|
||||
MODULE_AUTHOR("Chaitanya Bandi <bandik@nvidia.com>");
|
||||
MODULE_ALIAS("platform:max77620-gpio");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -139,8 +139,6 @@ static int mc33880_remove(struct spi_device *spi)
|
||||
struct mc33880 *mc;
|
||||
|
||||
mc = spi_get_drvdata(spi);
|
||||
if (!mc)
|
||||
return -ENODEV;
|
||||
|
||||
gpiochip_remove(&mc->chip);
|
||||
mutex_destroy(&mc->lock);
|
||||
|
@ -1,9 +1,14 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020-2021 NVIDIA CORPORATION & AFFILIATES
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/kernel.h>
|
||||
@ -43,9 +48,14 @@
|
||||
#define YU_GPIO_MODE0 0x0c
|
||||
#define YU_GPIO_DATASET 0x14
|
||||
#define YU_GPIO_DATACLEAR 0x18
|
||||
#define YU_GPIO_CAUSE_RISE_EN 0x44
|
||||
#define YU_GPIO_CAUSE_FALL_EN 0x48
|
||||
#define YU_GPIO_MODE1_CLEAR 0x50
|
||||
#define YU_GPIO_MODE0_SET 0x54
|
||||
#define YU_GPIO_MODE0_CLEAR 0x58
|
||||
#define YU_GPIO_CAUSE_OR_CAUSE_EVTEN0 0x80
|
||||
#define YU_GPIO_CAUSE_OR_EVTEN0 0x94
|
||||
#define YU_GPIO_CAUSE_OR_CLRCAUSE 0x98
|
||||
|
||||
struct mlxbf2_gpio_context_save_regs {
|
||||
u32 gpio_mode0;
|
||||
@ -55,6 +65,7 @@ struct mlxbf2_gpio_context_save_regs {
|
||||
/* BlueField-2 gpio block context structure. */
|
||||
struct mlxbf2_gpio_context {
|
||||
struct gpio_chip gc;
|
||||
struct irq_chip irq_chip;
|
||||
|
||||
/* YU GPIO blocks address */
|
||||
void __iomem *gpio_io;
|
||||
@ -218,15 +229,114 @@ static int mlxbf2_gpio_direction_output(struct gpio_chip *chip,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mlxbf2_gpio_irq_enable(struct irq_data *irqd)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
|
||||
struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc);
|
||||
int offset = irqd_to_hwirq(irqd);
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&gs->gc.bgpio_lock, flags);
|
||||
val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE);
|
||||
val |= BIT(offset);
|
||||
writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE);
|
||||
|
||||
val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0);
|
||||
val |= BIT(offset);
|
||||
writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0);
|
||||
spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);
|
||||
}
|
||||
|
||||
static void mlxbf2_gpio_irq_disable(struct irq_data *irqd)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
|
||||
struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc);
|
||||
int offset = irqd_to_hwirq(irqd);
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&gs->gc.bgpio_lock, flags);
|
||||
val = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0);
|
||||
val &= ~BIT(offset);
|
||||
writel(val, gs->gpio_io + YU_GPIO_CAUSE_OR_EVTEN0);
|
||||
spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);
|
||||
}
|
||||
|
||||
static irqreturn_t mlxbf2_gpio_irq_handler(int irq, void *ptr)
|
||||
{
|
||||
struct mlxbf2_gpio_context *gs = ptr;
|
||||
struct gpio_chip *gc = &gs->gc;
|
||||
unsigned long pending;
|
||||
u32 level;
|
||||
|
||||
pending = readl(gs->gpio_io + YU_GPIO_CAUSE_OR_CAUSE_EVTEN0);
|
||||
writel(pending, gs->gpio_io + YU_GPIO_CAUSE_OR_CLRCAUSE);
|
||||
|
||||
for_each_set_bit(level, &pending, gc->ngpio) {
|
||||
int gpio_irq = irq_find_mapping(gc->irq.domain, level);
|
||||
generic_handle_irq(gpio_irq);
|
||||
}
|
||||
|
||||
return IRQ_RETVAL(pending);
|
||||
}
|
||||
|
||||
static int
|
||||
mlxbf2_gpio_irq_set_type(struct irq_data *irqd, unsigned int type)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
|
||||
struct mlxbf2_gpio_context *gs = gpiochip_get_data(gc);
|
||||
int offset = irqd_to_hwirq(irqd);
|
||||
unsigned long flags;
|
||||
bool fall = false;
|
||||
bool rise = false;
|
||||
u32 val;
|
||||
|
||||
switch (type & IRQ_TYPE_SENSE_MASK) {
|
||||
case IRQ_TYPE_EDGE_BOTH:
|
||||
fall = true;
|
||||
rise = true;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
rise = true;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
fall = true;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&gs->gc.bgpio_lock, flags);
|
||||
if (fall) {
|
||||
val = readl(gs->gpio_io + YU_GPIO_CAUSE_FALL_EN);
|
||||
val |= BIT(offset);
|
||||
writel(val, gs->gpio_io + YU_GPIO_CAUSE_FALL_EN);
|
||||
}
|
||||
|
||||
if (rise) {
|
||||
val = readl(gs->gpio_io + YU_GPIO_CAUSE_RISE_EN);
|
||||
val |= BIT(offset);
|
||||
writel(val, gs->gpio_io + YU_GPIO_CAUSE_RISE_EN);
|
||||
}
|
||||
spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* BlueField-2 GPIO driver initialization routine. */
|
||||
static int
|
||||
mlxbf2_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mlxbf2_gpio_context *gs;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct gpio_irq_chip *girq;
|
||||
struct gpio_chip *gc;
|
||||
unsigned int npins;
|
||||
int ret;
|
||||
const char *name;
|
||||
int ret, irq;
|
||||
|
||||
name = dev_name(dev);
|
||||
|
||||
gs = devm_kzalloc(dev, sizeof(*gs), GFP_KERNEL);
|
||||
if (!gs)
|
||||
@ -266,6 +376,34 @@ mlxbf2_gpio_probe(struct platform_device *pdev)
|
||||
gc->ngpio = npins;
|
||||
gc->owner = THIS_MODULE;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq >= 0) {
|
||||
gs->irq_chip.name = name;
|
||||
gs->irq_chip.irq_set_type = mlxbf2_gpio_irq_set_type;
|
||||
gs->irq_chip.irq_enable = mlxbf2_gpio_irq_enable;
|
||||
gs->irq_chip.irq_disable = mlxbf2_gpio_irq_disable;
|
||||
|
||||
girq = &gs->gc.irq;
|
||||
girq->chip = &gs->irq_chip;
|
||||
girq->handler = handle_simple_irq;
|
||||
girq->default_type = IRQ_TYPE_NONE;
|
||||
/* This will let us handle the parent IRQ in the driver */
|
||||
girq->num_parents = 0;
|
||||
girq->parents = NULL;
|
||||
girq->parent_handler = NULL;
|
||||
|
||||
/*
|
||||
* Directly request the irq here instead of passing
|
||||
* a flow-handler because the irq is shared.
|
||||
*/
|
||||
ret = devm_request_irq(dev, irq, mlxbf2_gpio_irq_handler,
|
||||
IRQF_SHARED, name, gs);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request IRQ");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, gs);
|
||||
|
||||
ret = devm_gpiochip_add_data(dev, &gs->gc, gs);
|
||||
@ -320,5 +458,5 @@ static struct platform_driver mlxbf2_gpio_driver = {
|
||||
module_platform_driver(mlxbf2_gpio_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Mellanox BlueField-2 GPIO Driver");
|
||||
MODULE_AUTHOR("Mellanox Technologies");
|
||||
MODULE_AUTHOR("Asmaa Mnebhi <asmaa@nvidia.com>");
|
||||
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);
|
||||
port_pin_count = min(gc->ngpio - lines_done, 8U);
|
||||
for_each_set_bit(offset, &status, port_pin_count)
|
||||
generic_handle_domain_irq(gc->irq.domain, offset);
|
||||
generic_handle_domain_irq(gc->irq.domain, offset + lines_done);
|
||||
}
|
||||
|
||||
chained_irq_exit(irq_chip, desc);
|
||||
|
@ -69,6 +69,8 @@ struct tegra_gpio_soc {
|
||||
const char *name;
|
||||
unsigned int instance;
|
||||
|
||||
unsigned int num_irqs_per_bank;
|
||||
|
||||
const struct tegra186_pin_range *pin_ranges;
|
||||
unsigned int num_pin_ranges;
|
||||
const char *pinmux;
|
||||
@ -81,6 +83,8 @@ struct tegra_gpio {
|
||||
unsigned int *irq;
|
||||
|
||||
const struct tegra_gpio_soc *soc;
|
||||
unsigned int num_irqs_per_bank;
|
||||
unsigned int num_banks;
|
||||
|
||||
void __iomem *secure;
|
||||
void __iomem *base;
|
||||
@ -450,7 +454,7 @@ static void tegra186_gpio_irq(struct irq_desc *desc)
|
||||
struct irq_domain *domain = gpio->gpio.irq.domain;
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
unsigned int parent = irq_desc_get_irq(desc);
|
||||
unsigned int i, offset = 0;
|
||||
unsigned int i, j, offset = 0;
|
||||
|
||||
chained_irq_enter(chip, desc);
|
||||
|
||||
@ -463,7 +467,12 @@ static void tegra186_gpio_irq(struct irq_desc *desc)
|
||||
base = gpio->base + port->bank * 0x1000 + port->port * 0x200;
|
||||
|
||||
/* skip ports that are not associated with this bank */
|
||||
if (parent != gpio->irq[port->bank])
|
||||
for (j = 0; j < gpio->num_irqs_per_bank; j++) {
|
||||
if (parent == gpio->irq[port->bank * gpio->num_irqs_per_bank + j])
|
||||
break;
|
||||
}
|
||||
|
||||
if (j == gpio->num_irqs_per_bank)
|
||||
goto skip;
|
||||
|
||||
value = readl(base + TEGRA186_GPIO_INTERRUPT_STATUS(1));
|
||||
@ -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)
|
||||
{
|
||||
struct device *dev = gpio->gpio.parent;
|
||||
unsigned int i, j;
|
||||
u32 value;
|
||||
|
||||
@ -583,17 +593,60 @@ static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio)
|
||||
*/
|
||||
if ((value & TEGRA186_GPIO_CTL_SCR_SEC_REN) == 0 &&
|
||||
(value & TEGRA186_GPIO_CTL_SCR_SEC_WEN) == 0) {
|
||||
for (j = 0; j < 8; j++) {
|
||||
/*
|
||||
* On Tegra194 and later, each pin can be routed to one or more
|
||||
* interrupts.
|
||||
*/
|
||||
for (j = 0; j < gpio->num_irqs_per_bank; j++) {
|
||||
dev_dbg(dev, "programming default interrupt routing for port %s\n",
|
||||
port->name);
|
||||
|
||||
offset = TEGRA186_GPIO_INT_ROUTE_MAPPING(p, j);
|
||||
|
||||
value = readl(base + offset);
|
||||
value = BIT(port->pins) - 1;
|
||||
writel(value, base + offset);
|
||||
/*
|
||||
* By default we only want to route GPIO pins to IRQ 0. This works
|
||||
* only under the assumption that we're running as the host kernel
|
||||
* and hence all GPIO pins are owned by Linux.
|
||||
*
|
||||
* For cases where Linux is the guest OS, the hypervisor will have
|
||||
* to configure the interrupt routing and pass only the valid
|
||||
* interrupts via device tree.
|
||||
*/
|
||||
if (j == 0) {
|
||||
value = readl(base + offset);
|
||||
value = BIT(port->pins) - 1;
|
||||
writel(value, base + offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
unsigned int i, j, offset;
|
||||
@ -608,7 +661,17 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
gpio->soc = device_get_match_data(&pdev->dev);
|
||||
gpio->gpio.label = gpio->soc->name;
|
||||
gpio->gpio.parent = &pdev->dev;
|
||||
|
||||
/* count the number of banks in the controller */
|
||||
for (i = 0; i < gpio->soc->num_ports; i++)
|
||||
if (gpio->soc->ports[i].bank > gpio->num_banks)
|
||||
gpio->num_banks = gpio->soc->ports[i].bank;
|
||||
|
||||
gpio->num_banks++;
|
||||
|
||||
/* get register apertures */
|
||||
gpio->secure = devm_platform_ioremap_resource_byname(pdev, "security");
|
||||
if (IS_ERR(gpio->secure)) {
|
||||
gpio->secure = devm_platform_ioremap_resource(pdev, 0);
|
||||
@ -629,6 +692,10 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
|
||||
|
||||
gpio->num_irq = err;
|
||||
|
||||
err = tegra186_gpio_irqs_per_bank(gpio);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
gpio->irq = devm_kcalloc(&pdev->dev, gpio->num_irq, sizeof(*gpio->irq),
|
||||
GFP_KERNEL);
|
||||
if (!gpio->irq)
|
||||
@ -642,9 +709,6 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
|
||||
gpio->irq[i] = err;
|
||||
}
|
||||
|
||||
gpio->gpio.label = gpio->soc->name;
|
||||
gpio->gpio.parent = &pdev->dev;
|
||||
|
||||
gpio->gpio.request = gpiochip_generic_request;
|
||||
gpio->gpio.free = gpiochip_generic_free;
|
||||
gpio->gpio.get_direction = tegra186_gpio_get_direction;
|
||||
@ -708,7 +772,31 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
|
||||
irq->parent_handler = tegra186_gpio_irq;
|
||||
irq->parent_handler_data = gpio;
|
||||
irq->num_parents = gpio->num_irq;
|
||||
irq->parents = gpio->irq;
|
||||
|
||||
/*
|
||||
* To simplify things, use a single interrupt per bank for now. Some
|
||||
* chips support up to 8 interrupts per bank, which can be useful to
|
||||
* distribute the load and decrease the processing latency for GPIOs
|
||||
* but it also requires a more complicated interrupt routing than we
|
||||
* currently program.
|
||||
*/
|
||||
if (gpio->num_irqs_per_bank > 1) {
|
||||
irq->parents = devm_kcalloc(&pdev->dev, gpio->num_banks,
|
||||
sizeof(*irq->parents), GFP_KERNEL);
|
||||
if (!irq->parents)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < gpio->num_banks; i++)
|
||||
irq->parents[i] = gpio->irq[i * gpio->num_irqs_per_bank];
|
||||
|
||||
irq->num_parents = gpio->num_banks;
|
||||
} else {
|
||||
irq->num_parents = gpio->num_irq;
|
||||
irq->parents = gpio->irq;
|
||||
}
|
||||
|
||||
if (gpio->soc->num_irqs_per_bank > 1)
|
||||
tegra186_gpio_init_route_mapping(gpio);
|
||||
|
||||
np = of_find_matching_node(NULL, tegra186_pmc_of_match);
|
||||
if (np) {
|
||||
@ -719,8 +807,6 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
tegra186_gpio_init_route_mapping(gpio);
|
||||
|
||||
irq->map = devm_kcalloc(&pdev->dev, gpio->gpio.ngpio,
|
||||
sizeof(*irq->map), GFP_KERNEL);
|
||||
if (!irq->map)
|
||||
@ -777,6 +863,7 @@ static const struct tegra_gpio_soc tegra186_main_soc = {
|
||||
.ports = tegra186_main_ports,
|
||||
.name = "tegra186-gpio",
|
||||
.instance = 0,
|
||||
.num_irqs_per_bank = 1,
|
||||
};
|
||||
|
||||
#define TEGRA186_AON_GPIO_PORT(_name, _bank, _port, _pins) \
|
||||
@ -803,6 +890,7 @@ static const struct tegra_gpio_soc tegra186_aon_soc = {
|
||||
.ports = tegra186_aon_ports,
|
||||
.name = "tegra186-gpio-aon",
|
||||
.instance = 1,
|
||||
.num_irqs_per_bank = 1,
|
||||
};
|
||||
|
||||
#define TEGRA194_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
|
||||
@ -854,6 +942,7 @@ static const struct tegra_gpio_soc tegra194_main_soc = {
|
||||
.ports = tegra194_main_ports,
|
||||
.name = "tegra194-gpio",
|
||||
.instance = 0,
|
||||
.num_irqs_per_bank = 8,
|
||||
.num_pin_ranges = ARRAY_SIZE(tegra194_main_pin_ranges),
|
||||
.pin_ranges = tegra194_main_pin_ranges,
|
||||
.pinmux = "nvidia,tegra194-pinmux",
|
||||
@ -880,6 +969,7 @@ static const struct tegra_gpio_soc tegra194_aon_soc = {
|
||||
.ports = tegra194_aon_ports,
|
||||
.name = "tegra194-gpio-aon",
|
||||
.instance = 1,
|
||||
.num_irqs_per_bank = 8,
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra186_gpio_of_match[] = {
|
||||
|
@ -230,4 +230,3 @@ module_platform_driver(tps65218_gpio_driver);
|
||||
MODULE_AUTHOR("Nicolas Saenz Julienne <nicolassaenzj@gmail.com>");
|
||||
MODULE_DESCRIPTION("GPO interface for TPS65218 PMICs");
|
||||
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)
|
||||
{
|
||||
struct uniphier_gpio_priv *priv = data->chip_data;
|
||||
u32 mask = BIT(data->hwirq);
|
||||
struct uniphier_gpio_priv *priv = irq_data_get_irq_chip_data(data);
|
||||
u32 mask = BIT(irqd_to_hwirq(data));
|
||||
|
||||
uniphier_gpio_reg_update(priv, UNIPHIER_GPIO_IRQ_EN, mask, 0);
|
||||
|
||||
@ -189,8 +189,8 @@ static void uniphier_gpio_irq_mask(struct irq_data *data)
|
||||
|
||||
static void uniphier_gpio_irq_unmask(struct irq_data *data)
|
||||
{
|
||||
struct uniphier_gpio_priv *priv = data->chip_data;
|
||||
u32 mask = BIT(data->hwirq);
|
||||
struct uniphier_gpio_priv *priv = irq_data_get_irq_chip_data(data);
|
||||
u32 mask = BIT(irqd_to_hwirq(data));
|
||||
|
||||
uniphier_gpio_reg_update(priv, UNIPHIER_GPIO_IRQ_EN, mask, mask);
|
||||
|
||||
@ -199,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)
|
||||
{
|
||||
struct uniphier_gpio_priv *priv = data->chip_data;
|
||||
u32 mask = BIT(data->hwirq);
|
||||
struct uniphier_gpio_priv *priv = irq_data_get_irq_chip_data(data);
|
||||
u32 mask = BIT(irqd_to_hwirq(data));
|
||||
u32 val = 0;
|
||||
|
||||
if (type == IRQ_TYPE_EDGE_BOTH) {
|
||||
@ -297,7 +297,8 @@ static int uniphier_gpio_irq_domain_activate(struct irq_domain *domain,
|
||||
struct uniphier_gpio_priv *priv = domain->host_data;
|
||||
struct gpio_chip *chip = &priv->chip;
|
||||
|
||||
return gpiochip_lock_as_irq(chip, data->hwirq + UNIPHIER_GPIO_IRQ_OFFSET);
|
||||
return gpiochip_lock_as_irq(chip,
|
||||
irqd_to_hwirq(data) + UNIPHIER_GPIO_IRQ_OFFSET);
|
||||
}
|
||||
|
||||
static void uniphier_gpio_irq_domain_deactivate(struct irq_domain *domain,
|
||||
@ -306,7 +307,8 @@ static void uniphier_gpio_irq_domain_deactivate(struct irq_domain *domain,
|
||||
struct uniphier_gpio_priv *priv = domain->host_data;
|
||||
struct gpio_chip *chip = &priv->chip;
|
||||
|
||||
gpiochip_unlock_as_irq(chip, data->hwirq + UNIPHIER_GPIO_IRQ_OFFSET);
|
||||
gpiochip_unlock_as_irq(chip,
|
||||
irqd_to_hwirq(data) + UNIPHIER_GPIO_IRQ_OFFSET);
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops uniphier_gpio_irq_domain_ops = {
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/virtio_config.h>
|
||||
#include <uapi/linux/virtio_gpio.h>
|
||||
#include <uapi/linux/virtio_ids.h>
|
||||
@ -28,12 +29,30 @@ struct virtio_gpio_line {
|
||||
unsigned int rxlen;
|
||||
};
|
||||
|
||||
struct vgpio_irq_line {
|
||||
u8 type;
|
||||
bool disabled;
|
||||
bool masked;
|
||||
bool queued;
|
||||
bool update_pending;
|
||||
bool queue_pending;
|
||||
|
||||
struct virtio_gpio_irq_request ireq ____cacheline_aligned;
|
||||
struct virtio_gpio_irq_response ires ____cacheline_aligned;
|
||||
};
|
||||
|
||||
struct virtio_gpio {
|
||||
struct virtio_device *vdev;
|
||||
struct mutex lock; /* Protects virtqueue operation */
|
||||
struct gpio_chip gc;
|
||||
struct virtio_gpio_line *lines;
|
||||
struct virtqueue *request_vq;
|
||||
|
||||
/* irq support */
|
||||
struct virtqueue *event_vq;
|
||||
struct mutex irq_lock; /* Protects irq operation */
|
||||
raw_spinlock_t eventq_lock; /* Protects queuing of the buffer */
|
||||
struct vgpio_irq_line *irq_lines;
|
||||
};
|
||||
|
||||
static int _virtio_gpio_req(struct virtio_gpio *vgpio, u16 type, u16 gpio,
|
||||
@ -186,6 +205,238 @@ static void virtio_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
|
||||
virtio_gpio_req(vgpio, VIRTIO_GPIO_MSG_SET_VALUE, gpio, value, NULL);
|
||||
}
|
||||
|
||||
/* Interrupt handling */
|
||||
static void virtio_gpio_irq_prepare(struct virtio_gpio *vgpio, u16 gpio)
|
||||
{
|
||||
struct vgpio_irq_line *irq_line = &vgpio->irq_lines[gpio];
|
||||
struct virtio_gpio_irq_request *ireq = &irq_line->ireq;
|
||||
struct virtio_gpio_irq_response *ires = &irq_line->ires;
|
||||
struct scatterlist *sgs[2], req_sg, res_sg;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(irq_line->queued || irq_line->masked || irq_line->disabled))
|
||||
return;
|
||||
|
||||
ireq->gpio = cpu_to_le16(gpio);
|
||||
sg_init_one(&req_sg, ireq, sizeof(*ireq));
|
||||
sg_init_one(&res_sg, ires, sizeof(*ires));
|
||||
sgs[0] = &req_sg;
|
||||
sgs[1] = &res_sg;
|
||||
|
||||
ret = virtqueue_add_sgs(vgpio->event_vq, sgs, 1, 1, irq_line, GFP_ATOMIC);
|
||||
if (ret) {
|
||||
dev_err(&vgpio->vdev->dev, "failed to add request to eventq\n");
|
||||
return;
|
||||
}
|
||||
|
||||
irq_line->queued = true;
|
||||
virtqueue_kick(vgpio->event_vq);
|
||||
}
|
||||
|
||||
static void virtio_gpio_irq_enable(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct virtio_gpio *vgpio = gpiochip_get_data(gc);
|
||||
struct vgpio_irq_line *irq_line = &vgpio->irq_lines[d->hwirq];
|
||||
|
||||
raw_spin_lock(&vgpio->eventq_lock);
|
||||
irq_line->disabled = false;
|
||||
irq_line->masked = false;
|
||||
irq_line->queue_pending = true;
|
||||
raw_spin_unlock(&vgpio->eventq_lock);
|
||||
|
||||
irq_line->update_pending = true;
|
||||
}
|
||||
|
||||
static void virtio_gpio_irq_disable(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct virtio_gpio *vgpio = gpiochip_get_data(gc);
|
||||
struct vgpio_irq_line *irq_line = &vgpio->irq_lines[d->hwirq];
|
||||
|
||||
raw_spin_lock(&vgpio->eventq_lock);
|
||||
irq_line->disabled = true;
|
||||
irq_line->masked = true;
|
||||
irq_line->queue_pending = false;
|
||||
raw_spin_unlock(&vgpio->eventq_lock);
|
||||
|
||||
irq_line->update_pending = true;
|
||||
}
|
||||
|
||||
static void virtio_gpio_irq_mask(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct virtio_gpio *vgpio = gpiochip_get_data(gc);
|
||||
struct vgpio_irq_line *irq_line = &vgpio->irq_lines[d->hwirq];
|
||||
|
||||
raw_spin_lock(&vgpio->eventq_lock);
|
||||
irq_line->masked = true;
|
||||
raw_spin_unlock(&vgpio->eventq_lock);
|
||||
}
|
||||
|
||||
static void virtio_gpio_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct virtio_gpio *vgpio = gpiochip_get_data(gc);
|
||||
struct vgpio_irq_line *irq_line = &vgpio->irq_lines[d->hwirq];
|
||||
|
||||
raw_spin_lock(&vgpio->eventq_lock);
|
||||
irq_line->masked = false;
|
||||
|
||||
/* Queue the buffer unconditionally on unmask */
|
||||
virtio_gpio_irq_prepare(vgpio, d->hwirq);
|
||||
raw_spin_unlock(&vgpio->eventq_lock);
|
||||
}
|
||||
|
||||
static int virtio_gpio_irq_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct virtio_gpio *vgpio = gpiochip_get_data(gc);
|
||||
struct vgpio_irq_line *irq_line = &vgpio->irq_lines[d->hwirq];
|
||||
|
||||
switch (type) {
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
type = VIRTIO_GPIO_IRQ_TYPE_EDGE_RISING;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
type = VIRTIO_GPIO_IRQ_TYPE_EDGE_FALLING;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_BOTH:
|
||||
type = VIRTIO_GPIO_IRQ_TYPE_EDGE_BOTH;
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
type = VIRTIO_GPIO_IRQ_TYPE_LEVEL_LOW;
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
type = VIRTIO_GPIO_IRQ_TYPE_LEVEL_HIGH;
|
||||
break;
|
||||
default:
|
||||
dev_err(&vgpio->vdev->dev, "unsupported irq type: %u\n", type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq_line->type = type;
|
||||
irq_line->update_pending = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void virtio_gpio_irq_bus_lock(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct virtio_gpio *vgpio = gpiochip_get_data(gc);
|
||||
|
||||
mutex_lock(&vgpio->irq_lock);
|
||||
}
|
||||
|
||||
static void virtio_gpio_irq_bus_sync_unlock(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct virtio_gpio *vgpio = gpiochip_get_data(gc);
|
||||
struct vgpio_irq_line *irq_line = &vgpio->irq_lines[d->hwirq];
|
||||
u8 type = irq_line->disabled ? VIRTIO_GPIO_IRQ_TYPE_NONE : irq_line->type;
|
||||
unsigned long flags;
|
||||
|
||||
if (irq_line->update_pending) {
|
||||
irq_line->update_pending = false;
|
||||
virtio_gpio_req(vgpio, VIRTIO_GPIO_MSG_IRQ_TYPE, d->hwirq, type,
|
||||
NULL);
|
||||
|
||||
/* Queue the buffer only after interrupt is enabled */
|
||||
raw_spin_lock_irqsave(&vgpio->eventq_lock, flags);
|
||||
if (irq_line->queue_pending) {
|
||||
irq_line->queue_pending = false;
|
||||
virtio_gpio_irq_prepare(vgpio, d->hwirq);
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&vgpio->eventq_lock, flags);
|
||||
}
|
||||
|
||||
mutex_unlock(&vgpio->irq_lock);
|
||||
}
|
||||
|
||||
static struct irq_chip vgpio_irq_chip = {
|
||||
.name = "virtio-gpio",
|
||||
.irq_enable = virtio_gpio_irq_enable,
|
||||
.irq_disable = virtio_gpio_irq_disable,
|
||||
.irq_mask = virtio_gpio_irq_mask,
|
||||
.irq_unmask = virtio_gpio_irq_unmask,
|
||||
.irq_set_type = virtio_gpio_irq_set_type,
|
||||
|
||||
/* These are required to implement irqchip for slow busses */
|
||||
.irq_bus_lock = virtio_gpio_irq_bus_lock,
|
||||
.irq_bus_sync_unlock = virtio_gpio_irq_bus_sync_unlock,
|
||||
};
|
||||
|
||||
static bool ignore_irq(struct virtio_gpio *vgpio, int gpio,
|
||||
struct vgpio_irq_line *irq_line)
|
||||
{
|
||||
bool ignore = false;
|
||||
|
||||
raw_spin_lock(&vgpio->eventq_lock);
|
||||
irq_line->queued = false;
|
||||
|
||||
/* Interrupt is disabled currently */
|
||||
if (irq_line->masked || irq_line->disabled) {
|
||||
ignore = true;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* Buffer is returned as the interrupt was disabled earlier, but is
|
||||
* enabled again now. Requeue the buffers.
|
||||
*/
|
||||
if (irq_line->ires.status == VIRTIO_GPIO_IRQ_STATUS_INVALID) {
|
||||
virtio_gpio_irq_prepare(vgpio, gpio);
|
||||
ignore = true;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (WARN_ON(irq_line->ires.status != VIRTIO_GPIO_IRQ_STATUS_VALID))
|
||||
ignore = true;
|
||||
|
||||
unlock:
|
||||
raw_spin_unlock(&vgpio->eventq_lock);
|
||||
|
||||
return ignore;
|
||||
}
|
||||
|
||||
static void virtio_gpio_event_vq(struct virtqueue *vq)
|
||||
{
|
||||
struct virtio_gpio *vgpio = vq->vdev->priv;
|
||||
struct device *dev = &vgpio->vdev->dev;
|
||||
struct vgpio_irq_line *irq_line;
|
||||
int gpio, ret;
|
||||
unsigned int len;
|
||||
|
||||
while (true) {
|
||||
irq_line = virtqueue_get_buf(vgpio->event_vq, &len);
|
||||
if (!irq_line)
|
||||
break;
|
||||
|
||||
if (len != sizeof(irq_line->ires)) {
|
||||
dev_err(dev, "irq with incorrect length (%u : %u)\n",
|
||||
len, (unsigned int)sizeof(irq_line->ires));
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find GPIO line number from the offset of irq_line within the
|
||||
* irq_lines block. We can also get GPIO number from
|
||||
* irq-request, but better not to rely on a buffer returned by
|
||||
* remote.
|
||||
*/
|
||||
gpio = irq_line - vgpio->irq_lines;
|
||||
WARN_ON(gpio >= vgpio->gc.ngpio);
|
||||
|
||||
if (unlikely(ignore_irq(vgpio, gpio, irq_line)))
|
||||
continue;
|
||||
|
||||
ret = generic_handle_domain_irq(vgpio->gc.irq.domain, gpio);
|
||||
if (ret)
|
||||
dev_err(dev, "failed to handle interrupt: %d\n", ret);
|
||||
};
|
||||
}
|
||||
|
||||
static void virtio_gpio_request_vq(struct virtqueue *vq)
|
||||
{
|
||||
struct virtio_gpio_line *line;
|
||||
@ -210,14 +461,15 @@ static void virtio_gpio_free_vqs(struct virtio_device *vdev)
|
||||
static int virtio_gpio_alloc_vqs(struct virtio_gpio *vgpio,
|
||||
struct virtio_device *vdev)
|
||||
{
|
||||
const char * const names[] = { "requestq" };
|
||||
const char * const names[] = { "requestq", "eventq" };
|
||||
vq_callback_t *cbs[] = {
|
||||
virtio_gpio_request_vq,
|
||||
virtio_gpio_event_vq,
|
||||
};
|
||||
struct virtqueue *vqs[1] = { NULL };
|
||||
struct virtqueue *vqs[2] = { NULL, NULL };
|
||||
int ret;
|
||||
|
||||
ret = virtio_find_vqs(vdev, 1, vqs, cbs, names, NULL);
|
||||
ret = virtio_find_vqs(vdev, vgpio->irq_lines ? 2 : 1, vqs, cbs, names, NULL);
|
||||
if (ret) {
|
||||
dev_err(&vdev->dev, "failed to find vqs: %d\n", ret);
|
||||
return ret;
|
||||
@ -225,11 +477,23 @@ static int virtio_gpio_alloc_vqs(struct virtio_gpio *vgpio,
|
||||
|
||||
if (!vqs[0]) {
|
||||
dev_err(&vdev->dev, "failed to find requestq vq\n");
|
||||
return -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
vgpio->request_vq = vqs[0];
|
||||
|
||||
if (vgpio->irq_lines && !vqs[1]) {
|
||||
dev_err(&vdev->dev, "failed to find eventq vq\n");
|
||||
goto out;
|
||||
}
|
||||
vgpio->event_vq = vqs[1];
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
if (vqs[0] || vqs[1])
|
||||
virtio_gpio_free_vqs(vdev);
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static const char **virtio_gpio_get_names(struct virtio_gpio *vgpio,
|
||||
@ -325,6 +589,30 @@ static int virtio_gpio_probe(struct virtio_device *vdev)
|
||||
vgpio->gc.owner = THIS_MODULE;
|
||||
vgpio->gc.can_sleep = true;
|
||||
|
||||
/* Interrupt support */
|
||||
if (virtio_has_feature(vdev, VIRTIO_GPIO_F_IRQ)) {
|
||||
vgpio->irq_lines = devm_kcalloc(dev, ngpio, sizeof(*vgpio->irq_lines), GFP_KERNEL);
|
||||
if (!vgpio->irq_lines)
|
||||
return -ENOMEM;
|
||||
|
||||
/* The event comes from the outside so no parent handler */
|
||||
vgpio->gc.irq.parent_handler = NULL;
|
||||
vgpio->gc.irq.num_parents = 0;
|
||||
vgpio->gc.irq.parents = NULL;
|
||||
vgpio->gc.irq.default_type = IRQ_TYPE_NONE;
|
||||
vgpio->gc.irq.handler = handle_level_irq;
|
||||
vgpio->gc.irq.chip = &vgpio_irq_chip;
|
||||
|
||||
for (i = 0; i < ngpio; i++) {
|
||||
vgpio->irq_lines[i].type = VIRTIO_GPIO_IRQ_TYPE_NONE;
|
||||
vgpio->irq_lines[i].disabled = true;
|
||||
vgpio->irq_lines[i].masked = true;
|
||||
}
|
||||
|
||||
mutex_init(&vgpio->irq_lock);
|
||||
raw_spin_lock_init(&vgpio->eventq_lock);
|
||||
}
|
||||
|
||||
ret = virtio_gpio_alloc_vqs(vgpio, vdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -357,7 +645,13 @@ static const struct virtio_device_id id_table[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(virtio, id_table);
|
||||
|
||||
static const unsigned int features[] = {
|
||||
VIRTIO_GPIO_F_IRQ,
|
||||
};
|
||||
|
||||
static struct virtio_driver virtio_gpio_driver = {
|
||||
.feature_table = features,
|
||||
.feature_table_size = ARRAY_SIZE(features),
|
||||
.id_table = id_table,
|
||||
.probe = virtio_gpio_probe,
|
||||
.remove = virtio_gpio_remove,
|
||||
|
@ -371,8 +371,7 @@ static int __maybe_unused xgpio_resume(struct device *dev)
|
||||
|
||||
static int __maybe_unused xgpio_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct xgpio_instance *gpio = platform_get_drvdata(pdev);
|
||||
struct xgpio_instance *gpio = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable(gpio->clk);
|
||||
|
||||
@ -381,8 +380,7 @@ static int __maybe_unused xgpio_runtime_suspend(struct device *dev)
|
||||
|
||||
static int __maybe_unused xgpio_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct xgpio_instance *gpio = platform_get_drvdata(pdev);
|
||||
struct xgpio_instance *gpio = dev_get_drvdata(dev);
|
||||
|
||||
return clk_enable(gpio->clk);
|
||||
}
|
||||
|
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
|
||||
|
||||
mlxbf_gige-y := mlxbf_gige_ethtool.o \
|
||||
mlxbf_gige_gpio.o \
|
||||
mlxbf_gige_intr.o \
|
||||
mlxbf_gige_main.o \
|
||||
mlxbf_gige_mdio.o \
|
||||
|
@ -51,11 +51,6 @@
|
||||
#define MLXBF_GIGE_ERROR_INTR_IDX 0
|
||||
#define MLXBF_GIGE_RECEIVE_PKT_INTR_IDX 1
|
||||
#define MLXBF_GIGE_LLU_PLU_INTR_IDX 2
|
||||
#define MLXBF_GIGE_PHY_INT_N 3
|
||||
|
||||
#define MLXBF_GIGE_MDIO_DEFAULT_PHY_ADDR 0x3
|
||||
|
||||
#define MLXBF_GIGE_DEFAULT_PHY_INT_GPIO 12
|
||||
|
||||
struct mlxbf_gige_stats {
|
||||
u64 hw_access_errors;
|
||||
@ -81,11 +76,7 @@ struct mlxbf_gige {
|
||||
struct platform_device *pdev;
|
||||
void __iomem *mdio_io;
|
||||
struct mii_bus *mdiobus;
|
||||
void __iomem *gpio_io;
|
||||
struct irq_domain *irqdomain;
|
||||
u32 phy_int_gpio_mask;
|
||||
spinlock_t lock; /* for packet processing indices */
|
||||
spinlock_t gpio_lock; /* for GPIO bus access */
|
||||
u16 rx_q_entries;
|
||||
u16 tx_q_entries;
|
||||
u64 *tx_wqe_base;
|
||||
@ -184,7 +175,4 @@ int mlxbf_gige_poll(struct napi_struct *napi, int budget);
|
||||
extern const struct ethtool_ops mlxbf_gige_ethtool_ops;
|
||||
void mlxbf_gige_update_tx_wqe_next(struct mlxbf_gige *priv);
|
||||
|
||||
int mlxbf_gige_gpio_init(struct platform_device *pdev, struct mlxbf_gige *priv);
|
||||
void mlxbf_gige_gpio_free(struct mlxbf_gige *priv);
|
||||
|
||||
#endif /* !defined(__MLXBF_GIGE_H__) */
|
||||
|
@ -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 *plu_base;
|
||||
void __iomem *base;
|
||||
int addr, phy_irq;
|
||||
u64 control;
|
||||
int addr;
|
||||
int err;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, MLXBF_GIGE_RES_MAC);
|
||||
@ -316,20 +316,12 @@ static int mlxbf_gige_probe(struct platform_device *pdev)
|
||||
priv->pdev = pdev;
|
||||
|
||||
spin_lock_init(&priv->lock);
|
||||
spin_lock_init(&priv->gpio_lock);
|
||||
|
||||
/* Attach MDIO device */
|
||||
err = mlxbf_gige_mdio_probe(pdev, priv);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = mlxbf_gige_gpio_init(pdev, priv);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "PHY IRQ initialization failed\n");
|
||||
mlxbf_gige_mdio_remove(priv);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
priv->base = base;
|
||||
priv->llu_base = llu_base;
|
||||
priv->plu_base = plu_base;
|
||||
@ -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->llu_plu_irq = platform_get_irq(pdev, MLXBF_GIGE_LLU_PLU_INTR_IDX);
|
||||
|
||||
phy_irq = acpi_dev_gpio_irq_get_by(ACPI_COMPANION(&pdev->dev), "phy-gpios", 0);
|
||||
if (phy_irq < 0) {
|
||||
dev_err(&pdev->dev, "Error getting PHY irq. Use polling instead");
|
||||
phy_irq = PHY_POLL;
|
||||
}
|
||||
|
||||
phydev = phy_find_first(priv->mdiobus);
|
||||
if (!phydev) {
|
||||
err = -ENODEV;
|
||||
@ -357,8 +355,8 @@ static int mlxbf_gige_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
addr = phydev->mdio.addr;
|
||||
priv->mdiobus->irq[addr] = priv->phy_irq;
|
||||
phydev->irq = priv->phy_irq;
|
||||
priv->mdiobus->irq[addr] = phy_irq;
|
||||
phydev->irq = phy_irq;
|
||||
|
||||
err = phy_connect_direct(netdev, phydev,
|
||||
mlxbf_gige_adjust_link,
|
||||
@ -394,7 +392,6 @@ static int mlxbf_gige_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
out:
|
||||
mlxbf_gige_gpio_free(priv);
|
||||
mlxbf_gige_mdio_remove(priv);
|
||||
return err;
|
||||
}
|
||||
@ -405,7 +402,6 @@ static int mlxbf_gige_remove(struct platform_device *pdev)
|
||||
|
||||
unregister_netdev(priv->netdev);
|
||||
phy_disconnect(priv->netdev->phydev);
|
||||
mlxbf_gige_gpio_free(priv);
|
||||
mlxbf_gige_mdio_remove(priv);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
|
@ -72,6 +72,8 @@ enum pm_api_id {
|
||||
PM_SET_REQUIREMENT = 15,
|
||||
PM_RESET_ASSERT = 17,
|
||||
PM_RESET_GET_STATUS = 18,
|
||||
PM_MMIO_WRITE = 19,
|
||||
PM_MMIO_READ = 20,
|
||||
PM_PM_INIT_FINALIZE = 21,
|
||||
PM_FPGA_LOAD = 22,
|
||||
PM_FPGA_GET_STATUS = 23,
|
||||
@ -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,
|
||||
const enum zynqmp_pm_reset_action assert_flag);
|
||||
int zynqmp_pm_reset_get_status(const enum zynqmp_pm_reset reset, u32 *status);
|
||||
unsigned int zynqmp_pm_bootmode_read(u32 *ps_mode);
|
||||
int zynqmp_pm_bootmode_write(u32 ps_mode);
|
||||
int zynqmp_pm_init_finalize(void);
|
||||
int zynqmp_pm_set_suspend_mode(u32 mode);
|
||||
int zynqmp_pm_request_node(const u32 node, const u32 capabilities,
|
||||
@ -532,6 +536,16 @@ static inline int zynqmp_pm_reset_get_status(const enum zynqmp_pm_reset reset,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline unsigned int zynqmp_pm_bootmode_read(u32 *ps_mode)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int zynqmp_pm_bootmode_write(u32 ps_mode)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int zynqmp_pm_init_finalize(void)
|
||||
{
|
||||
return -ENODEV;
|
||||
|
@ -31,6 +31,6 @@ struct max7301_platform_data {
|
||||
u32 input_pullup_active;
|
||||
};
|
||||
|
||||
extern int __max730x_remove(struct device *dev);
|
||||
extern void __max730x_remove(struct device *dev);
|
||||
extern int __max730x_probe(struct max7301 *ts);
|
||||
#endif
|
||||
|
@ -5,12 +5,16 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* Virtio GPIO Feature bits */
|
||||
#define VIRTIO_GPIO_F_IRQ 0
|
||||
|
||||
/* Virtio GPIO request types */
|
||||
#define VIRTIO_GPIO_MSG_GET_NAMES 0x0001
|
||||
#define VIRTIO_GPIO_MSG_GET_DIRECTION 0x0002
|
||||
#define VIRTIO_GPIO_MSG_SET_DIRECTION 0x0003
|
||||
#define VIRTIO_GPIO_MSG_GET_VALUE 0x0004
|
||||
#define VIRTIO_GPIO_MSG_SET_VALUE 0x0005
|
||||
#define VIRTIO_GPIO_MSG_IRQ_TYPE 0x0006
|
||||
|
||||
/* Possible values of the status field */
|
||||
#define VIRTIO_GPIO_STATUS_OK 0x0
|
||||
@ -21,11 +25,19 @@
|
||||
#define VIRTIO_GPIO_DIRECTION_OUT 0x01
|
||||
#define VIRTIO_GPIO_DIRECTION_IN 0x02
|
||||
|
||||
/* Virtio GPIO IRQ types */
|
||||
#define VIRTIO_GPIO_IRQ_TYPE_NONE 0x00
|
||||
#define VIRTIO_GPIO_IRQ_TYPE_EDGE_RISING 0x01
|
||||
#define VIRTIO_GPIO_IRQ_TYPE_EDGE_FALLING 0x02
|
||||
#define VIRTIO_GPIO_IRQ_TYPE_EDGE_BOTH 0x03
|
||||
#define VIRTIO_GPIO_IRQ_TYPE_LEVEL_HIGH 0x04
|
||||
#define VIRTIO_GPIO_IRQ_TYPE_LEVEL_LOW 0x08
|
||||
|
||||
struct virtio_gpio_config {
|
||||
__le16 ngpio;
|
||||
__u8 padding[2];
|
||||
__le32 gpio_names_size;
|
||||
} __packed;
|
||||
};
|
||||
|
||||
/* Virtio GPIO Request / Response */
|
||||
struct virtio_gpio_request {
|
||||
@ -44,4 +56,17 @@ struct virtio_gpio_response_get_names {
|
||||
__u8 value[];
|
||||
};
|
||||
|
||||
/* Virtio GPIO IRQ Request / Response */
|
||||
struct virtio_gpio_irq_request {
|
||||
__le16 gpio;
|
||||
};
|
||||
|
||||
struct virtio_gpio_irq_response {
|
||||
__u8 status;
|
||||
};
|
||||
|
||||
/* Possible values of the interrupt status field */
|
||||
#define VIRTIO_GPIO_IRQ_STATUS_INVALID 0x0
|
||||
#define VIRTIO_GPIO_IRQ_STATUS_VALID 0x1
|
||||
|
||||
#endif /* _LINUX_VIRTIO_GPIO_H */
|
||||
|
Loading…
Reference in New Issue
Block a user