lkml.org 
[lkml]   [2023]   [Jun]   [12]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
From
Date
SubjectRe: [PATCH v2 3/3] soc: loongson2_pm: add power management support
Hi, Yinbo,

On Mon, May 22, 2023 at 5:33 PM Yinbo Zhu <zhuyinbo@loongson.cn> wrote:
>
> The Loongson-2's Power Management Controller was ACPI, supports ACPI
> S2Idle (Suspend To Idle), ACPI S3 (Suspend To RAM), ACPI S4 (Suspend To
> Disk), ACPI S5 (Soft Shutdown) and supports multiple wake-up methods
> (USB, GMAC, PWRBTN, etc.). This driver was to add Power Management
> Controller support that base on dts for Loongson-2 series SoCs.
>
> Signed-off-by: Liu Yun <liuyun@loongson.cn>
> Signed-off-by: Liu Peibao <liupeibao@loongson.cn>
> Signed-off-by: Yinbo Zhu <zhuyinbo@loongson.cn>
> ---
> MAINTAINERS | 1 +
> drivers/soc/loongson/Kconfig | 10 ++
> drivers/soc/loongson/Makefile | 1 +
> drivers/soc/loongson/loongson2_pm.c | 235 ++++++++++++++++++++++++++++
> 4 files changed, 247 insertions(+)
> create mode 100644 drivers/soc/loongson/loongson2_pm.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index bcd05f1fa5c1..7c4ad0cbaeff 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -12195,6 +12195,7 @@ M: Yinbo Zhu <zhuyinbo@loongson.cn>
> L: linux-pm@vger.kernel.org
> S: Maintained
> F: Documentation/devicetree/bindings/soc/loongson/loongson,ls2k-pmc.yaml
> +F: drivers/soc/loongson/loongson2_pm.c
>
> LOONGSON-2 SOC SERIES PINCTRL DRIVER
> M: zhanghongchen <zhanghongchen@loongson.cn>
> diff --git a/drivers/soc/loongson/Kconfig b/drivers/soc/loongson/Kconfig
> index 707f56358dc4..2431a0bcbd84 100644
> --- a/drivers/soc/loongson/Kconfig
> +++ b/drivers/soc/loongson/Kconfig
> @@ -16,3 +16,13 @@ config LOONGSON2_GUTS
> SoCs. Initially only reading SVR and registering soc device are
> supported. Other guts accesses, such as reading firmware configuration
> by default, should eventually be added into this driver as well.
> +
> +config LOONGSON2_PM
> + bool "Loongson-2 SoC Power Management Controller Driver"
> + depends on LOONGARCH && OF
> + help
> + The Loongson-2's Power Management Controller was ACPI, supports ACPI
> + S2Idle (Suspend To Idle), ACPI S3 (Suspend To RAM), ACPI S4 (Suspend To
> + Disk), ACPI S5 (Soft Shutdown) and supports multiple wake-up methods
> + (USB, GMAC, PWRBTN, etc.). This driver was to add Power Management
> + Controller support that base on dts for Loongson-2 series SoCs.
> diff --git a/drivers/soc/loongson/Makefile b/drivers/soc/loongson/Makefile
> index 263c486df638..4118f50f55e2 100644
> --- a/drivers/soc/loongson/Makefile
> +++ b/drivers/soc/loongson/Makefile
> @@ -4,3 +4,4 @@
> #
>
> obj-$(CONFIG_LOONGSON2_GUTS) += loongson2_guts.o
> +obj-$(CONFIG_LOONGSON2_PM) += loongson2_pm.o
> diff --git a/drivers/soc/loongson/loongson2_pm.c b/drivers/soc/loongson/loongson2_pm.c
> new file mode 100644
> index 000000000000..cd96a1ebbb6c
> --- /dev/null
> +++ b/drivers/soc/loongson/loongson2_pm.c
> @@ -0,0 +1,235 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Loongson-2 PM Support
> + *
> + * Copyright (C) 2023 Loongson Technology Corporation Limited
> + */
> +
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/init.h>
> +#include <linux/input.h>
> +#include <linux/suspend.h>
> +#include <linux/interrupt.h>
> +#include <linux/pm_wakeirq.h>
> +#include <linux/platform_device.h>
> +#include <asm/bootinfo.h>
> +#include <asm/suspend.h>
> +
> +#define LOONGSON2_PM1_CNT_REG 0x14
> +#define LOONGSON2_PM1_STS_REG 0x0c
> +#define LOONGSON2_PM1_ENA_REG 0x10
> +#define LOONGSON2_GPE0_STS_REG 0x28
> +#define LOONGSON2_GPE0_ENA_REG 0x2c
> +
> +#define LOONGSON2_PM1_PWRBTN_STS BIT(8)
> +#define LOONGSON2_PM1_PCIEXP_WAKE_STS BIT(14)
> +#define LOONGSON2_PM1_WAKE_STS BIT(15)
> +#define LOONGSON2_PM1_CNT_INT_EN BIT(0)
> +#define LOONGSON2_PM1_PWRBTN_EN LOONGSON2_PM1_PWRBTN_STS
> +
> +static struct loongson2_pm {
> + void __iomem *base;
> + struct input_dev *dev;
> + bool suspended;
> +} loongson2_pm;
> +
> +#define loongson2_pm_readw(reg) readw(loongson2_pm.base + reg)
> +#define loongson2_pm_readl(reg) readl(loongson2_pm.base + reg)
> +#define loongson2_pm_writew(val, reg) writew(val, loongson2_pm.base + reg)
> +#define loongson2_pm_writel(val, reg) writel(val, loongson2_pm.base + reg)
> +
> +static void loongson2_pm_status_clear(void)
> +{
> + u16 value;
> +
> + value = loongson2_pm_readw(LOONGSON2_PM1_STS_REG);
> + value |= (LOONGSON2_PM1_PWRBTN_STS | LOONGSON2_PM1_PCIEXP_WAKE_STS |
> + LOONGSON2_PM1_WAKE_STS);
> + loongson2_pm_writew(value, LOONGSON2_PM1_STS_REG);
> + loongson2_pm_writel(loongson2_pm_readl(LOONGSON2_GPE0_STS_REG),
> + LOONGSON2_GPE0_STS_REG);
> +}
> +
> +static void loongson2_pm_irq_enable(void)
> +{
> + u16 value;
> +
> + value = loongson2_pm_readw(LOONGSON2_PM1_CNT_REG);
> + value |= LOONGSON2_PM1_CNT_INT_EN;
> + loongson2_pm_writew(value, LOONGSON2_PM1_CNT_REG);
> +}
> +
> +static void loongson2_pm_pwrbtn_irq_enable(void)
> +{
> + u16 value;
> +
> + loongson2_pm_irq_enable();
> +
> + value = loongson2_pm_readw(LOONGSON2_PM1_ENA_REG);
> + value |= LOONGSON2_PM1_PWRBTN_EN;
> + loongson2_pm_writew(value, LOONGSON2_PM1_ENA_REG);
> +}
You can combine these two functions as loongson2_power_button_irq_enable().

> +
> +static void loongson2_pm_mach_resume(void)
> +{
> + loongson_common_resume();
> + loongson2_pm_irq_enable();
> +}
> +
> +static void loongson2_pm_mach_suspend(void)
> +{
> + loongson2_pm_status_clear();
> + loongson_common_suspend();
> +}
> +
> +static int loongson2_suspend_enter(suspend_state_t state)
> +{
> + loongson2_pm_mach_suspend();
> + loongson_suspend_enter();
> + pm_set_resume_via_firmware();
> + loongson2_pm_mach_resume();
> +
> + return 0;
> +}
After some thinkings, I found these three simple function can be combined as:
static int loongson2_suspend_enter(suspend_state_t state)
{
loongson2_pm_status_clear();
loongson_common_suspend();
loongson_suspend_enter();
loongson_common_resume();
loongson2_pm_irq_enable();
pm_set_resume_via_firmware();

return 0;
}

After this combining,
loongson_common_suspend()/loongson_suspend_enter()/loongson_common_resume()
can be still use the old naming
loongarch_common_suspend()/loongarch_suspend_enter()/loongarch_common_resume().

> +
> +static int loongson2_suspend_begin(suspend_state_t state)
> +{
> + pm_set_suspend_via_firmware();
> +
> + return 0;
> +}
> +
> +static int loongson2_suspend_valid_state(suspend_state_t state)
> +{
> + if (state == PM_SUSPEND_MEM)
> + return !!loongson_sysconf.suspend_addr;
> +
> + return 0;
> +}
> +
> +static const struct platform_suspend_ops loongson2_suspend_ops = {
> + .valid = loongson2_suspend_valid_state,
> + .begin = loongson2_suspend_begin,
> + .enter = loongson2_suspend_enter,
> +};
> +
> +static int loongson2_pm_pwrbtn_init(struct device *dev, int irq)
> +{
> + int ret;
> + struct input_dev *pwrbt;
Rename loongson2_pm_pwrbtn_init() to loongson2_power_button_init()
and rename 'pwrbt' to 'pwrbtn' or just 'button' is better.

Huacai
> +
> + pwrbt = input_allocate_device();
> + if (!dev)
> + return -ENOMEM;
> +
> + pwrbt->name = "Power Button";
> + pwrbt->phys = "pm/button/input0";
> + pwrbt->id.bustype = BUS_HOST;
> + pwrbt->dev.parent = NULL;
> + input_set_capability(pwrbt, EV_KEY, KEY_POWER);
> +
> + ret = input_register_device(pwrbt);
> + if (ret)
> + goto free_dev;
> +
> + dev_pm_set_wake_irq(&pwrbt->dev, irq);
> + device_set_wakeup_capable(&pwrbt->dev, true);
> + device_set_wakeup_enable(&pwrbt->dev, true);
> +
> + loongson2_pm.dev = pwrbt;
> + dev_info(dev, "Power Button: Init successful!\n");
> +
> + return 0;
> +
> +free_dev:
> + input_free_device(pwrbt);
> +
> + return ret;
> +}
> +
> +static irqreturn_t loongson2_pm_irq_handler(int irq, void *dev_id)
> +{
> + u16 status = loongson2_pm_readw(LOONGSON2_PM1_STS_REG);
> +
> + if (!loongson2_pm.suspended && (status & LOONGSON2_PM1_PWRBTN_STS)) {
> + pr_info("Power Button pressed...\n");
> + input_report_key(loongson2_pm.dev, KEY_POWER, 1);
> + input_sync(loongson2_pm.dev);
> + input_report_key(loongson2_pm.dev, KEY_POWER, 0);
> + input_sync(loongson2_pm.dev);
> + }
> +
> + loongson2_pm_status_clear();
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int __maybe_unused loongson2_pm_suspend(struct device *dev)
> +{
> + loongson2_pm.suspended = true;
> +
> + return 0;
> +}
> +
> +static int __maybe_unused loongson2_pm_resume(struct device *dev)
> +{
> + loongson2_pm.suspended = false;
> +
> + return 0;
> +}
> +static SIMPLE_DEV_PM_OPS(loongson2_pm_ops, loongson2_pm_suspend, loongson2_pm_resume);
> +
> +static int loongson2_pm_probe(struct platform_device *pdev)
> +{
> + int irq, retval;
> + u32 suspend_addr;
> + struct device *dev = &pdev->dev;
> +
> + loongson2_pm.base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(loongson2_pm.base))
> + return PTR_ERR(loongson2_pm.base);
> +
> + irq = platform_get_irq(pdev, 0);
> + if (irq < 0)
> + return irq;
> +
> + if (!device_property_read_u32(dev, "suspend-address", &suspend_addr))
> + loongson_sysconf.suspend_addr = (u64)phys_to_virt(suspend_addr);
> + else
> + dev_err(dev, "No suspend-address, could not support S3!\n");
> +
> + if (loongson2_pm_pwrbtn_init(dev, irq))
> + return -EINVAL;
> +
> + retval = devm_request_irq(&pdev->dev, irq, loongson2_pm_irq_handler,
> + IRQF_SHARED, "pm_irq", &loongson2_pm);
> + if (retval)
> + return retval;
> +
> + loongson2_pm_pwrbtn_irq_enable();
> + loongson2_pm_status_clear();
> +
> + if (loongson_sysconf.suspend_addr)
> + suspend_set_ops(&loongson2_suspend_ops);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id loongson2_pm_match[] = {
> + { .compatible = "loongson,ls2k-pmc", },
> + {},
> +};
> +
> +static struct platform_driver loongson2_pm_driver = {
> + .driver = {
> + .name = "ls2k-pm",
> + .pm = &loongson2_pm_ops,
> + .of_match_table = loongson2_pm_match,
> + },
> + .probe = loongson2_pm_probe,
> +};
> +module_platform_driver(loongson2_pm_driver);
> +
> +MODULE_DESCRIPTION("Loongson-2 PM driver");
> +MODULE_LICENSE("GPL");
> --
> 2.20.1
>
>

\
 
 \ /
  Last update: 2023-06-12 06:38    [W:0.056 / U:0.304 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site