mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 20:07:46 +09:00
irqchip: replace the gpio IRQ driver with upstream
PD#151900: irqchip: replace the gpio IRQ driver with upstream v4.15-rc2 1) separate the driver from the pinctrl driver 2) add support for GPIOAO* of M8B as interrupt source 3) irq lines of GPIOAO* maximum up to eight 4) remove the GPIO IRQ Controller in AO domain note that the driver does not support for detecting the double-edge signal through the pin,if you have related needs, please consider using polling. test pass on axg_skt, p212 Change-Id: Iaa8da8a0cbfa0fe90d26fc8c2b775e045c8768f2 Signed-off-by: Xingyu Chen <xingyu.chen@amlogic.com>
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
Amlogic meson GPIO interrupt controller
|
||||
|
||||
Meson SoCs contains an interrupt controller which is able to watch the SoC
|
||||
pads and generate an interrupt on edge or level. The controller is essentially
|
||||
a 256 pads to 8 GIC interrupt multiplexer, with a filter block to select edge
|
||||
or level and polarity. It does not expose all 256 mux inputs because the
|
||||
documentation shows that the upper part is not mapped to any pad. The actual
|
||||
number of interrupt exposed depends on the SoC.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : must have "amlogic,meson8-gpio-intc” and either
|
||||
“amlogic,meson8-gpio-intc” for meson8 SoCs (S802) or
|
||||
“amlogic,meson8b-gpio-intc” for meson8b SoCs (S805) or
|
||||
“amlogic,meson-gxbb-gpio-intc” for GXBB SoCs (S905) or
|
||||
“amlogic,meson-gxl-gpio-intc” for GXL SoCs (S905X, S912) or
|
||||
“amlogic,meson-axg-gpio-intc” for AXG SoCs (A113G, A113D) or
|
||||
“amlogic,meson-txlx-gpio-intc” for TXLX SoCs (T962X, T962E)
|
||||
- interrupt-parent : a phandle to the GIC the interrupts are routed to.
|
||||
Usually this is provided at the root level of the device tree as it is
|
||||
common to most of the SoC.
|
||||
- reg : Specifies base physical address and size of the registers.
|
||||
- interrupt-controller : Identifies the node as an interrupt controller.
|
||||
- #interrupt-cells : Specifies the number of cells needed to encode an
|
||||
interrupt source. The value must be 2.
|
||||
- meson,channel-interrupts: Array with the 8 upstream hwirq numbers. These
|
||||
are the hwirqs used on the parent interrupt controller.
|
||||
|
||||
Example:
|
||||
|
||||
gpio_interrupt: interrupt-controller@9880 {
|
||||
compatible = "amlogic,meson-gxbb-gpio-intc",
|
||||
"amlogic,meson-gpio-intc";
|
||||
reg = <0x0 0x9880 0x0 0x10>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
meson,channel-interrupts = <64 65 66 67 68 69 70 71>;
|
||||
};
|
||||
@@ -14222,3 +14222,8 @@ F: drivers/amlogic/media/vout/lcd/lcd_extern/mipi_ST7701.c
|
||||
AMLOGIC MESONGXL ADD SOUND DMIC MMAP FUNCTION
|
||||
M: peipeng.zhao <peipeng.zhao@amlogic.com>
|
||||
F: sound/soc/amlogic/meson/dmic.h
|
||||
|
||||
AMLOGIC GPIO IRQ
|
||||
M: Xingyu Chen <xingyu.chen@amlogic.com>
|
||||
F: drivers/amlogic/irqchip/*
|
||||
F: Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt
|
||||
|
||||
@@ -347,6 +347,16 @@
|
||||
clock-names = "clk_i2c";
|
||||
};
|
||||
|
||||
gpio_intc: interrupt-controller@9880 {
|
||||
compatible = "amlogic,meson-gpio-intc",
|
||||
"amlogic,meson8b-gpio-intc";
|
||||
reg = <0xc1109880 0x10>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
amlogic,channel-interrupts = <64 65 66 67 68 69 70 71>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
pinctrl_cbus: pinctrl@c1109880 {
|
||||
compatible = "amlogic,meson8b-cbus-pinctrl";
|
||||
#address-cells = <1>;
|
||||
@@ -357,25 +367,13 @@
|
||||
reg = <0xc11080b0 0x28>,
|
||||
<0xc11080e8 0x18>,
|
||||
<0xc1108120 0x18>,
|
||||
<0xc1108030 0x38>,
|
||||
<0xc1109880 0x10>;
|
||||
interrupts = <0 64 1>,
|
||||
<0 65 1>,
|
||||
<0 66 1>,
|
||||
<0 67 1>,
|
||||
<0 68 1>,
|
||||
<0 69 1>,
|
||||
<0 70 1>,
|
||||
<0 71 1>;
|
||||
<0xc1108030 0x38>;
|
||||
reg-names = "mux",
|
||||
"pull",
|
||||
"pull-enable",
|
||||
"gpio",
|
||||
"irq";
|
||||
"gpio";
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
};
|
||||
|
||||
a_i2c_master:a_i2c {
|
||||
@@ -634,16 +632,11 @@
|
||||
|
||||
gpio_ao: ao-bank@c1108030 {
|
||||
reg = <0xc8100014 0x4>,
|
||||
<0xc810002c 0x4>,
|
||||
<0xc8100024 0x8>,
|
||||
<0xc8100084 0x4>;
|
||||
interrupts = <0 200 1>,
|
||||
<0 201 1>;
|
||||
reg-names = "mux", "pull", "gpio", "irq";
|
||||
<0xc810002c 0x4>,
|
||||
<0xc8100024 0x8>;
|
||||
reg-names = "mux", "pull", "gpio";
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
};
|
||||
remote_pins:remote_pin {
|
||||
mux {
|
||||
|
||||
@@ -279,15 +279,10 @@
|
||||
gpio_ao: ao-bank@ff800014{
|
||||
reg = <0x0 0xff800014 0x0 0x8>,
|
||||
<0x0 0xff80002c 0x0 0x4>,
|
||||
<0x0 0xff800024 0x0 0x8>,
|
||||
<0x0 0xff800084 0x0 0x4>;
|
||||
interrupts = <0 200 1>,
|
||||
<0 201 1>;
|
||||
reg-names = "mux", "pull", "gpio", "irq";
|
||||
<0x0 0xff800024 0x0 0x8>;
|
||||
reg-names = "mux", "pull", "gpio";
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -301,25 +296,13 @@
|
||||
reg = <0x0 0xff634480 0x0 0x40>,
|
||||
<0x0 0xff6344e8 0x0 0x14>,
|
||||
<0x0 0xff634520 0x0 0x14>,
|
||||
<0x0 0xff634430 0x0 0x3c>,
|
||||
<0x0 0xffd0f080 0x0 0x10>;
|
||||
interrupts = <0 64 1>,
|
||||
<0 65 1>,
|
||||
<0 66 1>,
|
||||
<0 67 1>,
|
||||
<0 68 1>,
|
||||
<0 69 1>,
|
||||
<0 70 1>,
|
||||
<0 71 1>;
|
||||
<0x0 0xff634430 0x0 0x3c>;
|
||||
reg-names = "mux",
|
||||
"pull",
|
||||
"pull-enable",
|
||||
"gpio",
|
||||
"irq";
|
||||
"gpio";
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -337,6 +320,17 @@
|
||||
#size-cells = <2>;
|
||||
ranges = <0x0 0x0 0x0 0xffd00000 0x0 0x25000>;
|
||||
|
||||
gpio_intc: interrupt-controller@f080 {
|
||||
compatible = "amlogic,meson-gpio-intc",
|
||||
"amlogic,meson-axg-gpio-intc";
|
||||
reg = <0x0 0xf080 0x0 0x10>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
amlogic,channel-interrupts =
|
||||
<64 65 66 67 68 69 70 71>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
meson_clk_msr {
|
||||
compatible = "amlogic, gxl_measure";
|
||||
reg = <0x0 0x18004 0x0 0x4
|
||||
|
||||
@@ -378,15 +378,10 @@
|
||||
gpio_ao: bank@14 {
|
||||
reg = <0x0 0xc8100014 0x0 0x8>,
|
||||
<0x0 0xc810002c 0x0 0x4>,
|
||||
<0x0 0xc8100024 0x0 0x8>,
|
||||
<0x0 0xc8100084 0x0 0x4>;
|
||||
interrupts = <0 200 IRQ_TYPE_EDGE_RISING>,
|
||||
<0 201 IRQ_TYPE_EDGE_RISING>;
|
||||
reg-names = "mux", "pull", "gpio", "irq";
|
||||
<0x0 0xc8100024 0x0 0x8>;
|
||||
reg-names = "mux", "pull", "gpio";
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -400,22 +395,11 @@
|
||||
reg = <0x0 0xc88344b0 0x0 0x28>,
|
||||
<0x0 0xc88344e8 0x0 0x14>,
|
||||
<0x0 0xc8834520 0x0 0x14>,
|
||||
<0x0 0xc8834430 0x0 0x40>,
|
||||
<0x0 0xc1109880 0x0 0x10>;
|
||||
interrupts = <0 64 IRQ_TYPE_EDGE_RISING>,
|
||||
<0 65 IRQ_TYPE_EDGE_RISING>,
|
||||
<0 66 IRQ_TYPE_EDGE_RISING>,
|
||||
<0 67 IRQ_TYPE_EDGE_RISING>,
|
||||
<0 68 IRQ_TYPE_EDGE_RISING>,
|
||||
<0 69 IRQ_TYPE_EDGE_RISING>,
|
||||
<0 70 IRQ_TYPE_EDGE_RISING>,
|
||||
<0 71 IRQ_TYPE_EDGE_RISING>;
|
||||
<0x0 0xc8834430 0x0 0x40>;
|
||||
reg-names = "mux", "pull",
|
||||
"pull-enable", "gpio", "irq";
|
||||
"pull-enable", "gpio";
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -432,6 +416,17 @@
|
||||
#size-cells = <2>;
|
||||
ranges = <0x0 0x0 0x0 0xc1100000 0x0 0x100000>;
|
||||
|
||||
gpio_intc: interrupt-controller@9880 {
|
||||
compatible = "amlogic,meson-gpio-intc",
|
||||
"amlogic,meson-gxl-gpio-intc";
|
||||
reg = <0x0 0x9880 0x0 0x10>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
amlogic,channel-interrupts =
|
||||
<64 65 66 67 68 69 70 71>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
meson_clk_msr{
|
||||
compatible = "amlogic, gxl_measure";
|
||||
reg = <0x0 0x875c 0x0 0x4
|
||||
|
||||
@@ -461,15 +461,10 @@
|
||||
gpio_ao: bank@14 {
|
||||
reg = <0x0 0xc8100014 0x0 0x8>,
|
||||
<0x0 0xc810002c 0x0 0x4>,
|
||||
<0x0 0xc8100024 0x0 0x8>,
|
||||
<0x0 0xc8100084 0x0 0x4>;
|
||||
interrupts = <0 200 IRQ_TYPE_EDGE_RISING>,
|
||||
<0 201 IRQ_TYPE_EDGE_RISING>;
|
||||
reg-names = "mux", "pull", "gpio", "irq";
|
||||
<0x0 0xc8100024 0x0 0x8>;
|
||||
reg-names = "mux", "pull", "gpio";
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -483,22 +478,11 @@
|
||||
reg = <0x0 0xc88344b0 0x0 0x28>,
|
||||
<0x0 0xc88344e8 0x0 0x14>,
|
||||
<0x0 0xc8834520 0x0 0x14>,
|
||||
<0x0 0xc8834430 0x0 0x40>,
|
||||
<0x0 0xc1109880 0x0 0x10>;
|
||||
interrupts = <0 64 IRQ_TYPE_EDGE_RISING>,
|
||||
<0 65 IRQ_TYPE_EDGE_RISING>,
|
||||
<0 66 IRQ_TYPE_EDGE_RISING>,
|
||||
<0 67 IRQ_TYPE_EDGE_RISING>,
|
||||
<0 68 IRQ_TYPE_EDGE_RISING>,
|
||||
<0 69 IRQ_TYPE_EDGE_RISING>,
|
||||
<0 70 IRQ_TYPE_EDGE_RISING>,
|
||||
<0 71 IRQ_TYPE_EDGE_RISING>;
|
||||
<0x0 0xc8834430 0x0 0x40>;
|
||||
reg-names = "mux", "pull",
|
||||
"pull-enable", "gpio", "irq";
|
||||
"pull-enable", "gpio";
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -515,6 +499,17 @@
|
||||
#size-cells = <2>;
|
||||
ranges = <0x0 0x0 0x0 0xc1100000 0x0 0x100000>;
|
||||
|
||||
gpio_intc: interrupt-controller@9880 {
|
||||
compatible = "amlogic,meson-gpio-intc",
|
||||
"amlogic,meson-gxl-gpio-intc";
|
||||
reg = <0x0 0x9880 0x0 0x10>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
amlogic,channel-interrupts =
|
||||
<64 65 66 67 68 69 70 71>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
meson_clk_msr{
|
||||
compatible = "amlogic, gxl_measure";
|
||||
reg = <0x0 0x875c 0x0 0x4
|
||||
|
||||
@@ -305,15 +305,10 @@
|
||||
gpio_ao: ao-bank@ff800014{
|
||||
reg = <0x0 0xff800014 0x0 0x8>,
|
||||
<0x0 0xff80002c 0x0 0x4>,
|
||||
<0x0 0xff800024 0x0 0x8>,
|
||||
<0x0 0xff800084 0x0 0x4>;
|
||||
interrupts = <0 200 1>,
|
||||
<0 201 1>;
|
||||
reg-names = "mux", "pull", "gpio", "irq";
|
||||
<0x0 0xff800024 0x0 0x8>;
|
||||
reg-names = "mux", "pull", "gpio";
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -327,25 +322,13 @@
|
||||
reg = <0x0 0xff6344b0 0x0 0x34>,
|
||||
<0x0 0xff6344e8 0x0 0x14>,
|
||||
<0x0 0xff634520 0x0 0x14>,
|
||||
<0x0 0xff634430 0x0 0x3c>,
|
||||
<0x0 0xffd0f080 0x0 0x10>;
|
||||
interrupts = <0 64 1>,
|
||||
<0 65 1>,
|
||||
<0 66 1>,
|
||||
<0 67 1>,
|
||||
<0 68 1>,
|
||||
<0 69 1>,
|
||||
<0 70 1>,
|
||||
<0 71 1>;
|
||||
<0x0 0xff634430 0x0 0x3c>;
|
||||
reg-names = "mux",
|
||||
"pull",
|
||||
"pull-enable",
|
||||
"gpio",
|
||||
"irq";
|
||||
"gpio";
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -557,6 +540,17 @@
|
||||
#size-cells = <2>;
|
||||
ranges = <0x0 0x0 0x0 0xffd00000 0x0 0x25000>;
|
||||
|
||||
gpio_intc: interrupt-controller@f080 {
|
||||
compatible = "amlogic,meson-gpio-intc",
|
||||
"amlogic,meson-txlx-gpio-intc";
|
||||
reg = <0x0 0xf080 0x0 0x10>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
amlogic,channel-interrupts =
|
||||
<64 65 66 67 68 69 70 71>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
meson_clk_msr {
|
||||
compatible = "amlogic, gxl_measure";
|
||||
reg = <0x0 0x18004 0x0 0x4
|
||||
|
||||
@@ -330,6 +330,7 @@ CONFIG_AMLOGIC_IIO=y
|
||||
CONFIG_AMLOGIC_SARADC=y
|
||||
CONFIG_AMLOGIC_DDR_WINDOW_TOOL=m
|
||||
CONFIG_AMLOGIC_TEE=y
|
||||
CONFIG_AMLOGIC_GPIO_IRQ=y
|
||||
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
|
||||
CONFIG_DEVTMPFS=y
|
||||
CONFIG_DEVTMPFS_MOUNT=y
|
||||
|
||||
@@ -302,6 +302,7 @@ CONFIG_AMLOGIC_IRBLASTER=y
|
||||
CONFIG_AMLOGIC_IIO=y
|
||||
CONFIG_AMLOGIC_SARADC=y
|
||||
CONFIG_AMLOGIC_DDR_WINDOW_TOOL=m
|
||||
CONFIG_AMLOGIC_GPIO_IRQ=y
|
||||
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
|
||||
CONFIG_DEVTMPFS=y
|
||||
CONFIG_DEVTMPFS_MOUNT=y
|
||||
|
||||
@@ -124,5 +124,6 @@ source "drivers/amlogic/ledring/Kconfig"
|
||||
|
||||
source "drivers/amlogic/memory_ext/Kconfig"
|
||||
|
||||
source "drivers/amlogic/irqchip/Kconfig"
|
||||
endmenu
|
||||
endif
|
||||
|
||||
@@ -114,3 +114,5 @@ obj-$(CONFIG_AMLOGIC_TEE) += tee/
|
||||
obj-$(CONFIG_AMLOGIC_MEMORY_EXTEND) += memory_ext/
|
||||
|
||||
obj-$(CONFIG_AMLOGIC_LEDRING) += ledring/
|
||||
|
||||
obj-$(CONFIG_AMLOGIC_GPIO_IRQ) += irqchip/
|
||||
|
||||
7
drivers/amlogic/irqchip/Kconfig
Normal file
7
drivers/amlogic/irqchip/Kconfig
Normal file
@@ -0,0 +1,7 @@
|
||||
config AMLOGIC_GPIO_IRQ
|
||||
bool "Meson GPIO Interrupt Multiplexer"
|
||||
depends on IRQ_DOMAIN
|
||||
depends on IRQ_DOMAIN_HIERARCHY
|
||||
default n
|
||||
help
|
||||
Support Meson SoC Family GPIO Interrupt Multiplexer
|
||||
1
drivers/amlogic/irqchip/Makefile
Normal file
1
drivers/amlogic/irqchip/Makefile
Normal file
@@ -0,0 +1 @@
|
||||
obj-$(CONFIG_AMLOGIC_GPIO_IRQ) += irq-meson-gpio.o
|
||||
429
drivers/amlogic/irqchip/irq-meson-gpio.c
Normal file
429
drivers/amlogic/irqchip/irq-meson-gpio.c
Normal file
@@ -0,0 +1,429 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Endless Mobile, Inc.
|
||||
* Author: Carlo Caione <carlo@endlessm.com>
|
||||
* Copyright (c) 2016 BayLibre, SAS.
|
||||
* Author: Jerome Brunet <jbrunet@baylibre.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called COPYING.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#define NUM_CHANNEL 8
|
||||
#define MAX_INPUT_MUX 256
|
||||
|
||||
#define REG_EDGE_POL 0x00
|
||||
#define REG_PIN_03_SEL 0x04
|
||||
#define REG_PIN_47_SEL 0x08
|
||||
#define REG_FILTER_SEL 0x0c
|
||||
|
||||
#define REG_EDGE_POL_MASK(x) (BIT(x) | BIT(16 + (x)))
|
||||
#define REG_EDGE_POL_EDGE(x) BIT(x)
|
||||
#define REG_EDGE_POL_LOW(x) BIT(16 + (x))
|
||||
#define REG_PIN_SEL_SHIFT(x) (((x) % 4) * 8)
|
||||
#define REG_FILTER_SEL_SHIFT(x) ((x) * 4)
|
||||
|
||||
struct meson_gpio_irq_params {
|
||||
unsigned int nr_hwirq;
|
||||
};
|
||||
|
||||
static const struct meson_gpio_irq_params meson8_params = {
|
||||
.nr_hwirq = 134,
|
||||
};
|
||||
|
||||
static const struct meson_gpio_irq_params meson8b_params = {
|
||||
.nr_hwirq = 119,
|
||||
};
|
||||
|
||||
static const struct meson_gpio_irq_params gxbb_params = {
|
||||
.nr_hwirq = 133,
|
||||
};
|
||||
|
||||
static const struct meson_gpio_irq_params gxl_params = {
|
||||
.nr_hwirq = 110,
|
||||
};
|
||||
|
||||
static const struct meson_gpio_irq_params axg_params = {
|
||||
.nr_hwirq = 100,
|
||||
};
|
||||
|
||||
static const struct meson_gpio_irq_params txlx_params = {
|
||||
.nr_hwirq = 119,
|
||||
};
|
||||
|
||||
static const struct of_device_id meson_irq_gpio_matches[] = {
|
||||
{ .compatible = "amlogic,meson8-gpio-intc", .data = &meson8_params },
|
||||
{ .compatible = "amlogic,meson8b-gpio-intc", .data = &meson8b_params },
|
||||
{ .compatible = "amlogic,meson-gxbb-gpio-intc", .data = &gxbb_params },
|
||||
{ .compatible = "amlogic,meson-gxl-gpio-intc", .data = &gxl_params },
|
||||
{ .compatible = "amlogic,meson-axg-gpio-intc", .data = &axg_params },
|
||||
{ .compatible = "amlogic,meson-txlx-gpio-intc", .data = &txlx_params },
|
||||
{ }
|
||||
};
|
||||
|
||||
struct meson_gpio_irq_controller {
|
||||
unsigned int nr_hwirq;
|
||||
void __iomem *base;
|
||||
u32 channel_irqs[NUM_CHANNEL];
|
||||
DECLARE_BITMAP(channel_map, NUM_CHANNEL);
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static void meson_gpio_irq_update_bits(struct meson_gpio_irq_controller *ctl,
|
||||
unsigned int reg, u32 mask, u32 val)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
tmp = readl_relaxed(ctl->base + reg);
|
||||
tmp &= ~mask;
|
||||
tmp |= val;
|
||||
writel_relaxed(tmp, ctl->base + reg);
|
||||
}
|
||||
|
||||
static unsigned int meson_gpio_irq_channel_to_reg(unsigned int channel)
|
||||
{
|
||||
return (channel < 4) ? REG_PIN_03_SEL : REG_PIN_47_SEL;
|
||||
}
|
||||
|
||||
static int
|
||||
meson_gpio_irq_request_channel(struct meson_gpio_irq_controller *ctl,
|
||||
unsigned long hwirq,
|
||||
u32 **channel_hwirq)
|
||||
{
|
||||
unsigned int reg, idx;
|
||||
|
||||
spin_lock(&ctl->lock);
|
||||
|
||||
/* Find a free channel */
|
||||
idx = find_first_zero_bit(ctl->channel_map, NUM_CHANNEL);
|
||||
if (idx >= NUM_CHANNEL) {
|
||||
spin_unlock(&ctl->lock);
|
||||
pr_debug("No channel available\n");
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
/* Mark the channel as used */
|
||||
set_bit(idx, ctl->channel_map);
|
||||
|
||||
/*
|
||||
* Setup the mux of the channel to route the signal of the pad
|
||||
* to the appropriate input of the GIC
|
||||
*/
|
||||
reg = meson_gpio_irq_channel_to_reg(idx);
|
||||
meson_gpio_irq_update_bits(ctl, reg,
|
||||
0xff << REG_PIN_SEL_SHIFT(idx),
|
||||
hwirq << REG_PIN_SEL_SHIFT(idx));
|
||||
|
||||
/*
|
||||
* Get the hwirq number assigned to this channel through
|
||||
* a pointer the channel_irq table. The added benifit of this
|
||||
* method is that we can also retrieve the channel index with
|
||||
* it, using the table base.
|
||||
*/
|
||||
*channel_hwirq = &(ctl->channel_irqs[idx]);
|
||||
|
||||
spin_unlock(&ctl->lock);
|
||||
|
||||
pr_debug("hwirq %lu assigned to channel %d - irq %u\n",
|
||||
hwirq, idx, **channel_hwirq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
meson_gpio_irq_get_channel_idx(struct meson_gpio_irq_controller *ctl,
|
||||
u32 *channel_hwirq)
|
||||
{
|
||||
return channel_hwirq - ctl->channel_irqs;
|
||||
}
|
||||
|
||||
static void
|
||||
meson_gpio_irq_release_channel(struct meson_gpio_irq_controller *ctl,
|
||||
u32 *channel_hwirq)
|
||||
{
|
||||
unsigned int idx;
|
||||
|
||||
idx = meson_gpio_irq_get_channel_idx(ctl, channel_hwirq);
|
||||
clear_bit(idx, ctl->channel_map);
|
||||
}
|
||||
|
||||
static int meson_gpio_irq_type_setup(struct meson_gpio_irq_controller *ctl,
|
||||
unsigned int type,
|
||||
u32 *channel_hwirq)
|
||||
{
|
||||
u32 val = 0;
|
||||
unsigned int idx;
|
||||
|
||||
idx = meson_gpio_irq_get_channel_idx(ctl, channel_hwirq);
|
||||
|
||||
/*
|
||||
* The controller has a filter block to operate in either LEVEL or
|
||||
* EDGE mode, then signal is sent to the GIC. To enable LEVEL_LOW and
|
||||
* EDGE_FALLING support (which the GIC does not support), the filter
|
||||
* block is also able to invert the input signal it gets before
|
||||
* providing it to the GIC.
|
||||
*/
|
||||
type &= IRQ_TYPE_SENSE_MASK;
|
||||
|
||||
if (type == IRQ_TYPE_EDGE_BOTH)
|
||||
return -EINVAL;
|
||||
|
||||
if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))
|
||||
val |= REG_EDGE_POL_EDGE(idx);
|
||||
|
||||
if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING))
|
||||
val |= REG_EDGE_POL_LOW(idx);
|
||||
|
||||
spin_lock(&ctl->lock);
|
||||
|
||||
meson_gpio_irq_update_bits(ctl, REG_EDGE_POL,
|
||||
REG_EDGE_POL_MASK(idx), val);
|
||||
|
||||
spin_unlock(&ctl->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int meson_gpio_irq_type_output(unsigned int type)
|
||||
{
|
||||
unsigned int sense = type & IRQ_TYPE_SENSE_MASK;
|
||||
|
||||
type &= ~IRQ_TYPE_SENSE_MASK;
|
||||
|
||||
/*
|
||||
* The polarity of the signal provided to the GIC should always
|
||||
* be high.
|
||||
*/
|
||||
if (sense & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
|
||||
type |= IRQ_TYPE_LEVEL_HIGH;
|
||||
else if (sense & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))
|
||||
type |= IRQ_TYPE_EDGE_RISING;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static int meson_gpio_irq_set_type(struct irq_data *data, unsigned int type)
|
||||
{
|
||||
struct meson_gpio_irq_controller *ctl = data->domain->host_data;
|
||||
u32 *channel_hwirq = irq_data_get_irq_chip_data(data);
|
||||
int ret;
|
||||
|
||||
ret = meson_gpio_irq_type_setup(ctl, type, channel_hwirq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return irq_chip_set_type_parent(data,
|
||||
meson_gpio_irq_type_output(type));
|
||||
}
|
||||
|
||||
static struct irq_chip meson_gpio_irq_chip = {
|
||||
.name = "meson-gpio-irqchip",
|
||||
.irq_mask = irq_chip_mask_parent,
|
||||
.irq_unmask = irq_chip_unmask_parent,
|
||||
.irq_eoi = irq_chip_eoi_parent,
|
||||
.irq_set_type = meson_gpio_irq_set_type,
|
||||
.irq_retrigger = irq_chip_retrigger_hierarchy,
|
||||
#ifdef CONFIG_SMP
|
||||
.irq_set_affinity = irq_chip_set_affinity_parent,
|
||||
#endif
|
||||
.flags = IRQCHIP_SET_TYPE_MASKED,
|
||||
};
|
||||
|
||||
static int meson_gpio_irq_domain_translate(struct irq_domain *domain,
|
||||
struct irq_fwspec *fwspec,
|
||||
unsigned long *hwirq,
|
||||
unsigned int *type)
|
||||
{
|
||||
if (is_of_node(fwspec->fwnode) && fwspec->param_count == 2) {
|
||||
*hwirq = fwspec->param[0];
|
||||
*type = fwspec->param[1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int meson_gpio_irq_allocate_gic_irq(struct irq_domain *domain,
|
||||
unsigned int virq,
|
||||
u32 hwirq,
|
||||
unsigned int type)
|
||||
{
|
||||
struct irq_fwspec fwspec;
|
||||
|
||||
fwspec.fwnode = domain->parent->fwnode;
|
||||
fwspec.param_count = 3;
|
||||
fwspec.param[0] = 0; /* SPI */
|
||||
fwspec.param[1] = hwirq;
|
||||
fwspec.param[2] = meson_gpio_irq_type_output(type);
|
||||
|
||||
return irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
|
||||
}
|
||||
|
||||
static int meson_gpio_irq_domain_alloc(struct irq_domain *domain,
|
||||
unsigned int virq,
|
||||
unsigned int nr_irqs,
|
||||
void *data)
|
||||
{
|
||||
struct irq_fwspec *fwspec = data;
|
||||
struct meson_gpio_irq_controller *ctl = domain->host_data;
|
||||
unsigned long hwirq;
|
||||
u32 *channel_hwirq;
|
||||
unsigned int type;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(nr_irqs != 1))
|
||||
return -EINVAL;
|
||||
|
||||
ret = meson_gpio_irq_domain_translate(domain, fwspec, &hwirq, &type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = meson_gpio_irq_request_channel(ctl, hwirq, &channel_hwirq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = meson_gpio_irq_allocate_gic_irq(domain, virq,
|
||||
*channel_hwirq, type);
|
||||
if (ret < 0) {
|
||||
pr_err("failed to allocate gic irq %u\n", *channel_hwirq);
|
||||
meson_gpio_irq_release_channel(ctl, channel_hwirq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
|
||||
&meson_gpio_irq_chip, channel_hwirq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void meson_gpio_irq_domain_free(struct irq_domain *domain,
|
||||
unsigned int virq,
|
||||
unsigned int nr_irqs)
|
||||
{
|
||||
struct meson_gpio_irq_controller *ctl = domain->host_data;
|
||||
struct irq_data *irq_data;
|
||||
u32 *channel_hwirq;
|
||||
|
||||
if (WARN_ON(nr_irqs != 1))
|
||||
return;
|
||||
|
||||
irq_domain_free_irqs_parent(domain, virq, 1);
|
||||
|
||||
irq_data = irq_domain_get_irq_data(domain, virq);
|
||||
channel_hwirq = irq_data_get_irq_chip_data(irq_data);
|
||||
|
||||
meson_gpio_irq_release_channel(ctl, channel_hwirq);
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops meson_gpio_irq_domain_ops = {
|
||||
.alloc = meson_gpio_irq_domain_alloc,
|
||||
.free = meson_gpio_irq_domain_free,
|
||||
.translate = meson_gpio_irq_domain_translate,
|
||||
};
|
||||
|
||||
static int __init meson_gpio_irq_parse_dt(struct device_node *node,
|
||||
struct meson_gpio_irq_controller *ctl)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
const struct meson_gpio_irq_params *params;
|
||||
int ret;
|
||||
|
||||
match = of_match_node(meson_irq_gpio_matches, node);
|
||||
if (!match)
|
||||
return -ENODEV;
|
||||
|
||||
params = match->data;
|
||||
ctl->nr_hwirq = params->nr_hwirq;
|
||||
|
||||
ret = of_property_read_variable_u32_array(node,
|
||||
"amlogic,channel-interrupts",
|
||||
ctl->channel_irqs,
|
||||
NUM_CHANNEL,
|
||||
NUM_CHANNEL);
|
||||
if (ret < 0) {
|
||||
pr_err("can't get %d channel interrupts\n", NUM_CHANNEL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init meson_gpio_irq_of_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
struct irq_domain *domain, *parent_domain;
|
||||
struct meson_gpio_irq_controller *ctl;
|
||||
int ret;
|
||||
|
||||
if (!parent) {
|
||||
pr_err("missing parent interrupt node\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
parent_domain = irq_find_host(parent);
|
||||
if (!parent_domain) {
|
||||
pr_err("unable to obtain parent domain\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
|
||||
if (!ctl)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&ctl->lock);
|
||||
|
||||
ctl->base = of_iomap(node, 0);
|
||||
if (!ctl->base) {
|
||||
ret = -ENOMEM;
|
||||
goto free_ctl;
|
||||
}
|
||||
|
||||
ret = meson_gpio_irq_parse_dt(node, ctl);
|
||||
if (ret)
|
||||
goto free_channel_irqs;
|
||||
|
||||
domain = irq_domain_create_hierarchy(parent_domain, 0, ctl->nr_hwirq,
|
||||
of_node_to_fwnode(node),
|
||||
&meson_gpio_irq_domain_ops,
|
||||
ctl);
|
||||
if (!domain) {
|
||||
pr_err("failed to add domain\n");
|
||||
ret = -ENODEV;
|
||||
goto free_channel_irqs;
|
||||
}
|
||||
|
||||
pr_info("%d to %d gpio interrupt mux initialized\n",
|
||||
ctl->nr_hwirq, NUM_CHANNEL);
|
||||
|
||||
return 0;
|
||||
|
||||
free_channel_irqs:
|
||||
iounmap(ctl->base);
|
||||
free_ctl:
|
||||
kfree(ctl);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(meson_gpio_intc, "amlogic,meson-gpio-intc",
|
||||
meson_gpio_irq_of_init);
|
||||
@@ -711,383 +711,84 @@ static int meson_gpio_get(struct gpio_chip *chip, unsigned int gpio)
|
||||
return !!(val & BIT(bit));
|
||||
}
|
||||
|
||||
/*
|
||||
* NOP functions
|
||||
*/
|
||||
static void noop(struct irq_data *irqd) { }
|
||||
|
||||
static void meson_gpio_irq_enable(struct irq_data *irqd)
|
||||
static int meson_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct meson_domain *domain = to_meson_domain(irqd->chip_data);
|
||||
struct irq_data *parent_data;
|
||||
unsigned long flags;
|
||||
unsigned char cnt;
|
||||
|
||||
spin_lock_irqsave(&domain->irq_res.irq_res_lock, flags);
|
||||
for (cnt = 0; cnt < domain->irq_res.irq_num; cnt++) {
|
||||
if (domain->irq_res.gpio_irq[cnt].hwirq == irqd->hwirq) {
|
||||
parent_data =
|
||||
irq_get_irq_data(
|
||||
domain->irq_res.gpio_irq[cnt].parent_virq);
|
||||
/*enable the interrupt line of gpio in GIC controller*/
|
||||
parent_data->chip->irq_unmask(parent_data);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&domain->irq_res.irq_res_lock, flags);
|
||||
|
||||
}
|
||||
|
||||
static void meson_gpio_irq_disable(struct irq_data *irqd)
|
||||
{
|
||||
struct meson_domain *domain = to_meson_domain(irqd->chip_data);
|
||||
struct irq_data *parent_data;
|
||||
unsigned long flags;
|
||||
unsigned char cnt;
|
||||
|
||||
spin_lock_irqsave(&domain->irq_res.irq_res_lock, flags);
|
||||
for (cnt = 0; cnt < domain->irq_res.irq_num; cnt++) {
|
||||
if (domain->irq_res.gpio_irq[cnt].hwirq == irqd->hwirq) {
|
||||
parent_data =
|
||||
irq_get_irq_data(
|
||||
domain->irq_res.gpio_irq[cnt].parent_virq);
|
||||
/*disable the interrupt line of gpio in GIC controller*/
|
||||
parent_data->chip->irq_mask(parent_data);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&domain->irq_res.irq_res_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
*free gpio irq when free_irq() is called, and another pin can use it again.
|
||||
*/
|
||||
static void meson_gpio_irq_shutdown(struct irq_data *irqd)
|
||||
{
|
||||
struct meson_domain *domain = to_meson_domain(irqd->chip_data);
|
||||
unsigned long flags;
|
||||
unsigned char cnt;
|
||||
|
||||
spin_lock_irqsave(&domain->irq_res.irq_res_lock, flags);
|
||||
for (cnt = 0; cnt < domain->irq_res.irq_num; cnt++) {
|
||||
if (domain->irq_res.gpio_irq[cnt].hwirq == irqd->hwirq)
|
||||
domain->irq_res.gpio_irq[cnt].used_flag = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&domain->irq_res.irq_res_lock, flags);
|
||||
}
|
||||
|
||||
static int meson_ee_gpio_irq_type(struct irq_data *irqd, unsigned int type)
|
||||
{
|
||||
struct meson_domain *domain = to_meson_domain(irqd->chip_data);
|
||||
struct meson_domain *domain = to_meson_domain(chip);
|
||||
struct meson_bank *bank;
|
||||
struct irq_data *parent_data;
|
||||
unsigned long flags;
|
||||
unsigned int trigger_type[2];
|
||||
unsigned char type_num;
|
||||
unsigned char type_cnt;
|
||||
unsigned char start_bit;
|
||||
unsigned int gpio_virq;
|
||||
unsigned char cnt;
|
||||
unsigned char pin;
|
||||
unsigned char irq_pin;
|
||||
int ret;
|
||||
struct irq_fwspec fwspec;
|
||||
int hwirq;
|
||||
int gpio;
|
||||
|
||||
type = type & IRQ_TYPE_SENSE_MASK;
|
||||
gpio = chip->base + offset;
|
||||
|
||||
switch (type) {
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
trigger_type[0] = 0x10000;
|
||||
type_num = 1;
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
trigger_type[0] = 0x0;
|
||||
type_num = 1;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
trigger_type[0] = 0x1;
|
||||
type_num = 1;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
trigger_type[0] = 0x10001;
|
||||
type_num = 1;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_BOTH:
|
||||
trigger_type[0] = 0x1;
|
||||
trigger_type[1] = 0x10001;
|
||||
type_num = 2;
|
||||
break;
|
||||
default:
|
||||
if (meson_get_bank(domain, gpio, &bank))
|
||||
return -EINVAL;
|
||||
|
||||
if (bank->irq < 0) {
|
||||
pr_warn("pinctrl-meson: no support irq for pin[%d]\n", gpio);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (type_cnt = 0; type_cnt < type_num; type_cnt++) {
|
||||
/* dynamic allocate gpio irq for request pin*/
|
||||
spin_lock_irqsave(&domain->irq_res.irq_res_lock, flags);
|
||||
for (cnt = 0; cnt < domain->irq_res.irq_num; cnt++) {
|
||||
if (domain->irq_res.gpio_irq[cnt].used_flag)
|
||||
continue;
|
||||
else {
|
||||
domain->irq_res.gpio_irq[cnt].used_flag = 1;
|
||||
domain->irq_res.gpio_irq[cnt].hwirq =
|
||||
irqd->hwirq;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&domain->irq_res.irq_res_lock, flags);
|
||||
|
||||
if (domain->irq_res.irq_num == cnt) {
|
||||
pr_err("meson_pinctrl: no more gpio irq available in EE GPIO INTC, allocate gpio irq for pin[%ld] failed.\n",
|
||||
irqd->hwirq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_update_bits(domain->reg_irq,
|
||||
(GPIO_IRQ_EDGE_OFFSET * 4),
|
||||
0x10001 << cnt,
|
||||
trigger_type[type_cnt] << cnt);
|
||||
|
||||
/*the gpio hwirq eqaul to gpio offset in gpio chip*/
|
||||
pin = domain->data->pin_base + irqd->hwirq;
|
||||
|
||||
ret = meson_get_bank(domain, pin, &bank);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (bank->irq < 0)
|
||||
return -EINVAL;
|
||||
|
||||
irq_pin = bank->irq + pin - bank->first;
|
||||
|
||||
/*set pin select register*/
|
||||
start_bit = (cnt & 3) << 3;
|
||||
regmap_update_bits(domain->reg_irq,
|
||||
(cnt < 4)?(GPIO_IRQ_MUX_0_3 * 4):(GPIO_IRQ_MUX_4_7 * 4),
|
||||
0xff << start_bit,
|
||||
irq_pin << start_bit);
|
||||
/**
|
||||
*TODO: support to configure the filter registers by
|
||||
* the func interface.
|
||||
* all filter registers for gpio will been set 0x7.
|
||||
*/
|
||||
start_bit = cnt << 2;
|
||||
regmap_update_bits(domain->reg_irq,
|
||||
(GPIO_IRQ_FILTER_OFFSET * 4),
|
||||
0x7 << start_bit, 0x7 << start_bit);
|
||||
|
||||
parent_data =
|
||||
irq_get_irq_data(
|
||||
domain->irq_res.gpio_irq[cnt].parent_virq);
|
||||
|
||||
/*set trigger type of gpio in GIC controller*/
|
||||
if (type & IRQ_TYPE_EDGE_BOTH)
|
||||
parent_data->chip->irq_set_type(parent_data,
|
||||
IRQ_TYPE_EDGE_RISING);
|
||||
else
|
||||
parent_data->chip->irq_set_type(parent_data,
|
||||
IRQ_TYPE_LEVEL_HIGH);
|
||||
|
||||
gpio_virq = irq_find_mapping(domain->chip.irqdomain,
|
||||
domain->irq_res.gpio_irq[cnt].hwirq);
|
||||
|
||||
pr_info("meson_pinctrl: gpio virq[%d] connect to GIC hwirq[%ld]\n",
|
||||
gpio_virq,
|
||||
parent_data->hwirq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_ao_gpio_irq_type(struct irq_data *irqd, unsigned int type)
|
||||
{
|
||||
struct meson_domain *domain = to_meson_domain(irqd->chip_data);
|
||||
struct meson_bank *bank;
|
||||
struct irq_data *parent_data;
|
||||
unsigned long flags;
|
||||
unsigned int trigger_type[2];
|
||||
unsigned char type_num;
|
||||
unsigned char type_cnt;
|
||||
unsigned char start_bit;
|
||||
unsigned int gpio_virq;
|
||||
unsigned char cnt;
|
||||
unsigned char pin;
|
||||
unsigned char irq_pin;
|
||||
int ret;
|
||||
|
||||
type = type & IRQ_TYPE_SENSE_MASK;
|
||||
|
||||
switch (type) {
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
trigger_type[0] = 0x10000;
|
||||
type_num = 1;
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
trigger_type[0] = 0x0;
|
||||
type_num = 1;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
trigger_type[0] = 0x40000;
|
||||
type_num = 1;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
trigger_type[0] = 0x50000;
|
||||
type_num = 1;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_BOTH:
|
||||
trigger_type[0] = 0x40000;
|
||||
trigger_type[1] = 0x50000;
|
||||
type_num = 2;
|
||||
break;
|
||||
default:
|
||||
if (!domain->of_irq) {
|
||||
pr_err("pinctrl-meson: invalid device node of gpio INTC\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hwirq = gpio - bank->first + bank->irq;
|
||||
|
||||
for (type_cnt = 0; type_cnt < type_num; type_cnt++) {
|
||||
/* dynamic allocate gpio irq for request pin*/
|
||||
spin_lock_irqsave(&domain->irq_res.irq_res_lock, flags);
|
||||
for (cnt = 0; cnt < domain->irq_res.irq_num; cnt++) {
|
||||
if (domain->irq_res.gpio_irq[cnt].used_flag)
|
||||
continue;
|
||||
else {
|
||||
domain->irq_res.gpio_irq[cnt].used_flag = 1;
|
||||
domain->irq_res.gpio_irq[cnt].hwirq =
|
||||
irqd->hwirq;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&domain->irq_res.irq_res_lock, flags);
|
||||
|
||||
if (domain->irq_res.irq_num == cnt) {
|
||||
pr_err("meson_pinctrl: no more gpio irq available in AO GPIO INTC, allocate gpio irq for pin[%ld] failed.\n",
|
||||
irqd->hwirq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*set trigger type*/
|
||||
regmap_update_bits(domain->reg_irq, 0,
|
||||
0x50000 << cnt,
|
||||
trigger_type[type_cnt] << cnt);
|
||||
|
||||
/*the gpio hwirq eqaul to gpio offset in gpio chip*/
|
||||
pin = domain->data->pin_base + irqd->hwirq;
|
||||
|
||||
ret = meson_get_bank(domain, pin, &bank);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (bank->irq < 0)
|
||||
return -EINVAL;
|
||||
|
||||
irq_pin = bank->irq + pin - bank->first;
|
||||
|
||||
/*set pin select register*/
|
||||
start_bit = cnt << 2;
|
||||
regmap_update_bits(domain->reg_irq, 0,
|
||||
0xf << start_bit,
|
||||
irq_pin << start_bit);
|
||||
/**
|
||||
*TODO: support to configure the filter registers by
|
||||
* the func interface.
|
||||
* all filter registers for gpio will been set 0x7.
|
||||
*/
|
||||
start_bit = cnt << 2;
|
||||
regmap_update_bits(domain->reg_irq, 0,
|
||||
0x700 << start_bit, 0x700 << start_bit);
|
||||
|
||||
parent_data =
|
||||
irq_get_irq_data(
|
||||
domain->irq_res.gpio_irq[cnt].parent_virq);
|
||||
|
||||
/*set trigger type of gpio in GIC controller*/
|
||||
if (type & IRQ_TYPE_EDGE_BOTH)
|
||||
parent_data->chip->irq_set_type(parent_data,
|
||||
IRQ_TYPE_EDGE_RISING);
|
||||
else
|
||||
parent_data->chip->irq_set_type(parent_data,
|
||||
IRQ_TYPE_LEVEL_HIGH);
|
||||
|
||||
gpio_virq = irq_find_mapping(domain->chip.irqdomain,
|
||||
domain->irq_res.gpio_irq[cnt].hwirq);
|
||||
|
||||
pr_info("meson_pinctrl: gpio virq[%d] connect to GIC hwirq[%ld]\n",
|
||||
gpio_virq,
|
||||
parent_data->hwirq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
fwspec.fwnode = of_node_to_fwnode(domain->of_irq);
|
||||
fwspec.param_count = 2;
|
||||
fwspec.param[0] = hwirq;
|
||||
fwspec.param[1] = IRQ_TYPE_NONE;
|
||||
|
||||
return irq_create_fwspec_mapping(&fwspec);
|
||||
}
|
||||
static struct irq_chip meson_ee_gpio_irq_chip = {
|
||||
.name = "GPIO-EE",
|
||||
.irq_enable = meson_gpio_irq_enable,
|
||||
.irq_disable = meson_gpio_irq_disable,
|
||||
.irq_set_type = meson_ee_gpio_irq_type,
|
||||
.irq_mask = noop,
|
||||
.irq_unmask = noop,
|
||||
.irq_shutdown = meson_gpio_irq_shutdown,
|
||||
};
|
||||
|
||||
static struct irq_chip meson_ao_gpio_irq_chip = {
|
||||
.name = "GPIO-AO",
|
||||
.irq_enable = meson_gpio_irq_enable,
|
||||
.irq_disable = meson_gpio_irq_disable,
|
||||
.irq_set_type = meson_ao_gpio_irq_type,
|
||||
.irq_mask = noop,
|
||||
.irq_unmask = noop,
|
||||
.irq_shutdown = meson_gpio_irq_shutdown,
|
||||
};
|
||||
|
||||
struct meson_pinctrl_private meson_gxl_periphs = {
|
||||
.pinmux_type = PINMUX_V1,
|
||||
.pinctrl_data = &meson_gxl_periphs_pinctrl_data,
|
||||
.irq_chip = &meson_ee_gpio_irq_chip,
|
||||
.init = meson_gxl_periphs_init,
|
||||
};
|
||||
|
||||
struct meson_pinctrl_private meson_gxl_aobus = {
|
||||
.pinmux_type = PINMUX_V1,
|
||||
.pinctrl_data = &meson_gxl_aobus_pinctrl_data,
|
||||
.irq_chip = &meson_ao_gpio_irq_chip,
|
||||
.init = meson_gxl_aobus_init,
|
||||
};
|
||||
|
||||
struct meson_pinctrl_private meson_m8b_cbus = {
|
||||
.pinmux_type = PINMUX_V1,
|
||||
.pinctrl_data = &meson8b_cbus_pinctrl_data,
|
||||
.irq_chip = &meson_ee_gpio_irq_chip,
|
||||
.init = NULL,
|
||||
};
|
||||
|
||||
struct meson_pinctrl_private meson_m8b_aobus = {
|
||||
.pinmux_type = PINMUX_V1,
|
||||
.pinctrl_data = &meson8b_aobus_pinctrl_data,
|
||||
.irq_chip = &meson_ao_gpio_irq_chip,
|
||||
.init = NULL,
|
||||
};
|
||||
|
||||
struct meson_pinctrl_private meson_axg_periphs = {
|
||||
.pinmux_type = PINMUX_V2,
|
||||
.pinctrl_data = &meson_axg_periphs_pinctrl_data,
|
||||
.irq_chip = &meson_ee_gpio_irq_chip,
|
||||
.init = NULL,
|
||||
};
|
||||
|
||||
struct meson_pinctrl_private meson_axg_aobus = {
|
||||
.pinmux_type = PINMUX_V2,
|
||||
.pinctrl_data = &meson_axg_aobus_pinctrl_data,
|
||||
.irq_chip = &meson_ao_gpio_irq_chip,
|
||||
.init = meson_axg_aobus_init,
|
||||
};
|
||||
|
||||
struct meson_pinctrl_private meson_txlx_periphs = {
|
||||
.pinmux_type = PINMUX_V1,
|
||||
.pinctrl_data = &meson_txlx_periphs_pinctrl_data,
|
||||
.irq_chip = &meson_ee_gpio_irq_chip,
|
||||
.init = NULL,
|
||||
};
|
||||
|
||||
struct meson_pinctrl_private meson_txlx_aobus = {
|
||||
.pinmux_type = PINMUX_V1,
|
||||
.pinctrl_data = &meson_txlx_aobus_pinctrl_data,
|
||||
.irq_chip = &meson_ao_gpio_irq_chip,
|
||||
.init = meson_txlx_aobus_init,
|
||||
};
|
||||
|
||||
@@ -1145,6 +846,7 @@ static int meson_gpiolib_register(struct meson_pinctrl *pc)
|
||||
domain->chip.direction_output = meson_gpio_direction_output;
|
||||
domain->chip.get = meson_gpio_get;
|
||||
domain->chip.set = meson_gpio_set;
|
||||
domain->chip.to_irq = meson_gpio_to_irq;
|
||||
domain->chip.set_pull = meson_gpio_pull_set;
|
||||
domain->chip.base = domain->data->pin_base;
|
||||
domain->chip.ngpio = domain->data->num_pins;
|
||||
@@ -1205,30 +907,6 @@ static struct regmap *meson_map_resource(struct meson_pinctrl *pc,
|
||||
return devm_regmap_init_mmio(pc->dev, base, &meson_regmap_config);
|
||||
}
|
||||
|
||||
static int meson_irq_parse_and_map(struct meson_pinctrl *pc,
|
||||
struct device_node *node)
|
||||
{
|
||||
struct meson_domain *domain = pc->domain;
|
||||
int cnt;
|
||||
|
||||
domain->irq_res.irq_num = of_irq_count(node);
|
||||
if (!domain->irq_res.irq_num) {
|
||||
dev_err(pc->dev, "meson_pinctrl: can't find valid property 'interrupts'\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
domain->irq_res.gpio_irq = devm_kzalloc(pc->dev,
|
||||
sizeof(struct meson_gpio_irq_desc)*(domain->irq_res.irq_num),
|
||||
GFP_KERNEL);
|
||||
if (IS_ERR_OR_NULL(domain->irq_res.gpio_irq))
|
||||
return -ENOMEM;
|
||||
|
||||
for (cnt = 0; cnt < domain->irq_res.irq_num; cnt++) {
|
||||
domain->irq_res.gpio_irq[cnt].parent_virq =
|
||||
irq_of_parse_and_map(node, cnt);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_pinctrl_parse_dt(struct meson_pinctrl *pc,
|
||||
struct device_node *node)
|
||||
{
|
||||
@@ -1255,6 +933,9 @@ static int meson_pinctrl_parse_dt(struct meson_pinctrl *pc,
|
||||
domain = pc->domain;
|
||||
domain->data = pc->data->domain_data;
|
||||
|
||||
domain->of_irq = of_find_compatible_node(NULL,
|
||||
NULL, "amlogic,meson-gpio-intc");
|
||||
|
||||
for_each_child_of_node(node, np) {
|
||||
if (!of_find_property(np, "gpio-controller", NULL))
|
||||
continue;
|
||||
@@ -1284,76 +965,11 @@ static int meson_pinctrl_parse_dt(struct meson_pinctrl *pc,
|
||||
return PTR_ERR(domain->reg_gpio);
|
||||
}
|
||||
|
||||
domain->reg_irq = meson_map_resource(pc, np, "irq");
|
||||
if (IS_ERR(domain->reg_irq)) {
|
||||
dev_err(pc->dev, "gpio irq registers not found\n");
|
||||
return PTR_ERR(domain->reg_irq);
|
||||
}
|
||||
|
||||
meson_irq_parse_and_map(pc, np);
|
||||
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void meson_gpio_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
struct gpio_chip *gpio_chip = irq_desc_get_handler_data(desc);
|
||||
struct meson_domain *domain = to_meson_domain(gpio_chip);
|
||||
unsigned char cnt;
|
||||
unsigned int parent_virq;
|
||||
|
||||
parent_virq = irq_desc_get_irq(desc);
|
||||
|
||||
chained_irq_enter(chip, desc);
|
||||
for (cnt = 0; cnt < domain->irq_res.irq_num; cnt++) {
|
||||
if (parent_virq == domain->irq_res.gpio_irq[cnt].parent_virq &&
|
||||
domain->irq_res.gpio_irq[cnt].used_flag)
|
||||
generic_handle_irq(irq_find_mapping(gpio_chip->irqdomain,
|
||||
domain->irq_res.gpio_irq[cnt].hwirq));
|
||||
}
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
static int meson_irq_setup(struct meson_pinctrl *pc, struct irq_chip *irq_chip)
|
||||
{
|
||||
struct meson_domain *domain = pc->domain;
|
||||
struct irq_data *parent_data;
|
||||
unsigned char cnt;
|
||||
unsigned char ret;
|
||||
|
||||
spin_lock_init(&domain->irq_res.irq_res_lock);
|
||||
|
||||
ret = gpiochip_irqchip_add(&domain->chip,
|
||||
irq_chip,
|
||||
0,
|
||||
handle_simple_irq,
|
||||
IRQ_TYPE_NONE);
|
||||
if (ret) {
|
||||
dev_err(pc->dev, "couldn't add irqchip to gpiochip.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Then register the chain on the parent IRQ */
|
||||
for (cnt = 0; cnt < domain->irq_res.irq_num; cnt++) {
|
||||
gpiochip_set_chained_irqchip(&domain->chip,
|
||||
irq_chip,
|
||||
domain->irq_res.gpio_irq[cnt].parent_virq,
|
||||
meson_gpio_irq_handler);
|
||||
|
||||
/*disable the interrupt line of gpio in GIC controller*/
|
||||
parent_data =
|
||||
irq_get_irq_data(
|
||||
domain->irq_res.gpio_irq[cnt].parent_virq);
|
||||
parent_data->chip->irq_mask(parent_data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int meson_pinctrl_add_function(struct meson_pinctrl *pc,
|
||||
const char *name)
|
||||
{
|
||||
@@ -1546,8 +1162,6 @@ static int meson_pinctrl_probe(struct platform_device *pdev)
|
||||
if (priv->init)
|
||||
priv->init(pc);
|
||||
|
||||
meson_irq_setup(pc, priv->irq_chip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -116,41 +116,6 @@ struct meson_domain_data {
|
||||
unsigned int num_pins;
|
||||
};
|
||||
|
||||
/**
|
||||
*enum meson_irq_register - registers offset of gpio irq
|
||||
*/
|
||||
enum meson_irq_register {
|
||||
GPIO_IRQ_EDGE_OFFSET,
|
||||
GPIO_IRQ_MUX_0_3,
|
||||
GPIO_IRQ_MUX_4_7,
|
||||
GPIO_IRQ_FILTER_OFFSET,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct meson_gpio_irq_desc - describe the gpio irq
|
||||
*
|
||||
* @used_flag: indicate the 'parent_virq' whether be used or not
|
||||
* @parent_virq: gpio virtual interrupt number
|
||||
* @hwirq: hw irq for gpio
|
||||
*/
|
||||
struct meson_gpio_irq_desc {
|
||||
unsigned char used_flag;
|
||||
unsigned int parent_virq;
|
||||
unsigned int hwirq;
|
||||
};
|
||||
|
||||
/*struct meson_irq_res - describe resource for gpio irq
|
||||
*
|
||||
* @irq_num: number of gpio irq
|
||||
* @irq_res_lock:
|
||||
* @gpio_irq: a pointer to 'struct meson_gpio_irq_desc'
|
||||
*/
|
||||
struct meson_irq_resource {
|
||||
unsigned char irq_num;
|
||||
spinlock_t irq_res_lock;
|
||||
struct meson_gpio_irq_desc *gpio_irq;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct meson_domain
|
||||
*
|
||||
@@ -158,9 +123,7 @@ struct meson_irq_resource {
|
||||
* @reg_pullen: registers for pull-enable settings
|
||||
* @reg_pull: registers for pull settings
|
||||
* @reg_gpio: registers for gpio settings
|
||||
* @reg_irq: registers for gpio irq settings
|
||||
* @chip: gpio chip associated with the domain
|
||||
* @irq_res: irq resource
|
||||
* @data: platform data for the domain
|
||||
* @node: device tree node for the domain
|
||||
*
|
||||
@@ -172,11 +135,10 @@ struct meson_domain {
|
||||
struct regmap *reg_pullen;
|
||||
struct regmap *reg_pull;
|
||||
struct regmap *reg_gpio;
|
||||
struct regmap *reg_irq;
|
||||
|
||||
struct gpio_chip chip;
|
||||
struct meson_irq_resource irq_res;
|
||||
struct meson_domain_data *data;
|
||||
struct device_node *of_irq;
|
||||
struct device_node *of_node;
|
||||
};
|
||||
|
||||
@@ -202,7 +164,6 @@ struct meson_pinctrl {
|
||||
struct meson_pinctrl_private {
|
||||
unsigned char pinmux_type;
|
||||
struct meson_pinctrl_data *pinctrl_data;
|
||||
struct irq_chip *irq_chip;
|
||||
int (*init)(struct meson_pinctrl *pc);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user