Commit fa889d85 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'gpio-v5.6-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 v5.6 kernel cycle.

  This is a pretty calm cycle so far, nothing special going on really.
  Some more changes will come in from the irqchip and pin control trees.

  I also deleted an orphan include file for FMC that was dangling since
  subsystem was removed.

  Core changes:

   - Document the usecases for the kernelspace vs userspace handling of
     GPIOs.

   - Handle MSI (message signalled interrupts) properly in the core
     hierarchical irqdomain code.

   - Fix a rare race condition while initializing the descriptor array.

  New drivers:

   - Xylon LogiCVC GPIO driver.

   - WDC934x GPIO controller driver.

  Driver improvements:

   - Implemented suspend/resume in the Tegra driver.

   - MPC8xx edge detection fixup.

   - Properly convert ThunderX to use hierarchical irqdomain with
     GPIOLIB_IRQCHIP on top of the revert of the previous buggy
     switchover. This time it works (hopefully).

  Misc:

   - Drop a FMC remnant file <linux/ipmi-fru.h>

   - A slew of fixes"

* tag 'gpio-v5.6-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (48 commits)
  MAINTAINERS: Replace Tien Hock Loh as Altera PIO maintainer
  gpiolib: hold gpio devices lock until ->descs array is initialised
  gpio: aspeed-sgpio: fixed typos
  gpio: mvebu: clear irq in edge cause register before unmask edge irq
  gpiolib: Lower verbosity when allocating hierarchy irq
  gpiolib: Remove duplicated function gpio_do_set_config()
  gpio: Fix the no return statement warning
  gpio: wcd934x: Add support to wcd934x gpio controller
  gpiolib: remove set but not used variable 'config'
  gpio: vx855: fixed a typo
  gpio: mockup: sort headers alphabetically
  gpio: mockup: update the license tag
  gpio: Remove the unused flags
  gpiolib: Set lockdep class for hierarchical irq domains
  gpio: thunderx: Switch to GPIOLIB_IRQCHIP
  gpiolib: Add the support for the msi parent domain
  gpiolib: Add support for the irqdomain which doesn't use irq_fwspec as arg
  gpio: Add use guidance documentation
  dt-bindings: gpio: wcd934x: Add bindings for gpio
  gpio: altera: change to platform_get_irq_optional to avoid false-positive error
  ...
parents b3a60822 0282c72d
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/gpio/qcom,wcd934x-gpio.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: WCD9340/WCD9341 GPIO controller
maintainers:
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
description: |
Qualcomm Technologies Inc WCD9340/WCD9341 Audio Codec has integrated
gpio controller to control 5 gpios on the chip.
properties:
compatible:
enum:
- qcom,wcd9340-gpio
- qcom,wcd9341-gpio
reg:
maxItems: 1
gpio-controller: true
'#gpio-cells':
const: 2
required:
- compatible
- reg
- gpio-controller
- "#gpio-cells"
additionalProperties: false
examples:
- |
wcdgpio: gpio@42 {
compatible = "qcom,wcd9340-gpio";
reg = <0x042 0x2>;
gpio-controller;
#gpio-cells = <2>;
};
...
...@@ -18,7 +18,8 @@ Required Properties: ...@@ -18,7 +18,8 @@ Required Properties:
- "renesas,gpio-r8a7793": for R8A7793 (R-Car M2-N) 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-r8a7794": for R8A7794 (R-Car E2) compatible GPIO controller.
- "renesas,gpio-r8a7795": for R8A7795 (R-Car H3) compatible GPIO controller. - "renesas,gpio-r8a7795": for R8A7795 (R-Car H3) compatible GPIO controller.
- "renesas,gpio-r8a7796": for R8A7796 (R-Car M3-W) compatible GPIO controller. - "renesas,gpio-r8a7796": for R8A77960 (R-Car M3-W) compatible GPIO controller.
- "renesas,gpio-r8a77961": for R8A77961 (R-Car M3-W+) compatible GPIO controller.
- "renesas,gpio-r8a77965": for R8A77965 (R-Car M3-N) compatible GPIO controller. - "renesas,gpio-r8a77965": for R8A77965 (R-Car M3-N) compatible GPIO controller.
- "renesas,gpio-r8a77970": for R8A77970 (R-Car V3M) compatible GPIO controller. - "renesas,gpio-r8a77970": for R8A77970 (R-Car V3M) compatible GPIO controller.
- "renesas,gpio-r8a77980": for R8A77980 (R-Car V3H) compatible GPIO controller. - "renesas,gpio-r8a77980": for R8A77980 (R-Car V3H) compatible GPIO controller.
......
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
# Copyright 2019 Bootlin
%YAML 1.2
---
$id: "http://devicetree.org/schemas/gpio/xylon,logicvc-gpio.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Xylon LogiCVC GPIO controller
maintainers:
- Paul Kocialkowski <paul.kocialkowski@bootlin.com>
description: |
The LogiCVC GPIO describes the GPIO block included in the LogiCVC display
controller. These are meant to be used for controlling display-related
signals.
The controller exposes GPIOs from the display and power control registers,
which are mapped by the driver as follows:
- GPIO[4:0] (display control) mapped to index 0-4
- EN_BLIGHT (power control) mapped to index 5
- EN_VDD (power control) mapped to index 6
- EN_VEE (power control) mapped to index 7
- V_EN (power control) mapped to index 8
properties:
$nodename:
pattern: "^gpio@[0-9a-f]+$"
compatible:
enum:
- xylon,logicvc-3.02.a-gpio
reg:
maxItems: 1
"#gpio-cells":
const: 2
gpio-controller: true
gpio-line-names:
minItems: 1
maxItems: 9
required:
- compatible
- reg
- "#gpio-cells"
- gpio-controller
examples:
- |
logicvc: logicvc@43c00000 {
compatible = "xylon,logicvc-3.02.a", "syscon", "simple-mfd";
reg = <0x43c00000 0x6000>;
#address-cells = <1>;
#size-cells = <1>;
logicvc_gpio: gpio@40 {
compatible = "xylon,logicvc-3.02.a-gpio";
reg = <0x40 0x40>;
gpio-controller;
#gpio-cells = <2>;
gpio-line-names = "GPIO0", "GPIO1", "GPIO2", "GPIO3", "GPIO4",
"EN_BLIGHT", "EN_VDD", "EN_VEE", "V_EN";
};
};
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
# Copyright 2019 Bootlin
%YAML 1.2
---
$id: "http://devicetree.org/schemas/mfd/xylon,logicvc.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Xylon LogiCVC multi-function device
maintainers:
- Paul Kocialkowski <paul.kocialkowski@bootlin.com>
description: |
The LogiCVC is a display controller that also contains a GPIO controller.
As a result, a multi-function device is exposed as parent of the display
and GPIO blocks.
properties:
compatible:
items:
- enum:
- xylon,logicvc-3.02.a
- const: syscon
- const: simple-mfd
reg:
maxItems: 1
select:
properties:
compatible:
contains:
enum:
- xylon,logicvc-3.02.a
required:
- compatible
required:
- compatible
- reg
examples:
- |
logicvc: logicvc@43c00000 {
compatible = "xylon,logicvc-3.02.a", "syscon", "simple-mfd";
reg = <0x43c00000 0x6000>;
#address-cells = <1>;
#size-cells = <1>;
};
...@@ -1060,6 +1060,8 @@ patternProperties: ...@@ -1060,6 +1060,8 @@ patternProperties:
description: Xilinx description: Xilinx
"^xunlong,.*": "^xunlong,.*":
description: Shenzhen Xunlong Software CO.,Limited description: Shenzhen Xunlong Software CO.,Limited
"^xylon,.*":
description: Xylon
"^yones-toptech,.*": "^yones-toptech,.*":
description: Yones Toptech Co., Ltd. description: Yones Toptech Co., Ltd.
"^ysoft,.*": "^ysoft,.*":
......
...@@ -267,6 +267,8 @@ DRM ...@@ -267,6 +267,8 @@ DRM
GPIO GPIO
devm_gpiod_get() devm_gpiod_get()
devm_gpiod_get_array()
devm_gpiod_get_array_optional()
devm_gpiod_get_index() devm_gpiod_get_index()
devm_gpiod_get_index_optional() devm_gpiod_get_index_optional()
devm_gpiod_get_optional() devm_gpiod_get_optional()
......
...@@ -95,7 +95,7 @@ to emulate MCTRL (modem control) signals CTS/RTS by using two GPIO lines. The ...@@ -95,7 +95,7 @@ to emulate MCTRL (modem control) signals CTS/RTS by using two GPIO lines. The
MTD NOR flash has add-ons for extra GPIO lines too, though the address bus is MTD NOR flash has add-ons for extra GPIO lines too, though the address bus is
usually connected directly to the flash. usually connected directly to the flash.
Use those instead of talking directly to the GPIOs using sysfs; they integrate Use those instead of talking directly to the GPIOs from userspace; they
with kernel frameworks better than your userspace code could. Needless to say, integrate with kernel frameworks better than your userspace code could.
just using the appropriate kernel drivers will simplify and speed up your Needless to say, just using the appropriate kernel drivers will simplify and
embedded hacking in particular by providing ready-made components. speed up your embedded hacking in particular by providing ready-made components.
...@@ -8,6 +8,7 @@ Contents: ...@@ -8,6 +8,7 @@ Contents:
:maxdepth: 2 :maxdepth: 2
intro intro
using-gpio
driver driver
consumer consumer
board board
......
=========================
Using GPIO Lines in Linux
=========================
The Linux kernel exists to abstract and present hardware to users. GPIO lines
as such are normally not user facing abstractions. The most obvious, natural
and preferred way to use GPIO lines is to let kernel hardware drivers deal
with them.
For examples of already existing generic drivers that will also be good
examples for any other kernel drivers you want to author, refer to
:doc:`drivers-on-gpio`
For any kind of mass produced system you want to support, such as servers,
laptops, phones, tablets, routers, and any consumer or office or business goods
using appropriate kernel drivers is paramount. Submit your code for inclusion
in the upstream Linux kernel when you feel it is mature enough and you will get
help to refine it, see :doc:`../../process/submitting-patches`.
In Linux GPIO lines also have a userspace ABI.
The userspace ABI is intended for one-off deployments. Examples are prototypes,
factory lines, maker community projects, workshop specimen, production tools,
industrial automation, PLC-type use cases, door controllers, in short a piece
of specialized equipment that is not produced by the numbers, requiring
operators to have a deep knowledge of the equipment and knows about the
software-hardware interface to be set up. They should not have a natural fit
to any existing kernel subsystem and not be a good fit for an operating system,
because of not being reusable or abstract enough, or involving a lot of non
computer hardware related policy.
Applications that have a good reason to use the industrial I/O (IIO) subsystem
from userspace will likely be a good fit for using GPIO lines from userspace as
well.
Do not under any circumstances abuse the GPIO userspace ABI to cut corners in
any product development projects. If you use it for prototyping, then do not
productify the prototype: rewrite it using proper kernel drivers. Do not under
any circumstances deploy any uniform products using GPIO from userspace.
The userspace ABI is a character device for each GPIO hardware unit (GPIO chip).
These devices will appear on the system as ``/dev/gpiochip0`` thru
``/dev/gpiochipN``. Examples of how to directly use the userspace ABI can be
found in the kernel tree ``tools/gpio`` subdirectory.
For structured and managed applications, we recommend that you make use of the
libgpiod_ library. This provides helper abstractions, command line utlities
and arbitration for multiple simultaneous consumers on the same GPIO chip.
.. _libgpiod: https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/
...@@ -734,7 +734,7 @@ S: Maintained ...@@ -734,7 +734,7 @@ S: Maintained
F: drivers/mailbox/mailbox-altera.c F: drivers/mailbox/mailbox-altera.c
ALTERA PIO DRIVER ALTERA PIO DRIVER
M: Tien Hock Loh <thloh@altera.com> M: Joyce Ooi <joyce.ooi@intel.com>
L: linux-gpio@vger.kernel.org L: linux-gpio@vger.kernel.org
S: Maintained S: Maintained
F: drivers/gpio/gpio-altera.c F: drivers/gpio/gpio-altera.c
......
...@@ -312,6 +312,12 @@ config GPIO_IXP4XX ...@@ -312,6 +312,12 @@ config GPIO_IXP4XX
IXP4xx series of chips. IXP4xx series of chips.
If unsure, say N. If unsure, say N.
config GPIO_LOGICVC
tristate "Xylon LogiCVC GPIO support"
depends on MFD_SYSCON && OF
help
Say yes here to support GPIO functionality of the Xylon LogiCVC
programmable logic block.
config GPIO_LOONGSON config GPIO_LOONGSON
bool "Loongson-2/3 GPIO support" bool "Loongson-2/3 GPIO support"
...@@ -582,6 +588,7 @@ config GPIO_THUNDERX ...@@ -582,6 +588,7 @@ config GPIO_THUNDERX
tristate "Cavium ThunderX/OCTEON-TX GPIO" tristate "Cavium ThunderX/OCTEON-TX GPIO"
depends on ARCH_THUNDER || (64BIT && COMPILE_TEST) depends on ARCH_THUNDER || (64BIT && COMPILE_TEST)
depends on PCI_MSI depends on PCI_MSI
select GPIOLIB_IRQCHIP
select IRQ_DOMAIN_HIERARCHY select IRQ_DOMAIN_HIERARCHY
select IRQ_FASTEOI_HIERARCHY_HANDLERS select IRQ_FASTEOI_HIERARCHY_HANDLERS
help help
...@@ -621,6 +628,13 @@ config GPIO_VX855 ...@@ -621,6 +628,13 @@ config GPIO_VX855
additional drivers must be enabled in order to use the additional drivers must be enabled in order to use the
functionality of the device. functionality of the device.
config GPIO_WCD934X
tristate "Qualcomm Technologies Inc WCD9340/WCD9341 gpio controller driver"
depends on MFD_WCD934X && OF_GPIO
help
This driver is to supprot GPIO block found on the Qualcomm Technologies
Inc WCD9340/WCD9341 Audio Codec.
config GPIO_XGENE config GPIO_XGENE
bool "APM X-Gene GPIO controller support" bool "APM X-Gene GPIO controller support"
depends on ARM64 && OF_GPIO depends on ARM64 && OF_GPIO
......
...@@ -69,6 +69,7 @@ obj-$(CONFIG_GPIO_IT87) += gpio-it87.o ...@@ -69,6 +69,7 @@ obj-$(CONFIG_GPIO_IT87) += gpio-it87.o
obj-$(CONFIG_GPIO_IXP4XX) += gpio-ixp4xx.o obj-$(CONFIG_GPIO_IXP4XX) += gpio-ixp4xx.o
obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o
obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o
obj-$(CONFIG_GPIO_LOGICVC) += gpio-logicvc.o
obj-$(CONFIG_GPIO_LOONGSON1) += gpio-loongson1.o obj-$(CONFIG_GPIO_LOONGSON1) += gpio-loongson1.o
obj-$(CONFIG_GPIO_LOONGSON) += gpio-loongson.o obj-$(CONFIG_GPIO_LOONGSON) += gpio-loongson.o
obj-$(CONFIG_GPIO_LP3943) += gpio-lp3943.o obj-$(CONFIG_GPIO_LP3943) += gpio-lp3943.o
...@@ -158,6 +159,7 @@ obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o ...@@ -158,6 +159,7 @@ obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o
obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o
obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o
obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o
obj-$(CONFIG_GPIO_WCD934X) += gpio-wcd934x.o
obj-$(CONFIG_GPIO_WHISKEY_COVE) += gpio-wcove.o obj-$(CONFIG_GPIO_WHISKEY_COVE) += gpio-wcove.o
obj-$(CONFIG_GPIO_WINBOND) += gpio-winbond.o obj-$(CONFIG_GPIO_WINBOND) += gpio-winbond.o
obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o
......
...@@ -10,6 +10,28 @@ approach. This means that GPIO consumers, drivers and machine descriptions ...@@ -10,6 +10,28 @@ approach. This means that GPIO consumers, drivers and machine descriptions
ideally have no use or idea of the global GPIO numberspace that has/was ideally have no use or idea of the global GPIO numberspace that has/was
used in the inception of the GPIO subsystem. used in the inception of the GPIO subsystem.
The numberspace issue is the same as to why irq is moving away from irq
numbers to IRQ descriptors.
The underlying motivation for this is that the GPIO numberspace has become
unmanageable: machine board files tend to become full of macros trying to
establish the numberspace at compile-time, making it hard to add any numbers
in the middle (such as if you missed a pin on a chip) without the numberspace
breaking.
Machine descriptions such as device tree or ACPI does not have a concept of the
Linux GPIO number as those descriptions are external to the Linux kernel
and treat GPIO lines as abstract entities.
The runtime-assigned GPIO numberspace (what you get if you assign the GPIO
base as -1 in struct gpio_chip) has also became unpredictable due to factors
such as probe ordering and the introduction of -EPROBE_DEFER making probe
ordering of independent GPIO chips essentially unpredictable, as their base
number will be assigned on a first come first serve basis.
The best way to get out of the problem is to make the global GPIO numbers
unimportant by simply not using them. GPIO descriptors deal with this.
Work items: Work items:
- Convert all GPIO device drivers to only #include <linux/gpio/driver.h> - Convert all GPIO device drivers to only #include <linux/gpio/driver.h>
...@@ -33,7 +55,7 @@ This header and helpers appeared at one point when there was no proper ...@@ -33,7 +55,7 @@ This header and helpers appeared at one point when there was no proper
driver infrastructure for doing simpler MMIO GPIO devices and there was driver infrastructure for doing simpler MMIO GPIO devices and there was
no core support for parsing device tree GPIOs from the core library with no core support for parsing device tree GPIOs from the core library with
the [devm_]gpiod_get() calls we have today that will implicitly go into the [devm_]gpiod_get() calls we have today that will implicitly go into
the device tree back-end. the device tree back-end. It is legacy and should not be used in new code.
Work items: Work items:
...@@ -59,6 +81,15 @@ Work items: ...@@ -59,6 +81,15 @@ Work items:
uses <linux/gpio/consumer.h> or <linux/gpio/driver.h> instead. uses <linux/gpio/consumer.h> or <linux/gpio/driver.h> instead.
Get rid of <linux/gpio.h>
This legacy header is a one stop shop for anything GPIO is closely tied
to the global GPIO numberspace. The endgame of the above refactorings will
be the removal of <linux/gpio.h> and from that point only the specialized
headers under <linux/gpio/*.h> will be used. This requires all the above to
be completed and is expected to take a long time.
Collect drivers Collect drivers
Collect GPIO drivers from arch/* and other places that should be placed Collect GPIO drivers from arch/* and other places that should be placed
...@@ -109,7 +140,7 @@ try to cover any generic kind of irqchip cascaded from a GPIO. ...@@ -109,7 +140,7 @@ try to cover any generic kind of irqchip cascaded from a GPIO.
int irq; /* from platform etc */ int irq; /* from platform etc */
struct my_gpio *g; struct my_gpio *g;
struct gpio_irq_chip *girq struct gpio_irq_chip *girq;
/* Set up the irqchip dynamically */ /* Set up the irqchip dynamically */
g->irq.name = "my_gpio_irq"; g->irq.name = "my_gpio_irq";
...@@ -137,9 +168,14 @@ try to cover any generic kind of irqchip cascaded from a GPIO. ...@@ -137,9 +168,14 @@ try to cover any generic kind of irqchip cascaded from a GPIO.
- Look over and identify any remaining easily converted drivers and - Look over and identify any remaining easily converted drivers and
dry-code conversions to gpiolib irqchip for maintainers to test dry-code conversions to gpiolib irqchip for maintainers to test
- Support generic hierarchical GPIO interrupts: these are for the - Drop gpiochip_set_chained_irqchip() when all the chained irqchips
non-cascading case where there is one IRQ per GPIO line, there is have been converted to the above infrastructure.
currently no common infrastructure for this.
- Add more infrastructure to make it possible to also pass a threaded
irqchip in struct gpio_irq_chip.
- Drop gpiochip_irqchip_add_nested() when all the chained irqchips
have been converted to the above infrastructure.
Increase integration with pin control Increase integration with pin control
......
...@@ -266,7 +266,7 @@ static int altera_gpio_probe(struct platform_device *pdev) ...@@ -266,7 +266,7 @@ static int altera_gpio_probe(struct platform_device *pdev)
altera_gc->mmchip.gc.owner = THIS_MODULE; altera_gc->mmchip.gc.owner = THIS_MODULE;
altera_gc->mmchip.gc.parent = &pdev->dev; altera_gc->mmchip.gc.parent = &pdev->dev;
altera_gc->mapped_irq = platform_get_irq(pdev, 0); altera_gc->mapped_irq = platform_get_irq_optional(pdev, 0);
if (altera_gc->mapped_irq < 0) if (altera_gc->mapped_irq < 0)
goto skip_irq; goto skip_irq;
......
...@@ -391,7 +391,7 @@ static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio, ...@@ -391,7 +391,7 @@ static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio,
gpio->irq = rc; gpio->irq = rc;
/* Disable IRQ and clear Interrupt status registers for all SPGIO Pins. */ /* Disable IRQ and clear Interrupt status registers for all SGPIO Pins. */
for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) { for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) {
bank = &aspeed_sgpio_banks[i]; bank = &aspeed_sgpio_banks[i];
/* disable irq enable bits */ /* disable irq enable bits */
......
...@@ -978,7 +978,7 @@ static int aspeed_gpio_set_config(struct gpio_chip *chip, unsigned int offset, ...@@ -978,7 +978,7 @@ static int aspeed_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
} }
/** /**
* aspeed_gpio_copro_set_ops - Sets the callbacks used for handhsaking with * aspeed_gpio_copro_set_ops - Sets the callbacks used for handshaking with
* the coprocessor for shared GPIO banks * the coprocessor for shared GPIO banks
* @ops: The callbacks * @ops: The callbacks
* @data: Pointer passed back to the callbacks * @data: Pointer passed back to the callbacks
......
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/irqdomain.h> #include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h> #include <linux/irqchip/chained_irq.h>
...@@ -586,11 +585,18 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev) ...@@ -586,11 +585,18 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev)
kona_gpio->gpio_chip = template_chip; kona_gpio->gpio_chip = template_chip;
chip = &kona_gpio->gpio_chip; chip = &kona_gpio->gpio_chip;
kona_gpio->num_bank = of_irq_count(dev->of_node); ret = platform_irq_count(pdev);
if (kona_gpio->num_bank == 0) { if (!ret) {
dev_err(dev, "Couldn't determine # GPIO banks\n"); dev_err(dev, "Couldn't determine # GPIO banks\n");
return -ENOENT; return -ENOENT;
} else if (ret < 0) {
if (ret != -EPROBE_DEFER)
dev_err(dev, "Couldn't determine GPIO banks: (%pe)\n",
ERR_PTR(ret));
return ret;
} }
kona_gpio->num_bank = ret;
if (kona_gpio->num_bank > GPIO_MAX_BANK_NUM) { if (kona_gpio->num_bank > GPIO_MAX_BANK_NUM) {
dev_err(dev, "Too many GPIO banks configured (max=%d)\n", dev_err(dev, "Too many GPIO banks configured (max=%d)\n",
GPIO_MAX_BANK_NUM); GPIO_MAX_BANK_NUM);
......
...@@ -64,11 +64,11 @@ static int creg_gpio_validate_pg(struct device *dev, struct creg_gpio *hcg, ...@@ -64,11 +64,11 @@ static int creg_gpio_validate_pg(struct device *dev, struct creg_gpio *hcg,
if (layout->bit_per_gpio[i] < 1 || layout->bit_per_gpio[i] > 8) if (layout->bit_per_gpio[i] < 1 || layout->bit_per_gpio[i] > 8)
return -EINVAL; return -EINVAL;
/* Check that on valiue fits it's placeholder */ /* Check that on value fits its placeholder */
if (GENMASK(31, layout->bit_per_gpio[i]) & layout->on[i]) if (GENMASK(31, layout->bit_per_gpio[i]) & layout->on[i])
return -EINVAL; return -EINVAL;
/* Check that off valiue fits it's placeholder */ /* Check that off value fits its placeholder */
if (GENMASK(31, layout->bit_per_gpio[i]) & layout->off[i]) if (GENMASK(31, layout->bit_per_gpio[i]) & layout->off[i])
return -EINVAL; return -EINVAL;
......
...@@ -253,17 +253,16 @@ static int grgpio_irq_map(struct irq_domain *d, unsigned int irq, ...@@ -253,17 +253,16 @@ static int grgpio_irq_map(struct irq_domain *d, unsigned int irq,
lirq->irq = irq; lirq->irq = irq;
uirq = &priv->uirqs[lirq->index]; uirq = &priv->uirqs[lirq->index];
if (uirq->refcnt == 0) { if (uirq->refcnt == 0) {
spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
ret = request_irq(uirq->uirq, grgpio_irq_handler, 0, ret = request_irq(uirq->uirq, grgpio_irq_handler, 0,
dev_name(priv->dev), priv); dev_name(priv->dev), priv);
if (ret) { if (ret) {
dev_err(priv->dev, dev_err(priv->dev,
"Could not request underlying irq %d\n", "Could not request underlying irq %d\n",
uirq->uirq); uirq->uirq);
spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
return ret; return ret;
} }
spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
} }
uirq->refcnt++; uirq->refcnt++;
...@@ -309,8 +308,11 @@ static void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq) ...@@ -309,8 +308,11 @@ static void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq)
if (index >= 0) { if (index >= 0) {
uirq = &priv->uirqs[lirq->index]; uirq = &priv->uirqs[lirq->index];
uirq->refcnt--; uirq->refcnt--;
if (uirq->refcnt == 0) if (uirq->refcnt == 0) {
spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
free_irq(uirq->uirq, priv); free_irq(uirq->uirq, priv);
return;
}
} }
spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
...@@ -433,12 +435,9 @@ static int grgpio_probe(struct platform_device *ofdev) ...@@ -433,12 +435,9 @@ static int grgpio_probe(struct platform_device *ofdev)
static int grgpio_remove(struct platform_device *ofdev) static int grgpio_remove(struct platform_device *ofdev)
{ {
struct grgpio_priv *priv = platform_get_drvdata(ofdev); struct grgpio_priv *priv = platform_get_drvdata(ofdev);
unsigned long flags;
int i; int i;
int ret = 0; int ret = 0;
spin_lock_irqsave(&priv->gc.bgpio_lock, flags);
if (priv->domain) { if (priv->domain) {
for (i = 0; i < GRGPIO_MAX_NGPIO; i++) { for (i = 0; i < GRGPIO_MAX_NGPIO; i++) {
if (priv->uirqs[i].refcnt != 0) { if (priv->uirqs[i].refcnt != 0) {
...@@ -454,8 +453,6 @@ static int grgpio_remove(struct platform_device *ofdev) ...@@ -454,8 +453,6 @@ static int grgpio_remove(struct platform_device *ofdev)
irq_domain_remove(priv->domain); irq_domain_remove(priv->domain);
out: out:
spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags);
return ret; return ret;
} }
......
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019 Bootlin
* Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
*/
#include <linux/err.h>
#include <linux/gpio/driver.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#define LOGICVC_CTRL_REG 0x40
#define LOGICVC_CTRL_GPIO_SHIFT 11
#define LOGICVC_CTRL_GPIO_BITS 5
#define LOGICVC_POWER_CTRL_REG 0x78
#define LOGICVC_POWER_CTRL_GPIO_SHIFT 0
#define LOGICVC_POWER_CTRL_GPIO_BITS 4
struct logicvc_gpio {
struct gpio_chip chip;
struct regmap *regmap;
};
static void logicvc_gpio_offset(struct logicvc_gpio *logicvc, unsigned offset,
unsigned int *reg, unsigned int *bit)
{
if (offset >= LOGICVC_CTRL_GPIO_BITS) {
*reg = LOGICVC_POWER_CTRL_REG;
/* To the (virtual) power ctrl offset. */
offset -= LOGICVC_CTRL_GPIO_BITS;
/* To the actual bit offset in reg. */
offset += LOGICVC_POWER_CTRL_GPIO_SHIFT;
} else {
*reg = LOGICVC_CTRL_REG;
/* To the actual bit offset in reg. */
offset += LOGICVC_CTRL_GPIO_SHIFT;
}
*bit = BIT(offset);
}
static int logicvc_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct logicvc_gpio *logicvc = gpiochip_get_data(chip);
unsigned int reg, bit, value;
int ret;
logicvc_gpio_offset(logicvc, offset, &reg, &bit);
ret = regmap_read(logicvc->regmap, reg, &value);
if (ret)
return ret;
return !!(value & bit);
}
static void logicvc_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct logicvc_gpio *logicvc = gpiochip_get_data(chip);
unsigned int reg, bit;
logicvc_gpio_offset(logicvc, offset, &reg, &bit);
regmap_update_bits(logicvc->regmap, reg, bit, value ? bit : 0);
}
static int logicvc_gpio_direction_output(struct gpio_chip *chip,
unsigned offset, int value)
{
/* Pins are always configured as output, so just set the value. */
logicvc_gpio_set(chip, offset, value);
return 0;
}
static struct regmap_config logicvc_gpio_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.name = "logicvc-gpio",
};
static int logicvc_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *of_node = dev->of_node;
struct logicvc_gpio *logicvc;
int ret;
logicvc = devm_kzalloc(dev, sizeof(*logicvc), GFP_KERNEL);
if (!logicvc)
return -ENOMEM;
/* Try to get regmap from parent first. */
logicvc->regmap = syscon_node_to_regmap(of_node->parent);
/* Grab our own regmap if that fails. */
if (IS_ERR(logicvc->regmap)) {
struct resource res;
void __iomem *base;
ret = of_address_to_resource(of_node, 0, &res);
if (ret) {
dev_err(dev, "Failed to get resource from address\n");
return ret;
}
base = devm_ioremap_resource(dev, &res);
if (IS_ERR(base)) {
dev_err(dev, "Failed to map I/O base\n");
return PTR_ERR(base);
}
logicvc_gpio_regmap_config.max_register = resource_size(&res) -
logicvc_gpio_regmap_config.reg_stride;
logicvc->regmap =
devm_regmap_init_mmio(dev, base,
&logicvc_gpio_regmap_config);
if (IS_ERR(logicvc->regmap)) {
dev_err(dev, "Failed to create regmap for I/O\n");
return PTR_ERR(logicvc->regmap);
}
}
logicvc->chip.parent = dev;
logicvc->chip.owner = THIS_MODULE;
logicvc->chip.label = dev_name(dev);
logicvc->chip.base = -1;
logicvc->chip.ngpio = LOGICVC_CTRL_GPIO_BITS +
LOGICVC_POWER_CTRL_GPIO_BITS;
logicvc->chip.get = logicvc_gpio_get;
logicvc->chip.set = logicvc_gpio_set;
logicvc->chip.direction_output = logicvc_gpio_direction_output;
platform_set_drvdata(pdev, logicvc);
return devm_gpiochip_add_data(dev, &logicvc->chip, logicvc);
}
static const struct of_device_id logicivc_gpio_of_table[] = {
{
.compatible = "xylon,logicvc-3.02.a-gpio",
},
{ }
};
MODULE_DEVICE_TABLE(of, logicivc_gpio_of_table);
static struct platform_driver logicvc_gpio_driver = {
.driver = {
.name = "gpio-logicvc",
.of_match_table = logicivc_gpio_of_table,
},
.probe = logicvc_gpio_probe,
};
module_platform_driver(logicvc_gpio_driver);
MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>");
MODULE_DESCRIPTION("Xylon LogiCVC GPIO driver");
MODULE_LICENSE("GPL");
// SPDX-License-Identifier: GPL-2.0+ // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* GPIO Testing Device Driver * GPIO Testing Device Driver
* *
...@@ -7,18 +7,18 @@ ...@@ -7,18 +7,18 @@
* Copyright (C) 2017 Bartosz Golaszewski <brgl@bgdev.pl> * Copyright (C) 2017 Bartosz Golaszewski <brgl@bgdev.pl>
*/ */
#include <linux/init.h> #include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/consumer.h> #include <linux/gpio/consumer.h>
#include <linux/platform_device.h> #include <linux/gpio/driver.h>
#include <linux/slab.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/irq_sim.h> #include <linux/irq_sim.h>
#include <linux/debugfs.h> #include <linux/module.h>
#include <linux/uaccess.h> #include <linux/platform_device.h>
#include <linux/property.h> #include <linux/property.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "gpiolib.h" #include "gpiolib.h"
......
...@@ -296,6 +296,7 @@ static const struct mpc8xxx_gpio_devtype mpc512x_gpio_devtype = { ...@@ -296,6 +296,7 @@ static const struct mpc8xxx_gpio_devtype mpc512x_gpio_devtype = {
static const struct mpc8xxx_gpio_devtype ls1028a_gpio_devtype = { static const struct mpc8xxx_gpio_devtype ls1028a_gpio_devtype = {
.gpio_dir_in_init = ls1028a_gpio_dir_in_init, .gpio_dir_in_init = ls1028a_gpio_dir_in_init,
.irq_set_type = mpc8xxx_irq_set_type,
}; };
static const struct mpc8xxx_gpio_devtype mpc5125_gpio_devtype = { static const struct mpc8xxx_gpio_devtype mpc5125_gpio_devtype = {
......
...@@ -46,7 +46,6 @@ ...@@ -46,7 +46,6 @@
#include <linux/irqdomain.h> #include <linux/irqdomain.h>
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/pinctrl/consumer.h> #include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pwm.h> #include <linux/pwm.h>
...@@ -432,6 +431,7 @@ static void mvebu_gpio_edge_irq_unmask(struct irq_data *d) ...@@ -432,6 +431,7 @@ static void mvebu_gpio_edge_irq_unmask(struct irq_data *d)
u32 mask = d->mask; u32 mask = d->mask;
irq_gc_lock(gc); irq_gc_lock(gc);
mvebu_gpio_write_edge_cause(mvchip, ~mask);
ct->mask_cache_priv |= mask; ct->mask_cache_priv |= mask;
mvebu_gpio_write_edge_mask(mvchip, ct->mask_cache_priv); mvebu_gpio_write_edge_mask(mvchip, ct->mask_cache_priv);
irq_gc_unlock(gc); irq_gc_unlock(gc);
...@@ -1102,7 +1102,11 @@ static int mvebu_gpio_probe(struct platform_device *pdev) ...@@ -1102,7 +1102,11 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
soc_variant = MVEBU_GPIO_SOC_VARIANT_ORION; soc_variant = MVEBU_GPIO_SOC_VARIANT_ORION;
/* Some gpio controllers do not provide irq support */ /* Some gpio controllers do not provide irq support */
have_irqs = of_irq_count(np) != 0; err = platform_irq_count(pdev);
if (err < 0)
return err;
have_irqs = err != 0;
mvchip = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_gpio_chip), mvchip = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_gpio_chip),
GFP_KERNEL); GFP_KERNEL);
......
...@@ -764,8 +764,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) ...@@ -764,8 +764,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base)
ret = devm_request_threaded_irq(&client->dev, client->irq, ret = devm_request_threaded_irq(&client->dev, client->irq,
NULL, pca953x_irq_handler, NULL, pca953x_irq_handler,
IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_ONESHOT | IRQF_SHARED,
IRQF_SHARED,
dev_name(&client->dev), chip); dev_name(&client->dev), chip);
if (ret) { if (ret) {
dev_err(&client->dev, "failed to request irq %d\n", dev_err(&client->dev, "failed to request irq %d\n",
...@@ -855,8 +854,6 @@ static int device_pca957x_init(struct pca953x_chip *chip, u32 invert) ...@@ -855,8 +854,6 @@ static int device_pca957x_init(struct pca953x_chip *chip, u32 invert)
return ret; return ret;
} }
static const struct of_device_id pca953x_dt_ids[];
static int pca953x_probe(struct i2c_client *client, static int pca953x_probe(struct i2c_client *client,
const struct i2c_device_id *i2c_id) const struct i2c_device_id *i2c_id)
{ {
......
...@@ -244,7 +244,6 @@ static struct platform_driver sama5d2_piobu_driver = { ...@@ -244,7 +244,6 @@ static struct platform_driver sama5d2_piobu_driver = {
module_platform_driver(sama5d2_piobu_driver); module_platform_driver(sama5d2_piobu_driver);
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("SAMA5D2 PIOBU controller driver"); MODULE_DESCRIPTION("SAMA5D2 PIOBU controller driver");
MODULE_AUTHOR("Andrei Stefanescu <andrei.stefanescu@microchip.com>"); MODULE_AUTHOR("Andrei Stefanescu <andrei.stefanescu@microchip.com>");
...@@ -243,4 +243,3 @@ static struct platform_driver tb10x_gpio_driver = { ...@@ -243,4 +243,3 @@ static struct platform_driver tb10x_gpio_driver = {
module_platform_driver(tb10x_gpio_driver); module_platform_driver(tb10x_gpio_driver);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("tb10x gpio."); MODULE_DESCRIPTION("tb10x gpio.");
MODULE_VERSION("0.0.1");
...@@ -96,12 +96,12 @@ struct tegra_gpio_info { ...@@ -96,12 +96,12 @@ struct tegra_gpio_info {
static inline void tegra_gpio_writel(struct tegra_gpio_info *tgi, static inline void tegra_gpio_writel(struct tegra_gpio_info *tgi,
u32 val, u32 reg) u32 val, u32 reg)
{ {
__raw_writel(val, tgi->regs + reg); writel_relaxed(val, tgi->regs + reg);
} }
static inline u32 tegra_gpio_readl(struct tegra_gpio_info *tgi, u32 reg) static inline u32 tegra_gpio_readl(struct tegra_gpio_info *tgi, u32 reg)
{ {
return __raw_readl(tgi->regs + reg); return readl_relaxed(tgi->regs + reg);
} }
static unsigned int tegra_gpio_compose(unsigned int bank, unsigned int port, static unsigned int tegra_gpio_compose(unsigned int bank, unsigned int port,
...@@ -416,11 +416,8 @@ static void tegra_gpio_irq_handler(struct irq_desc *desc) ...@@ -416,11 +416,8 @@ static void tegra_gpio_irq_handler(struct irq_desc *desc)
static int tegra_gpio_resume(struct device *dev) static int tegra_gpio_resume(struct device *dev)
{ {
struct tegra_gpio_info *tgi = dev_get_drvdata(dev); struct tegra_gpio_info *tgi = dev_get_drvdata(dev);
unsigned long flags;
unsigned int b, p; unsigned int b, p;
local_irq_save(flags);
for (b = 0; b < tgi->bank_count; b++) { for (b = 0; b < tgi->bank_count; b++) {
struct tegra_gpio_bank *bank = &tgi->bank_info[b]; struct tegra_gpio_bank *bank = &tgi->bank_info[b];
...@@ -448,17 +445,14 @@ static int tegra_gpio_resume(struct device *dev) ...@@ -448,17 +445,14 @@ static int tegra_gpio_resume(struct device *dev)
} }
} }
local_irq_restore(flags);
return 0; return 0;
} }
static int tegra_gpio_suspend(struct device *dev) static int tegra_gpio_suspend(struct device *dev)
{ {
struct tegra_gpio_info *tgi = dev_get_drvdata(dev); struct tegra_gpio_info *tgi = dev_get_drvdata(dev);
unsigned long flags;
unsigned int b, p; unsigned int b, p;
local_irq_save(flags);
for (b = 0; b < tgi->bank_count; b++) { for (b = 0; b < tgi->bank_count; b++) {
struct tegra_gpio_bank *bank = &tgi->bank_info[b]; struct tegra_gpio_bank *bank = &tgi->bank_info[b];
...@@ -488,7 +482,7 @@ static int tegra_gpio_suspend(struct device *dev) ...@@ -488,7 +482,7 @@ static int tegra_gpio_suspend(struct device *dev)
GPIO_INT_ENB(tgi, gpio)); GPIO_INT_ENB(tgi, gpio));
} }
} }
local_irq_restore(flags);
return 0; return 0;
} }
...@@ -497,6 +491,11 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) ...@@ -497,6 +491,11 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable)
struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d);
unsigned int gpio = d->hwirq; unsigned int gpio = d->hwirq;
u32 port, bit, mask; u32 port, bit, mask;
int err;
err = irq_set_irq_wake(bank->irq, enable);
if (err)
return err;
port = GPIO_PORT(gpio); port = GPIO_PORT(gpio);
bit = GPIO_BIT(gpio); bit = GPIO_BIT(gpio);
...@@ -507,7 +506,7 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) ...@@ -507,7 +506,7 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable)
else else
bank->wake_enb[port] &= ~mask; bank->wake_enb[port] &= ~mask;
return irq_set_irq_wake(bank->irq, enable); return 0;
} }
#endif #endif
...@@ -557,7 +556,7 @@ static inline void tegra_gpio_debuginit(struct tegra_gpio_info *tgi) ...@@ -557,7 +556,7 @@ static inline void tegra_gpio_debuginit(struct tegra_gpio_info *tgi)
#endif #endif
static const struct dev_pm_ops tegra_gpio_pm_ops = { static const struct dev_pm_ops tegra_gpio_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume) SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume)
}; };
static int tegra_gpio_probe(struct platform_device *pdev) static int tegra_gpio_probe(struct platform_device *pdev)
......
...@@ -448,17 +448,24 @@ static int tegra186_gpio_irq_domain_translate(struct irq_domain *domain, ...@@ -448,17 +448,24 @@ static int tegra186_gpio_irq_domain_translate(struct irq_domain *domain,
return 0; return 0;
} }
static void tegra186_gpio_populate_parent_fwspec(struct gpio_chip *chip, static void *tegra186_gpio_populate_parent_fwspec(struct gpio_chip *chip,
struct irq_fwspec *fwspec,
unsigned int parent_hwirq, unsigned int parent_hwirq,
unsigned int parent_type) unsigned int parent_type)
{ {
struct tegra_gpio *gpio = gpiochip_get_data(chip); struct tegra_gpio *gpio = gpiochip_get_data(chip);
struct irq_fwspec *fwspec;
fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
if (!fwspec)
return NULL;
fwspec->fwnode = chip->irq.parent_domain->fwnode;
fwspec->param_count = 3; fwspec->param_count = 3;
fwspec->param[0] = gpio->soc->instance; fwspec->param[0] = gpio->soc->instance;
fwspec->param[1] = parent_hwirq; fwspec->param[1] = parent_hwirq;
fwspec->param[2] = parent_type; fwspec->param[2] = parent_type;
return fwspec;
} }
static int tegra186_gpio_child_to_parent_hwirq(struct gpio_chip *chip, static int tegra186_gpio_child_to_parent_hwirq(struct gpio_chip *chip,
...@@ -621,7 +628,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) ...@@ -621,7 +628,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
irq->chip = &gpio->intc; irq->chip = &gpio->intc;
irq->fwnode = of_node_to_fwnode(pdev->dev.of_node); irq->fwnode = of_node_to_fwnode(pdev->dev.of_node);
irq->child_to_parent_hwirq = tegra186_gpio_child_to_parent_hwirq; irq->child_to_parent_hwirq = tegra186_gpio_child_to_parent_hwirq;
irq->populate_parent_fwspec = tegra186_gpio_populate_parent_fwspec; irq->populate_parent_alloc_arg = tegra186_gpio_populate_parent_fwspec;
irq->child_offset_to_irq = tegra186_gpio_child_offset_to_irq; irq->child_offset_to_irq = tegra186_gpio_child_offset_to_irq;
irq->child_irq_domain_ops.translate = tegra186_gpio_irq_domain_translate; irq->child_irq_domain_ops.translate = tegra186_gpio_irq_domain_translate;
irq->handler = handle_simple_irq; irq->handler = handle_simple_irq;
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <asm-generic/msi.h>
#define GPIO_RX_DAT 0x0 #define GPIO_RX_DAT 0x0
...@@ -53,7 +54,6 @@ struct thunderx_line { ...@@ -53,7 +54,6 @@ struct thunderx_line {
struct thunderx_gpio { struct thunderx_gpio {
struct gpio_chip chip; struct gpio_chip chip;
u8 __iomem *register_base; u8 __iomem *register_base;
struct irq_domain *irqd;
struct msix_entry *msix_entries; /* per line MSI-X */ struct msix_entry *msix_entries; /* per line MSI-X */
struct thunderx_line *line_entries; /* per line irq info */ struct thunderx_line *line_entries; /* per line irq info */
raw_spinlock_t lock; raw_spinlock_t lock;
...@@ -286,54 +286,60 @@ static void thunderx_gpio_set_multiple(struct gpio_chip *chip, ...@@ -286,54 +286,60 @@ static void thunderx_gpio_set_multiple(struct gpio_chip *chip,
} }
} }
static void thunderx_gpio_irq_ack(struct irq_data *data) static void thunderx_gpio_irq_ack(struct irq_data *d)
{ {
struct thunderx_line *txline = irq_data_get_irq_chip_data(data); struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
writeq(GPIO_INTR_INTR, writeq(GPIO_INTR_INTR,
txline->txgpio->register_base + intr_reg(txline->line)); txgpio->register_base + intr_reg(irqd_to_hwirq(d)));
} }
static void thunderx_gpio_irq_mask(struct irq_data *data) static void thunderx_gpio_irq_mask(struct irq_data *d)
{ {
struct thunderx_line *txline = irq_data_get_irq_chip_data(data); struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
writeq(GPIO_INTR_ENA_W1C, writeq(GPIO_INTR_ENA_W1C,
txline->txgpio->register_base + intr_reg(txline->line)); txgpio->register_base + intr_reg(irqd_to_hwirq(d)));
} }
static void thunderx_gpio_irq_mask_ack(struct irq_data *data) static void thunderx_gpio_irq_mask_ack(struct irq_data *d)
{ {
struct thunderx_line *txline = irq_data_get_irq_chip_data(data); struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
writeq(GPIO_INTR_ENA_W1C | GPIO_INTR_INTR, writeq(GPIO_INTR_ENA_W1C | GPIO_INTR_INTR,
txline->txgpio->register_base + intr_reg(txline->line)); txgpio->register_base + intr_reg(irqd_to_hwirq(d)));
} }
static void thunderx_gpio_irq_unmask(struct irq_data *data) static void thunderx_gpio_irq_unmask(struct irq_data *d)
{ {
struct thunderx_line *txline = irq_data_get_irq_chip_data(data); struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
writeq(GPIO_INTR_ENA_W1S, writeq(GPIO_INTR_ENA_W1S,
txline->txgpio->register_base + intr_reg(txline->line)); txgpio->register_base + intr_reg(irqd_to_hwirq(d)));
} }
static int thunderx_gpio_irq_set_type(struct irq_data *data, static int thunderx_gpio_irq_set_type(struct irq_data *d,
unsigned int flow_type) unsigned int flow_type)
{ {
struct thunderx_line *txline = irq_data_get_irq_chip_data(data); struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct thunderx_gpio *txgpio = txline->txgpio; struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
struct thunderx_line *txline =
&txgpio->line_entries[irqd_to_hwirq(d)];
u64 bit_cfg; u64 bit_cfg;
irqd_set_trigger_type(data, flow_type); irqd_set_trigger_type(d, flow_type);
bit_cfg = txline->fil_bits | GPIO_BIT_CFG_INT_EN; bit_cfg = txline->fil_bits | GPIO_BIT_CFG_INT_EN;
if (flow_type & IRQ_TYPE_EDGE_BOTH) { if (flow_type & IRQ_TYPE_EDGE_BOTH) {
irq_set_handler_locked(data, handle_fasteoi_ack_irq); irq_set_handler_locked(d, handle_fasteoi_ack_irq);
bit_cfg |= GPIO_BIT_CFG_INT_TYPE; bit_cfg |= GPIO_BIT_CFG_INT_TYPE;
} else { } else {
irq_set_handler_locked(data, handle_fasteoi_mask_irq); irq_set_handler_locked(d, handle_fasteoi_mask_irq);
} }
raw_spin_lock(&txgpio->lock); raw_spin_lock(&txgpio->lock);
...@@ -362,33 +368,6 @@ static void thunderx_gpio_irq_disable(struct irq_data *data) ...@@ -362,33 +368,6 @@ static void thunderx_gpio_irq_disable(struct irq_data *data)
irq_chip_disable_parent(data); irq_chip_disable_parent(data);
} }
static int thunderx_gpio_irq_request_resources(struct irq_data *data)
{
struct thunderx_line *txline = irq_data_get_irq_chip_data(data);
struct thunderx_gpio *txgpio = txline->txgpio;
int r;
r = gpiochip_lock_as_irq(&txgpio->chip, txline->line);
if (r)
return r;
r = irq_chip_request_resources_parent(data);
if (r)
gpiochip_unlock_as_irq(&txgpio->chip, txline->line);
return r;
}
static void thunderx_gpio_irq_release_resources(struct irq_data *data)
{
struct thunderx_line *txline = irq_data_get_irq_chip_data(data);
struct thunderx_gpio *txgpio = txline->txgpio;
irq_chip_release_resources_parent(data);
gpiochip_unlock_as_irq(&txgpio->chip, txline->line);
}
/* /*
* Interrupts are chained from underlying MSI-X vectors. We have * Interrupts are chained from underlying MSI-X vectors. We have
* these irq_chip functions to be able to handle level triggering * these irq_chip functions to be able to handle level triggering
...@@ -405,48 +384,42 @@ static struct irq_chip thunderx_gpio_irq_chip = { ...@@ -405,48 +384,42 @@ static struct irq_chip thunderx_gpio_irq_chip = {
.irq_unmask = thunderx_gpio_irq_unmask, .irq_unmask = thunderx_gpio_irq_unmask,
.irq_eoi = irq_chip_eoi_parent, .irq_eoi = irq_chip_eoi_parent,
.irq_set_affinity = irq_chip_set_affinity_parent, .irq_set_affinity = irq_chip_set_affinity_parent,
.irq_request_resources = thunderx_gpio_irq_request_resources,
.irq_release_resources = thunderx_gpio_irq_release_resources,
.irq_set_type = thunderx_gpio_irq_set_type, .irq_set_type = thunderx_gpio_irq_set_type,
.flags = IRQCHIP_SET_TYPE_MASKED .flags = IRQCHIP_SET_TYPE_MASKED
}; };
static int thunderx_gpio_irq_translate(struct irq_domain *d, static int thunderx_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
struct irq_fwspec *fwspec, unsigned int child,
irq_hw_number_t *hwirq, unsigned int child_type,
unsigned int *type) unsigned int *parent,
unsigned int *parent_type)
{ {
struct thunderx_gpio *txgpio = d->host_data; struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
struct irq_data *irqd;
unsigned int irq;
if (WARN_ON(fwspec->param_count < 2)) irq = txgpio->msix_entries[child].vector;
irqd = irq_domain_get_irq_data(gc->irq.parent_domain, irq);
if (!irqd)
return -EINVAL; return -EINVAL;
if (fwspec->param[0] >= txgpio->chip.ngpio) *parent = irqd_to_hwirq(irqd);
return -EINVAL; *parent_type = IRQ_TYPE_LEVEL_HIGH;
*hwirq = fwspec->param[0];
*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
return 0; return 0;
} }
static int thunderx_gpio_irq_alloc(struct irq_domain *d, unsigned int virq, static void *thunderx_gpio_populate_parent_alloc_info(struct gpio_chip *chip,
unsigned int nr_irqs, void *arg) unsigned int parent_hwirq,
unsigned int parent_type)
{ {
struct thunderx_line *txline = arg; msi_alloc_info_t *info;
return irq_domain_set_hwirq_and_chip(d, virq, txline->line,
&thunderx_gpio_irq_chip, txline);
}
static const struct irq_domain_ops thunderx_gpio_irqd_ops = {
.alloc = thunderx_gpio_irq_alloc,
.translate = thunderx_gpio_irq_translate
};
static int thunderx_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) info = kmalloc(sizeof(*info), GFP_KERNEL);
{ if (!info)
struct thunderx_gpio *txgpio = gpiochip_get_data(chip); return NULL;
return irq_find_mapping(txgpio->irqd, offset); info->hwirq = parent_hwirq;
return info;
} }
static int thunderx_gpio_probe(struct pci_dev *pdev, static int thunderx_gpio_probe(struct pci_dev *pdev,
...@@ -456,6 +429,7 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, ...@@ -456,6 +429,7 @@ static int thunderx_gpio_probe(struct pci_dev *pdev,
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct thunderx_gpio *txgpio; struct thunderx_gpio *txgpio;
struct gpio_chip *chip; struct gpio_chip *chip;
struct gpio_irq_chip *girq;
int ngpio, i; int ngpio, i;
int err = 0; int err = 0;
...@@ -500,8 +474,8 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, ...@@ -500,8 +474,8 @@ static int thunderx_gpio_probe(struct pci_dev *pdev,
} }
txgpio->msix_entries = devm_kcalloc(dev, txgpio->msix_entries = devm_kcalloc(dev,
ngpio, sizeof(struct msix_entry), ngpio, sizeof(struct msix_entry),
GFP_KERNEL); GFP_KERNEL);
if (!txgpio->msix_entries) { if (!txgpio->msix_entries) {
err = -ENOMEM; err = -ENOMEM;
goto out; goto out;
...@@ -542,27 +516,6 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, ...@@ -542,27 +516,6 @@ static int thunderx_gpio_probe(struct pci_dev *pdev,
if (err < 0) if (err < 0)
goto out; goto out;
/*
* Push GPIO specific irqdomain on hierarchy created as a side
* effect of the pci_enable_msix()
*/
txgpio->irqd = irq_domain_create_hierarchy(irq_get_irq_data(txgpio->msix_entries[0].vector)->domain,
0, 0, of_node_to_fwnode(dev->of_node),
&thunderx_gpio_irqd_ops, txgpio);
if (!txgpio->irqd) {
err = -ENOMEM;
goto out;
}
/* Push on irq_data and the domain for each line. */
for (i = 0; i < ngpio; i++) {
err = irq_domain_push_irq(txgpio->irqd,
txgpio->msix_entries[i].vector,
&txgpio->line_entries[i]);
if (err < 0)
dev_err(dev, "irq_domain_push_irq: %d\n", err);
}
chip->label = KBUILD_MODNAME; chip->label = KBUILD_MODNAME;
chip->parent = dev; chip->parent = dev;
chip->owner = THIS_MODULE; chip->owner = THIS_MODULE;
...@@ -577,11 +530,35 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, ...@@ -577,11 +530,35 @@ static int thunderx_gpio_probe(struct pci_dev *pdev,
chip->set = thunderx_gpio_set; chip->set = thunderx_gpio_set;
chip->set_multiple = thunderx_gpio_set_multiple; chip->set_multiple = thunderx_gpio_set_multiple;
chip->set_config = thunderx_gpio_set_config; chip->set_config = thunderx_gpio_set_config;
chip->to_irq = thunderx_gpio_to_irq; girq = &chip->irq;
girq->chip = &thunderx_gpio_irq_chip;
girq->fwnode = of_node_to_fwnode(dev->of_node);
girq->parent_domain =
irq_get_irq_data(txgpio->msix_entries[0].vector)->domain;
girq->child_to_parent_hwirq = thunderx_gpio_child_to_parent_hwirq;
girq->populate_parent_alloc_arg = thunderx_gpio_populate_parent_alloc_info;
girq->handler = handle_bad_irq;
girq->default_type = IRQ_TYPE_NONE;
err = devm_gpiochip_add_data(dev, chip, txgpio); err = devm_gpiochip_add_data(dev, chip, txgpio);
if (err) if (err)
goto out; goto out;
/* Push on irq_data and the domain for each line. */
for (i = 0; i < ngpio; i++) {
struct irq_fwspec fwspec;
fwspec.fwnode = of_node_to_fwnode(dev->of_node);
fwspec.param_count = 2;
fwspec.param[0] = i;
fwspec.param[1] = IRQ_TYPE_NONE;
err = irq_domain_push_irq(girq->domain,
txgpio->msix_entries[i].vector,
&fwspec);
if (err < 0)
dev_err(dev, "irq_domain_push_irq: %d\n", err);
}
dev_info(dev, "ThunderX GPIO: %d lines with base %d.\n", dev_info(dev, "ThunderX GPIO: %d lines with base %d.\n",
ngpio, chip->base); ngpio, chip->base);
return 0; return 0;
...@@ -596,10 +573,10 @@ static void thunderx_gpio_remove(struct pci_dev *pdev) ...@@ -596,10 +573,10 @@ static void thunderx_gpio_remove(struct pci_dev *pdev)
struct thunderx_gpio *txgpio = pci_get_drvdata(pdev); struct thunderx_gpio *txgpio = pci_get_drvdata(pdev);
for (i = 0; i < txgpio->chip.ngpio; i++) for (i = 0; i < txgpio->chip.ngpio; i++)
irq_domain_pop_irq(txgpio->irqd, irq_domain_pop_irq(txgpio->chip.irq.domain,
txgpio->msix_entries[i].vector); txgpio->msix_entries[i].vector);
irq_domain_remove(txgpio->irqd); irq_domain_remove(txgpio->chip.irq.domain);
pci_set_drvdata(pdev, NULL); pci_set_drvdata(pdev, NULL);
} }
......
...@@ -71,7 +71,7 @@ static inline u_int32_t gpio_o_bit(int i) ...@@ -71,7 +71,7 @@ static inline u_int32_t gpio_o_bit(int i)
return 1 << (i + 13); return 1 << (i + 13);
} }
/* Mapping betwee numeric GPIO ID and the actual GPIO hardware numbering: /* Mapping between numeric GPIO ID and the actual GPIO hardware numbering:
* 0..13 GPI 0..13 * 0..13 GPI 0..13
* 14..26 GPO 0..12 * 14..26 GPO 0..12
* 27..41 GPIO 0..14 * 27..41 GPIO 0..14
......
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019, Linaro Limited
#include <linux/module.h>
#include <linux/gpio/driver.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/of_device.h>
#define WCD_PIN_MASK(p) BIT(p - 1)
#define WCD_REG_DIR_CTL_OFFSET 0x42
#define WCD_REG_VAL_CTL_OFFSET 0x43
#define WCD934X_NPINS 5
struct wcd_gpio_data {
struct regmap *map;
struct gpio_chip chip;
};
static int wcd_gpio_get_direction(struct gpio_chip *chip, unsigned int pin)
{
struct wcd_gpio_data *data = gpiochip_get_data(chip);
unsigned int value;
int ret;
ret = regmap_read(data->map, WCD_REG_DIR_CTL_OFFSET, &value);
if (ret < 0)
return ret;
if (value & WCD_PIN_MASK(pin))
return GPIO_LINE_DIRECTION_OUT;
return GPIO_LINE_DIRECTION_IN;
}
static int wcd_gpio_direction_input(struct gpio_chip *chip, unsigned int pin)
{
struct wcd_gpio_data *data = gpiochip_get_data(chip);
return regmap_update_bits(data->map, WCD_REG_DIR_CTL_OFFSET,
WCD_PIN_MASK(pin), 0);
}
static int wcd_gpio_direction_output(struct gpio_chip *chip, unsigned int pin,
int val)
{
struct wcd_gpio_data *data = gpiochip_get_data(chip);
regmap_update_bits(data->map, WCD_REG_DIR_CTL_OFFSET,
WCD_PIN_MASK(pin), WCD_PIN_MASK(pin));
return regmap_update_bits(data->map, WCD_REG_VAL_CTL_OFFSET,
WCD_PIN_MASK(pin),
val ? WCD_PIN_MASK(pin) : 0);
}
static int wcd_gpio_get(struct gpio_chip *chip, unsigned int pin)
{
struct wcd_gpio_data *data = gpiochip_get_data(chip);
int value;
regmap_read(data->map, WCD_REG_VAL_CTL_OFFSET, &value);
return !!(value && WCD_PIN_MASK(pin));
}
static void wcd_gpio_set(struct gpio_chip *chip, unsigned int pin, int val)
{
wcd_gpio_direction_output(chip, pin, val);
}
static int wcd_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct wcd_gpio_data *data;
struct gpio_chip *chip;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->map = dev_get_regmap(dev->parent, NULL);
if (!data->map) {
dev_err(dev, "%s: failed to get regmap\n", __func__);
return -EINVAL;
}
chip = &data->chip;
chip->direction_input = wcd_gpio_direction_input;
chip->direction_output = wcd_gpio_direction_output;
chip->get_direction = wcd_gpio_get_direction;
chip->get = wcd_gpio_get;
chip->set = wcd_gpio_set;
chip->parent = dev;
chip->base = -1;
chip->ngpio = WCD934X_NPINS;
chip->label = dev_name(dev);
chip->of_gpio_n_cells = 2;
chip->can_sleep = false;
return devm_gpiochip_add_data(dev, chip, data);
}
static const struct of_device_id wcd_gpio_of_match[] = {
{ .compatible = "qcom,wcd9340-gpio" },
{ .compatible = "qcom,wcd9341-gpio" },
{ }
};
MODULE_DEVICE_TABLE(of, wcd_gpio_of_match);
static struct platform_driver wcd_gpio_driver = {
.driver = {
.name = "wcd934x-gpio",
.of_match_table = wcd_gpio_of_match,
},
.probe = wcd_gpio_probe,
};
module_platform_driver(wcd_gpio_driver);
MODULE_DESCRIPTION("Qualcomm Technologies, Inc WCD GPIO control driver");
MODULE_LICENSE("GPL v2");
...@@ -762,10 +762,9 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) ...@@ -762,10 +762,9 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
parent = &gdev->dev; parent = &gdev->dev;
/* use chip->base for the ID; it's already known to be unique */ /* use chip->base for the ID; it's already known to be unique */
dev = device_create_with_groups(&gpio_class, parent, dev = device_create_with_groups(&gpio_class, parent, MKDEV(0, 0), chip,
MKDEV(0, 0), gpiochip_groups, GPIOCHIP_NAME "%d",
chip, gpiochip_groups, chip->base);
"gpiochip%d", chip->base);
if (IS_ERR(dev)) if (IS_ERR(dev))
return PTR_ERR(dev); return PTR_ERR(dev);
......
...@@ -140,7 +140,7 @@ EXPORT_SYMBOL_GPL(gpio_to_desc); ...@@ -140,7 +140,7 @@ EXPORT_SYMBOL_GPL(gpio_to_desc);
* in the given chip for the specified hardware number. * in the given chip for the specified hardware number.
*/ */
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip,
u16 hwnum) unsigned int hwnum)
{ {
struct gpio_device *gdev = chip->gpiodev; struct gpio_device *gdev = chip->gpiodev;
...@@ -232,15 +232,15 @@ int gpiod_get_direction(struct gpio_desc *desc) ...@@ -232,15 +232,15 @@ int gpiod_get_direction(struct gpio_desc *desc)
return -ENOTSUPP; return -ENOTSUPP;
ret = chip->get_direction(chip, offset); ret = chip->get_direction(chip, offset);
if (ret > 0) { if (ret < 0)
/* GPIOF_DIR_IN, or other positive */ return ret;
/* GPIOF_DIR_IN or other positive, otherwise GPIOF_DIR_OUT */
if (ret > 0)
ret = 1; ret = 1;
clear_bit(FLAG_IS_OUT, &desc->flags);
} assign_bit(FLAG_IS_OUT, &desc->flags, !ret);
if (ret == 0) {
/* GPIOF_DIR_OUT */
set_bit(FLAG_IS_OUT, &desc->flags);
}
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(gpiod_get_direction); EXPORT_SYMBOL_GPL(gpiod_get_direction);
...@@ -492,15 +492,6 @@ static int linehandle_validate_flags(u32 flags) ...@@ -492,15 +492,6 @@ static int linehandle_validate_flags(u32 flags)
return 0; return 0;
} }
static void linehandle_configure_flag(unsigned long *flagsp,
u32 bit, bool active)
{
if (active)
set_bit(bit, flagsp);
else
clear_bit(bit, flagsp);
}
static long linehandle_set_config(struct linehandle_state *lh, static long linehandle_set_config(struct linehandle_state *lh,
void __user *ip) void __user *ip)
{ {
...@@ -522,22 +513,22 @@ static long linehandle_set_config(struct linehandle_state *lh, ...@@ -522,22 +513,22 @@ static long linehandle_set_config(struct linehandle_state *lh,
desc = lh->descs[i]; desc = lh->descs[i];
flagsp = &desc->flags; flagsp = &desc->flags;
linehandle_configure_flag(flagsp, FLAG_ACTIVE_LOW, assign_bit(FLAG_ACTIVE_LOW, flagsp,
lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW); lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW);
linehandle_configure_flag(flagsp, FLAG_OPEN_DRAIN, assign_bit(FLAG_OPEN_DRAIN, flagsp,
lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN); lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN);
linehandle_configure_flag(flagsp, FLAG_OPEN_SOURCE, assign_bit(FLAG_OPEN_SOURCE, flagsp,
lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE); lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE);
linehandle_configure_flag(flagsp, FLAG_PULL_UP, assign_bit(FLAG_PULL_UP, flagsp,
lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP); lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP);
linehandle_configure_flag(flagsp, FLAG_PULL_DOWN, assign_bit(FLAG_PULL_DOWN, flagsp,
lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN); lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN);
linehandle_configure_flag(flagsp, FLAG_BIAS_DISABLE, assign_bit(FLAG_BIAS_DISABLE, flagsp,
lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE); lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE);
/* /*
...@@ -686,14 +677,13 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) ...@@ -686,14 +677,13 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
/* Request each GPIO */ /* Request each GPIO */
for (i = 0; i < handlereq.lines; i++) { for (i = 0; i < handlereq.lines; i++) {
u32 offset = handlereq.lineoffsets[i]; u32 offset = handlereq.lineoffsets[i];
struct gpio_desc *desc; struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset);
if (offset >= gdev->ngpio) { if (IS_ERR(desc)) {
ret = -EINVAL; ret = PTR_ERR(desc);
goto out_free_descs; goto out_free_descs;
} }
desc = &gdev->descs[offset];
ret = gpiod_request(desc, lh->label); ret = gpiod_request(desc, lh->label);
if (ret) if (ret)
goto out_free_descs; goto out_free_descs;
...@@ -1018,8 +1008,9 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) ...@@ -1018,8 +1008,9 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
lflags = eventreq.handleflags; lflags = eventreq.handleflags;
eflags = eventreq.eventflags; eflags = eventreq.eventflags;
if (offset >= gdev->ngpio) desc = gpiochip_get_desc(gdev->chip, offset);
return -EINVAL; if (IS_ERR(desc))
return PTR_ERR(desc);
/* Return an error if a unknown flag is set */ /* Return an error if a unknown flag is set */
if ((lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) || if ((lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) ||
...@@ -1057,7 +1048,6 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) ...@@ -1057,7 +1048,6 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
} }
} }
desc = &gdev->descs[offset];
ret = gpiod_request(desc, le->label); ret = gpiod_request(desc, le->label);
if (ret) if (ret)
goto out_free_label; goto out_free_label;
...@@ -1184,10 +1174,11 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -1184,10 +1174,11 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
return -EFAULT; return -EFAULT;
if (lineinfo.line_offset >= gdev->ngpio)
return -EINVAL;
desc = &gdev->descs[lineinfo.line_offset]; desc = gpiochip_get_desc(chip, lineinfo.line_offset);
if (IS_ERR(desc))
return PTR_ERR(desc);
if (desc->name) { if (desc->name) {
strncpy(lineinfo.name, desc->name, strncpy(lineinfo.name, desc->name,
sizeof(lineinfo.name)); sizeof(lineinfo.name));
...@@ -1427,7 +1418,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, ...@@ -1427,7 +1418,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
ret = gdev->id; ret = gdev->id;
goto err_free_gdev; goto err_free_gdev;
} }
dev_set_name(&gdev->dev, "gpiochip%d", gdev->id); dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id);
device_initialize(&gdev->dev); device_initialize(&gdev->dev);
dev_set_drvdata(&gdev->dev, gdev); dev_set_drvdata(&gdev->dev, gdev);
if (chip->parent && chip->parent->driver) if (chip->parent && chip->parent->driver)
...@@ -1452,7 +1443,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, ...@@ -1452,7 +1443,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
if (chip->ngpio > FASTPATH_NGPIO) if (chip->ngpio > FASTPATH_NGPIO)
chip_warn(chip, "line cnt %u is greater than fast path cnt %u\n", chip_warn(chip, "line cnt %u is greater than fast path cnt %u\n",
chip->ngpio, FASTPATH_NGPIO); chip->ngpio, FASTPATH_NGPIO);
gdev->label = kstrdup_const(chip->label ?: "unknown", GFP_KERNEL); gdev->label = kstrdup_const(chip->label ?: "unknown", GFP_KERNEL);
if (!gdev->label) { if (!gdev->label) {
...@@ -1495,11 +1486,11 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, ...@@ -1495,11 +1486,11 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
goto err_free_label; goto err_free_label;
} }
spin_unlock_irqrestore(&gpio_lock, flags);
for (i = 0; i < chip->ngpio; i++) for (i = 0; i < chip->ngpio; i++)
gdev->descs[i].gdev = gdev; gdev->descs[i].gdev = gdev;
spin_unlock_irqrestore(&gpio_lock, flags);
#ifdef CONFIG_PINCTRL #ifdef CONFIG_PINCTRL
INIT_LIST_HEAD(&gdev->pin_ranges); INIT_LIST_HEAD(&gdev->pin_ranges);
#endif #endif
...@@ -1524,15 +1515,11 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, ...@@ -1524,15 +1515,11 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
struct gpio_desc *desc = &gdev->descs[i]; struct gpio_desc *desc = &gdev->descs[i];
if (chip->get_direction && gpiochip_line_is_valid(chip, i)) { if (chip->get_direction && gpiochip_line_is_valid(chip, i)) {
if (!chip->get_direction(chip, i)) assign_bit(FLAG_IS_OUT,
set_bit(FLAG_IS_OUT, &desc->flags); &desc->flags, !chip->get_direction(chip, i));
else
clear_bit(FLAG_IS_OUT, &desc->flags);
} else { } else {
if (!chip->direction_input) assign_bit(FLAG_IS_OUT,
set_bit(FLAG_IS_OUT, &desc->flags); &desc->flags, !chip->direction_input);
else
clear_bit(FLAG_IS_OUT, &desc->flags);
} }
} }
...@@ -2003,7 +1990,7 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, ...@@ -2003,7 +1990,7 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
irq_hw_number_t hwirq; irq_hw_number_t hwirq;
unsigned int type = IRQ_TYPE_NONE; unsigned int type = IRQ_TYPE_NONE;
struct irq_fwspec *fwspec = data; struct irq_fwspec *fwspec = data;
struct irq_fwspec parent_fwspec; void *parent_arg;
unsigned int parent_hwirq; unsigned int parent_hwirq;
unsigned int parent_type; unsigned int parent_type;
struct gpio_irq_chip *girq = &gc->irq; struct gpio_irq_chip *girq = &gc->irq;
...@@ -2019,7 +2006,7 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, ...@@ -2019,7 +2006,7 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
if (ret) if (ret)
return ret; return ret;
chip_info(gc, "allocate IRQ %d, hwirq %lu\n", irq, hwirq); chip_dbg(gc, "allocate IRQ %d, hwirq %lu\n", irq, hwirq);
ret = girq->child_to_parent_hwirq(gc, hwirq, type, ret = girq->child_to_parent_hwirq(gc, hwirq, type,
&parent_hwirq, &parent_type); &parent_hwirq, &parent_type);
...@@ -2027,7 +2014,7 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, ...@@ -2027,7 +2014,7 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
chip_err(gc, "can't look up hwirq %lu\n", hwirq); chip_err(gc, "can't look up hwirq %lu\n", hwirq);
return ret; return ret;
} }
chip_info(gc, "found parent hwirq %u\n", parent_hwirq); chip_dbg(gc, "found parent hwirq %u\n", parent_hwirq);
/* /*
* We set handle_bad_irq because the .set_type() should * We set handle_bad_irq because the .set_type() should
...@@ -2042,23 +2029,27 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, ...@@ -2042,23 +2029,27 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
NULL, NULL); NULL, NULL);
irq_set_probe(irq); irq_set_probe(irq);
/*
* Create a IRQ fwspec to send up to the parent irqdomain:
* specify the hwirq we address on the parent and tie it
* all together up the chain.
*/
parent_fwspec.fwnode = d->parent->fwnode;
/* This parent only handles asserted level IRQs */ /* This parent only handles asserted level IRQs */
girq->populate_parent_fwspec(gc, &parent_fwspec, parent_hwirq, parent_arg = girq->populate_parent_alloc_arg(gc, parent_hwirq, parent_type);
parent_type); if (!parent_arg)
chip_info(gc, "alloc_irqs_parent for %d parent hwirq %d\n", return -ENOMEM;
chip_dbg(gc, "alloc_irqs_parent for %d parent hwirq %d\n",
irq, parent_hwirq); irq, parent_hwirq);
ret = irq_domain_alloc_irqs_parent(d, irq, 1, &parent_fwspec); irq_set_lockdep_class(irq, gc->irq.lock_key, gc->irq.request_key);
ret = irq_domain_alloc_irqs_parent(d, irq, 1, parent_arg);
/*
* If the parent irqdomain is msi, the interrupts have already
* been allocated, so the EEXIST is good.
*/
if (irq_domain_is_msi(d->parent) && (ret == -EEXIST))
ret = 0;
if (ret) if (ret)
chip_err(gc, chip_err(gc,
"failed to allocate parent hwirq %d for hwirq %lu\n", "failed to allocate parent hwirq %d for hwirq %lu\n",
parent_hwirq, hwirq); parent_hwirq, hwirq);
kfree(parent_arg);
return ret; return ret;
} }
...@@ -2095,8 +2086,8 @@ static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc) ...@@ -2095,8 +2086,8 @@ static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc)
if (!gc->irq.child_offset_to_irq) if (!gc->irq.child_offset_to_irq)
gc->irq.child_offset_to_irq = gpiochip_child_offset_to_irq_noop; gc->irq.child_offset_to_irq = gpiochip_child_offset_to_irq_noop;
if (!gc->irq.populate_parent_fwspec) if (!gc->irq.populate_parent_alloc_arg)
gc->irq.populate_parent_fwspec = gc->irq.populate_parent_alloc_arg =
gpiochip_populate_parent_fwspec_twocell; gpiochip_populate_parent_fwspec_twocell;
gpiochip_hierarchy_setup_domain_ops(&gc->irq.child_irq_domain_ops); gpiochip_hierarchy_setup_domain_ops(&gc->irq.child_irq_domain_ops);
...@@ -2122,27 +2113,43 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) ...@@ -2122,27 +2113,43 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc)
return !!gc->irq.parent_domain; return !!gc->irq.parent_domain;
} }
void gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *chip, void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *chip,
struct irq_fwspec *fwspec,
unsigned int parent_hwirq, unsigned int parent_hwirq,
unsigned int parent_type) unsigned int parent_type)
{ {
struct irq_fwspec *fwspec;
fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
if (!fwspec)
return NULL;
fwspec->fwnode = chip->irq.parent_domain->fwnode;
fwspec->param_count = 2; fwspec->param_count = 2;
fwspec->param[0] = parent_hwirq; fwspec->param[0] = parent_hwirq;
fwspec->param[1] = parent_type; fwspec->param[1] = parent_type;
return fwspec;
} }
EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_twocell); EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_twocell);
void gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *chip, void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *chip,
struct irq_fwspec *fwspec,
unsigned int parent_hwirq, unsigned int parent_hwirq,
unsigned int parent_type) unsigned int parent_type)
{ {
struct irq_fwspec *fwspec;
fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
if (!fwspec)
return NULL;
fwspec->fwnode = chip->irq.parent_domain->fwnode;
fwspec->param_count = 4; fwspec->param_count = 4;
fwspec->param[0] = 0; fwspec->param[0] = 0;
fwspec->param[1] = parent_hwirq; fwspec->param[1] = parent_hwirq;
fwspec->param[2] = 0; fwspec->param[2] = 0;
fwspec->param[3] = parent_type; fwspec->param[3] = parent_type;
return fwspec;
} }
EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_fourcell); EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_fourcell);
...@@ -2998,7 +3005,8 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested); ...@@ -2998,7 +3005,8 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested);
* A pointer to the GPIO descriptor, or an ERR_PTR()-encoded negative error * A pointer to the GPIO descriptor, or an ERR_PTR()-encoded negative error
* code on failure. * code on failure.
*/ */
struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum, struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip,
unsigned int hwnum,
const char *label, const char *label,
enum gpio_lookup_flags lflags, enum gpio_lookup_flags lflags,
enum gpiod_flags dflags) enum gpiod_flags dflags)
...@@ -3050,25 +3058,13 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); ...@@ -3050,25 +3058,13 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc);
* rely on gpio_request() having been called beforehand. * rely on gpio_request() having been called beforehand.
*/ */
static int gpio_set_config(struct gpio_chip *gc, unsigned offset, static int gpio_set_config(struct gpio_chip *gc, unsigned int offset,
enum pin_config_param mode) enum pin_config_param mode)
{ {
unsigned long config; if (!gc->set_config)
unsigned arg; return -ENOTSUPP;
switch (mode) {
case PIN_CONFIG_BIAS_DISABLE:
case PIN_CONFIG_BIAS_PULL_DOWN:
case PIN_CONFIG_BIAS_PULL_UP:
arg = 1;
break;
default:
arg = 0;
}
config = PIN_CONF_PACKED(mode, arg); return gc->set_config(gc, offset, mode);
return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP;
} }
static int gpio_set_bias(struct gpio_chip *chip, struct gpio_desc *desc) static int gpio_set_bias(struct gpio_chip *chip, struct gpio_desc *desc)
...@@ -3302,15 +3298,9 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) ...@@ -3302,15 +3298,9 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)
VALIDATE_DESC(desc); VALIDATE_DESC(desc);
chip = desc->gdev->chip; chip = desc->gdev->chip;
if (!chip->set || !chip->set_config) {
gpiod_dbg(desc,
"%s: missing set() or set_config() operations\n",
__func__);
return -ENOTSUPP;
}
config = pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE, debounce); config = pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE, debounce);
return chip->set_config(chip, gpio_chip_hwgpio(desc), config); return gpio_set_config(chip, gpio_chip_hwgpio(desc), config);
} }
EXPORT_SYMBOL_GPL(gpiod_set_debounce); EXPORT_SYMBOL_GPL(gpiod_set_debounce);
...@@ -3334,10 +3324,7 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) ...@@ -3334,10 +3324,7 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory)
* Handle FLAG_TRANSITORY first, enabling queries to gpiolib for * Handle FLAG_TRANSITORY first, enabling queries to gpiolib for
* persistence state. * persistence state.
*/ */
if (transitory) assign_bit(FLAG_TRANSITORY, &desc->flags, transitory);
set_bit(FLAG_TRANSITORY, &desc->flags);
else
clear_bit(FLAG_TRANSITORY, &desc->flags);
/* If the driver supports it, set the persistence state now */ /* If the driver supports it, set the persistence state now */
chip = desc->gdev->chip; chip = desc->gdev->chip;
...@@ -3347,7 +3334,7 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) ...@@ -3347,7 +3334,7 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory)
packed = pinconf_to_config_packed(PIN_CONFIG_PERSIST_STATE, packed = pinconf_to_config_packed(PIN_CONFIG_PERSIST_STATE,
!transitory); !transitory);
gpio = gpio_chip_hwgpio(desc); gpio = gpio_chip_hwgpio(desc);
rc = chip->set_config(chip, gpio, packed); rc = gpio_set_config(chip, gpio, packed);
if (rc == -ENOTSUPP) { if (rc == -ENOTSUPP) {
dev_dbg(&desc->gdev->dev, "Persistence not supported for GPIO %d\n", dev_dbg(&desc->gdev->dev, "Persistence not supported for GPIO %d\n",
gpio); gpio);
...@@ -3804,10 +3791,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, ...@@ -3804,10 +3791,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
gpio_set_open_source_value_commit(desc, value); gpio_set_open_source_value_commit(desc, value);
} else { } else {
__set_bit(hwgpio, mask); __set_bit(hwgpio, mask);
if (value) __assign_bit(hwgpio, bits, value);
__set_bit(hwgpio, bits);
else
__clear_bit(hwgpio, bits);
count++; count++;
} }
i++; i++;
...@@ -5124,7 +5108,7 @@ static int __init gpiolib_dev_init(void) ...@@ -5124,7 +5108,7 @@ static int __init gpiolib_dev_init(void)
return ret; return ret;
} }
ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, "gpiochip"); ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, GPIOCHIP_NAME);
if (ret < 0) { if (ret < 0) {
pr_err("gpiolib: failed to allocate char dev region\n"); pr_err("gpiolib: failed to allocate char dev region\n");
bus_unregister(&gpio_bus_type); bus_unregister(&gpio_bus_type);
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/cdev.h> #include <linux/cdev.h>
#define GPIOCHIP_NAME "gpiochip"
/** /**
* struct gpio_device - internal state container for GPIO devices * struct gpio_device - internal state container for GPIO devices
* @id: numerical ID number for the GPIO chip * @id: numerical ID number for the GPIO chip
...@@ -78,7 +80,8 @@ struct gpio_array { ...@@ -78,7 +80,8 @@ struct gpio_array {
unsigned long invert_mask[]; unsigned long invert_mask[];
}; };
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum); struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip,
unsigned int hwnum);
int gpiod_get_array_value_complex(bool raw, bool can_sleep, int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size, unsigned int array_size,
struct gpio_desc **desc_array, struct gpio_desc **desc_array,
......
...@@ -1060,7 +1060,7 @@ static int pmic_gpio_probe(struct platform_device *pdev) ...@@ -1060,7 +1060,7 @@ static int pmic_gpio_probe(struct platform_device *pdev)
girq->fwnode = of_node_to_fwnode(state->dev->of_node); girq->fwnode = of_node_to_fwnode(state->dev->of_node);
girq->parent_domain = parent_domain; girq->parent_domain = parent_domain;
girq->child_to_parent_hwirq = pmic_gpio_child_to_parent_hwirq; girq->child_to_parent_hwirq = pmic_gpio_child_to_parent_hwirq;
girq->populate_parent_fwspec = gpiochip_populate_parent_fwspec_fourcell; girq->populate_parent_alloc_arg = gpiochip_populate_parent_fwspec_fourcell;
girq->child_offset_to_irq = pmic_gpio_child_offset_to_irq; girq->child_offset_to_irq = pmic_gpio_child_offset_to_irq;
girq->child_irq_domain_ops.translate = pmic_gpio_domain_translate; girq->child_irq_domain_ops.translate = pmic_gpio_domain_translate;
......
...@@ -794,7 +794,7 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev) ...@@ -794,7 +794,7 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev)
girq->fwnode = of_node_to_fwnode(pctrl->dev->of_node); girq->fwnode = of_node_to_fwnode(pctrl->dev->of_node);
girq->parent_domain = parent_domain; girq->parent_domain = parent_domain;
girq->child_to_parent_hwirq = pm8xxx_child_to_parent_hwirq; girq->child_to_parent_hwirq = pm8xxx_child_to_parent_hwirq;
girq->populate_parent_fwspec = gpiochip_populate_parent_fwspec_fourcell; girq->populate_parent_alloc_arg = gpiochip_populate_parent_fwspec_fourcell;
girq->child_offset_to_irq = pm8xxx_child_offset_to_irq; girq->child_offset_to_irq = pm8xxx_child_offset_to_irq;
girq->child_irq_domain_ops.translate = pm8xxx_domain_translate; girq->child_irq_domain_ops.translate = pm8xxx_domain_translate;
......
...@@ -94,16 +94,15 @@ struct gpio_irq_chip { ...@@ -94,16 +94,15 @@ struct gpio_irq_chip {
unsigned int *parent_type); unsigned int *parent_type);
/** /**
* @populate_parent_fwspec: * @populate_parent_alloc_arg :
* *
* This optional callback populates the &struct irq_fwspec for the * This optional callback allocates and populates the specific struct
* parent's IRQ domain. If this is not specified, then * for the parent's IRQ domain. If this is not specified, then
* &gpiochip_populate_parent_fwspec_twocell will be used. A four-cell * &gpiochip_populate_parent_fwspec_twocell will be used. A four-cell
* variant named &gpiochip_populate_parent_fwspec_fourcell is also * variant named &gpiochip_populate_parent_fwspec_fourcell is also
* available. * available.
*/ */
void (*populate_parent_fwspec)(struct gpio_chip *chip, void *(*populate_parent_alloc_arg)(struct gpio_chip *chip,
struct irq_fwspec *fwspec,
unsigned int parent_hwirq, unsigned int parent_hwirq,
unsigned int parent_type); unsigned int parent_type);
...@@ -537,29 +536,27 @@ struct bgpio_pdata { ...@@ -537,29 +536,27 @@ struct bgpio_pdata {
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
void gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *chip, void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *chip,
struct irq_fwspec *fwspec,
unsigned int parent_hwirq, unsigned int parent_hwirq,
unsigned int parent_type); unsigned int parent_type);
void gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *chip, void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *chip,
struct irq_fwspec *fwspec,
unsigned int parent_hwirq, unsigned int parent_hwirq,
unsigned int parent_type); unsigned int parent_type);
#else #else
static inline void gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *chip, static inline void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *chip,
struct irq_fwspec *fwspec,
unsigned int parent_hwirq, unsigned int parent_hwirq,
unsigned int parent_type) unsigned int parent_type)
{ {
return NULL;
} }
static inline void gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *chip, static inline void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *chip,
struct irq_fwspec *fwspec,
unsigned int parent_hwirq, unsigned int parent_hwirq,
unsigned int parent_type) unsigned int parent_type)
{ {
return NULL;
} }
#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */ #endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */
...@@ -715,7 +712,8 @@ gpiochip_remove_pin_ranges(struct gpio_chip *chip) ...@@ -715,7 +712,8 @@ gpiochip_remove_pin_ranges(struct gpio_chip *chip)
#endif /* CONFIG_PINCTRL */ #endif /* CONFIG_PINCTRL */
struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum, struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip,
unsigned int hwnum,
const char *label, const char *label,
enum gpio_lookup_flags lflags, enum gpio_lookup_flags lflags,
enum gpiod_flags dflags); enum gpiod_flags dflags);
......
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#ifndef __LINUX_IPMI_FRU_H__
#define __LINUX_IPMI_FRU_H__
#ifdef __KERNEL__
# include <linux/types.h>
# include <linux/string.h>
#else
# include <stdint.h>
# include <string.h>
#endif
/*
* These structures match the unaligned crap we have in FRU1011.pdf
* (http://download.intel.com/design/servers/ipmi/FRU1011.pdf)
*/
/* chapter 8, page 5 */
struct fru_common_header {
uint8_t format; /* 0x01 */
uint8_t internal_use_off; /* multiple of 8 bytes */
uint8_t chassis_info_off; /* multiple of 8 bytes */
uint8_t board_area_off; /* multiple of 8 bytes */
uint8_t product_area_off; /* multiple of 8 bytes */
uint8_t multirecord_off; /* multiple of 8 bytes */
uint8_t pad; /* must be 0 */
uint8_t checksum; /* sum modulo 256 must be 0 */
};
/* chapter 9, page 5 -- internal_use: not used by us */
/* chapter 10, page 6 -- chassis info: not used by us */
/* chapter 13, page 9 -- used by board_info_area below */
struct fru_type_length {
uint8_t type_length;
uint8_t data[0];
};
/* chapter 11, page 7 */
struct fru_board_info_area {
uint8_t format; /* 0x01 */
uint8_t area_len; /* multiple of 8 bytes */
uint8_t language; /* I hope it's 0 */
uint8_t mfg_date[3]; /* LSB, minutes since 1996-01-01 */
struct fru_type_length tl[0]; /* type-length stuff follows */
/*
* the TL there are in order:
* Board Manufacturer
* Board Product Name
* Board Serial Number
* Board Part Number
* FRU File ID (may be null)
* more manufacturer-specific stuff
* 0xc1 as a terminator
* 0x00 pad to a multiple of 8 bytes - 1
* checksum (sum of all stuff module 256 must be zero)
*/
};
enum fru_type {
FRU_TYPE_BINARY = 0x00,
FRU_TYPE_BCDPLUS = 0x40,
FRU_TYPE_ASCII6 = 0x80,
FRU_TYPE_ASCII = 0xc0, /* not ascii: depends on language */
};
/*
* some helpers
*/
static inline struct fru_board_info_area *fru_get_board_area(
const struct fru_common_header *header)
{
/* we know for sure that the header is 8 bytes in size */
return (struct fru_board_info_area *)(header + header->board_area_off);
}
static inline int fru_type(struct fru_type_length *tl)
{
return tl->type_length & 0xc0;
}
static inline int fru_length(struct fru_type_length *tl)
{
return (tl->type_length & 0x3f) + 1; /* len of whole record */
}
/* assume ascii-latin1 encoding */
static inline int fru_strlen(struct fru_type_length *tl)
{
return fru_length(tl) - 1;
}
static inline char *fru_strcpy(char *dest, struct fru_type_length *tl)
{
int len = fru_strlen(tl);
memcpy(dest, tl->data, len);
dest[len] = '\0';
return dest;
}
static inline struct fru_type_length *fru_next_tl(struct fru_type_length *tl)
{
return tl + fru_length(tl);
}
static inline int fru_is_eof(struct fru_type_length *tl)
{
return tl->type_length == 0xc1;
}
/*
* External functions defined in fru-parse.c.
*/
extern int fru_header_cksum_ok(struct fru_common_header *header);
extern int fru_bia_cksum_ok(struct fru_board_info_area *bia);
/* All these 4 return allocated strings by calling fru_alloc() */
extern char *fru_get_board_manufacturer(struct fru_common_header *header);
extern char *fru_get_product_name(struct fru_common_header *header);
extern char *fru_get_serial_number(struct fru_common_header *header);
extern char *fru_get_part_number(struct fru_common_header *header);
/* This must be defined by the caller of the above functions */
extern void *fru_alloc(size_t size);
#endif /* __LINUX_IMPI_FRU_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment