1
0
Fork 0

MLK-19305-2 gpio: mxc: add gpio PAD wakeup support

This patch enables gpio pin's pad wakeup function which
is supported by SCFW, with pad wakeup enabled, GPIO's
power is no need to be enabled after suspend, hence
save a sub-system's power.

To enable pad wakeup, dtb needs to provide pad wakeup
number for each gpio port, and each pin has to provide
<pin_id, type, line>, they should be inside each gpio node,
this is for calling SCFW APIs to enable/disable pad wakeup,
example of adding GPIO4_22 pad wakeup in dtb:

gpio4: gpio@5d0c0000 {
        compatible = "fsl,imx8qm-gpio", "fsl,imx35-gpio";
        reg = <0x0 0x5d0c0000 0x0 0x10000>;
        interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
        gpio-controller;
        #gpio-cells = <2>;
        power-domains = <&pd_lsio_gpio4>;
        interrupt-controller;
        #interrupt-cells = <2>;
        /* total pad wakeup number in gpio4 */
        pad-wakeup-num = <1>;
        /* SC_P_USDHC1_CD_B, SC_PAD_WAKEUP_LOW_LVL, LINE 22 */
        pad-wakeup = <27 4 22>;
};

Pad wakeup will be enabled after GPIO port suspend, and
once any pad wakes up system, gpio driver will get the
wakeup line and handles the event during noirq resume
phase.

Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
Reviewed-by: Bai Ping <ping.bai@nxp.com>
(cherry picked from commit 1c7ffe9bf3a115031cec8c759a4cd0e09146de09)
pull/10/head
Anson Huang 2018-08-23 11:11:36 +08:00 committed by Jason Liu
parent da291e8f6b
commit 4864262f8f
2 changed files with 140 additions and 0 deletions

View File

@ -354,6 +354,13 @@ config GPIO_MXS
select GPIO_GENERIC
select GENERIC_IRQ_CHIP
config GPIO_MXC_PAD_WAKEUP
def_bool n
depends on ARCH_MXC_ARM64 || COMPILE_TEST
select GPIO_MXC
help
Say Y here to enable the imx8 gpio pad wakeup
config GPIO_OCTEON
tristate "Cavium OCTEON GPIO"
depends on GPIOLIB && CAVIUM_OCTEON_SOC

View File

@ -37,6 +37,10 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/bug.h>
#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
#include <soc/imx8/sc/sci.h>
#include <soc/imx8/sc/svc/irq/api.h>
#endif
enum mxc_gpio_hwtype {
IMX1_GPIO, /* runs on i.mx1 */
@ -61,6 +65,14 @@ struct mxc_gpio_hwdata {
unsigned fall_edge;
};
#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
struct mxc_gpio_pad_wakeup {
u32 pin_id;
u32 type;
u32 line;
};
#endif
struct mxc_gpio_port {
struct list_head node;
struct clk *clk;
@ -74,8 +86,16 @@ struct mxc_gpio_port {
int saved_reg[6];
int suspend_saved_reg[6];
bool gpio_ranges;
#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
u32 pad_wakeup_num;
struct mxc_gpio_pad_wakeup pad_wakeup[32];
#endif
};
#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
static sc_ipc_t gpio_ipc_handle;
#endif
static struct mxc_gpio_hwdata imx1_imx21_gpio_hwdata = {
.dr_reg = 0x1c,
.gdir_reg = 0x00,
@ -317,6 +337,67 @@ static void mx2_gpio_irq_handler(struct irq_desc *desc)
chained_irq_exit(chip, desc);
}
#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
static int mxc_gpio_get_pad_wakeup(struct mxc_gpio_port *port)
{
sc_err_t sciErr;
u8 wakeup_type;
int i;
for (i = 0; i < port->pad_wakeup_num; i++) {
/* get original pad type */
wakeup_type = port->pad_wakeup[i].type;
sciErr = sc_pad_get_wakeup(gpio_ipc_handle,
port->pad_wakeup[i].pin_id, &wakeup_type);
if (sciErr)
dev_err(port->gc.parent, "sc_pad_get_wakeup failed\n");
/* return wakeup gpio pin's line */
if (wakeup_type != port->pad_wakeup[i].type)
return port->pad_wakeup[i].line;
}
return -EINVAL;
}
static void mxc_gpio_set_pad_wakeup(struct mxc_gpio_port *port, bool enable)
{
sc_err_t sciErr;
int i;
for (i = 0; i < port->pad_wakeup_num; i++) {
sciErr = sc_pad_set_wakeup(gpio_ipc_handle,
port->pad_wakeup[i].pin_id,
enable ? port->pad_wakeup[i].type :
SC_PAD_WAKEUP_OFF);
if (sciErr)
dev_err(port->gc.parent, "sc_pad_set_wakeup failed\n");
}
}
static void mxc_gpio_handle_pad_wakeup(struct mxc_gpio_port *port, int line)
{
struct irq_desc *desc = irq_to_desc(port->irq);
struct irq_chip *chip = irq_desc_get_chip(desc);
u32 irq_stat;
/* skip invalid line */
if (line > 31) {
dev_err(port->gc.parent, "invalid wakeup line %d\n", line);
return;
}
dev_info(port->gc.parent, "wakeup by pad, line %d\n", line);
chained_irq_enter(chip, desc);
irq_stat = (1 << line);
mxc_gpio_irq_handler(port, irq_stat);
chained_irq_exit(chip, desc);
}
#endif
/*
* Set interrupt number "irq" in the GPIO as a wake-up source.
* While system is running, all registered GPIO interrupts need to have
@ -473,6 +554,11 @@ static int mxc_gpio_probe(struct platform_device *pdev)
struct resource *iores;
int irq_base = 0;
int err;
#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
int i;
uint32_t mu_id;
sc_err_t sciErr;
#endif
mxc_gpio_get_hw(pdev);
@ -506,6 +592,41 @@ static int mxc_gpio_probe(struct platform_device *pdev)
return err;
}
#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
/*
* parse pad wakeup info from dtb, each pad has to provide
* <pin_id, type, line>, these info should be put in each
* gpio node and with a "pad-wakeup-num" to indicate the
* total lines are with pad wakeup enabled.
*/
if (!of_property_read_u32(np, "pad-wakeup-num", &port->pad_wakeup_num)) {
if (port->pad_wakeup_num != 0) {
if (!gpio_ipc_handle) {
sciErr = sc_ipc_getMuID(&mu_id);
if (sciErr != SC_ERR_NONE) {
dev_err(&pdev->dev,
"can not obtain mu id: %d\n", sciErr);
return sciErr;
}
sciErr = sc_ipc_open(&gpio_ipc_handle, mu_id);
if (sciErr != SC_ERR_NONE) {
dev_err(&pdev->dev,
"can not open mu channel to scu: %d\n", sciErr);
return sciErr;
}
}
for (i = 0; i < port->pad_wakeup_num; i++) {
of_property_read_u32_index(np, "pad-wakeup",
i * 3 + 0, &port->pad_wakeup[i].pin_id);
of_property_read_u32_index(np, "pad-wakeup",
i * 3 + 1, &port->pad_wakeup[i].type);
of_property_read_u32_index(np, "pad-wakeup",
i * 3 + 2, &port->pad_wakeup[i].line);
}
}
}
#endif
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
err = pm_runtime_get_sync(&pdev->dev);
@ -660,6 +781,9 @@ static int __maybe_unused mxc_gpio_noirq_suspend(struct device *dev)
unsigned long flags;
int ret;
#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
mxc_gpio_set_pad_wakeup(port, true);
#endif
if (mxc_gpio_hwtype == IMX21_GPIO)
return 0;
@ -687,6 +811,11 @@ static int __maybe_unused mxc_gpio_noirq_resume(struct device *dev)
struct mxc_gpio_port *port = platform_get_drvdata(pdev);
unsigned long flags;
int ret;
#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
int wakeup_line = mxc_gpio_get_pad_wakeup(port);
mxc_gpio_set_pad_wakeup(port, false);
#endif
if (mxc_gpio_hwtype == IMX21_GPIO)
return 0;
@ -704,6 +833,10 @@ static int __maybe_unused mxc_gpio_noirq_resume(struct device *dev)
writel(port->suspend_saved_reg[5], port->base + GPIO_DR);
spin_unlock_irqrestore(&port->gc.bgpio_lock, flags);
#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
if (wakeup_line > 0)
mxc_gpio_handle_pad_wakeup(port, wakeup_line);
#endif
clk_disable_unprepare(port->clk);
return 0;