[v3,0/3] soc: loongson2_pm: add power management support

Message ID 20230615091757.24686-1-zhuyinbo@loongson.cn
Headers
Series soc: loongson2_pm: add power management support |

Message

Yinbo Zhu June 15, 2023, 9:17 a.m. UTC
  Loongson-2 platform support Power Management Controller (ACPI) and this
series patch was to add PM driver that base on dts and PM binding support.

Change in v3:
		1. Reword the [1/3] patch commit log and title.
		2. Use the old naming for suspend interface for the [1/3] and
		   [3/3] patch.
		3. Combine some small function in the driver patch.
		4. Rename 'pwrbt' to 'button' in the driver patch.
		5. Use the specific compatible in yaml file.
Change in v2:
		1. Fixup the "suspend-address" description.
		2. Remove the "return -EINVAL" in PM driver probe when firmware
		   no configure "suspend-address" property in dts in oder to
		   other PM state to work.

Yinbo Zhu (3):
  loongarch: export some arch-specific pm interfaces
  soc: dt-bindings: add loongson-2 pm
  soc: loongson2_pm: add power management support

 .../soc/loongson/loongson,ls2k-pmc.yaml       |  53 +++++
 MAINTAINERS                                   |   7 +
 arch/loongarch/include/asm/acpi.h             |   3 +-
 arch/loongarch/include/asm/suspend.h          |  10 +
 arch/loongarch/power/suspend.c                |   8 +-
 drivers/soc/loongson/Kconfig                  |  10 +
 drivers/soc/loongson/Makefile                 |   1 +
 drivers/soc/loongson/loongson2_pm.c           | 218 ++++++++++++++++++
 8 files changed, 304 insertions(+), 6 deletions(-)
  

Comments

Huacai Chen June 15, 2023, 10 a.m. UTC | #1
Hi, Yinbo,

I'm sorry I still have some comments.

On Thu, Jun 15, 2023 at 5:37 PM zhuyinbo <zhuyinbo@loongson.cn> wrote:
>
>  From 6edcb9d6a1b18ccbecaf283b4f543afc9e7126d6 Mon Sep 17 00:00:00 2001
> From: Yinbo Zhu <zhuyinbo@loongson.cn>
> Date: Tue, 18 Apr 2023 14:18:00 +0800
> Subject: [PATCH v3 3/3] soc: loongson2_pm: add power management support
>
> 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 | 218 ++++++++++++++++++++++++++++
>   4 files changed, 230 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..4f3ce9eb7520 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..287828413d72
> --- /dev/null
> +++ b/drivers/soc/loongson/loongson2_pm.c
> @@ -0,0 +1,218 @@
> +// 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);
Long-line warnings is removed in latest kernel, so you don't need to split here.

> +}
> +
> +static void loongson2_power_button_irq_enable(void)

Using loongson2_pm_irq_enable is a little better.

> +{
> +       u16 value;
> +
> +       value = loongson2_pm_readw(LOONGSON2_PM1_CNT_REG);
> +       value |= LOONGSON2_PM1_CNT_INT_EN;
> +       loongson2_pm_writew(value, LOONGSON2_PM1_CNT_REG);
> +
> +       value = loongson2_pm_readw(LOONGSON2_PM1_ENA_REG);
> +       value |= LOONGSON2_PM1_PWRBTN_EN;
> +       loongson2_pm_writew(value, LOONGSON2_PM1_ENA_REG);
> +}
> +
> +static int loongson2_suspend_enter(suspend_state_t state)
> +{
> +       loongson2_pm_status_clear();
> +       loongarch_common_suspend();
> +       loongarch_suspend_enter();
> +       loongarch_common_resume();
> +       loongson2_power_button_irq_enable();
> +       pm_set_resume_via_firmware();
> +
> +       return 0;
> +}
> +
> +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 1;
> +
> +       return 0;
"return (state == PM_SUSPEND_MEM)" is enough.

Huacai
> +}
> +
> +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_power_button_init(struct device *dev, int irq)
> +{
> +       int ret;
> +       struct input_dev *button;
> +
> +       button = input_allocate_device();
> +       if (!dev)
> +               return -ENOMEM;
> +
> +       button->name = "Power Button";
> +       button->phys = "pm/button/input0";
> +       button->id.bustype = BUS_HOST;
> +       button->dev.parent = NULL;
> +       input_set_capability(button, EV_KEY, KEY_POWER);
> +
> +       ret = input_register_device(button);
> +       if (ret)
> +               goto free_dev;
> +
> +       dev_pm_set_wake_irq(&button->dev, irq);
> +       device_set_wakeup_capable(&button->dev, true);
> +       device_set_wakeup_enable(&button->dev, true);
> +
> +       loongson2_pm.dev = button;
> +       dev_info(dev, "Power Button: Init successful!\n");
> +
> +       return 0;
> +
> +free_dev:
> +       input_free_device(button);
> +
> +       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_power_button_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_power_button_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,ls2k1000-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
>
>