This is the bulk of GPIO changes for the v4.8 kernel cycle.
Core changes: - The big item is of course the completion of the character device ABI. It has now replaced and surpassed the former unmaintainable sysfs ABI: we can now hammer (bitbang) individual lines or sets of lines and read individual lines or sets of lines from userspace, and we can also register to listen to GPIO events from userspace. As a tie-in we have two new tools in tools/gpio: gpio-hammer and gpio-event-mon that illustrate the proper use of the new ABI. As someone said: the wild west days of GPIO are now over. - Continued to remove the pointless ARCH_[WANT_OPTIONAL|REQUIRE]_GPIOLIB Kconfig symbols. I'm patching hexagon, openrisc, powerpc, sh, unicore, ia64 and microblaze. These are either ACKed by their maintainers or patched anyways after a grace period and no response from maintainers. Some archs (ARM) come in from their trees, and others (x86) are still not fixed, so I might send a second pull request to root it out later in this merge window, or just defer to v4.9. - The GPIO tools are moved to the tools build system. New drivers: - New driver for the MAX77620/MAX20024. - New driver for the Intel Merrifield. - Enabled PCA953x for the TI PCA9536. - Enabled PCA953x for the Intel Edison. - Enabled R8A7792 in the RCAR driver. Driver improvements: - The STMPE and F7188x now supports the .get_direction() callback. - The Xilinx driver supports setting multiple lines at once. - ACPI support for the Vulcan GPIO controller. - The MMIO GPIO driver supports device tree probing. - The Acer One 10 is supported through the _DEP ACPI attribute. Cleanups: - A major cleanup of the OF/DT support code. It is way easier to read and understand now, probably this improves performance too. - Drop a few redundant .owner assignments. - Remove CLPS711x boardfile support: we are 100% DT. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJXlcT4AAoJEEEQszewGV1zACwQAK5SZr0F5c3QvYbJSiJBCGA7 MZKUYHnYoBpZaPKcFKoOXEM1WOvlABlh9U0y0xkL8gQ6giyKup1wYJJCuYgW29gL ny4r7Z8rs2Wm1ujL+FLAwuxIwCY3BnhUucp8YiSaHPBuKRfsHorFPvXiAgLZjNYC Qk3Q48xYW4inw9sy2BbMfsU3CZnkvgy5euooyy1ezwachRhuHdBy/MVCG012PC4s 0d6LGdByEx1uK4NeV7ssPys444M8unep2EWgy6Rvc1U+FmGA487EvL+X8nxTQTj3 uTMxA8nddmZTEeEIqhpRw/dPiFlWxPFwfWmNEre05gKLb/LUK2tgsUOnmIFgVUw/ t41IzdQNLQQZxmiXplZn6s5mAr2VNuTxkRq1CIl4SwQW+Uy4TU3q8aDPkKzsyhiR yw6o6ul0pQs8UZEggnht8ie6JiSnJ55ehI/nlRxpK/797Ff6Yp4FARs3ZtFnQDDu SWewnbRatZQ89lvy4BA7QCWeV4Scjk4k/e2HjUAFnkfMDaYqpi4vTdzwnWdVjd+F hMgu6VnkN3oSE7ZMrKJMh7b7h1uMnIwKBFWbkrlOEuhT1X0ZDsEOBv5juSBPYomN EOIJUyWqxn0ZfxeONbdbCPteYlfJF+TW/rE9LQMxS1nNwsqw2IQW6NCmrM9Nx6Fv FP++26nYMTSh82gwOYw3 =NwcK -----END PGP SIGNATURE----- Merge tag 'gpio-v4.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio Pull GPIO updates from Linus Walleij: "This is the bulk of GPIO changes for the v4.8 kernel cycle. The big news is the completion of the chardev ABI which I'm very happy about and apart from that it's an ordinary, quite busy cycle. The details are below. The patches are tested in linux-next for some time, patches to other subsystem mostly have ACKs. I got overly ambitious with configureing lines as input for IRQ lines but it turns out that some controllers have their interrupt-enable and input-enabling in orthogonal settings so the assumption that all IRQ lines are input lines does not hold. Oh well, revert and back to the drawing board with that. Core changes: - The big item is of course the completion of the character device ABI. It has now replaced and surpassed the former unmaintainable sysfs ABI: we can now hammer (bitbang) individual lines or sets of lines and read individual lines or sets of lines from userspace, and we can also register to listen to GPIO events from userspace. As a tie-in we have two new tools in tools/gpio: gpio-hammer and gpio-event-mon that illustrate the proper use of the new ABI. As someone said: the wild west days of GPIO are now over. - Continued to remove the pointless ARCH_[WANT_OPTIONAL|REQUIRE]_GPIOLIB Kconfig symbols. I'm patching hexagon, openrisc, powerpc, sh, unicore, ia64 and microblaze. These are either ACKed by their maintainers or patched anyways after a grace period and no response from maintainers. Some archs (ARM) come in from their trees, and others (x86) are still not fixed, so I might send a second pull request to root it out later in this merge window, or just defer to v4.9. - The GPIO tools are moved to the tools build system. New drivers: - New driver for the MAX77620/MAX20024. - New driver for the Intel Merrifield. - Enabled PCA953x for the TI PCA9536. - Enabled PCA953x for the Intel Edison. - Enabled R8A7792 in the RCAR driver. Driver improvements: - The STMPE and F7188x now supports the .get_direction() callback. - The Xilinx driver supports setting multiple lines at once. - ACPI support for the Vulcan GPIO controller. - The MMIO GPIO driver supports device tree probing. - The Acer One 10 is supported through the _DEP ACPI attribute. Cleanups: - A major cleanup of the OF/DT support code. It is way easier to read and understand now, probably this improves performance too. - Drop a few redundant .owner assignments. - Remove CLPS711x boardfile support: we are 100% DT" * tag 'gpio-v4.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (67 commits) MAINTAINERS: Add INTEL MERRIFIELD GPIO entry gpio: dwapb: add missing fwnode_handle_put() in dwapb_gpio_get_pdata() gpio: merrifield: Protect irq_ack() and gpio_set() by lock gpio: merrifield: Introduce GPIO driver to support Merrifield gpio: intel-mid: Make it depend to X86_INTEL_MID gpio: intel-mid: Sort header block alphabetically gpio: intel-mid: Remove potentially harmful code gpio: rcar: add R8A7792 support gpiolib: remove duplicated include from gpiolib.c Revert "gpio: convince line to become input in irq helper" gpiolib: of_find_gpio(): Don't discard errors gpio: of: Allow overriding the device node gpio: free handles in fringe cases gpio: tps65218: Add platform_device_id table gpio: max77620: get gpio value based on direction gpio: lynxpoint: avoid potential warning on error path tools/gpio: add install section tools/gpio: move to tools buildsystem gpio: intel-mid: switch to devm_gpiochip_add_data() gpio: 74x164: Use spi_write() helper instead of open coding ...
This commit is contained in:
commit
1cd04d293c
|
@ -1,7 +1,7 @@
|
|||
* ARM Cirrus Logic CLPS711X SYSFLG1 MCTRL GPIOs
|
||||
|
||||
Required properties:
|
||||
- compatible: Should contain "cirrus,clps711x-mctrl-gpio".
|
||||
- compatible: Should contain "cirrus,ep7209-mctrl-gpio".
|
||||
- gpio-controller: Marks the device node as a gpio controller.
|
||||
- #gpio-cells: Should be two. The first cell is the pin number and
|
||||
the second cell is used to specify the gpio polarity:
|
||||
|
@ -11,7 +11,7 @@ Required properties:
|
|||
Example:
|
||||
sysgpio: sysgpio {
|
||||
compatible = "cirrus,ep7312-mctrl-gpio",
|
||||
"cirrus,clps711x-mctrl-gpio";
|
||||
"cirrus,ep7209-mctrl-gpio";
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Cirrus Logic CLPS711X GPIO controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "cirrus,clps711x-gpio"
|
||||
- compatible: Should be "cirrus,ep7209-gpio"
|
||||
- reg: Physical base GPIO controller registers location and length.
|
||||
There should be two registers, first is DATA register, the second
|
||||
is DIRECTION.
|
||||
|
@ -21,7 +21,7 @@ aliases {
|
|||
};
|
||||
|
||||
porta: gpio@80000000 {
|
||||
compatible = "cirrus,clps711x-gpio";
|
||||
compatible = "cirrus,ep7312-gpio","cirrus,ep7209-gpio";
|
||||
reg = <0x80000000 0x1>, <0x80000040 0x1>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
GPIO driver for MAX77620 Power management IC from Maxim Semiconductor.
|
||||
|
||||
Device has 8 GPIO pins which can be configured as GPIO as well as the
|
||||
special IO functions.
|
||||
|
||||
Required properties:
|
||||
-------------------
|
||||
- gpio-controller : Marks the device node as a gpio controller.
|
||||
- #gpio-cells : Should be two. The first cell is the pin number and
|
||||
the second cell is used to specify the gpio polarity:
|
||||
0 = active high
|
||||
1 = active low
|
||||
For more details, please refer generic GPIO DT binding document
|
||||
<devicetree/bindings/gpio/gpio.txt>.
|
||||
|
||||
Example:
|
||||
--------
|
||||
#include <dt-bindings/mfd/max77620.h>
|
||||
...
|
||||
max77620@3c {
|
||||
compatible = "maxim,max77620";
|
||||
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
};
|
|
@ -21,6 +21,7 @@ Required properties:
|
|||
maxim,max7313
|
||||
maxim,max7315
|
||||
ti,pca6107
|
||||
ti,pca9536
|
||||
ti,tca6408
|
||||
ti,tca6416
|
||||
ti,tca6424
|
||||
|
|
|
@ -7,6 +7,7 @@ Required Properties:
|
|||
- "renesas,gpio-r8a7779": for R8A7779 (R-Car H1) compatible GPIO controller.
|
||||
- "renesas,gpio-r8a7790": for R8A7790 (R-Car H2) compatible GPIO controller.
|
||||
- "renesas,gpio-r8a7791": for R8A7791 (R-Car M2-W) compatible GPIO controller.
|
||||
- "renesas,gpio-r8a7792": for R8A7792 (R-Car V2H) compatible GPIO controller.
|
||||
- "renesas,gpio-r8a7793": for R8A7793 (R-Car M2-N) compatible GPIO controller.
|
||||
- "renesas,gpio-r8a7794": for R8A7794 (R-Car E2) compatible GPIO controller.
|
||||
- "renesas,gpio-r8a7795": for R8A7795 (R-Car H3) compatible GPIO controller.
|
||||
|
|
|
@ -37,15 +37,16 @@ hardware descriptions such as device tree or ACPI:
|
|||
external connector status, such as a headset line for an audio driver or an
|
||||
HDMI connector. It will provide a better userspace sysfs interface than GPIO.
|
||||
|
||||
- restart-gpio: drivers/power/gpio-restart.c is used to restart/reboot the
|
||||
system by pulling a GPIO line and will register a restart handler so
|
||||
- restart-gpio: drivers/power/reset/gpio-restart.c is used to restart/reboot
|
||||
the system by pulling a GPIO line and will register a restart handler so
|
||||
userspace can issue the right system call to restart the system.
|
||||
|
||||
- poweroff-gpio: drivers/power/gpio-poweroff.c is used to power the system down
|
||||
by pulling a GPIO line and will register a pm_power_off() callback so that
|
||||
userspace can issue the right system call to power down the system.
|
||||
- poweroff-gpio: drivers/power/reset/gpio-poweroff.c is used to power the
|
||||
system down by pulling a GPIO line and will register a pm_power_off()
|
||||
callback so that userspace can issue the right system call to power down the
|
||||
system.
|
||||
|
||||
- gpio-gate-clock: drivers/clk/clk-gpio-gate.c is used to control a gated clock
|
||||
- gpio-gate-clock: drivers/clk/clk-gpio.c is used to control a gated clock
|
||||
(off/on) that uses a GPIO, and integrated with the clock subsystem.
|
||||
|
||||
- i2c-gpio: drivers/i2c/busses/i2c-gpio.c is used to drive an I2C bus
|
||||
|
|
|
@ -6060,6 +6060,12 @@ L: linux-rdma@vger.kernel.org
|
|||
S: Supported
|
||||
F: drivers/infiniband/hw/i40iw/
|
||||
|
||||
INTEL MERRIFIELD GPIO DRIVER
|
||||
M: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
L: linux-gpio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/gpio/gpio-merrifield.c
|
||||
|
||||
INTEL-MID GPIO DRIVER
|
||||
M: David Cohen <david.a.cohen@linux.intel.com>
|
||||
L: linux-gpio@vger.kernel.org
|
||||
|
|
|
@ -8,8 +8,7 @@ config HEXAGON
|
|||
# select HAVE_REGS_AND_STACK_ACCESS_API
|
||||
# select HAVE_HW_BREAKPOINT if PERF_EVENTS
|
||||
# select ARCH_HAS_CPU_IDLE_WAIT
|
||||
# select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
# select ARCH_REQUIRE_GPIOLIB
|
||||
# select GPIOLIB
|
||||
# select HAVE_CLK
|
||||
# select GENERIC_PENDING_IRQ if SMP
|
||||
select GENERIC_ATOMIC64
|
||||
|
|
|
@ -39,7 +39,6 @@ config IA64
|
|||
select GENERIC_PENDING_IRQ if SMP
|
||||
select GENERIC_IRQ_SHOW
|
||||
select GENERIC_IRQ_LEGACY
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select ARCH_HAVE_NMI_SAFE_CMPXCHG
|
||||
select GENERIC_IOMAP
|
||||
select GENERIC_SMP_IDLE_THREAD
|
||||
|
|
|
@ -3,7 +3,6 @@ config MICROBLAZE
|
|||
select ARCH_HAS_GCOV_PROFILE_ALL
|
||||
select ARCH_MIGHT_HAVE_PC_PARPORT
|
||||
select ARCH_WANT_IPC_PARSE_VERSION
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select BUILDTIME_EXTABLE_SORT
|
||||
select CLKSRC_OF
|
||||
select CLONE_BACKWARDS3
|
||||
|
|
|
@ -10,7 +10,7 @@ config OPENRISC
|
|||
select IRQ_DOMAIN
|
||||
select HANDLE_DOMAIN_IRQ
|
||||
select HAVE_MEMBLOCK
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
select HAVE_ARCH_TRACEHOOK
|
||||
select GENERIC_IRQ_CHIP
|
||||
select GENERIC_IRQ_PROBE
|
||||
|
|
|
@ -98,7 +98,6 @@ config PPC
|
|||
select HAVE_FUNCTION_TRACER
|
||||
select HAVE_FUNCTION_GRAPH_TRACER
|
||||
select SYSCTL_EXCEPTION_TRACE
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select VIRT_TO_BUS if !PPC64
|
||||
select HAVE_IDE
|
||||
select HAVE_IOREMAP_PROT
|
||||
|
|
|
@ -137,7 +137,7 @@ config STB03xxx
|
|||
config PPC4xx_GPIO
|
||||
bool "PPC4xx GPIO support"
|
||||
depends on 40x
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
help
|
||||
Enable gpiolib support for ppc40x based boards
|
||||
|
||||
|
|
|
@ -273,7 +273,7 @@ config PPC44x_SIMPLE
|
|||
config PPC4xx_GPIO
|
||||
bool "PPC4xx GPIO support"
|
||||
depends on 44x
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
help
|
||||
Enable gpiolib support for ppc440 based boards
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ config PPC_MPC512x
|
|||
select IPIC
|
||||
select PPC_PCI_CHOICE
|
||||
select FSL_PCI if PCI
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select USB_EHCI_BIG_ENDIAN_MMIO if USB_EHCI_HCD
|
||||
select USB_EHCI_BIG_ENDIAN_DESC if USB_EHCI_HCD
|
||||
|
||||
|
|
|
@ -116,7 +116,6 @@ endif
|
|||
# used for usb & gpio
|
||||
config PPC_MPC831x
|
||||
bool
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
|
||||
# used for math-emu
|
||||
config PPC_MPC832x
|
||||
|
@ -125,9 +124,7 @@ config PPC_MPC832x
|
|||
# used for usb & gpio
|
||||
config PPC_MPC834x
|
||||
bool
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
|
||||
# used for usb & gpio
|
||||
config PPC_MPC837x
|
||||
bool
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
|
|
|
@ -225,7 +225,7 @@ config GE_IMP3A
|
|||
select DEFAULT_UIMAGE
|
||||
select SWIOTLB
|
||||
select MMIO_NVRAM
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
select GE_FPGA
|
||||
help
|
||||
This option enables support for the GE Intelligent Platforms IMP3A
|
||||
|
@ -272,7 +272,7 @@ config CORENET_GENERIC
|
|||
select PPC_E500MC
|
||||
select PHYS_64BIT
|
||||
select SWIOTLB
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
select GPIO_MPC8XXX
|
||||
select HAS_RAPIDIO
|
||||
select PPC_EPAPR_HV_PIC
|
||||
|
|
|
@ -4,7 +4,6 @@ menuconfig PPC_86xx
|
|||
depends on 6xx
|
||||
select FSL_SOC
|
||||
select ALTIVEC
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
help
|
||||
The Freescale E600 SoCs have 74xx cores.
|
||||
|
||||
|
@ -37,7 +36,7 @@ config GEF_PPC9A
|
|||
bool "GE PPC9A"
|
||||
select DEFAULT_UIMAGE
|
||||
select MMIO_NVRAM
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
select GE_FPGA
|
||||
help
|
||||
This option enables support for the GE PPC9A.
|
||||
|
@ -46,7 +45,7 @@ config GEF_SBC310
|
|||
bool "GE SBC310"
|
||||
select DEFAULT_UIMAGE
|
||||
select MMIO_NVRAM
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
select GE_FPGA
|
||||
help
|
||||
This option enables support for the GE SBC310.
|
||||
|
@ -55,7 +54,7 @@ config GEF_SBC610
|
|||
bool "GE SBC610"
|
||||
select DEFAULT_UIMAGE
|
||||
select MMIO_NVRAM
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
select GE_FPGA
|
||||
select HAS_RAPIDIO
|
||||
help
|
||||
|
|
|
@ -109,7 +109,7 @@ config 8xx_COPYBACK
|
|||
|
||||
config 8xx_GPIO
|
||||
bool "GPIO API Support"
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
help
|
||||
Saying Y here will cause the ports on an MPC8xx processor to be used
|
||||
with the GPIO API. If you say N here, the kernel needs less memory.
|
||||
|
|
|
@ -275,7 +275,7 @@ config TAU_AVERAGE
|
|||
config QE_GPIO
|
||||
bool "QE GPIO support"
|
||||
depends on QUICC_ENGINE
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
help
|
||||
Say Y here if you're going to use hardware that connects to the
|
||||
QE GPIOs.
|
||||
|
@ -285,7 +285,7 @@ config CPM2
|
|||
depends on (FSL_SOC_BOOKE && PPC32) || 8260
|
||||
select CPM
|
||||
select PPC_PCI_CHOICE
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
help
|
||||
The CPM2 (Communications Processor Module) is a coprocessor on
|
||||
embedded CPUs made by Freescale. Selecting this option means that
|
||||
|
@ -324,7 +324,7 @@ config OF_RTC
|
|||
config SIMPLE_GPIO
|
||||
bool "Support for simple, memory-mapped GPIO controllers"
|
||||
depends on PPC
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
help
|
||||
Say Y here to support simple, memory-mapped GPIO controllers.
|
||||
These are usually BCSRs used to control board's switches, LEDs,
|
||||
|
@ -334,7 +334,7 @@ config SIMPLE_GPIO
|
|||
config MCU_MPC8349EMITX
|
||||
bool "MPC8349E-mITX MCU driver"
|
||||
depends on I2C=y && PPC_83xx
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
help
|
||||
Say Y here to enable soft power-off functionality on the Freescale
|
||||
boards with the MPC8349E-mITX-compatible MCU chips. This driver will
|
||||
|
|
|
@ -264,7 +264,6 @@ config CPU_SUBTYPE_SH7203
|
|||
select CPU_HAS_FPU
|
||||
select SYS_SUPPORTS_SH_CMT
|
||||
select SYS_SUPPORTS_SH_MTU2
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select PINCTRL
|
||||
|
||||
config CPU_SUBTYPE_SH7206
|
||||
|
@ -353,7 +352,6 @@ config CPU_SUBTYPE_SH7720
|
|||
select CPU_SH3
|
||||
select CPU_HAS_DSP
|
||||
select SYS_SUPPORTS_SH_CMT
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select USB_OHCI_SH if USB_OHCI_HCD
|
||||
select PINCTRL
|
||||
help
|
||||
|
@ -419,7 +417,6 @@ config CPU_SUBTYPE_SH7723
|
|||
select ARCH_SHMOBILE
|
||||
select ARCH_SPARSEMEM_ENABLE
|
||||
select SYS_SUPPORTS_SH_CMT
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select PINCTRL
|
||||
help
|
||||
Select SH7723 if you have an SH-MobileR2 CPU.
|
||||
|
@ -431,7 +428,6 @@ config CPU_SUBTYPE_SH7724
|
|||
select ARCH_SHMOBILE
|
||||
select ARCH_SPARSEMEM_ENABLE
|
||||
select SYS_SUPPORTS_SH_CMT
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select PINCTRL
|
||||
help
|
||||
Select SH7724 if you have an SH-MobileR2R CPU.
|
||||
|
@ -440,7 +436,6 @@ config CPU_SUBTYPE_SH7734
|
|||
bool "Support SH7734 processor"
|
||||
select CPU_SH4A
|
||||
select CPU_SHX2
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select PINCTRL
|
||||
help
|
||||
Select SH7734 if you have a SH4A SH7734 CPU.
|
||||
|
@ -449,7 +444,6 @@ config CPU_SUBTYPE_SH7757
|
|||
bool "Support SH7757 processor"
|
||||
select CPU_SH4A
|
||||
select CPU_SHX2
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select PINCTRL
|
||||
help
|
||||
Select SH7757 if you have a SH4A SH7757 CPU.
|
||||
|
@ -475,7 +469,6 @@ config CPU_SUBTYPE_SH7785
|
|||
select CPU_SHX2
|
||||
select ARCH_SPARSEMEM_ENABLE
|
||||
select SYS_SUPPORTS_NUMA
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select PINCTRL
|
||||
|
||||
config CPU_SUBTYPE_SH7786
|
||||
|
@ -484,7 +477,6 @@ config CPU_SUBTYPE_SH7786
|
|||
select CPU_SHX3
|
||||
select CPU_HAS_PTEAEX
|
||||
select GENERIC_CLOCKEVENTS_BROADCAST if SMP
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select USB_OHCI_SH if USB_OHCI_HCD
|
||||
select USB_EHCI_SH if USB_EHCI_HCD
|
||||
select PINCTRL
|
||||
|
@ -494,7 +486,7 @@ config CPU_SUBTYPE_SHX3
|
|||
select CPU_SH4A
|
||||
select CPU_SHX3
|
||||
select GENERIC_CLOCKEVENTS_BROADCAST if SMP
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
select PINCTRL
|
||||
|
||||
# SH4AL-DSP Processor Support
|
||||
|
@ -513,7 +505,6 @@ config CPU_SUBTYPE_SH7722
|
|||
select ARCH_SPARSEMEM_ENABLE
|
||||
select SYS_SUPPORTS_NUMA
|
||||
select SYS_SUPPORTS_SH_CMT
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select PINCTRL
|
||||
|
||||
config CPU_SUBTYPE_SH7366
|
||||
|
|
|
@ -70,7 +70,7 @@ config SH_7724_SOLUTION_ENGINE
|
|||
bool "SolutionEngine7724"
|
||||
select SOLUTION_ENGINE
|
||||
depends on CPU_SUBTYPE_SH7724
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
select SND_SOC_AK4642 if SND_SIMPLE_CARD
|
||||
select REGULATOR_FIXED_VOLTAGE if REGULATOR
|
||||
help
|
||||
|
@ -174,7 +174,6 @@ config SH_SDK7786
|
|||
depends on CPU_SUBTYPE_SH7786
|
||||
select SYS_SUPPORTS_PCI
|
||||
select NO_IOPORT_MAP if !PCI
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select HAVE_SRAM_POOL
|
||||
select REGULATOR_FIXED_VOLTAGE if REGULATOR
|
||||
help
|
||||
|
@ -190,7 +189,7 @@ config SH_HIGHLANDER
|
|||
config SH_SH7757LCR
|
||||
bool "SH7757LCR"
|
||||
depends on CPU_SUBTYPE_SH7757
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
select REGULATOR_FIXED_VOLTAGE if REGULATOR
|
||||
|
||||
config SH_SH7785LCR
|
||||
|
@ -217,14 +216,14 @@ config SH_SH7785LCR_PT
|
|||
config SH_URQUELL
|
||||
bool "Urquell"
|
||||
depends on CPU_SUBTYPE_SH7786
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
select SYS_SUPPORTS_PCI
|
||||
select NO_IOPORT_MAP if !PCI
|
||||
|
||||
config SH_MIGOR
|
||||
bool "Migo-R"
|
||||
depends on CPU_SUBTYPE_SH7722
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
select REGULATOR_FIXED_VOLTAGE if REGULATOR
|
||||
help
|
||||
Select Migo-R if configuring for the SH7722 Migo-R platform
|
||||
|
@ -233,7 +232,7 @@ config SH_MIGOR
|
|||
config SH_AP325RXA
|
||||
bool "AP-325RXA"
|
||||
depends on CPU_SUBTYPE_SH7723
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
select REGULATOR_FIXED_VOLTAGE if REGULATOR
|
||||
help
|
||||
Renesas "AP-325RXA" support.
|
||||
|
@ -242,7 +241,7 @@ config SH_AP325RXA
|
|||
config SH_KFR2R09
|
||||
bool "KFR2R09"
|
||||
depends on CPU_SUBTYPE_SH7724
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
select REGULATOR_FIXED_VOLTAGE if REGULATOR
|
||||
help
|
||||
"Kit For R2R for 2009" support.
|
||||
|
@ -250,7 +249,7 @@ config SH_KFR2R09
|
|||
config SH_ECOVEC
|
||||
bool "EcoVec"
|
||||
depends on CPU_SUBTYPE_SH7724
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
select SND_SOC_DA7210 if SND_SIMPLE_CARD
|
||||
select REGULATOR_FIXED_VOLTAGE if REGULATOR
|
||||
help
|
||||
|
@ -327,7 +326,7 @@ config SH_X3PROTO
|
|||
config SH_MAGIC_PANEL_R2
|
||||
bool "Magic Panel R2"
|
||||
depends on CPU_SUBTYPE_SH7720
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
select REGULATOR_FIXED_VOLTAGE if REGULATOR
|
||||
help
|
||||
Select Magic Panel R2 if configuring for Magic Panel R2.
|
||||
|
|
|
@ -18,7 +18,7 @@ config SH_R7780MP
|
|||
config SH_R7785RP
|
||||
bool "R7785RP board support"
|
||||
depends on CPU_SUBTYPE_SH7785
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
|
||||
endchoice
|
||||
|
||||
|
|
|
@ -10,17 +10,17 @@ config SH_RSK7201
|
|||
|
||||
config SH_RSK7203
|
||||
bool "RSK7203"
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
depends on CPU_SUBTYPE_SH7203
|
||||
|
||||
config SH_RSK7264
|
||||
bool "RSK2+SH7264"
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
depends on CPU_SUBTYPE_SH7264
|
||||
|
||||
config SH_RSK7269
|
||||
bool "RSK2+SH7269"
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
depends on CPU_SUBTYPE_SH7269
|
||||
|
||||
endchoice
|
||||
|
|
|
@ -80,7 +80,7 @@ config ARCH_PUV3
|
|||
select CPU_UCV2
|
||||
select GENERIC_CLOCKEVENTS
|
||||
select HAVE_CLK
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select GPIOLIB
|
||||
|
||||
# CONFIGs for ARCH_PUV3
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ obj-$(CONFIG_GENERIC_PHY) += phy/
|
|||
|
||||
# GPIO must come after pinctrl as gpios may need to mux pins etc
|
||||
obj-$(CONFIG_PINCTRL) += pinctrl/
|
||||
obj-y += gpio/
|
||||
obj-$(CONFIG_GPIOLIB) += gpio/
|
||||
obj-y += pwm/
|
||||
obj-$(CONFIG_PCI) += pci/
|
||||
obj-$(CONFIG_PARISC) += parisc/
|
||||
|
|
|
@ -250,7 +250,7 @@ config GPIO_LOONGSON
|
|||
driver for GPIO functionality on Loongson-2F/3A/3B processors.
|
||||
|
||||
config GPIO_LPC18XX
|
||||
bool "NXP LPC18XX/43XX GPIO support"
|
||||
tristate "NXP LPC18XX/43XX GPIO support"
|
||||
default y if ARCH_LPC18XX
|
||||
depends on OF_GPIO && (ARCH_LPC18XX || COMPILE_TEST)
|
||||
help
|
||||
|
@ -874,6 +874,15 @@ config GPIO_LP3943
|
|||
LP3943 can be used as a GPIO expander which provides up to 16 GPIOs.
|
||||
Open drain outputs are required for this usage.
|
||||
|
||||
config GPIO_MAX77620
|
||||
tristate "GPIO support for PMIC MAX77620 and MAX20024"
|
||||
depends on MFD_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.
|
||||
|
||||
config GPIO_MSIC
|
||||
bool "Intel MSIC mixed signal gpio support"
|
||||
depends on MFD_INTEL_MSIC
|
||||
|
@ -1029,11 +1038,18 @@ config GPIO_BT8XX
|
|||
If unsure, say N.
|
||||
|
||||
config GPIO_INTEL_MID
|
||||
bool "Intel Mid GPIO support"
|
||||
depends on X86
|
||||
bool "Intel MID GPIO support"
|
||||
depends on X86_INTEL_MID
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Say Y here to support Intel Mid GPIO.
|
||||
Say Y here to support Intel MID GPIO.
|
||||
|
||||
config GPIO_MERRIFIELD
|
||||
tristate "Intel Merrifield GPIO support"
|
||||
depends on X86_INTEL_MID
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Say Y here to support Intel Merrifield GPIO.
|
||||
|
||||
config GPIO_ML_IOH
|
||||
tristate "OKI SEMICONDUCTOR ML7213 IOH GPIO support"
|
||||
|
|
|
@ -61,8 +61,10 @@ obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o
|
|||
obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o
|
||||
obj-$(CONFIG_GPIO_MAX7301) += gpio-max7301.o
|
||||
obj-$(CONFIG_GPIO_MAX732X) += gpio-max732x.o
|
||||
obj-$(CONFIG_GPIO_MAX77620) += gpio-max77620.o
|
||||
obj-$(CONFIG_GPIO_MB86S7X) += gpio-mb86s7x.o
|
||||
obj-$(CONFIG_GPIO_MENZ127) += gpio-menz127.o
|
||||
obj-$(CONFIG_GPIO_MERRIFIELD) += gpio-merrifield.o
|
||||
obj-$(CONFIG_GPIO_MC33880) += gpio-mc33880.o
|
||||
obj-$(CONFIG_GPIO_MC9S08DZ60) += gpio-mc9s08dz60.o
|
||||
obj-$(CONFIG_GPIO_MCP23S08) += gpio-mcp23s08.o
|
||||
|
|
|
@ -35,13 +35,8 @@ struct gen_74x164_chip {
|
|||
|
||||
static int __gen_74x164_write_config(struct gen_74x164_chip *chip)
|
||||
{
|
||||
struct spi_transfer xfer = {
|
||||
.tx_buf = chip->buffer,
|
||||
.len = chip->registers,
|
||||
};
|
||||
|
||||
return spi_sync_transfer(to_spi_device(chip->gpio_chip.parent),
|
||||
&xfer, 1);
|
||||
return spi_write(to_spi_device(chip->gpio_chip.parent), chip->buffer,
|
||||
chip->registers);
|
||||
}
|
||||
|
||||
static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset)
|
||||
|
|
|
@ -20,8 +20,12 @@ static int clps711x_gpio_probe(struct platform_device *pdev)
|
|||
void __iomem *dat, *dir;
|
||||
struct gpio_chip *gc;
|
||||
struct resource *res;
|
||||
int err, id = np ? of_alias_get_id(np, "gpio") : pdev->id;
|
||||
int err, id;
|
||||
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
id = of_alias_get_id(np, "gpio");
|
||||
if ((id < 0) || (id > 4))
|
||||
return -ENODEV;
|
||||
|
||||
|
@ -63,7 +67,7 @@ static int clps711x_gpio_probe(struct platform_device *pdev)
|
|||
break;
|
||||
}
|
||||
|
||||
gc->base = id * 8;
|
||||
gc->base = -1;
|
||||
gc->owner = THIS_MODULE;
|
||||
platform_set_drvdata(pdev, gc);
|
||||
|
||||
|
@ -71,7 +75,7 @@ static int clps711x_gpio_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
static const struct of_device_id __maybe_unused clps711x_gpio_ids[] = {
|
||||
{ .compatible = "cirrus,clps711x-gpio" },
|
||||
{ .compatible = "cirrus,ep7209-gpio" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, clps711x_gpio_ids);
|
||||
|
|
|
@ -486,6 +486,7 @@ dwapb_gpio_get_pdata(struct device *dev)
|
|||
pp->idx >= DWAPB_MAX_PORTS) {
|
||||
dev_err(dev,
|
||||
"missing/invalid port index for port%d\n", i);
|
||||
fwnode_handle_put(fwnode);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
|
|
|
@ -125,6 +125,7 @@ static inline void superio_exit(int base)
|
|||
* GPIO chip.
|
||||
*/
|
||||
|
||||
static int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset);
|
||||
static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset);
|
||||
static int f7188x_gpio_get(struct gpio_chip *chip, unsigned offset);
|
||||
static int f7188x_gpio_direction_out(struct gpio_chip *chip,
|
||||
|
@ -139,6 +140,7 @@ static int f7188x_gpio_set_single_ended(struct gpio_chip *gc,
|
|||
.chip = { \
|
||||
.label = DRVNAME, \
|
||||
.owner = THIS_MODULE, \
|
||||
.get_direction = f7188x_gpio_get_direction, \
|
||||
.direction_input = f7188x_gpio_direction_in, \
|
||||
.get = f7188x_gpio_get, \
|
||||
.direction_output = f7188x_gpio_direction_out, \
|
||||
|
@ -209,6 +211,26 @@ static struct f7188x_gpio_bank f81866_gpio_bank[] = {
|
|||
F7188X_GPIO_BANK(80, 8, 0x88),
|
||||
};
|
||||
|
||||
static int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
int err;
|
||||
struct f7188x_gpio_bank *bank =
|
||||
container_of(chip, struct f7188x_gpio_bank, chip);
|
||||
struct f7188x_sio *sio = bank->data->sio;
|
||||
u8 dir;
|
||||
|
||||
err = superio_enter(sio->addr);
|
||||
if (err)
|
||||
return err;
|
||||
superio_select(sio->addr, SIO_LD_GPIO);
|
||||
|
||||
dir = superio_inb(sio->addr, gpio_dir(bank->regbase));
|
||||
|
||||
superio_exit(sio->addr);
|
||||
|
||||
return !(dir & 1 << offset);
|
||||
}
|
||||
|
||||
static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
int err;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Intel MID GPIO driver
|
||||
*
|
||||
* Copyright (c) 2008-2014 Intel Corporation.
|
||||
* Copyright (c) 2008-2014,2016 Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
|
@ -17,21 +17,20 @@
|
|||
* Moorestown platform Langwell chip.
|
||||
* Medfield platform Penwell chip.
|
||||
* Clovertrail platform Cloverview chip.
|
||||
* Merrifield platform Tangier chip.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stddef.h>
|
||||
|
||||
#define INTEL_MID_IRQ_TYPE_EDGE (1 << 0)
|
||||
#define INTEL_MID_IRQ_TYPE_LEVEL (1 << 1)
|
||||
|
@ -64,10 +63,6 @@ enum GPIO_REG {
|
|||
/* intel_mid gpio driver data */
|
||||
struct intel_mid_gpio_ddata {
|
||||
u16 ngpio; /* number of gpio pins */
|
||||
u32 gplr_offset; /* offset of first GPLR register from base */
|
||||
u32 flis_base; /* base address of FLIS registers */
|
||||
u32 flis_len; /* length of FLIS registers */
|
||||
u32 (*get_flis_offset)(int gpio);
|
||||
u32 chip_irq_type; /* chip interrupt type */
|
||||
};
|
||||
|
||||
|
@ -252,15 +247,6 @@ static const struct intel_mid_gpio_ddata gpio_cloverview_core = {
|
|||
.chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE,
|
||||
};
|
||||
|
||||
static const struct intel_mid_gpio_ddata gpio_tangier = {
|
||||
.ngpio = 192,
|
||||
.gplr_offset = 4,
|
||||
.flis_base = 0xff0c0000,
|
||||
.flis_len = 0x8000,
|
||||
.get_flis_offset = NULL,
|
||||
.chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE,
|
||||
};
|
||||
|
||||
static const struct pci_device_id intel_gpio_ids[] = {
|
||||
{
|
||||
/* Lincroft */
|
||||
|
@ -287,11 +273,6 @@ static const struct pci_device_id intel_gpio_ids[] = {
|
|||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08f7),
|
||||
.driver_data = (kernel_ulong_t)&gpio_cloverview_core,
|
||||
},
|
||||
{
|
||||
/* Tangier */
|
||||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x1199),
|
||||
.driver_data = (kernel_ulong_t)&gpio_tangier,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, intel_gpio_ids);
|
||||
|
@ -401,7 +382,7 @@ static int intel_gpio_probe(struct pci_dev *pdev,
|
|||
spin_lock_init(&priv->lock);
|
||||
|
||||
pci_set_drvdata(pdev, priv);
|
||||
retval = gpiochip_add_data(&priv->chip, priv);
|
||||
retval = devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv);
|
||||
if (retval) {
|
||||
dev_err(&pdev->dev, "gpiochip_add error %d\n", retval);
|
||||
return retval;
|
||||
|
|
|
@ -383,7 +383,6 @@ static int lp_gpio_probe(struct platform_device *pdev)
|
|||
handle_simple_irq, IRQ_TYPE_NONE);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add irqchip\n");
|
||||
gpiochip_remove(gc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,315 @@
|
|||
/*
|
||||
* MAXIM MAX77620 GPIO driver
|
||||
*
|
||||
* Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/max77620.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define GPIO_REG_ADDR(offset) (MAX77620_REG_GPIO0 + offset)
|
||||
|
||||
struct max77620_gpio {
|
||||
struct gpio_chip gpio_chip;
|
||||
struct regmap *rmap;
|
||||
struct device *dev;
|
||||
int gpio_irq;
|
||||
int irq_base;
|
||||
int gpio_base;
|
||||
};
|
||||
|
||||
static const struct regmap_irq max77620_gpio_irqs[] = {
|
||||
[0] = {
|
||||
.mask = MAX77620_IRQ_LVL2_GPIO_EDGE0,
|
||||
.type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING,
|
||||
.type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,
|
||||
.reg_offset = 0,
|
||||
.type_reg_offset = 0,
|
||||
},
|
||||
[1] = {
|
||||
.mask = MAX77620_IRQ_LVL2_GPIO_EDGE1,
|
||||
.type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING,
|
||||
.type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,
|
||||
.reg_offset = 0,
|
||||
.type_reg_offset = 1,
|
||||
},
|
||||
[2] = {
|
||||
.mask = MAX77620_IRQ_LVL2_GPIO_EDGE2,
|
||||
.type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING,
|
||||
.type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,
|
||||
.reg_offset = 0,
|
||||
.type_reg_offset = 2,
|
||||
},
|
||||
[3] = {
|
||||
.mask = MAX77620_IRQ_LVL2_GPIO_EDGE3,
|
||||
.type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING,
|
||||
.type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,
|
||||
.reg_offset = 0,
|
||||
.type_reg_offset = 3,
|
||||
},
|
||||
[4] = {
|
||||
.mask = MAX77620_IRQ_LVL2_GPIO_EDGE4,
|
||||
.type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING,
|
||||
.type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,
|
||||
.reg_offset = 0,
|
||||
.type_reg_offset = 4,
|
||||
},
|
||||
[5] = {
|
||||
.mask = MAX77620_IRQ_LVL2_GPIO_EDGE5,
|
||||
.type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING,
|
||||
.type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,
|
||||
.reg_offset = 0,
|
||||
.type_reg_offset = 5,
|
||||
},
|
||||
[6] = {
|
||||
.mask = MAX77620_IRQ_LVL2_GPIO_EDGE6,
|
||||
.type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING,
|
||||
.type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,
|
||||
.reg_offset = 0,
|
||||
.type_reg_offset = 6,
|
||||
},
|
||||
[7] = {
|
||||
.mask = MAX77620_IRQ_LVL2_GPIO_EDGE7,
|
||||
.type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING,
|
||||
.type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,
|
||||
.reg_offset = 0,
|
||||
.type_reg_offset = 7,
|
||||
},
|
||||
};
|
||||
|
||||
static struct regmap_irq_chip max77620_gpio_irq_chip = {
|
||||
.name = "max77620-gpio",
|
||||
.irqs = max77620_gpio_irqs,
|
||||
.num_irqs = ARRAY_SIZE(max77620_gpio_irqs),
|
||||
.num_regs = 1,
|
||||
.num_type_reg = 8,
|
||||
.irq_reg_stride = 1,
|
||||
.type_reg_stride = 1,
|
||||
.status_base = MAX77620_REG_IRQ_LVL2_GPIO,
|
||||
.type_base = MAX77620_REG_GPIO0,
|
||||
};
|
||||
|
||||
static int max77620_gpio_dir_input(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
struct max77620_gpio *mgpio = gpiochip_get_data(gc);
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset),
|
||||
MAX77620_CNFG_GPIO_DIR_MASK,
|
||||
MAX77620_CNFG_GPIO_DIR_INPUT);
|
||||
if (ret < 0)
|
||||
dev_err(mgpio->dev, "CNFG_GPIOx dir update failed: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max77620_gpio_get(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
struct max77620_gpio *mgpio = gpiochip_get_data(gc);
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(mgpio->rmap, GPIO_REG_ADDR(offset), &val);
|
||||
if (ret < 0) {
|
||||
dev_err(mgpio->dev, "CNFG_GPIOx read failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (val & MAX77620_CNFG_GPIO_DIR_MASK)
|
||||
return !!(val & MAX77620_CNFG_GPIO_INPUT_VAL_MASK);
|
||||
else
|
||||
return !!(val & MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK);
|
||||
}
|
||||
|
||||
static int max77620_gpio_dir_output(struct gpio_chip *gc, unsigned int offset,
|
||||
int value)
|
||||
{
|
||||
struct max77620_gpio *mgpio = gpiochip_get_data(gc);
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
val = (value) ? MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH :
|
||||
MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW;
|
||||
|
||||
ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset),
|
||||
MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK, val);
|
||||
if (ret < 0) {
|
||||
dev_err(mgpio->dev, "CNFG_GPIOx val update failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset),
|
||||
MAX77620_CNFG_GPIO_DIR_MASK,
|
||||
MAX77620_CNFG_GPIO_DIR_OUTPUT);
|
||||
if (ret < 0)
|
||||
dev_err(mgpio->dev, "CNFG_GPIOx dir update failed: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max77620_gpio_set_debounce(struct gpio_chip *gc,
|
||||
unsigned int offset,
|
||||
unsigned int debounce)
|
||||
{
|
||||
struct max77620_gpio *mgpio = gpiochip_get_data(gc);
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
switch (debounce) {
|
||||
case 0:
|
||||
val = MAX77620_CNFG_GPIO_DBNC_None;
|
||||
break;
|
||||
case 1 ... 8:
|
||||
val = MAX77620_CNFG_GPIO_DBNC_8ms;
|
||||
break;
|
||||
case 9 ... 16:
|
||||
val = MAX77620_CNFG_GPIO_DBNC_16ms;
|
||||
break;
|
||||
case 17 ... 32:
|
||||
val = MAX77620_CNFG_GPIO_DBNC_32ms;
|
||||
break;
|
||||
default:
|
||||
dev_err(mgpio->dev, "Illegal value %u\n", debounce);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset),
|
||||
MAX77620_CNFG_GPIO_DBNC_MASK, val);
|
||||
if (ret < 0)
|
||||
dev_err(mgpio->dev, "CNFG_GPIOx_DBNC update failed: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void max77620_gpio_set(struct gpio_chip *gc, unsigned int offset,
|
||||
int value)
|
||||
{
|
||||
struct max77620_gpio *mgpio = gpiochip_get_data(gc);
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
val = (value) ? MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH :
|
||||
MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW;
|
||||
|
||||
ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset),
|
||||
MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK, val);
|
||||
if (ret < 0)
|
||||
dev_err(mgpio->dev, "CNFG_GPIO_OUT update failed: %d\n", ret);
|
||||
}
|
||||
|
||||
static int max77620_gpio_set_single_ended(struct gpio_chip *gc,
|
||||
unsigned int offset,
|
||||
enum single_ended_mode mode)
|
||||
{
|
||||
struct max77620_gpio *mgpio = gpiochip_get_data(gc);
|
||||
|
||||
switch (mode) {
|
||||
case LINE_MODE_OPEN_DRAIN:
|
||||
return regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset),
|
||||
MAX77620_CNFG_GPIO_DRV_MASK,
|
||||
MAX77620_CNFG_GPIO_DRV_OPENDRAIN);
|
||||
case LINE_MODE_PUSH_PULL:
|
||||
return regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset),
|
||||
MAX77620_CNFG_GPIO_DRV_MASK,
|
||||
MAX77620_CNFG_GPIO_DRV_PUSHPULL);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static int max77620_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
struct max77620_gpio *mgpio = gpiochip_get_data(gc);
|
||||
struct max77620_chip *chip = dev_get_drvdata(mgpio->dev->parent);
|
||||
|
||||
return regmap_irq_get_virq(chip->gpio_irq_data, offset);
|
||||
}
|
||||
|
||||
static int max77620_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct max77620_chip *chip = dev_get_drvdata(pdev->dev.parent);
|
||||
struct max77620_gpio *mgpio;
|
||||
int gpio_irq;
|
||||
int ret;
|
||||
|
||||
gpio_irq = platform_get_irq(pdev, 0);
|
||||
if (gpio_irq <= 0) {
|
||||
dev_err(&pdev->dev, "GPIO irq not available %d\n", gpio_irq);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mgpio = devm_kzalloc(&pdev->dev, sizeof(*mgpio), GFP_KERNEL);
|
||||
if (!mgpio)
|
||||
return -ENOMEM;
|
||||
|
||||
mgpio->rmap = chip->rmap;
|
||||
mgpio->dev = &pdev->dev;
|
||||
mgpio->gpio_irq = gpio_irq;
|
||||
|
||||
mgpio->gpio_chip.label = pdev->name;
|
||||
mgpio->gpio_chip.parent = &pdev->dev;
|
||||
mgpio->gpio_chip.direction_input = max77620_gpio_dir_input;
|
||||
mgpio->gpio_chip.get = max77620_gpio_get;
|
||||
mgpio->gpio_chip.direction_output = max77620_gpio_dir_output;
|
||||
mgpio->gpio_chip.set_debounce = max77620_gpio_set_debounce;
|
||||
mgpio->gpio_chip.set = max77620_gpio_set;
|
||||
mgpio->gpio_chip.set_single_ended = max77620_gpio_set_single_ended;
|
||||
mgpio->gpio_chip.to_irq = max77620_gpio_to_irq;
|
||||
mgpio->gpio_chip.ngpio = MAX77620_GPIO_NR;
|
||||
mgpio->gpio_chip.can_sleep = 1;
|
||||
mgpio->gpio_chip.base = -1;
|
||||
mgpio->irq_base = -1;
|
||||
#ifdef CONFIG_OF_GPIO
|
||||
mgpio->gpio_chip.of_node = pdev->dev.parent->of_node;
|
||||
#endif
|
||||
|
||||
platform_set_drvdata(pdev, mgpio);
|
||||
|
||||
ret = devm_gpiochip_add_data(&pdev->dev, &mgpio->gpio_chip, mgpio);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "gpio_init: Failed to add max77620_gpio\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
mgpio->gpio_base = mgpio->gpio_chip.base;
|
||||
ret = devm_regmap_add_irq_chip(&pdev->dev, chip->rmap, mgpio->gpio_irq,
|
||||
IRQF_ONESHOT, mgpio->irq_base,
|
||||
&max77620_gpio_irq_chip,
|
||||
&chip->gpio_irq_data);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to add gpio irq_chip %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id max77620_gpio_devtype[] = {
|
||||
{ .name = "max77620-gpio", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, max77620_gpio_devtype);
|
||||
|
||||
static struct platform_driver max77620_gpio_driver = {
|
||||
.driver.name = "max77620-gpio",
|
||||
.probe = max77620_gpio_probe,
|
||||
.id_table = max77620_gpio_devtype,
|
||||
};
|
||||
|
||||
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");
|
|
@ -187,7 +187,6 @@ MODULE_DEVICE_TABLE(mcb, men_z127_ids);
|
|||
static struct mcb_driver men_z127_driver = {
|
||||
.driver = {
|
||||
.name = "z127-gpio",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = men_z127_probe,
|
||||
.remove = men_z127_remove,
|
||||
|
|
|
@ -0,0 +1,444 @@
|
|||
/*
|
||||
* Intel Merrifield SoC GPIO driver
|
||||
*
|
||||
* Copyright (c) 2016 Intel Corporation.
|
||||
* Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
#define GCCR 0x000 /* controller configuration */
|
||||
#define GPLR 0x004 /* pin level r/o */
|
||||
#define GPDR 0x01c /* pin direction */
|
||||
#define GPSR 0x034 /* pin set w/o */
|
||||
#define GPCR 0x04c /* pin clear w/o */
|
||||
#define GRER 0x064 /* rising edge detect */
|
||||
#define GFER 0x07c /* falling edge detect */
|
||||
#define GFBR 0x094 /* glitch filter bypass */
|
||||
#define GIMR 0x0ac /* interrupt mask */
|
||||
#define GISR 0x0c4 /* interrupt source */
|
||||
#define GITR 0x300 /* input type */
|
||||
#define GLPR 0x318 /* level input polarity */
|
||||
#define GWMR 0x400 /* wake mask */
|
||||
#define GWSR 0x418 /* wake source */
|
||||
#define GSIR 0xc00 /* secure input */
|
||||
|
||||
/* Intel Merrifield has 192 GPIO pins */
|
||||
#define MRFLD_NGPIO 192
|
||||
|
||||
struct mrfld_gpio_pinrange {
|
||||
unsigned int gpio_base;
|
||||
unsigned int pin_base;
|
||||
unsigned int npins;
|
||||
};
|
||||
|
||||
#define GPIO_PINRANGE(gstart, gend, pstart) \
|
||||
{ \
|
||||
.gpio_base = (gstart), \
|
||||
.pin_base = (pstart), \
|
||||
.npins = (gend) - (gstart) + 1, \
|
||||
}
|
||||
|
||||
struct mrfld_gpio {
|
||||
struct gpio_chip chip;
|
||||
void __iomem *reg_base;
|
||||
raw_spinlock_t lock;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static const struct mrfld_gpio_pinrange mrfld_gpio_ranges[] = {
|
||||
GPIO_PINRANGE(0, 11, 146),
|
||||
GPIO_PINRANGE(12, 13, 144),
|
||||
GPIO_PINRANGE(14, 15, 35),
|
||||
GPIO_PINRANGE(16, 16, 164),
|
||||
GPIO_PINRANGE(17, 18, 105),
|
||||
GPIO_PINRANGE(19, 22, 101),
|
||||
GPIO_PINRANGE(23, 30, 107),
|
||||
GPIO_PINRANGE(32, 43, 67),
|
||||
GPIO_PINRANGE(44, 63, 195),
|
||||
GPIO_PINRANGE(64, 67, 140),
|
||||
GPIO_PINRANGE(68, 69, 165),
|
||||
GPIO_PINRANGE(70, 71, 65),
|
||||
GPIO_PINRANGE(72, 76, 228),
|
||||
GPIO_PINRANGE(77, 86, 37),
|
||||
GPIO_PINRANGE(87, 87, 48),
|
||||
GPIO_PINRANGE(88, 88, 47),
|
||||
GPIO_PINRANGE(89, 96, 49),
|
||||
GPIO_PINRANGE(97, 97, 34),
|
||||
GPIO_PINRANGE(102, 119, 83),
|
||||
GPIO_PINRANGE(120, 123, 79),
|
||||
GPIO_PINRANGE(124, 135, 115),
|
||||
GPIO_PINRANGE(137, 142, 158),
|
||||
GPIO_PINRANGE(154, 163, 24),
|
||||
GPIO_PINRANGE(164, 176, 215),
|
||||
GPIO_PINRANGE(177, 189, 127),
|
||||
GPIO_PINRANGE(190, 191, 178),
|
||||
};
|
||||
|
||||
static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned int offset,
|
||||
unsigned int reg_type_offset)
|
||||
{
|
||||
struct mrfld_gpio *priv = gpiochip_get_data(chip);
|
||||
u8 reg = offset / 32;
|
||||
|
||||
return priv->reg_base + reg_type_offset + reg * 4;
|
||||
}
|
||||
|
||||
static int mrfld_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
void __iomem *gplr = gpio_reg(chip, offset, GPLR);
|
||||
|
||||
return !!(readl(gplr) & BIT(offset % 32));
|
||||
}
|
||||
|
||||
static void mrfld_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
int value)
|
||||
{
|
||||
struct mrfld_gpio *priv = gpiochip_get_data(chip);
|
||||
void __iomem *gpsr, *gpcr;
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
if (value) {
|
||||
gpsr = gpio_reg(chip, offset, GPSR);
|
||||
writel(BIT(offset % 32), gpsr);
|
||||
} else {
|
||||
gpcr = gpio_reg(chip, offset, GPCR);
|
||||
writel(BIT(offset % 32), gpcr);
|
||||
}
|
||||
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
static int mrfld_gpio_direction_input(struct gpio_chip *chip,
|
||||
unsigned int offset)
|
||||
{
|
||||
struct mrfld_gpio *priv = gpiochip_get_data(chip);
|
||||
void __iomem *gpdr = gpio_reg(chip, offset, GPDR);
|
||||
unsigned long flags;
|
||||
u32 value;
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
value = readl(gpdr);
|
||||
value &= ~BIT(offset % 32);
|
||||
writel(value, gpdr);
|
||||
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mrfld_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned int offset, int value)
|
||||
{
|
||||
struct mrfld_gpio *priv = gpiochip_get_data(chip);
|
||||
void __iomem *gpdr = gpio_reg(chip, offset, GPDR);
|
||||
unsigned long flags;
|
||||
|
||||
mrfld_gpio_set(chip, offset, value);
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
value = readl(gpdr);
|
||||
value |= BIT(offset % 32);
|
||||
writel(value, gpdr);
|
||||
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mrfld_irq_ack(struct irq_data *d)
|
||||
{
|
||||
struct mrfld_gpio *priv = irq_data_get_irq_chip_data(d);
|
||||
u32 gpio = irqd_to_hwirq(d);
|
||||
void __iomem *gisr = gpio_reg(&priv->chip, gpio, GISR);
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
writel(BIT(gpio % 32), gisr);
|
||||
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
static void mrfld_irq_unmask_mask(struct irq_data *d, bool unmask)
|
||||
{
|
||||
struct mrfld_gpio *priv = irq_data_get_irq_chip_data(d);
|
||||
u32 gpio = irqd_to_hwirq(d);
|
||||
void __iomem *gimr = gpio_reg(&priv->chip, gpio, GIMR);
|
||||
unsigned long flags;
|
||||
u32 value;
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
if (unmask)
|
||||
value = readl(gimr) | BIT(gpio % 32);
|
||||
else
|
||||
value = readl(gimr) & ~BIT(gpio % 32);
|
||||
writel(value, gimr);
|
||||
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
static void mrfld_irq_mask(struct irq_data *d)
|
||||
{
|
||||
mrfld_irq_unmask_mask(d, false);
|
||||
}
|
||||
|
||||
static void mrfld_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
mrfld_irq_unmask_mask(d, true);
|
||||
}
|
||||
|
||||
static int mrfld_irq_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct mrfld_gpio *priv = gpiochip_get_data(gc);
|
||||
u32 gpio = irqd_to_hwirq(d);
|
||||
void __iomem *grer = gpio_reg(&priv->chip, gpio, GRER);
|
||||
void __iomem *gfer = gpio_reg(&priv->chip, gpio, GFER);
|
||||
void __iomem *gitr = gpio_reg(&priv->chip, gpio, GITR);
|
||||
void __iomem *glpr = gpio_reg(&priv->chip, gpio, GLPR);
|
||||
unsigned long flags;
|
||||
u32 value;
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
if (type & IRQ_TYPE_EDGE_RISING)
|
||||
value = readl(grer) | BIT(gpio % 32);
|
||||
else
|
||||
value = readl(grer) & ~BIT(gpio % 32);
|
||||
writel(value, grer);
|
||||
|
||||
if (type & IRQ_TYPE_EDGE_FALLING)
|
||||
value = readl(gfer) | BIT(gpio % 32);
|
||||
else
|
||||
value = readl(gfer) & ~BIT(gpio % 32);
|
||||
writel(value, gfer);
|
||||
|
||||
/*
|
||||
* To prevent glitches from triggering an unintended level interrupt,
|
||||
* configure GLPR register first and then configure GITR.
|
||||
*/
|
||||
if (type & IRQ_TYPE_LEVEL_LOW)
|
||||
value = readl(glpr) | BIT(gpio % 32);
|
||||
else
|
||||
value = readl(glpr) & ~BIT(gpio % 32);
|
||||
writel(value, glpr);
|
||||
|
||||
if (type & IRQ_TYPE_LEVEL_MASK) {
|
||||
value = readl(gitr) | BIT(gpio % 32);
|
||||
writel(value, gitr);
|
||||
|
||||
irq_set_handler_locked(d, handle_level_irq);
|
||||
} else if (type & IRQ_TYPE_EDGE_BOTH) {
|
||||
value = readl(gitr) & ~BIT(gpio % 32);
|
||||
writel(value, gitr);
|
||||
|
||||
irq_set_handler_locked(d, handle_edge_irq);
|
||||
}
|
||||
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mrfld_irq_set_wake(struct irq_data *d, unsigned int on)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct mrfld_gpio *priv = gpiochip_get_data(gc);
|
||||
u32 gpio = irqd_to_hwirq(d);
|
||||
void __iomem *gwmr = gpio_reg(&priv->chip, gpio, GWMR);
|
||||
void __iomem *gwsr = gpio_reg(&priv->chip, gpio, GWSR);
|
||||
unsigned long flags;
|
||||
u32 value;
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
/* Clear the existing wake status */
|
||||
writel(BIT(gpio % 32), gwsr);
|
||||
|
||||
if (on)
|
||||
value = readl(gwmr) | BIT(gpio % 32);
|
||||
else
|
||||
value = readl(gwmr) & ~BIT(gpio % 32);
|
||||
writel(value, gwmr);
|
||||
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
dev_dbg(priv->dev, "%sable wake for gpio %u\n", on ? "en" : "dis", gpio);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_chip mrfld_irqchip = {
|
||||
.name = "gpio-merrifield",
|
||||
.irq_ack = mrfld_irq_ack,
|
||||
.irq_mask = mrfld_irq_mask,
|
||||
.irq_unmask = mrfld_irq_unmask,
|
||||
.irq_set_type = mrfld_irq_set_type,
|
||||
.irq_set_wake = mrfld_irq_set_wake,
|
||||
};
|
||||
|
||||
static void mrfld_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
|
||||
struct mrfld_gpio *priv = gpiochip_get_data(gc);
|
||||
struct irq_chip *irqchip = irq_desc_get_chip(desc);
|
||||
unsigned long base, gpio;
|
||||
|
||||
chained_irq_enter(irqchip, desc);
|
||||
|
||||
/* Check GPIO controller to check which pin triggered the interrupt */
|
||||
for (base = 0; base < priv->chip.ngpio; base += 32) {
|
||||
void __iomem *gisr = gpio_reg(&priv->chip, base, GISR);
|
||||
void __iomem *gimr = gpio_reg(&priv->chip, base, GIMR);
|
||||
unsigned long pending, enabled;
|
||||
|
||||
pending = readl(gisr);
|
||||
enabled = readl(gimr);
|
||||
|
||||
/* Only interrupts that are enabled */
|
||||
pending &= enabled;
|
||||
|
||||
for_each_set_bit(gpio, &pending, 32) {
|
||||
unsigned int irq;
|
||||
|
||||
irq = irq_find_mapping(gc->irqdomain, base + gpio);
|
||||
generic_handle_irq(irq);
|
||||
}
|
||||
}
|
||||
|
||||
chained_irq_exit(irqchip, desc);
|
||||
}
|
||||
|
||||
static void mrfld_irq_init_hw(struct mrfld_gpio *priv)
|
||||
{
|
||||
void __iomem *reg;
|
||||
unsigned int base;
|
||||
|
||||
for (base = 0; base < priv->chip.ngpio; base += 32) {
|
||||
/* Clear the rising-edge detect register */
|
||||
reg = gpio_reg(&priv->chip, base, GRER);
|
||||
writel(0, reg);
|
||||
/* Clear the falling-edge detect register */
|
||||
reg = gpio_reg(&priv->chip, base, GFER);
|
||||
writel(0, reg);
|
||||
}
|
||||
}
|
||||
|
||||
static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
const struct mrfld_gpio_pinrange *range;
|
||||
struct mrfld_gpio *priv;
|
||||
u32 gpio_base, irq_base;
|
||||
void __iomem *base;
|
||||
unsigned int i;
|
||||
int retval;
|
||||
|
||||
retval = pcim_enable_device(pdev);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = pcim_iomap_regions(pdev, BIT(1) | BIT(0), pci_name(pdev));
|
||||
if (retval) {
|
||||
dev_err(&pdev->dev, "I/O memory mapping error\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
base = pcim_iomap_table(pdev)[1];
|
||||
|
||||
irq_base = readl(base);
|
||||
gpio_base = readl(sizeof(u32) + base);
|
||||
|
||||
/* Release the IO mapping, since we already get the info from BAR1 */
|
||||
pcim_iounmap_regions(pdev, BIT(1));
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(&pdev->dev, "can't allocate chip data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->dev = &pdev->dev;
|
||||
priv->reg_base = pcim_iomap_table(pdev)[0];
|
||||
|
||||
priv->chip.label = dev_name(&pdev->dev);
|
||||
priv->chip.parent = &pdev->dev;
|
||||
priv->chip.request = gpiochip_generic_request;
|
||||
priv->chip.free = gpiochip_generic_free;
|
||||
priv->chip.direction_input = mrfld_gpio_direction_input;
|
||||
priv->chip.direction_output = mrfld_gpio_direction_output;
|
||||
priv->chip.get = mrfld_gpio_get;
|
||||
priv->chip.set = mrfld_gpio_set;
|
||||
priv->chip.base = gpio_base;
|
||||
priv->chip.ngpio = MRFLD_NGPIO;
|
||||
priv->chip.can_sleep = false;
|
||||
|
||||
raw_spin_lock_init(&priv->lock);
|
||||
|
||||
pci_set_drvdata(pdev, priv);
|
||||
retval = devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv);
|
||||
if (retval) {
|
||||
dev_err(&pdev->dev, "gpiochip_add error %d\n", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mrfld_gpio_ranges); i++) {
|
||||
range = &mrfld_gpio_ranges[i];
|
||||
retval = gpiochip_add_pin_range(&priv->chip,
|
||||
"pinctrl-merrifield",
|
||||
range->gpio_base,
|
||||
range->pin_base,
|
||||
range->npins);
|
||||
if (retval) {
|
||||
dev_err(&pdev->dev, "failed to add GPIO pin range\n");
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
retval = gpiochip_irqchip_add(&priv->chip, &mrfld_irqchip, irq_base,
|
||||
handle_simple_irq, IRQ_TYPE_NONE);
|
||||
if (retval) {
|
||||
dev_err(&pdev->dev, "could not connect irqchip to gpiochip\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
mrfld_irq_init_hw(priv);
|
||||
|
||||
gpiochip_set_chained_irqchip(&priv->chip, &mrfld_irqchip, pdev->irq,
|
||||
mrfld_irq_handler);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pci_device_id mrfld_gpio_ids[] = {
|
||||
{ PCI_VDEVICE(INTEL, 0x1199) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, mrfld_gpio_ids);
|
||||
|
||||
static struct pci_driver mrfld_gpio_driver = {
|
||||
.name = "gpio-merrifield",
|
||||
.id_table = mrfld_gpio_ids,
|
||||
.probe = mrfld_gpio_probe,
|
||||
};
|
||||
|
||||
module_pci_driver(mrfld_gpio_driver);
|
||||
|
||||
MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
|
||||
MODULE_DESCRIPTION("Intel Merrifield SoC GPIO driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -61,6 +61,8 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.`
|
|||
#include <linux/bitops.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
static void bgpio_write8(void __iomem *reg, unsigned long data)
|
||||
{
|
||||
|
@ -569,6 +571,41 @@ static void __iomem *bgpio_map(struct platform_device *pdev,
|
|||
return devm_ioremap_resource(&pdev->dev, r);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id bgpio_of_match[] = {
|
||||
{ .compatible = "wd,mbl-gpio" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bgpio_of_match);
|
||||
|
||||
static struct bgpio_pdata *bgpio_parse_dt(struct platform_device *pdev,
|
||||
unsigned long *flags)
|
||||
{
|
||||
struct bgpio_pdata *pdata;
|
||||
|
||||
if (!of_match_device(bgpio_of_match, &pdev->dev))
|
||||
return NULL;
|
||||
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(struct bgpio_pdata),
|
||||
GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pdata->base = -1;
|
||||
|
||||
if (of_property_read_bool(pdev->dev.of_node, "no-output"))
|
||||
*flags |= BGPIOF_NO_OUTPUT;
|
||||
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
static struct bgpio_pdata *bgpio_parse_dt(struct platform_device *pdev,
|
||||
unsigned long *flags)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static int bgpio_pdev_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
@ -579,10 +616,19 @@ static int bgpio_pdev_probe(struct platform_device *pdev)
|
|||
void __iomem *dirout;
|
||||
void __iomem *dirin;
|
||||
unsigned long sz;
|
||||
unsigned long flags = pdev->id_entry->driver_data;
|
||||
unsigned long flags = 0;
|
||||
int err;
|
||||
struct gpio_chip *gc;
|
||||
struct bgpio_pdata *pdata = dev_get_platdata(dev);
|
||||
struct bgpio_pdata *pdata;
|
||||
|
||||
pdata = bgpio_parse_dt(pdev, &flags);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
|
||||
if (!pdata) {
|
||||
pdata = dev_get_platdata(dev);
|
||||
flags = pdev->id_entry->driver_data;
|
||||
}
|
||||
|
||||
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat");
|
||||
if (!r)
|
||||
|
@ -646,6 +692,7 @@ MODULE_DEVICE_TABLE(platform, bgpio_id_table);
|
|||
static struct platform_driver bgpio_driver = {
|
||||
.driver = {
|
||||
.name = "basic-mmio-gpio",
|
||||
.of_match_table = of_match_ptr(bgpio_of_match),
|
||||
},
|
||||
.id_table = bgpio_id_table,
|
||||
.probe = bgpio_pdev_probe,
|
||||
|
|
|
@ -208,7 +208,6 @@ static int palmas_gpio_probe(struct platform_device *pdev)
|
|||
|
||||
static struct platform_driver palmas_gpio_driver = {
|
||||
.driver.name = "palmas-gpio",
|
||||
.driver.owner = THIS_MODULE,
|
||||
.driver.of_match_table = of_palmas_gpio_match,
|
||||
.probe = palmas_gpio_probe,
|
||||
};
|
||||
|
|
|
@ -67,6 +67,8 @@ static const struct i2c_device_id pca953x_id[] = {
|
|||
{ "pca9575", 16 | PCA957X_TYPE | PCA_INT, },
|
||||
{ "pca9698", 40 | PCA953X_TYPE, },
|
||||
|
||||
{ "pcal9555a", 16 | PCA953X_TYPE | PCA_INT | PCA_PCAL, },
|
||||
|
||||
{ "max7310", 8 | PCA953X_TYPE, },
|
||||
{ "max7312", 16 | PCA953X_TYPE | PCA_INT, },
|
||||
{ "max7313", 16 | PCA953X_TYPE | PCA_INT, },
|
||||
|
@ -90,7 +92,7 @@ MODULE_DEVICE_TABLE(acpi, pca953x_acpi_ids);
|
|||
#define MAX_BANK 5
|
||||
#define BANK_SZ 8
|
||||
|
||||
#define NBANK(chip) (chip->gpio_chip.ngpio / BANK_SZ)
|
||||
#define NBANK(chip) DIV_ROUND_UP(chip->gpio_chip.ngpio, BANK_SZ)
|
||||
|
||||
struct pca953x_chip {
|
||||
unsigned gpio_start;
|
||||
|
@ -135,7 +137,7 @@ static int pca953x_read_single(struct pca953x_chip *chip, int reg, u32 *val,
|
|||
static int pca953x_write_single(struct pca953x_chip *chip, int reg, u32 val,
|
||||
int off)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret;
|
||||
int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
|
||||
int offset = off / BANK_SZ;
|
||||
|
||||
|
@ -163,10 +165,13 @@ static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val)
|
|||
NBANK(chip), val);
|
||||
} else {
|
||||
switch (chip->chip_type) {
|
||||
case PCA953X_TYPE:
|
||||
ret = i2c_smbus_write_word_data(chip->client,
|
||||
reg << 1, cpu_to_le16(get_unaligned((u16 *)val)));
|
||||
case PCA953X_TYPE: {
|
||||
__le16 word = cpu_to_le16(get_unaligned((u16 *)val));
|
||||
|
||||
ret = i2c_smbus_write_word_data(chip->client, reg << 1,
|
||||
(__force u16)word);
|
||||
break;
|
||||
}
|
||||
case PCA957X_TYPE:
|
||||
ret = i2c_smbus_write_byte_data(chip->client, reg << 1,
|
||||
val[0]);
|
||||
|
@ -235,7 +240,6 @@ static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
|
|||
goto exit;
|
||||
|
||||
chip->reg_direction[off / BANK_SZ] = reg_val;
|
||||
ret = 0;
|
||||
exit:
|
||||
mutex_unlock(&chip->i2c_lock);
|
||||
return ret;
|
||||
|
@ -286,7 +290,6 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
|
|||
goto exit;
|
||||
|
||||
chip->reg_direction[off / BANK_SZ] = reg_val;
|
||||
ret = 0;
|
||||
exit:
|
||||
mutex_unlock(&chip->i2c_lock);
|
||||
return ret;
|
||||
|
@ -351,7 +354,6 @@ exit:
|
|||
mutex_unlock(&chip->i2c_lock);
|
||||
}
|
||||
|
||||
|
||||
static void pca953x_gpio_set_multiple(struct gpio_chip *gc,
|
||||
unsigned long *mask, unsigned long *bits)
|
||||
{
|
||||
|
@ -820,7 +822,7 @@ static int pca953x_remove(struct i2c_client *client)
|
|||
{
|
||||
struct pca953x_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
struct pca953x_chip *chip = i2c_get_clientdata(client);
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
if (pdata && pdata->teardown) {
|
||||
ret = pdata->teardown(client, chip->gpio_chip.base,
|
||||
|
@ -861,6 +863,7 @@ static const struct of_device_id pca953x_dt_ids[] = {
|
|||
{ .compatible = "maxim,max7315", .data = OF_953X( 8, PCA_INT), },
|
||||
|
||||
{ .compatible = "ti,pca6107", .data = OF_953X( 8, PCA_INT), },
|
||||
{ .compatible = "ti,pca9536", .data = OF_953X( 4, 0), },
|
||||
{ .compatible = "ti,tca6408", .data = OF_953X( 8, PCA_INT), },
|
||||
{ .compatible = "ti,tca6416", .data = OF_953X(16, PCA_INT), },
|
||||
{ .compatible = "ti,tca6424", .data = OF_953X(24, PCA_INT), },
|
||||
|
|
|
@ -440,6 +440,14 @@ static int pcf857x_remove(struct i2c_client *client)
|
|||
return status;
|
||||
}
|
||||
|
||||
static void pcf857x_shutdown(struct i2c_client *client)
|
||||
{
|
||||
struct pcf857x *gpio = i2c_get_clientdata(client);
|
||||
|
||||
/* Drive all the I/O lines high */
|
||||
gpio->write(gpio->client, BIT(gpio->chip.ngpio) - 1);
|
||||
}
|
||||
|
||||
static struct i2c_driver pcf857x_driver = {
|
||||
.driver = {
|
||||
.name = "pcf857x",
|
||||
|
@ -447,6 +455,7 @@ static struct i2c_driver pcf857x_driver = {
|
|||
},
|
||||
.probe = pcf857x_probe,
|
||||
.remove = pcf857x_remove,
|
||||
.shutdown = pcf857x_shutdown,
|
||||
.id_table = pcf857x_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -334,6 +334,9 @@ static const struct of_device_id gpio_rcar_of_table[] = {
|
|||
}, {
|
||||
.compatible = "renesas,gpio-r8a7791",
|
||||
.data = &gpio_rcar_info_gen2,
|
||||
}, {
|
||||
.compatible = "renesas,gpio-r8a7792",
|
||||
.data = &gpio_rcar_info_gen2,
|
||||
}, {
|
||||
.compatible = "renesas,gpio-r8a7793",
|
||||
.data = &gpio_rcar_info_gen2,
|
||||
|
|
|
@ -200,7 +200,6 @@ static int rdc321x_gpio_probe(struct platform_device *pdev)
|
|||
|
||||
static struct platform_driver rdc321x_gpio_driver = {
|
||||
.driver.name = "rdc321x-gpio",
|
||||
.driver.owner = THIS_MODULE,
|
||||
.probe = rdc321x_gpio_probe,
|
||||
};
|
||||
|
||||
|
|
|
@ -296,7 +296,6 @@ static int sch311x_gpio_remove(struct platform_device *pdev)
|
|||
|
||||
static struct platform_driver sch311x_gpio_driver = {
|
||||
.driver.name = DRV_NAME,
|
||||
.driver.owner = THIS_MODULE,
|
||||
.probe = sch311x_gpio_probe,
|
||||
.remove = sch311x_gpio_remove,
|
||||
};
|
||||
|
|
|
@ -68,6 +68,22 @@ static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
|
|||
stmpe_reg_write(stmpe, reg, mask);
|
||||
}
|
||||
|
||||
static int stmpe_gpio_get_direction(struct gpio_chip *chip,
|
||||
unsigned offset)
|
||||
{
|
||||
struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(chip);
|
||||
struct stmpe *stmpe = stmpe_gpio->stmpe;
|
||||
u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8);
|
||||
u8 mask = 1 << (offset % 8);
|
||||
int ret;
|
||||
|
||||
ret = stmpe_reg_read(stmpe, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return !(ret & mask);
|
||||
}
|
||||
|
||||
static int stmpe_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned offset, int val)
|
||||
{
|
||||
|
@ -106,6 +122,7 @@ static int stmpe_gpio_request(struct gpio_chip *chip, unsigned offset)
|
|||
static struct gpio_chip template_chip = {
|
||||
.label = "stmpe",
|
||||
.owner = THIS_MODULE,
|
||||
.get_direction = stmpe_gpio_get_direction,
|
||||
.direction_input = stmpe_gpio_direction_input,
|
||||
.get = stmpe_gpio_get,
|
||||
.direction_output = stmpe_gpio_direction_output,
|
||||
|
@ -416,7 +433,6 @@ static struct platform_driver stmpe_gpio_driver = {
|
|||
.driver = {
|
||||
.suppress_bind_attrs = true,
|
||||
.name = "stmpe-gpio",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = stmpe_gpio_probe,
|
||||
};
|
||||
|
|
|
@ -129,7 +129,7 @@ static int syscon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int val)
|
|||
|
||||
static const struct syscon_gpio_data clps711x_mctrl_gpio = {
|
||||
/* ARM CLPS711X SYSFLG1 Bits 8-10 */
|
||||
.compatible = "cirrus,clps711x-syscon1",
|
||||
.compatible = "cirrus,ep7209-syscon1",
|
||||
.flags = GPIO_SYSCON_FEAT_IN,
|
||||
.bit_count = 3,
|
||||
.dat_bit_offset = 0x40 * 8 + 8,
|
||||
|
@ -168,7 +168,7 @@ static const struct syscon_gpio_data keystone_dsp_gpio = {
|
|||
|
||||
static const struct of_device_id syscon_gpio_ids[] = {
|
||||
{
|
||||
.compatible = "cirrus,clps711x-mctrl-gpio",
|
||||
.compatible = "cirrus,ep7209-mctrl-gpio",
|
||||
.data = &clps711x_mctrl_gpio,
|
||||
},
|
||||
{
|
||||
|
|
|
@ -343,7 +343,6 @@ static int tc3589x_gpio_probe(struct platform_device *pdev)
|
|||
|
||||
static struct platform_driver tc3589x_gpio_driver = {
|
||||
.driver.name = "tc3589x-gpio",
|
||||
.driver.owner = THIS_MODULE,
|
||||
.probe = tc3589x_gpio_probe,
|
||||
};
|
||||
|
||||
|
|
|
@ -230,6 +230,12 @@ static const struct of_device_id tps65218_dt_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, tps65218_dt_match);
|
||||
|
||||
static const struct platform_device_id tps65218_gpio_id_table[] = {
|
||||
{ "tps65218-gpio", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, tps65218_gpio_id_table);
|
||||
|
||||
static struct platform_driver tps65218_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "tps65218-gpio",
|
||||
|
@ -237,6 +243,7 @@ static struct platform_driver tps65218_gpio_driver = {
|
|||
},
|
||||
.probe = tps65218_gpio_probe,
|
||||
.remove = tps65218_gpio_remove,
|
||||
.id_table = tps65218_gpio_id_table,
|
||||
};
|
||||
|
||||
module_platform_driver(tps65218_gpio_driver);
|
||||
|
|
|
@ -131,7 +131,6 @@ static int tps6586x_gpio_probe(struct platform_device *pdev)
|
|||
|
||||
static struct platform_driver tps6586x_gpio_driver = {
|
||||
.driver.name = "tps6586x-gpio",
|
||||
.driver.owner = THIS_MODULE,
|
||||
.probe = tps6586x_gpio_probe,
|
||||
};
|
||||
|
||||
|
|
|
@ -184,7 +184,6 @@ skip_init:
|
|||
|
||||
static struct platform_driver tps65910_gpio_driver = {
|
||||
.driver.name = "tps65910-gpio",
|
||||
.driver.owner = THIS_MODULE,
|
||||
.probe = tps65910_gpio_probe,
|
||||
};
|
||||
|
||||
|
|
|
@ -440,7 +440,6 @@ static int vprbrd_gpio_probe(struct platform_device *pdev)
|
|||
|
||||
static struct platform_driver vprbrd_gpio_driver = {
|
||||
.driver.name = "viperboard-gpio",
|
||||
.driver.owner = THIS_MODULE,
|
||||
.probe = vprbrd_gpio_probe,
|
||||
};
|
||||
|
||||
|
|
|
@ -296,7 +296,6 @@ static int wm831x_gpio_probe(struct platform_device *pdev)
|
|||
|
||||
static struct platform_driver wm831x_gpio_driver = {
|
||||
.driver.name = "wm831x-gpio",
|
||||
.driver.owner = THIS_MODULE,
|
||||
.probe = wm831x_gpio_probe,
|
||||
};
|
||||
|
||||
|
|
|
@ -139,7 +139,6 @@ static int wm8350_gpio_probe(struct platform_device *pdev)
|
|||
|
||||
static struct platform_driver wm8350_gpio_driver = {
|
||||
.driver.name = "wm8350-gpio",
|
||||
.driver.owner = THIS_MODULE,
|
||||
.probe = wm8350_gpio_probe,
|
||||
};
|
||||
|
||||
|
|
|
@ -299,7 +299,6 @@ static int wm8994_gpio_probe(struct platform_device *pdev)
|
|||
|
||||
static struct platform_driver wm8994_gpio_driver = {
|
||||
.driver.name = "wm8994-gpio",
|
||||
.driver.owner = THIS_MODULE,
|
||||
.probe = wm8994_gpio_probe,
|
||||
};
|
||||
|
||||
|
|
|
@ -132,6 +132,53 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
|
|||
spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* xgpio_set_multiple - Write the specified signals of the GPIO device.
|
||||
* @gc: Pointer to gpio_chip device structure.
|
||||
* @mask: Mask of the GPIOS to modify.
|
||||
* @bits: Value to be wrote on each GPIO
|
||||
*
|
||||
* This function writes the specified values into the specified signals of the
|
||||
* GPIO devices.
|
||||
*/
|
||||
static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
|
||||
unsigned long *bits)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
|
||||
struct xgpio_instance *chip = gpiochip_get_data(gc);
|
||||
int index = xgpio_index(chip, 0);
|
||||
int offset, i;
|
||||
|
||||
spin_lock_irqsave(&chip->gpio_lock[index], flags);
|
||||
|
||||
/* Write to GPIO signals */
|
||||
for (i = 0; i < gc->ngpio; i++) {
|
||||
if (*mask == 0)
|
||||
break;
|
||||
if (index != xgpio_index(chip, i)) {
|
||||
xgpio_writereg(mm_gc->regs + XGPIO_DATA_OFFSET +
|
||||
xgpio_regoffset(chip, i),
|
||||
chip->gpio_state[index]);
|
||||
spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
|
||||
index = xgpio_index(chip, i);
|
||||
spin_lock_irqsave(&chip->gpio_lock[index], flags);
|
||||
}
|
||||
if (__test_and_clear_bit(i, mask)) {
|
||||
offset = xgpio_offset(chip, i);
|
||||
if (test_bit(i, bits))
|
||||
chip->gpio_state[index] |= BIT(offset);
|
||||
else
|
||||
chip->gpio_state[index] &= ~BIT(offset);
|
||||
}
|
||||
}
|
||||
|
||||
xgpio_writereg(mm_gc->regs + XGPIO_DATA_OFFSET +
|
||||
xgpio_regoffset(chip, i), chip->gpio_state[index]);
|
||||
|
||||
spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* xgpio_dir_in - Set the direction of the specified GPIO signal as input.
|
||||
* @gc: Pointer to gpio_chip device structure.
|
||||
|
@ -306,6 +353,7 @@ static int xgpio_probe(struct platform_device *pdev)
|
|||
chip->mmchip.gc.direction_output = xgpio_dir_out;
|
||||
chip->mmchip.gc.get = xgpio_get;
|
||||
chip->mmchip.gc.set = xgpio_set;
|
||||
chip->mmchip.gc.set_multiple = xgpio_set_multiple;
|
||||
|
||||
chip->mmchip.save_regs = xgpio_save_regs;
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
/*
|
||||
* XLP GPIO has multiple 32 bit registers for each feature where each register
|
||||
|
@ -299,7 +300,6 @@ static int xlp_gpio_probe(struct platform_device *pdev)
|
|||
struct gpio_chip *gc;
|
||||
struct resource *iores;
|
||||
struct xlp_gpio_priv *priv;
|
||||
const struct of_device_id *of_id;
|
||||
void __iomem *gpio_base;
|
||||
int irq_base, irq, err;
|
||||
int ngpio;
|
||||
|
@ -321,13 +321,26 @@ static int xlp_gpio_probe(struct platform_device *pdev)
|
|||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
const struct of_device_id *of_id;
|
||||
|
||||
of_id = of_match_device(xlp_gpio_of_ids, &pdev->dev);
|
||||
if (!of_id) {
|
||||
dev_err(&pdev->dev, "Failed to get soc type!\n");
|
||||
dev_err(&pdev->dev, "Unable to match OF ID\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
soc_type = (uintptr_t) of_id->data;
|
||||
} else {
|
||||
const struct acpi_device_id *acpi_id;
|
||||
|
||||
acpi_id = acpi_match_device(pdev->dev.driver->acpi_match_table,
|
||||
&pdev->dev);
|
||||
if (!acpi_id || !acpi_id->driver_data) {
|
||||
dev_err(&pdev->dev, "Unable to match ACPI ID\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
soc_type = (uintptr_t) acpi_id->driver_data;
|
||||
}
|
||||
|
||||
switch (soc_type) {
|
||||
case XLP_GPIO_VARIANT_XLP832:
|
||||
|
@ -388,15 +401,17 @@ static int xlp_gpio_probe(struct platform_device *pdev)
|
|||
gc->get = xlp_gpio_get;
|
||||
|
||||
spin_lock_init(&priv->lock);
|
||||
/* XLP has fixed IRQ range for GPIO interrupts */
|
||||
if (soc_type == GPIO_VARIANT_VULCAN)
|
||||
irq_base = irq_alloc_descs(-1, 0, gc->ngpio, 0);
|
||||
else
|
||||
|
||||
/* XLP(MIPS) has fixed range for GPIO IRQs, Vulcan(ARM64) does not */
|
||||
if (soc_type != GPIO_VARIANT_VULCAN) {
|
||||
irq_base = irq_alloc_descs(-1, XLP_GPIO_IRQ_BASE, gc->ngpio, 0);
|
||||
if (irq_base < 0) {
|
||||
dev_err(&pdev->dev, "Failed to allocate IRQ numbers\n");
|
||||
return irq_base;
|
||||
}
|
||||
} else {
|
||||
irq_base = 0;
|
||||
}
|
||||
|
||||
err = gpiochip_add_data(gc, priv);
|
||||
if (err < 0)
|
||||
|
@ -423,10 +438,19 @@ out_free_desc:
|
|||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id xlp_gpio_acpi_match[] = {
|
||||
{ "BRCM9006", GPIO_VARIANT_VULCAN },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, xlp_gpio_acpi_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver xlp_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "xlp-gpio",
|
||||
.of_match_table = xlp_gpio_of_ids,
|
||||
.acpi_match_table = ACPI_PTR(xlp_gpio_acpi_match),
|
||||
},
|
||||
.probe = xlp_gpio_probe,
|
||||
};
|
||||
|
|
|
@ -836,6 +836,7 @@ void acpi_gpiochip_add(struct gpio_chip *chip)
|
|||
}
|
||||
|
||||
acpi_gpiochip_request_regions(acpi_gpio);
|
||||
acpi_walk_dep_device_list(handle);
|
||||
}
|
||||
|
||||
void acpi_gpiochip_remove(struct gpio_chip *chip)
|
||||
|
|
|
@ -27,38 +27,30 @@
|
|||
|
||||
#include "gpiolib.h"
|
||||
|
||||
/* Private data structure for of_gpiochip_find_and_xlate */
|
||||
struct gg_data {
|
||||
enum of_gpio_flags *flags;
|
||||
struct of_phandle_args gpiospec;
|
||||
|
||||
struct gpio_desc *out_gpio;
|
||||
};
|
||||
|
||||
/* Private function for resolving node pointer to gpio_chip */
|
||||
static int of_gpiochip_find_and_xlate(struct gpio_chip *gc, void *data)
|
||||
static int of_gpiochip_match_node(struct gpio_chip *chip, void *data)
|
||||
{
|
||||
return chip->gpiodev->dev.of_node == data;
|
||||
}
|
||||
|
||||
static struct gpio_chip *of_find_gpiochip_by_node(struct device_node *np)
|
||||
{
|
||||
return gpiochip_find(np, of_gpiochip_match_node);
|
||||
}
|
||||
|
||||
static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip,
|
||||
struct of_phandle_args *gpiospec,
|
||||
enum of_gpio_flags *flags)
|
||||
{
|
||||
struct gg_data *gg_data = data;
|
||||
int ret;
|
||||
|
||||
if ((gc->of_node != gg_data->gpiospec.np) ||
|
||||
(gc->of_gpio_n_cells != gg_data->gpiospec.args_count) ||
|
||||
(!gc->of_xlate))
|
||||
return false;
|
||||
if (chip->of_gpio_n_cells != gpiospec->args_count)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
ret = gc->of_xlate(gc, &gg_data->gpiospec, gg_data->flags);
|
||||
if (ret < 0) {
|
||||
/* We've found a gpio chip, but the translation failed.
|
||||
* Store translation error in out_gpio.
|
||||
* Return false to keep looking, as more than one gpio chip
|
||||
* could be registered per of-node.
|
||||
*/
|
||||
gg_data->out_gpio = ERR_PTR(ret);
|
||||
return false;
|
||||
}
|
||||
ret = chip->of_xlate(chip, gpiospec, flags);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
gg_data->out_gpio = gpiochip_get_desc(gc, ret);
|
||||
return true;
|
||||
return gpiochip_get_desc(chip, ret);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,34 +67,37 @@ static int of_gpiochip_find_and_xlate(struct gpio_chip *gc, void *data)
|
|||
struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
|
||||
const char *propname, int index, enum of_gpio_flags *flags)
|
||||
{
|
||||
/* Return -EPROBE_DEFER to support probe() functions to be called
|
||||
* later when the GPIO actually becomes available
|
||||
*/
|
||||
struct gg_data gg_data = {
|
||||
.flags = flags,
|
||||
.out_gpio = ERR_PTR(-EPROBE_DEFER)
|
||||
};
|
||||
struct of_phandle_args gpiospec;
|
||||
struct gpio_chip *chip;
|
||||
struct gpio_desc *desc;
|
||||
int ret;
|
||||
|
||||
/* .of_xlate might decide to not fill in the flags, so clear it. */
|
||||
if (flags)
|
||||
*flags = 0;
|
||||
|
||||
ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index,
|
||||
&gg_data.gpiospec);
|
||||
&gpiospec);
|
||||
if (ret) {
|
||||
pr_debug("%s: can't parse '%s' property of node '%s[%d]'\n",
|
||||
__func__, propname, np->full_name, index);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
gpiochip_find(&gg_data, of_gpiochip_find_and_xlate);
|
||||
chip = of_find_gpiochip_by_node(gpiospec.np);
|
||||
if (!chip) {
|
||||
desc = ERR_PTR(-EPROBE_DEFER);
|
||||
goto out;
|
||||
}
|
||||
|
||||
desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, flags);
|
||||
if (IS_ERR(desc))
|
||||
goto out;
|
||||
|
||||
of_node_put(gg_data.gpiospec.np);
|
||||
pr_debug("%s: parsed '%s' property of node '%s[%d]' - status (%d)\n",
|
||||
__func__, propname, np->full_name, index,
|
||||
PTR_ERR_OR_ZERO(gg_data.out_gpio));
|
||||
return gg_data.out_gpio;
|
||||
PTR_ERR_OR_ZERO(desc));
|
||||
|
||||
out:
|
||||
of_node_put(gpiospec.np);
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
int of_get_named_gpio_flags(struct device_node *np, const char *list_name,
|
||||
|
@ -122,6 +117,7 @@ EXPORT_SYMBOL(of_get_named_gpio_flags);
|
|||
/**
|
||||
* of_parse_own_gpio() - Get a GPIO hog descriptor, names and flags for GPIO API
|
||||
* @np: device node to get GPIO from
|
||||
* @chip: GPIO chip whose hog is parsed
|
||||
* @name: GPIO line name
|
||||
* @lflags: gpio_lookup_flags - returned from of_find_gpio() or
|
||||
* of_parse_own_gpio()
|
||||
|
@ -131,19 +127,19 @@ EXPORT_SYMBOL(of_get_named_gpio_flags);
|
|||
* value on the error condition.
|
||||
*/
|
||||
static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
|
||||
struct gpio_chip *chip,
|
||||
const char **name,
|
||||
enum gpio_lookup_flags *lflags,
|
||||
enum gpiod_flags *dflags)
|
||||
{
|
||||
struct device_node *chip_np;
|
||||
enum of_gpio_flags xlate_flags;
|
||||
struct gg_data gg_data = {
|
||||
.flags = &xlate_flags,
|
||||
};
|
||||
struct of_phandle_args gpiospec;
|
||||
struct gpio_desc *desc;
|
||||
u32 tmp;
|
||||
int i, ret;
|
||||
int ret;
|
||||
|
||||
chip_np = np->parent;
|
||||
chip_np = chip->of_node;
|
||||
if (!chip_np)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
|
@ -155,25 +151,16 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
|
|||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
if (tmp > MAX_PHANDLE_ARGS)
|
||||
return ERR_PTR(-EINVAL);
|
||||
gpiospec.np = chip_np;
|
||||
gpiospec.args_count = tmp;
|
||||
|
||||
gg_data.gpiospec.args_count = tmp;
|
||||
gg_data.gpiospec.np = chip_np;
|
||||
for (i = 0; i < tmp; i++) {
|
||||
ret = of_property_read_u32_index(np, "gpios", i,
|
||||
&gg_data.gpiospec.args[i]);
|
||||
ret = of_property_read_u32_array(np, "gpios", gpiospec.args, tmp);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
gpiochip_find(&gg_data, of_gpiochip_find_and_xlate);
|
||||
if (!gg_data.out_gpio) {
|
||||
if (np->parent == np)
|
||||
return ERR_PTR(-ENXIO);
|
||||
else
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, &xlate_flags);
|
||||
if (IS_ERR(desc))
|
||||
return desc;
|
||||
|
||||
if (xlate_flags & OF_GPIO_ACTIVE_LOW)
|
||||
*lflags |= GPIO_ACTIVE_LOW;
|
||||
|
@ -186,14 +173,14 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
|
|||
*dflags |= GPIOD_OUT_HIGH;
|
||||
else {
|
||||
pr_warn("GPIO line %d (%s): no hogging state specified, bailing out\n",
|
||||
desc_to_gpio(gg_data.out_gpio), np->name);
|
||||
desc_to_gpio(desc), np->name);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (name && of_property_read_string(np, "line-name", name))
|
||||
*name = np->name;
|
||||
|
||||
return gg_data.out_gpio;
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -262,7 +249,7 @@ static int of_gpiochip_scan_gpios(struct gpio_chip *chip)
|
|||
if (!of_property_read_bool(np, "gpio-hog"))
|
||||
continue;
|
||||
|
||||
desc = of_parse_own_gpio(np, &name, &lflags, &dflags);
|
||||
desc = of_parse_own_gpio(np, chip, &name, &lflags, &dflags);
|
||||
if (IS_ERR(desc))
|
||||
continue;
|
||||
|
||||
|
@ -410,6 +397,7 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip)
|
|||
break;
|
||||
|
||||
pctldev = of_pinctrl_get(pinspec.np);
|
||||
of_node_put(pinspec.np);
|
||||
if (!pctldev)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
|
@ -487,6 +475,9 @@ int of_gpiochip_add(struct gpio_chip *chip)
|
|||
chip->of_xlate = of_gpio_simple_xlate;
|
||||
}
|
||||
|
||||
if (chip->of_gpio_n_cells > MAX_PHANDLE_ARGS)
|
||||
return -EINVAL;
|
||||
|
||||
status = of_gpiochip_add_pin_range(chip);
|
||||
if (status)
|
||||
return status;
|
||||
|
|
|
@ -16,11 +16,14 @@
|
|||
#include <linux/gpio/driver.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/anon_inodes.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/timekeeping.h>
|
||||
#include <uapi/linux/gpio.h>
|
||||
|
||||
#include "gpiolib.h"
|
||||
|
@ -310,6 +313,497 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* GPIO line handle management
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct linehandle_state - contains the state of a userspace handle
|
||||
* @gdev: the GPIO device the handle pertains to
|
||||
* @label: consumer label used to tag descriptors
|
||||
* @descs: the GPIO descriptors held by this handle
|
||||
* @numdescs: the number of descriptors held in the descs array
|
||||
*/
|
||||
struct linehandle_state {
|
||||
struct gpio_device *gdev;
|
||||
const char *label;
|
||||
struct gpio_desc *descs[GPIOHANDLES_MAX];
|
||||
u32 numdescs;
|
||||
};
|
||||
|
||||
static long linehandle_ioctl(struct file *filep, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct linehandle_state *lh = filep->private_data;
|
||||
void __user *ip = (void __user *)arg;
|
||||
struct gpiohandle_data ghd;
|
||||
int i;
|
||||
|
||||
if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
|
||||
int val;
|
||||
|
||||
/* TODO: check if descriptors are really input */
|
||||
for (i = 0; i < lh->numdescs; i++) {
|
||||
val = gpiod_get_value_cansleep(lh->descs[i]);
|
||||
if (val < 0)
|
||||
return val;
|
||||
ghd.values[i] = val;
|
||||
}
|
||||
|
||||
if (copy_to_user(ip, &ghd, sizeof(ghd)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
} else if (cmd == GPIOHANDLE_SET_LINE_VALUES_IOCTL) {
|
||||
int vals[GPIOHANDLES_MAX];
|
||||
|
||||
/* TODO: check if descriptors are really output */
|
||||
if (copy_from_user(&ghd, ip, sizeof(ghd)))
|
||||
return -EFAULT;
|
||||
|
||||
/* Clamp all values to [0,1] */
|
||||
for (i = 0; i < lh->numdescs; i++)
|
||||
vals[i] = !!ghd.values[i];
|
||||
|
||||
/* Reuse the array setting function */
|
||||
gpiod_set_array_value_complex(false,
|
||||
true,
|
||||
lh->numdescs,
|
||||
lh->descs,
|
||||
vals);
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static long linehandle_ioctl_compat(struct file *filep, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
return linehandle_ioctl(filep, cmd, (unsigned long)compat_ptr(arg));
|
||||
}
|
||||
#endif
|
||||
|
||||
static int linehandle_release(struct inode *inode, struct file *filep)
|
||||
{
|
||||
struct linehandle_state *lh = filep->private_data;
|
||||
struct gpio_device *gdev = lh->gdev;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < lh->numdescs; i++)
|
||||
gpiod_free(lh->descs[i]);
|
||||
kfree(lh->label);
|
||||
kfree(lh);
|
||||
put_device(&gdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations linehandle_fileops = {
|
||||
.release = linehandle_release,
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = noop_llseek,
|
||||
.unlocked_ioctl = linehandle_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = linehandle_ioctl_compat,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int linehandle_create(struct gpio_device *gdev, void __user *ip)
|
||||
{
|
||||
struct gpiohandle_request handlereq;
|
||||
struct linehandle_state *lh;
|
||||
int fd, i, ret;
|
||||
|
||||
if (copy_from_user(&handlereq, ip, sizeof(handlereq)))
|
||||
return -EFAULT;
|
||||
if ((handlereq.lines == 0) || (handlereq.lines > GPIOHANDLES_MAX))
|
||||
return -EINVAL;
|
||||
|
||||
lh = kzalloc(sizeof(*lh), GFP_KERNEL);
|
||||
if (!lh)
|
||||
return -ENOMEM;
|
||||
lh->gdev = gdev;
|
||||
get_device(&gdev->dev);
|
||||
|
||||
/* Make sure this is terminated */
|
||||
handlereq.consumer_label[sizeof(handlereq.consumer_label)-1] = '\0';
|
||||
if (strlen(handlereq.consumer_label)) {
|
||||
lh->label = kstrdup(handlereq.consumer_label,
|
||||
GFP_KERNEL);
|
||||
if (!lh->label) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free_lh;
|
||||
}
|
||||
}
|
||||
|
||||
/* Request each GPIO */
|
||||
for (i = 0; i < handlereq.lines; i++) {
|
||||
u32 offset = handlereq.lineoffsets[i];
|
||||
u32 lflags = handlereq.flags;
|
||||
struct gpio_desc *desc;
|
||||
|
||||
desc = &gdev->descs[offset];
|
||||
ret = gpiod_request(desc, lh->label);
|
||||
if (ret)
|
||||
goto out_free_descs;
|
||||
lh->descs[i] = desc;
|
||||
|
||||
if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW)
|
||||
set_bit(FLAG_ACTIVE_LOW, &desc->flags);
|
||||
if (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN)
|
||||
set_bit(FLAG_OPEN_DRAIN, &desc->flags);
|
||||
if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)
|
||||
set_bit(FLAG_OPEN_SOURCE, &desc->flags);
|
||||
|
||||
/*
|
||||
* Lines have to be requested explicitly for input
|
||||
* or output, else the line will be treated "as is".
|
||||
*/
|
||||
if (lflags & GPIOHANDLE_REQUEST_OUTPUT) {
|
||||
int val = !!handlereq.default_values[i];
|
||||
|
||||
ret = gpiod_direction_output(desc, val);
|
||||
if (ret)
|
||||
goto out_free_descs;
|
||||
} else if (lflags & GPIOHANDLE_REQUEST_INPUT) {
|
||||
ret = gpiod_direction_input(desc);
|
||||
if (ret)
|
||||
goto out_free_descs;
|
||||
}
|
||||
dev_dbg(&gdev->dev, "registered chardev handle for line %d\n",
|
||||
offset);
|
||||
}
|
||||
/* Let i point at the last handle */
|
||||
i--;
|
||||
lh->numdescs = handlereq.lines;
|
||||
|
||||
fd = anon_inode_getfd("gpio-linehandle",
|
||||
&linehandle_fileops,
|
||||
lh,
|
||||
O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
ret = fd;
|
||||
goto out_free_descs;
|
||||
}
|
||||
|
||||
handlereq.fd = fd;
|
||||
if (copy_to_user(ip, &handlereq, sizeof(handlereq))) {
|
||||
ret = -EFAULT;
|
||||
goto out_free_descs;
|
||||
}
|
||||
|
||||
dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n",
|
||||
lh->numdescs);
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_descs:
|
||||
for (; i >= 0; i--)
|
||||
gpiod_free(lh->descs[i]);
|
||||
kfree(lh->label);
|
||||
out_free_lh:
|
||||
kfree(lh);
|
||||
put_device(&gdev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* GPIO line event management
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct lineevent_state - contains the state of a userspace event
|
||||
* @gdev: the GPIO device the event pertains to
|
||||
* @label: consumer label used to tag descriptors
|
||||
* @desc: the GPIO descriptor held by this event
|
||||
* @eflags: the event flags this line was requested with
|
||||
* @irq: the interrupt that trigger in response to events on this GPIO
|
||||
* @wait: wait queue that handles blocking reads of events
|
||||
* @events: KFIFO for the GPIO events
|
||||
* @read_lock: mutex lock to protect reads from colliding with adding
|
||||
* new events to the FIFO
|
||||
*/
|
||||
struct lineevent_state {
|
||||
struct gpio_device *gdev;
|
||||
const char *label;
|
||||
struct gpio_desc *desc;
|
||||
u32 eflags;
|
||||
int irq;
|
||||
wait_queue_head_t wait;
|
||||
DECLARE_KFIFO(events, struct gpioevent_data, 16);
|
||||
struct mutex read_lock;
|
||||
};
|
||||
|
||||
static unsigned int lineevent_poll(struct file *filep,
|
||||
struct poll_table_struct *wait)
|
||||
{
|
||||
struct lineevent_state *le = filep->private_data;
|
||||
unsigned int events = 0;
|
||||
|
||||
poll_wait(filep, &le->wait, wait);
|
||||
|
||||
if (!kfifo_is_empty(&le->events))
|
||||
events = POLLIN | POLLRDNORM;
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t lineevent_read(struct file *filep,
|
||||
char __user *buf,
|
||||
size_t count,
|
||||
loff_t *f_ps)
|
||||
{
|
||||
struct lineevent_state *le = filep->private_data;
|
||||
unsigned int copied;
|
||||
int ret;
|
||||
|
||||
if (count < sizeof(struct gpioevent_data))
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
if (kfifo_is_empty(&le->events)) {
|
||||
if (filep->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
|
||||
ret = wait_event_interruptible(le->wait,
|
||||
!kfifo_is_empty(&le->events));
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (mutex_lock_interruptible(&le->read_lock))
|
||||
return -ERESTARTSYS;
|
||||
ret = kfifo_to_user(&le->events, buf, count, &copied);
|
||||
mutex_unlock(&le->read_lock);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* If we couldn't read anything from the fifo (a different
|
||||
* thread might have been faster) we either return -EAGAIN if
|
||||
* the file descriptor is non-blocking, otherwise we go back to
|
||||
* sleep and wait for more data to arrive.
|
||||
*/
|
||||
if (copied == 0 && (filep->f_flags & O_NONBLOCK))
|
||||
return -EAGAIN;
|
||||
|
||||
} while (copied == 0);
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
static int lineevent_release(struct inode *inode, struct file *filep)
|
||||
{
|
||||
struct lineevent_state *le = filep->private_data;
|
||||
struct gpio_device *gdev = le->gdev;
|
||||
|
||||
free_irq(le->irq, le);
|
||||
gpiod_free(le->desc);
|
||||
kfree(le->label);
|
||||
kfree(le);
|
||||
put_device(&gdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long lineevent_ioctl(struct file *filep, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct lineevent_state *le = filep->private_data;
|
||||
void __user *ip = (void __user *)arg;
|
||||
struct gpiohandle_data ghd;
|
||||
|
||||
/*
|
||||
* We can get the value for an event line but not set it,
|
||||
* because it is input by definition.
|
||||
*/
|
||||
if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
|
||||
int val;
|
||||
|
||||
val = gpiod_get_value_cansleep(le->desc);
|
||||
if (val < 0)
|
||||
return val;
|
||||
ghd.values[0] = val;
|
||||
|
||||
if (copy_to_user(ip, &ghd, sizeof(ghd)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static long lineevent_ioctl_compat(struct file *filep, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
return lineevent_ioctl(filep, cmd, (unsigned long)compat_ptr(arg));
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct file_operations lineevent_fileops = {
|
||||
.release = lineevent_release,
|
||||
.read = lineevent_read,
|
||||
.poll = lineevent_poll,
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = noop_llseek,
|
||||
.unlocked_ioctl = lineevent_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = lineevent_ioctl_compat,
|
||||
#endif
|
||||
};
|
||||
|
||||
static irqreturn_t lineevent_irq_thread(int irq, void *p)
|
||||
{
|
||||
struct lineevent_state *le = p;
|
||||
struct gpioevent_data ge;
|
||||
int ret;
|
||||
|
||||
ge.timestamp = ktime_get_real_ns();
|
||||
|
||||
if (le->eflags & GPIOEVENT_REQUEST_BOTH_EDGES) {
|
||||
int level = gpiod_get_value_cansleep(le->desc);
|
||||
|
||||
if (level)
|
||||
/* Emit low-to-high event */
|
||||
ge.id = GPIOEVENT_EVENT_RISING_EDGE;
|
||||
else
|
||||
/* Emit high-to-low event */
|
||||
ge.id = GPIOEVENT_EVENT_FALLING_EDGE;
|
||||
} else if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE) {
|
||||
/* Emit low-to-high event */
|
||||
ge.id = GPIOEVENT_EVENT_RISING_EDGE;
|
||||
} else if (le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) {
|
||||
/* Emit high-to-low event */
|
||||
ge.id = GPIOEVENT_EVENT_FALLING_EDGE;
|
||||
} else {
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
ret = kfifo_put(&le->events, ge);
|
||||
if (ret != 0)
|
||||
wake_up_poll(&le->wait, POLLIN);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int lineevent_create(struct gpio_device *gdev, void __user *ip)
|
||||
{
|
||||
struct gpioevent_request eventreq;
|
||||
struct lineevent_state *le;
|
||||
struct gpio_desc *desc;
|
||||
u32 offset;
|
||||
u32 lflags;
|
||||
u32 eflags;
|
||||
int fd;
|
||||
int ret;
|
||||
int irqflags = 0;
|
||||
|
||||
if (copy_from_user(&eventreq, ip, sizeof(eventreq)))
|
||||
return -EFAULT;
|
||||
|
||||
le = kzalloc(sizeof(*le), GFP_KERNEL);
|
||||
if (!le)
|
||||
return -ENOMEM;
|
||||
le->gdev = gdev;
|
||||
get_device(&gdev->dev);
|
||||
|
||||
/* Make sure this is terminated */
|
||||
eventreq.consumer_label[sizeof(eventreq.consumer_label)-1] = '\0';
|
||||
if (strlen(eventreq.consumer_label)) {
|
||||
le->label = kstrdup(eventreq.consumer_label,
|
||||
GFP_KERNEL);
|
||||
if (!le->label) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free_le;
|
||||
}
|
||||
}
|
||||
|
||||
offset = eventreq.lineoffset;
|
||||
lflags = eventreq.handleflags;
|
||||
eflags = eventreq.eventflags;
|
||||
|
||||
/* This is just wrong: we don't look for events on output lines */
|
||||
if (lflags & GPIOHANDLE_REQUEST_OUTPUT) {
|
||||
ret = -EINVAL;
|
||||
goto out_free_label;
|
||||
}
|
||||
|
||||
desc = &gdev->descs[offset];
|
||||
ret = gpiod_request(desc, le->label);
|
||||
if (ret)
|
||||
goto out_free_desc;
|
||||
le->desc = desc;
|
||||
le->eflags = eflags;
|
||||
|
||||
if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW)
|
||||
set_bit(FLAG_ACTIVE_LOW, &desc->flags);
|
||||
if (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN)
|
||||
set_bit(FLAG_OPEN_DRAIN, &desc->flags);
|
||||
if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)
|
||||
set_bit(FLAG_OPEN_SOURCE, &desc->flags);
|
||||
|
||||
ret = gpiod_direction_input(desc);
|
||||
if (ret)
|
||||
goto out_free_desc;
|
||||
|
||||
le->irq = gpiod_to_irq(desc);
|
||||
if (le->irq <= 0) {
|
||||
ret = -ENODEV;
|
||||
goto out_free_desc;
|
||||
}
|
||||
|
||||
if (eflags & GPIOEVENT_REQUEST_RISING_EDGE)
|
||||
irqflags |= IRQF_TRIGGER_RISING;
|
||||
if (eflags & GPIOEVENT_REQUEST_FALLING_EDGE)
|
||||
irqflags |= IRQF_TRIGGER_FALLING;
|
||||
irqflags |= IRQF_ONESHOT;
|
||||
irqflags |= IRQF_SHARED;
|
||||
|
||||
INIT_KFIFO(le->events);
|
||||
init_waitqueue_head(&le->wait);
|
||||
mutex_init(&le->read_lock);
|
||||
|
||||
/* Request a thread to read the events */
|
||||
ret = request_threaded_irq(le->irq,
|
||||
NULL,
|
||||
lineevent_irq_thread,
|
||||
irqflags,
|
||||
le->label,
|
||||
le);
|
||||
if (ret)
|
||||
goto out_free_desc;
|
||||
|
||||
fd = anon_inode_getfd("gpio-event",
|
||||
&lineevent_fileops,
|
||||
le,
|
||||
O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
ret = fd;
|
||||
goto out_free_irq;
|
||||
}
|
||||
|
||||
eventreq.fd = fd;
|
||||
if (copy_to_user(ip, &eventreq, sizeof(eventreq))) {
|
||||
ret = -EFAULT;
|
||||
goto out_free_irq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_irq:
|
||||
free_irq(le->irq, le);
|
||||
out_free_desc:
|
||||
gpiod_free(le->desc);
|
||||
out_free_label:
|
||||
kfree(le->label);
|
||||
out_free_le:
|
||||
kfree(le);
|
||||
put_device(&gdev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* gpio_ioctl() - ioctl handler for the GPIO chardev
|
||||
*/
|
||||
|
@ -385,6 +879,10 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
if (copy_to_user(ip, &lineinfo, sizeof(lineinfo)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
} else if (cmd == GPIO_GET_LINEHANDLE_IOCTL) {
|
||||
return linehandle_create(gdev, ip);
|
||||
} else if (cmd == GPIO_GET_LINEEVENT_IOCTL) {
|
||||
return lineevent_create(gdev, ip);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -548,13 +1046,14 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
|
|||
if (chip->parent) {
|
||||
gdev->dev.parent = chip->parent;
|
||||
gdev->dev.of_node = chip->parent->of_node;
|
||||
} else {
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF_GPIO
|
||||
/* If the gpiochip has an assigned OF node this takes precedence */
|
||||
if (chip->of_node)
|
||||
gdev->dev.of_node = chip->of_node;
|
||||
#endif
|
||||
}
|
||||
|
||||
gdev->id = ida_simple_get(&gpio_ida, 0, 0, GFP_KERNEL);
|
||||
if (gdev->id < 0) {
|
||||
status = gdev->id;
|
||||
|
@ -2333,7 +2832,7 @@ static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
|
|||
|
||||
desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx,
|
||||
&of_flags);
|
||||
if (!IS_ERR(desc) || (PTR_ERR(desc) == -EPROBE_DEFER))
|
||||
if (!IS_ERR(desc) || (PTR_ERR(desc) != -ENOENT))
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -1724,9 +1724,9 @@ static int pinmux_xway_probe(struct platform_device *pdev)
|
|||
}
|
||||
xway_pctrl_desc.pins = xway_info.pads;
|
||||
|
||||
/* load the gpio chip */
|
||||
/* register the gpio chip */
|
||||
xway_chip.parent = &pdev->dev;
|
||||
ret = gpiochip_add(&xway_chip);
|
||||
ret = devm_gpiochip_add_data(&pdev->dev, &xway_chip, NULL);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to register gpio chip\n");
|
||||
return ret;
|
||||
|
@ -1749,7 +1749,6 @@ static int pinmux_xway_probe(struct platform_device *pdev)
|
|||
/* register with the generic lantiq layer */
|
||||
ret = ltq_pinctrl_register(pdev, &xway_info);
|
||||
if (ret) {
|
||||
gpiochip_remove(&xway_chip);
|
||||
dev_err(&pdev->dev, "Failed to register pinctrl driver\n");
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* <linux/gpio.h> - userspace ABI for the GPIO character devices
|
||||
*
|
||||
* Copyright (C) 2015 Linus Walleij
|
||||
* Copyright (C) 2016 Linus Walleij
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
|
@ -26,8 +26,8 @@ struct gpiochip_info {
|
|||
__u32 lines;
|
||||
};
|
||||
|
||||
/* Line is in use by the kernel */
|
||||
#define GPIOLINE_FLAG_KERNEL (1UL << 0)
|
||||
/* Informational flags */
|
||||
#define GPIOLINE_FLAG_KERNEL (1UL << 0) /* Line used by the kernel */
|
||||
#define GPIOLINE_FLAG_IS_OUT (1UL << 1)
|
||||
#define GPIOLINE_FLAG_ACTIVE_LOW (1UL << 2)
|
||||
#define GPIOLINE_FLAG_OPEN_DRAIN (1UL << 3)
|
||||
|
@ -52,7 +52,106 @@ struct gpioline_info {
|
|||
char consumer[32];
|
||||
};
|
||||
|
||||
/* Maximum number of requested handles */
|
||||
#define GPIOHANDLES_MAX 64
|
||||
|
||||
/* Linerequest flags */
|
||||
#define GPIOHANDLE_REQUEST_INPUT (1UL << 0)
|
||||
#define GPIOHANDLE_REQUEST_OUTPUT (1UL << 1)
|
||||
#define GPIOHANDLE_REQUEST_ACTIVE_LOW (1UL << 2)
|
||||
#define GPIOHANDLE_REQUEST_OPEN_DRAIN (1UL << 3)
|
||||
#define GPIOHANDLE_REQUEST_OPEN_SOURCE (1UL << 4)
|
||||
|
||||
/**
|
||||
* struct gpiohandle_request - Information about a GPIO handle request
|
||||
* @lineoffsets: an array desired lines, specified by offset index for the
|
||||
* associated GPIO device
|
||||
* @flags: desired flags for the desired GPIO lines, such as
|
||||
* GPIOHANDLE_REQUEST_OUTPUT, GPIOHANDLE_REQUEST_ACTIVE_LOW etc, OR:ed
|
||||
* together. Note that even if multiple lines are requested, the same flags
|
||||
* must be applicable to all of them, if you want lines with individual
|
||||
* flags set, request them one by one. It is possible to select
|
||||
* a batch of input or output lines, but they must all have the same
|
||||
* characteristics, i.e. all inputs or all outputs, all active low etc
|
||||
* @default_values: if the GPIOHANDLE_REQUEST_OUTPUT is set for a requested
|
||||
* line, this specifies the default output value, should be 0 (low) or
|
||||
* 1 (high), anything else than 0 or 1 will be interpreted as 1 (high)
|
||||
* @consumer_label: a desired consumer label for the selected GPIO line(s)
|
||||
* such as "my-bitbanged-relay"
|
||||
* @lines: number of lines requested in this request, i.e. the number of
|
||||
* valid fields in the above arrays, set to 1 to request a single line
|
||||
* @fd: if successful this field will contain a valid anonymous file handle
|
||||
* after a GPIO_GET_LINEHANDLE_IOCTL operation, zero or negative value
|
||||
* means error
|
||||
*/
|
||||
struct gpiohandle_request {
|
||||
__u32 lineoffsets[GPIOHANDLES_MAX];
|
||||
__u32 flags;
|
||||
__u8 default_values[GPIOHANDLES_MAX];
|
||||
char consumer_label[32];
|
||||
__u32 lines;
|
||||
int fd;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct gpiohandle_data - Information of values on a GPIO handle
|
||||
* @values: when getting the state of lines this contains the current
|
||||
* state of a line, when setting the state of lines these should contain
|
||||
* the desired target state
|
||||
*/
|
||||
struct gpiohandle_data {
|
||||
__u8 values[GPIOHANDLES_MAX];
|
||||
};
|
||||
|
||||
#define GPIOHANDLE_GET_LINE_VALUES_IOCTL _IOWR(0xB4, 0x08, struct gpiohandle_data)
|
||||
#define GPIOHANDLE_SET_LINE_VALUES_IOCTL _IOWR(0xB4, 0x09, struct gpiohandle_data)
|
||||
|
||||
/* Eventrequest flags */
|
||||
#define GPIOEVENT_REQUEST_RISING_EDGE (1UL << 0)
|
||||
#define GPIOEVENT_REQUEST_FALLING_EDGE (1UL << 1)
|
||||
#define GPIOEVENT_REQUEST_BOTH_EDGES ((1UL << 0) | (1UL << 1))
|
||||
|
||||
/**
|
||||
* struct gpioevent_request - Information about a GPIO event request
|
||||
* @lineoffset: the desired line to subscribe to events from, specified by
|
||||
* offset index for the associated GPIO device
|
||||
* @handleflags: desired handle flags for the desired GPIO line, such as
|
||||
* GPIOHANDLE_REQUEST_ACTIVE_LOW or GPIOHANDLE_REQUEST_OPEN_DRAIN
|
||||
* @eventflags: desired flags for the desired GPIO event line, such as
|
||||
* GPIOEVENT_REQUEST_RISING_EDGE or GPIOEVENT_REQUEST_FALLING_EDGE
|
||||
* @consumer_label: a desired consumer label for the selected GPIO line(s)
|
||||
* such as "my-listener"
|
||||
* @fd: if successful this field will contain a valid anonymous file handle
|
||||
* after a GPIO_GET_LINEEVENT_IOCTL operation, zero or negative value
|
||||
* means error
|
||||
*/
|
||||
struct gpioevent_request {
|
||||
__u32 lineoffset;
|
||||
__u32 handleflags;
|
||||
__u32 eventflags;
|
||||
char consumer_label[32];
|
||||
int fd;
|
||||
};
|
||||
|
||||
/**
|
||||
* GPIO event types
|
||||
*/
|
||||
#define GPIOEVENT_EVENT_RISING_EDGE 0x01
|
||||
#define GPIOEVENT_EVENT_FALLING_EDGE 0x02
|
||||
|
||||
/**
|
||||
* struct gpioevent_data - The actual event being pushed to userspace
|
||||
* @timestamp: best estimate of time of event occurrence, in nanoseconds
|
||||
* @id: event identifier
|
||||
*/
|
||||
struct gpioevent_data {
|
||||
__u64 timestamp;
|
||||
__u32 id;
|
||||
};
|
||||
|
||||
#define GPIO_GET_CHIPINFO_IOCTL _IOR(0xB4, 0x01, struct gpiochip_info)
|
||||
#define GPIO_GET_LINEINFO_IOCTL _IOWR(0xB4, 0x02, struct gpioline_info)
|
||||
#define GPIO_GET_LINEHANDLE_IOCTL _IOWR(0xB4, 0x03, struct gpiohandle_request)
|
||||
#define GPIO_GET_LINEEVENT_IOCTL _IOWR(0xB4, 0x04, struct gpioevent_request)
|
||||
|
||||
#endif /* _UAPI_GPIO_H_ */
|
||||
|
|
|
@ -85,7 +85,7 @@ tmon: FORCE
|
|||
freefall: FORCE
|
||||
$(call descend,laptop/$@)
|
||||
|
||||
all: acpi cgroup cpupower hv firewire lguest \
|
||||
all: acpi cgroup cpupower gpio hv firewire lguest \
|
||||
perf selftests turbostat usb \
|
||||
virtio vm net x86_energy_perf_policy \
|
||||
tmon freefall objtool
|
||||
|
@ -96,7 +96,7 @@ acpi_install:
|
|||
cpupower_install:
|
||||
$(call descend,power/$(@:_install=),install)
|
||||
|
||||
cgroup_install firewire_install hv_install lguest_install perf_install usb_install virtio_install vm_install net_install objtool_install:
|
||||
cgroup_install firewire_install gpio_install hv_install lguest_install perf_install usb_install virtio_install vm_install net_install objtool_install:
|
||||
$(call descend,$(@:_install=),install)
|
||||
|
||||
selftests_install:
|
||||
|
@ -114,7 +114,8 @@ freefall_install:
|
|||
kvm_stat_install:
|
||||
$(call descend,kvm/$(@:_install=),install)
|
||||
|
||||
install: acpi_install cgroup_install cpupower_install hv_install firewire_install lguest_install \
|
||||
install: acpi_install cgroup_install cpupower_install gpio_install \
|
||||
hv_install firewire_install lguest_install \
|
||||
perf_install selftests_install turbostat_install usb_install \
|
||||
virtio_install vm_install net_install x86_energy_perf_policy_install \
|
||||
tmon_install freefall_install objtool_install kvm_stat_install
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
lsgpio-y += lsgpio.o gpio-utils.o
|
||||
gpio-hammer-y += gpio-hammer.o gpio-utils.o
|
||||
gpio-event-mon-y += gpio-event-mon.o gpio-utils.o
|
|
@ -1,12 +1,75 @@
|
|||
include ../scripts/Makefile.include
|
||||
|
||||
bindir ?= /usr/bin
|
||||
|
||||
ifeq ($(srctree),)
|
||||
srctree := $(patsubst %/,%,$(dir $(shell pwd)))
|
||||
srctree := $(patsubst %/,%,$(dir $(srctree)))
|
||||
endif
|
||||
|
||||
# Do not use make's built-in rules
|
||||
# (this improves performance and avoids hard-to-debug behaviour);
|
||||
MAKEFLAGS += -r
|
||||
|
||||
CC = $(CROSS_COMPILE)gcc
|
||||
CFLAGS += -O2 -Wall -g -D_GNU_SOURCE
|
||||
LD = $(CROSS_COMPILE)ld
|
||||
CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include
|
||||
|
||||
all: lsgpio
|
||||
ALL_TARGETS := lsgpio gpio-hammer gpio-event-mon
|
||||
ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
|
||||
|
||||
lsgpio: lsgpio.o gpio-utils.o
|
||||
all: $(ALL_PROGRAMS)
|
||||
|
||||
%.o: %.c gpio-utils.h
|
||||
export srctree OUTPUT CC LD CFLAGS
|
||||
include $(srctree)/tools/build/Makefile.include
|
||||
|
||||
#
|
||||
# We need the following to be outside of kernel tree
|
||||
#
|
||||
$(OUTPUT)include/linux/gpio.h: ../../include/uapi/linux/gpio.h
|
||||
mkdir -p $(OUTPUT)include/linux 2>&1 || true
|
||||
ln -sf $(CURDIR)/../../include/uapi/linux/gpio.h $@
|
||||
|
||||
prepare: $(OUTPUT)include/linux/gpio.h
|
||||
|
||||
#
|
||||
# lsgpio
|
||||
#
|
||||
LSGPIO_IN := $(OUTPUT)lsgpio-in.o
|
||||
$(LSGPIO_IN): prepare FORCE
|
||||
$(Q)$(MAKE) $(build)=lsgpio
|
||||
$(OUTPUT)lsgpio: $(LSGPIO_IN)
|
||||
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
|
||||
|
||||
#
|
||||
# gpio-hammer
|
||||
#
|
||||
GPIO_HAMMER_IN := $(OUTPUT)gpio-hammer-in.o
|
||||
$(GPIO_HAMMER_IN): prepare FORCE
|
||||
$(Q)$(MAKE) $(build)=gpio-hammer
|
||||
$(OUTPUT)gpio-hammer: $(GPIO_HAMMER_IN)
|
||||
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
|
||||
|
||||
#
|
||||
# gpio-event-mon
|
||||
#
|
||||
GPIO_EVENT_MON_IN := $(OUTPUT)gpio-event-mon-in.o
|
||||
$(GPIO_EVENT_MON_IN): prepare FORCE
|
||||
$(Q)$(MAKE) $(build)=gpio-event-mon
|
||||
$(OUTPUT)gpio-event-mon: $(GPIO_EVENT_MON_IN)
|
||||
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f *.o lsgpio
|
||||
rm -f $(ALL_PROGRAMS)
|
||||
rm -f $(OUTPUT)include/linux/gpio.h
|
||||
find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete
|
||||
|
||||
install: $(ALL_PROGRAMS)
|
||||
install -d -m 755 $(DESTDIR)$(bindir); \
|
||||
for program in $(ALL_PROGRAMS); do \
|
||||
install $$program $(DESTDIR)$(bindir); \
|
||||
done
|
||||
|
||||
FORCE:
|
||||
|
||||
.PHONY: all install clean FORCE prepare
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* gpio-hammer - example swiss army knife to shake GPIO lines on a system
|
||||
*
|
||||
* Copyright (C) 2016 Linus Walleij
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* Usage:
|
||||
* gpio-event-mon -n <device-name> -o <offset>
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <poll.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
int monitor_device(const char *device_name,
|
||||
unsigned int line,
|
||||
u_int32_t handleflags,
|
||||
u_int32_t eventflags,
|
||||
unsigned int loops)
|
||||
{
|
||||
struct gpioevent_request req;
|
||||
struct gpiohandle_data data;
|
||||
char *chrdev_name;
|
||||
int fd;
|
||||
int ret;
|
||||
int i = 0;
|
||||
|
||||
ret = asprintf(&chrdev_name, "/dev/%s", device_name);
|
||||
if (ret < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
fd = open(chrdev_name, 0);
|
||||
if (fd == -1) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to open %s\n", chrdev_name);
|
||||
goto exit_close_error;
|
||||
}
|
||||
|
||||
req.lineoffset = line;
|
||||
req.handleflags = handleflags;
|
||||
req.eventflags = eventflags;
|
||||
strcpy(req.consumer_label, "gpio-event-mon");
|
||||
|
||||
ret = ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &req);
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to issue GET EVENT "
|
||||
"IOCTL (%d)\n",
|
||||
ret);
|
||||
goto exit_close_error;
|
||||
}
|
||||
|
||||
/* Read initial states */
|
||||
ret = ioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to issue GPIOHANDLE GET LINE "
|
||||
"VALUES IOCTL (%d)\n",
|
||||
ret);
|
||||
goto exit_close_error;
|
||||
}
|
||||
|
||||
fprintf(stdout, "Monitoring line %d on %s\n", line, device_name);
|
||||
fprintf(stdout, "Initial line value: %d\n", data.values[0]);
|
||||
|
||||
while (1) {
|
||||
struct gpioevent_data event;
|
||||
|
||||
ret = read(req.fd, &event, sizeof(event));
|
||||
if (ret == -1) {
|
||||
if (errno == -EAGAIN) {
|
||||
fprintf(stderr, "nothing available\n");
|
||||
continue;
|
||||
} else {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to read event (%d)\n",
|
||||
ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret != sizeof(event)) {
|
||||
fprintf(stderr, "Reading event failed\n");
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
fprintf(stdout, "GPIO EVENT %" PRIu64 ": ", event.timestamp);
|
||||
switch (event.id) {
|
||||
case GPIOEVENT_EVENT_RISING_EDGE:
|
||||
fprintf(stdout, "rising edge");
|
||||
break;
|
||||
case GPIOEVENT_EVENT_FALLING_EDGE:
|
||||
fprintf(stdout, "falling edge");
|
||||
break;
|
||||
default:
|
||||
fprintf(stdout, "unknown event");
|
||||
}
|
||||
fprintf(stdout, "\n");
|
||||
|
||||
i++;
|
||||
if (i == loops)
|
||||
break;
|
||||
}
|
||||
|
||||
exit_close_error:
|
||||
if (close(fd) == -1)
|
||||
perror("Failed to close GPIO character device file");
|
||||
free(chrdev_name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void print_usage(void)
|
||||
{
|
||||
fprintf(stderr, "Usage: gpio-event-mon [options]...\n"
|
||||
"Listen to events on GPIO lines, 0->1 1->0\n"
|
||||
" -n <name> Listen on GPIOs on a named device (must be stated)\n"
|
||||
" -o <n> Offset to monitor\n"
|
||||
" -d Set line as open drain\n"
|
||||
" -s Set line as open source\n"
|
||||
" -r Listen for rising edges\n"
|
||||
" -f Listen for falling edges\n"
|
||||
" [-c <n>] Do <n> loops (optional, infinite loop if not stated)\n"
|
||||
" -? This helptext\n"
|
||||
"\n"
|
||||
"Example:\n"
|
||||
"gpio-event-mon -n gpiochip0 -o 4 -r -f\n"
|
||||
);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *device_name = NULL;
|
||||
unsigned int line = -1;
|
||||
unsigned int loops = 0;
|
||||
u_int32_t handleflags = GPIOHANDLE_REQUEST_INPUT;
|
||||
u_int32_t eventflags = 0;
|
||||
int c;
|
||||
|
||||
while ((c = getopt(argc, argv, "c:n:o:dsrf?")) != -1) {
|
||||
switch (c) {
|
||||
case 'c':
|
||||
loops = strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'n':
|
||||
device_name = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
line = strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'd':
|
||||
handleflags |= GPIOHANDLE_REQUEST_OPEN_DRAIN;
|
||||
break;
|
||||
case 's':
|
||||
handleflags |= GPIOHANDLE_REQUEST_OPEN_SOURCE;
|
||||
break;
|
||||
case 'r':
|
||||
eventflags |= GPIOEVENT_REQUEST_RISING_EDGE;
|
||||
break;
|
||||
case 'f':
|
||||
eventflags |= GPIOEVENT_REQUEST_FALLING_EDGE;
|
||||
break;
|
||||
case '?':
|
||||
print_usage();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!device_name || line == -1) {
|
||||
print_usage();
|
||||
return -1;
|
||||
}
|
||||
if (!eventflags) {
|
||||
printf("No flags specified, listening on both rising and "
|
||||
"falling edges\n");
|
||||
eventflags = GPIOEVENT_REQUEST_BOTH_EDGES;
|
||||
}
|
||||
return monitor_device(device_name, line, handleflags,
|
||||
eventflags, loops);
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* gpio-hammer - example swiss army knife to shake GPIO lines on a system
|
||||
*
|
||||
* Copyright (C) 2016 Linus Walleij
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* Usage:
|
||||
* gpio-hammer -n <device-name> -o <offset1> -o <offset2>
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <poll.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
int hammer_device(const char *device_name, unsigned int *lines, int nlines,
|
||||
unsigned int loops)
|
||||
{
|
||||
struct gpiohandle_request req;
|
||||
struct gpiohandle_data data;
|
||||
char *chrdev_name;
|
||||
char swirr[] = "-\\|/";
|
||||
int fd;
|
||||
int ret;
|
||||
int i, j;
|
||||
unsigned int iteration = 0;
|
||||
|
||||
ret = asprintf(&chrdev_name, "/dev/%s", device_name);
|
||||
if (ret < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
fd = open(chrdev_name, 0);
|
||||
if (fd == -1) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to open %s\n", chrdev_name);
|
||||
goto exit_close_error;
|
||||
}
|
||||
|
||||
/* Request lines as output */
|
||||
for (i = 0; i < nlines; i++)
|
||||
req.lineoffsets[i] = lines[i];
|
||||
req.flags = GPIOHANDLE_REQUEST_OUTPUT; /* Request as output */
|
||||
strcpy(req.consumer_label, "gpio-hammer");
|
||||
req.lines = nlines;
|
||||
ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to issue GET LINEHANDLE "
|
||||
"IOCTL (%d)\n",
|
||||
ret);
|
||||
goto exit_close_error;
|
||||
}
|
||||
|
||||
/* Read initial states */
|
||||
ret = ioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to issue GPIOHANDLE GET LINE "
|
||||
"VALUES IOCTL (%d)\n",
|
||||
ret);
|
||||
goto exit_close_error;
|
||||
}
|
||||
fprintf(stdout, "Hammer lines [");
|
||||
for (i = 0; i < nlines; i++) {
|
||||
fprintf(stdout, "%d", lines[i]);
|
||||
if (i != (nlines - 1))
|
||||
fprintf(stdout, ", ");
|
||||
}
|
||||
fprintf(stdout, "] on %s, initial states: [", device_name);
|
||||
for (i = 0; i < nlines; i++) {
|
||||
fprintf(stdout, "%d", data.values[i]);
|
||||
if (i != (nlines - 1))
|
||||
fprintf(stdout, ", ");
|
||||
}
|
||||
fprintf(stdout, "]\n");
|
||||
|
||||
/* Hammertime! */
|
||||
j = 0;
|
||||
while (1) {
|
||||
/* Invert all lines so we blink */
|
||||
for (i = 0; i < nlines; i++)
|
||||
data.values[i] = !data.values[i];
|
||||
|
||||
ret = ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to issue GPIOHANDLE SET LINE "
|
||||
"VALUES IOCTL (%d)\n",
|
||||
ret);
|
||||
goto exit_close_error;
|
||||
}
|
||||
/* Re-read values to get status */
|
||||
ret = ioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to issue GPIOHANDLE GET LINE "
|
||||
"VALUES IOCTL (%d)\n",
|
||||
ret);
|
||||
goto exit_close_error;
|
||||
}
|
||||
|
||||
fprintf(stdout, "[%c] ", swirr[j]);
|
||||
j++;
|
||||
if (j == sizeof(swirr)-1)
|
||||
j = 0;
|
||||
|
||||
fprintf(stdout, "[");
|
||||
for (i = 0; i < nlines; i++) {
|
||||
fprintf(stdout, "%d: %d", lines[i], data.values[i]);
|
||||
if (i != (nlines - 1))
|
||||
fprintf(stdout, ", ");
|
||||
}
|
||||
fprintf(stdout, "]\r");
|
||||
fflush(stdout);
|
||||
sleep(1);
|
||||
iteration++;
|
||||
if (loops && iteration == loops)
|
||||
break;
|
||||
}
|
||||
fprintf(stdout, "\n");
|
||||
ret = 0;
|
||||
|
||||
exit_close_error:
|
||||
if (close(fd) == -1)
|
||||
perror("Failed to close GPIO character device file");
|
||||
free(chrdev_name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void print_usage(void)
|
||||
{
|
||||
fprintf(stderr, "Usage: gpio-hammer [options]...\n"
|
||||
"Hammer GPIO lines, 0->1->0->1...\n"
|
||||
" -n <name> Hammer GPIOs on a named device (must be stated)\n"
|
||||
" -o <n> Offset[s] to hammer, at least one, several can be stated\n"
|
||||
" [-c <n>] Do <n> loops (optional, infinite loop if not stated)\n"
|
||||
" -? This helptext\n"
|
||||
"\n"
|
||||
"Example:\n"
|
||||
"gpio-hammer -n gpiochip0 -o 4\n"
|
||||
);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *device_name = NULL;
|
||||
unsigned int lines[GPIOHANDLES_MAX];
|
||||
unsigned int loops = 0;
|
||||
int nlines;
|
||||
int c;
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
while ((c = getopt(argc, argv, "c:n:o:?")) != -1) {
|
||||
switch (c) {
|
||||
case 'c':
|
||||
loops = strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'n':
|
||||
device_name = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
lines[i] = strtoul(optarg, NULL, 10);
|
||||
i++;
|
||||
break;
|
||||
case '?':
|
||||
print_usage();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
nlines = i;
|
||||
|
||||
if (!device_name || !nlines) {
|
||||
print_usage();
|
||||
return -1;
|
||||
}
|
||||
return hammer_device(device_name, lines, nlines, loops);
|
||||
}
|
Loading…
Reference in New Issue