1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Loongson-2 PM Support 4 * 5 * Copyright (C) 2023 Loongson Technology Corporation Limited 6 */ 7 8 #include <linux/io.h> 9 #include <linux/of.h> 10 #include <linux/init.h> 11 #include <linux/input.h> 12 #include <linux/suspend.h> 13 #include <linux/interrupt.h> 14 #include <linux/of_platform.h> 15 #include <linux/pm_wakeirq.h> 16 #include <linux/platform_device.h> 17 #include <asm/bootinfo.h> 18 #include <asm/suspend.h> 19 20 #define LOONGSON2_PM1_CNT_REG 0x14 21 #define LOONGSON2_PM1_STS_REG 0x0c 22 #define LOONGSON2_PM1_ENA_REG 0x10 23 #define LOONGSON2_GPE0_STS_REG 0x28 24 #define LOONGSON2_GPE0_ENA_REG 0x2c 25 26 #define LOONGSON2_PM1_PWRBTN_STS BIT(8) 27 #define LOONGSON2_PM1_PCIEXP_WAKE_STS BIT(14) 28 #define LOONGSON2_PM1_WAKE_STS BIT(15) 29 #define LOONGSON2_PM1_CNT_INT_EN BIT(0) 30 #define LOONGSON2_PM1_PWRBTN_EN LOONGSON2_PM1_PWRBTN_STS 31 32 static struct loongson2_pm { 33 void __iomem *base; 34 struct input_dev *dev; 35 bool suspended; 36 } loongson2_pm; 37 38 #define loongson2_pm_readw(reg) readw(loongson2_pm.base + reg) 39 #define loongson2_pm_readl(reg) readl(loongson2_pm.base + reg) 40 #define loongson2_pm_writew(val, reg) writew(val, loongson2_pm.base + reg) 41 #define loongson2_pm_writel(val, reg) writel(val, loongson2_pm.base + reg) 42 43 static void loongson2_pm_status_clear(void) 44 { 45 u16 value; 46 47 value = loongson2_pm_readw(LOONGSON2_PM1_STS_REG); 48 value |= (LOONGSON2_PM1_PWRBTN_STS | LOONGSON2_PM1_PCIEXP_WAKE_STS | 49 LOONGSON2_PM1_WAKE_STS); 50 loongson2_pm_writew(value, LOONGSON2_PM1_STS_REG); 51 loongson2_pm_writel(loongson2_pm_readl(LOONGSON2_GPE0_STS_REG), LOONGSON2_GPE0_STS_REG); 52 } 53 54 static void loongson2_pm_irq_enable(void) 55 { 56 u16 value; 57 58 value = loongson2_pm_readw(LOONGSON2_PM1_CNT_REG); 59 value |= LOONGSON2_PM1_CNT_INT_EN; 60 loongson2_pm_writew(value, LOONGSON2_PM1_CNT_REG); 61 62 value = loongson2_pm_readw(LOONGSON2_PM1_ENA_REG); 63 value |= LOONGSON2_PM1_PWRBTN_EN; 64 loongson2_pm_writew(value, LOONGSON2_PM1_ENA_REG); 65 } 66 67 static int loongson2_suspend_enter(suspend_state_t state) 68 { 69 loongson2_pm_status_clear(); 70 loongarch_common_suspend(); 71 loongarch_suspend_enter(); 72 loongarch_common_resume(); 73 loongson2_pm_irq_enable(); 74 pm_set_resume_via_firmware(); 75 76 return 0; 77 } 78 79 static int loongson2_suspend_begin(suspend_state_t state) 80 { 81 pm_set_suspend_via_firmware(); 82 83 return 0; 84 } 85 86 static int loongson2_suspend_valid_state(suspend_state_t state) 87 { 88 return (state == PM_SUSPEND_MEM); 89 } 90 91 static const struct platform_suspend_ops loongson2_suspend_ops = { 92 .valid = loongson2_suspend_valid_state, 93 .begin = loongson2_suspend_begin, 94 .enter = loongson2_suspend_enter, 95 }; 96 97 static int loongson2_power_button_init(struct device *dev, int irq) 98 { 99 int ret; 100 struct input_dev *button; 101 102 button = input_allocate_device(); 103 if (!dev) 104 return -ENOMEM; 105 106 button->name = "Power Button"; 107 button->phys = "pm/button/input0"; 108 button->id.bustype = BUS_HOST; 109 button->dev.parent = NULL; 110 input_set_capability(button, EV_KEY, KEY_POWER); 111 112 ret = input_register_device(button); 113 if (ret) 114 goto free_dev; 115 116 dev_pm_set_wake_irq(&button->dev, irq); 117 device_set_wakeup_capable(&button->dev, true); 118 device_set_wakeup_enable(&button->dev, true); 119 120 loongson2_pm.dev = button; 121 dev_info(dev, "Power Button: Init successful!\n"); 122 123 return 0; 124 125 free_dev: 126 input_free_device(button); 127 128 return ret; 129 } 130 131 static irqreturn_t loongson2_pm_irq_handler(int irq, void *dev_id) 132 { 133 u16 status = loongson2_pm_readw(LOONGSON2_PM1_STS_REG); 134 135 if (!loongson2_pm.suspended && (status & LOONGSON2_PM1_PWRBTN_STS)) { 136 pr_info("Power Button pressed...\n"); 137 input_report_key(loongson2_pm.dev, KEY_POWER, 1); 138 input_sync(loongson2_pm.dev); 139 input_report_key(loongson2_pm.dev, KEY_POWER, 0); 140 input_sync(loongson2_pm.dev); 141 } 142 143 loongson2_pm_status_clear(); 144 145 return IRQ_HANDLED; 146 } 147 148 static int __maybe_unused loongson2_pm_suspend(struct device *dev) 149 { 150 loongson2_pm.suspended = true; 151 152 return 0; 153 } 154 155 static int __maybe_unused loongson2_pm_resume(struct device *dev) 156 { 157 loongson2_pm.suspended = false; 158 159 return 0; 160 } 161 static SIMPLE_DEV_PM_OPS(loongson2_pm_ops, loongson2_pm_suspend, loongson2_pm_resume); 162 163 static int loongson2_pm_probe(struct platform_device *pdev) 164 { 165 int irq, retval; 166 u64 suspend_addr; 167 struct device *dev = &pdev->dev; 168 169 loongson2_pm.base = devm_platform_ioremap_resource(pdev, 0); 170 if (IS_ERR(loongson2_pm.base)) 171 return PTR_ERR(loongson2_pm.base); 172 173 irq = platform_get_irq(pdev, 0); 174 if (irq < 0) 175 return irq; 176 177 if (!device_property_read_u64(dev, "loongson,suspend-address", &suspend_addr)) 178 loongson_sysconf.suspend_addr = (u64)phys_to_virt(suspend_addr); 179 else 180 dev_err(dev, "No loongson,suspend-address, could not support S3!\n"); 181 182 if (loongson2_power_button_init(dev, irq)) 183 return -EINVAL; 184 185 retval = devm_request_irq(&pdev->dev, irq, loongson2_pm_irq_handler, 186 IRQF_SHARED, "pm_irq", &loongson2_pm); 187 if (retval) 188 return retval; 189 190 loongson2_pm_irq_enable(); 191 loongson2_pm_status_clear(); 192 193 if (loongson_sysconf.suspend_addr) 194 suspend_set_ops(&loongson2_suspend_ops); 195 196 /* Populate children */ 197 retval = devm_of_platform_populate(dev); 198 if (retval) 199 dev_err(dev, "Error populating children, reboot and poweroff might not work properly\n"); 200 201 return 0; 202 } 203 204 static const struct of_device_id loongson2_pm_match[] = { 205 { .compatible = "loongson,ls2k0500-pmc", }, 206 {}, 207 }; 208 209 static struct platform_driver loongson2_pm_driver = { 210 .driver = { 211 .name = "ls2k-pm", 212 .pm = &loongson2_pm_ops, 213 .of_match_table = loongson2_pm_match, 214 }, 215 .probe = loongson2_pm_probe, 216 }; 217 module_platform_driver(loongson2_pm_driver); 218 219 MODULE_DESCRIPTION("Loongson-2 PM driver"); 220 MODULE_LICENSE("GPL"); 221