brintos

brintos / linux-shallow public Read only

0
0
Text · 5.4 KiB · b8e5e1e Raw
221 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Loongson-2 PM Support4 *5 * Copyright (C) 2023 Loongson Technology Corporation Limited6 */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		0x1421#define LOONGSON2_PM1_STS_REG		0x0c22#define LOONGSON2_PM1_ENA_REG		0x1023#define LOONGSON2_GPE0_STS_REG		0x2824#define LOONGSON2_GPE0_ENA_REG		0x2c25 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_STS31 32static 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 43static 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 54static 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 67static 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 79static int loongson2_suspend_begin(suspend_state_t state)80{81	pm_set_suspend_via_firmware();82 83	return 0;84}85 86static int loongson2_suspend_valid_state(suspend_state_t state)87{88	return (state == PM_SUSPEND_MEM);89}90 91static 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 97static 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 125free_dev:126	input_free_device(button);127 128	return ret;129}130 131static 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 148static int __maybe_unused loongson2_pm_suspend(struct device *dev)149{150	loongson2_pm.suspended = true;151 152	return 0;153}154 155static int __maybe_unused loongson2_pm_resume(struct device *dev)156{157	loongson2_pm.suspended = false;158 159	return 0;160}161static SIMPLE_DEV_PM_OPS(loongson2_pm_ops, loongson2_pm_suspend, loongson2_pm_resume);162 163static 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	else180		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 204static const struct of_device_id loongson2_pm_match[] = {205	{ .compatible = "loongson,ls2k0500-pmc", },206	{},207};208 209static 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};217module_platform_driver(loongson2_pm_driver);218 219MODULE_DESCRIPTION("Loongson-2 PM driver");220MODULE_LICENSE("GPL");221