diff --git a/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt index 4dfe69f26186..3f305fdf4f94 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt @@ -19,6 +19,7 @@ Required properties: “amlogic,meson-g12a-gpio-intc” for G12A SoCs (S905D2, S905X2, S905Y2) “amlogic,meson-txl-gpio-intc” for TXL SoCs (T950, T952, T960, T962) “amlogic,meson-tl1-gpio-intc” for TL1 SoCs (T962X2) + “amlogic,meson-sm1-gpio-intc” for SM1 SoCs (S905D3, S905X3, S905Y3) - 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. diff --git a/arch/arm/boot/dts/amlogic/mesonsm1.dtsi b/arch/arm/boot/dts/amlogic/mesonsm1.dtsi index 5cde488a9144..e1a5651c670e 100644 --- a/arch/arm/boot/dts/amlogic/mesonsm1.dtsi +++ b/arch/arm/boot/dts/amlogic/mesonsm1.dtsi @@ -524,7 +524,7 @@ gpio_intc: interrupt-controller@f080 { compatible = "amlogic,meson-gpio-intc", - "amlogic,meson-g12a-gpio-intc"; + "amlogic,meson-sm1-gpio-intc"; reg = <0xf080 0x10>; interrupt-controller; #interrupt-cells = <2>; diff --git a/arch/arm64/boot/dts/amlogic/mesonsm1.dtsi b/arch/arm64/boot/dts/amlogic/mesonsm1.dtsi index 97bb11763ecb..92da5f6ccdbf 100644 --- a/arch/arm64/boot/dts/amlogic/mesonsm1.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesonsm1.dtsi @@ -524,7 +524,7 @@ gpio_intc: interrupt-controller@f080 { compatible = "amlogic,meson-gpio-intc", - "amlogic,meson-g12a-gpio-intc"; + "amlogic,meson-sm1-gpio-intc"; reg = <0x0 0xf080 0x0 0x10>; interrupt-controller; #interrupt-cells = <2>; diff --git a/drivers/amlogic/irqchip/irq-meson-gpio.c b/drivers/amlogic/irqchip/irq-meson-gpio.c index a875b218f026..d353df4abff9 100644 --- a/drivers/amlogic/irqchip/irq-meson-gpio.c +++ b/drivers/amlogic/irqchip/irq-meson-gpio.c @@ -40,11 +40,13 @@ #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_EDGE_BOTH_EDGE(x) BIT(8 + (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; + u8 support_double_edge; }; static const struct meson_gpio_irq_params meson8_params = { @@ -83,6 +85,11 @@ static const struct meson_gpio_irq_params tl1_params = { .nr_hwirq = 102, }; +static const struct meson_gpio_irq_params sm1_params = { + .nr_hwirq = 100, + .support_double_edge = 1, +}; + 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 }, @@ -93,11 +100,13 @@ static const struct of_device_id meson_irq_gpio_matches[] = { { .compatible = "amlogic,meson-g12a-gpio-intc", .data = &g12a_params }, { .compatible = "amlogic,meson-txl-gpio-intc", .data = &txl_params }, { .compatible = "amlogic,meson-tl1-gpio-intc", .data = &tl1_params }, + { .compatible = "amlogic,meson-sm1-gpio-intc", .data = &sm1_params }, { } }; struct meson_gpio_irq_controller { unsigned int nr_hwirq; + u8 support_double_edge; void __iomem *base; u32 channel_irqs[NUM_CHANNEL]; DECLARE_BITMAP(channel_map, NUM_CHANNEL); @@ -200,8 +209,16 @@ static int meson_gpio_irq_type_setup(struct meson_gpio_irq_controller *ctl, */ type &= IRQ_TYPE_SENSE_MASK; - if (type == IRQ_TYPE_EDGE_BOTH) - return -EINVAL; + if (type == IRQ_TYPE_EDGE_BOTH) { + if (!ctl->support_double_edge) + return -EINVAL; + val |= REG_EDGE_BOTH_EDGE(idx); + spin_lock(&ctl->lock); + meson_gpio_irq_update_bits(ctl, REG_EDGE_POL, + REG_EDGE_BOTH_EDGE(idx), val); + spin_unlock(&ctl->lock); + return 0; + } if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) val |= REG_EDGE_POL_EDGE(idx); @@ -211,6 +228,14 @@ static int meson_gpio_irq_type_setup(struct meson_gpio_irq_controller *ctl, spin_lock(&ctl->lock); + /* Double-edge has priority over all others. If a double-edge gpio + * changes to another method's, we need to reset the corresponding bit + * of double-edge register. + */ + if (ctl->support_double_edge) + meson_gpio_irq_update_bits(ctl, REG_EDGE_POL, + REG_EDGE_BOTH_EDGE(idx), 0); + meson_gpio_irq_update_bits(ctl, REG_EDGE_POL, REG_EDGE_POL_MASK(idx), val); @@ -369,6 +394,7 @@ static int __init meson_gpio_irq_parse_dt(struct device_node *node, params = match->data; ctl->nr_hwirq = params->nr_hwirq; + ctl->support_double_edge = params->support_double_edge; ret = of_property_read_variable_u32_array(node, "amlogic,channel-interrupts",