--- /dev/null
+// 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);
+
+       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_pm_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)
+{
+       return (state == PM_SUSPEND_MEM);
+}
+
+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;
+       u64 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_u64(dev, "loongson,suspend-address", &suspend_addr))
+               loongson_sysconf.suspend_addr = (u64)phys_to_virt(suspend_addr);
+       else
+               dev_err(dev, "No loongson,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_pm_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,ls2k0500-pmc", },
+       { .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");