|
@@ -33,6 +33,7 @@
|
|
|
#include <linux/mfd/syscon.h>
|
|
|
#include <linux/delay.h>
|
|
|
#include <linux/interrupt.h>
|
|
|
+#include <linux/pm.h>
|
|
|
#include <dt-bindings/pinctrl/mt65xx.h>
|
|
|
|
|
|
#include "../core.h"
|
|
@@ -1062,6 +1063,77 @@ static int mtk_eint_set_type(struct irq_data *d,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int mtk_eint_irq_set_wake(struct irq_data *d, unsigned int on)
|
|
|
+{
|
|
|
+ struct mtk_pinctrl *pctl = irq_data_get_irq_chip_data(d);
|
|
|
+ int shift = d->hwirq & 0x1f;
|
|
|
+ int reg = d->hwirq >> 5;
|
|
|
+
|
|
|
+ if (on)
|
|
|
+ pctl->wake_mask[reg] |= BIT(shift);
|
|
|
+ else
|
|
|
+ pctl->wake_mask[reg] &= ~BIT(shift);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void mtk_eint_chip_write_mask(const struct mtk_eint_offsets *chip,
|
|
|
+ void __iomem *eint_reg_base, u32 *buf)
|
|
|
+{
|
|
|
+ int port;
|
|
|
+ void __iomem *reg;
|
|
|
+
|
|
|
+ for (port = 0; port < chip->ports; port++) {
|
|
|
+ reg = eint_reg_base + (port << 2);
|
|
|
+ writel_relaxed(~buf[port], reg + chip->mask_set);
|
|
|
+ writel_relaxed(buf[port], reg + chip->mask_clr);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void mtk_eint_chip_read_mask(const struct mtk_eint_offsets *chip,
|
|
|
+ void __iomem *eint_reg_base, u32 *buf)
|
|
|
+{
|
|
|
+ int port;
|
|
|
+ void __iomem *reg;
|
|
|
+
|
|
|
+ for (port = 0; port < chip->ports; port++) {
|
|
|
+ reg = eint_reg_base + chip->mask + (port << 2);
|
|
|
+ buf[port] = ~readl_relaxed(reg);
|
|
|
+ /* Mask is 0 when irq is enabled, and 1 when disabled. */
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int mtk_eint_suspend(struct device *device)
|
|
|
+{
|
|
|
+ void __iomem *reg;
|
|
|
+ struct mtk_pinctrl *pctl = dev_get_drvdata(device);
|
|
|
+ const struct mtk_eint_offsets *eint_offsets =
|
|
|
+ &pctl->devdata->eint_offsets;
|
|
|
+
|
|
|
+ reg = pctl->eint_reg_base;
|
|
|
+ mtk_eint_chip_read_mask(eint_offsets, reg, pctl->cur_mask);
|
|
|
+ mtk_eint_chip_write_mask(eint_offsets, reg, pctl->wake_mask);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int mtk_eint_resume(struct device *device)
|
|
|
+{
|
|
|
+ struct mtk_pinctrl *pctl = dev_get_drvdata(device);
|
|
|
+ const struct mtk_eint_offsets *eint_offsets =
|
|
|
+ &pctl->devdata->eint_offsets;
|
|
|
+
|
|
|
+ mtk_eint_chip_write_mask(eint_offsets,
|
|
|
+ pctl->eint_reg_base, pctl->cur_mask);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+const struct dev_pm_ops mtk_eint_pm_ops = {
|
|
|
+ .suspend = mtk_eint_suspend,
|
|
|
+ .resume = mtk_eint_resume,
|
|
|
+};
|
|
|
+
|
|
|
static void mtk_eint_ack(struct irq_data *d)
|
|
|
{
|
|
|
struct mtk_pinctrl *pctl = irq_data_get_irq_chip_data(d);
|
|
@@ -1076,10 +1148,12 @@ static void mtk_eint_ack(struct irq_data *d)
|
|
|
|
|
|
static struct irq_chip mtk_pinctrl_irq_chip = {
|
|
|
.name = "mt-eint",
|
|
|
+ .irq_disable = mtk_eint_mask,
|
|
|
.irq_mask = mtk_eint_mask,
|
|
|
.irq_unmask = mtk_eint_unmask,
|
|
|
.irq_ack = mtk_eint_ack,
|
|
|
.irq_set_type = mtk_eint_set_type,
|
|
|
+ .irq_set_wake = mtk_eint_irq_set_wake,
|
|
|
.irq_request_resources = mtk_pinctrl_irq_request_resources,
|
|
|
.irq_release_resources = mtk_pinctrl_irq_release_resources,
|
|
|
};
|
|
@@ -1211,7 +1285,7 @@ int mtk_pctrl_init(struct platform_device *pdev,
|
|
|
struct device_node *np = pdev->dev.of_node, *node;
|
|
|
struct property *prop;
|
|
|
struct resource *res;
|
|
|
- int i, ret, irq;
|
|
|
+ int i, ret, irq, ports_buf;
|
|
|
|
|
|
pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL);
|
|
|
if (!pctl)
|
|
@@ -1318,6 +1392,21 @@ int mtk_pctrl_init(struct platform_device *pdev,
|
|
|
goto chip_error;
|
|
|
}
|
|
|
|
|
|
+ ports_buf = pctl->devdata->eint_offsets.ports;
|
|
|
+ pctl->wake_mask = devm_kcalloc(&pdev->dev, ports_buf,
|
|
|
+ sizeof(*pctl->wake_mask), GFP_KERNEL);
|
|
|
+ if (!pctl->wake_mask) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto chip_error;
|
|
|
+ }
|
|
|
+
|
|
|
+ pctl->cur_mask = devm_kcalloc(&pdev->dev, ports_buf,
|
|
|
+ sizeof(*pctl->cur_mask), GFP_KERNEL);
|
|
|
+ if (!pctl->cur_mask) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto chip_error;
|
|
|
+ }
|
|
|
+
|
|
|
pctl->eint_dual_edges = devm_kcalloc(&pdev->dev, pctl->devdata->ap_num,
|
|
|
sizeof(int), GFP_KERNEL);
|
|
|
if (!pctl->eint_dual_edges) {
|