brintos

brintos / linux-shallow public Read only

0
0
Text · 4.3 KiB · 06d6992 Raw
176 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Gemini power management controller4 * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>5 *6 * Inspired by code from the SL3516 board support by Jason Lee7 * Inspired by code from Janos Laube <janos.dev@gmail.com>8 */9#include <linux/of.h>10#include <linux/of_platform.h>11#include <linux/platform_device.h>12#include <linux/pm.h>13#include <linux/bitops.h>14#include <linux/interrupt.h>15#include <linux/io.h>16#include <linux/reboot.h>17 18#define GEMINI_PWC_ID		0x0001050019#define	GEMINI_PWC_IDREG	0x0020#define	GEMINI_PWC_CTRLREG	0x0421#define	GEMINI_PWC_STATREG	0x0822 23#define GEMINI_CTRL_SHUTDOWN	BIT(0)24#define GEMINI_CTRL_ENABLE	BIT(1)25#define GEMINI_CTRL_IRQ_CLR	BIT(2)26 27#define GEMINI_STAT_CIR		BIT(4)28#define	GEMINI_STAT_RTC		BIT(5)29#define	GEMINI_STAT_POWERBUTTON	BIT(6)30 31struct gemini_powercon {32        struct device           *dev;33        void __iomem            *base;34};35 36static irqreturn_t gemini_powerbutton_interrupt(int irq, void *data)37{38	struct gemini_powercon *gpw = data;39	u32 val;40 41	/* ACK the IRQ */42	val = readl(gpw->base + GEMINI_PWC_CTRLREG);43	val |= GEMINI_CTRL_IRQ_CLR;44	writel(val, gpw->base + GEMINI_PWC_CTRLREG);45 46	val = readl(gpw->base + GEMINI_PWC_STATREG);47	val &= 0x70U;48	switch (val) {49	case GEMINI_STAT_CIR:50		/*51		 * We do not yet have a driver for the infrared52		 * controller so it can cause spurious poweroff53		 * events. Ignore those for now.54		 */55		dev_info(gpw->dev, "infrared poweroff - ignored\n");56		break;57	case GEMINI_STAT_RTC:58		dev_info(gpw->dev, "RTC poweroff\n");59		orderly_poweroff(true);60		break;61	case GEMINI_STAT_POWERBUTTON:62		dev_info(gpw->dev, "poweroff button pressed\n");63		orderly_poweroff(true);64		break;65	default:66		dev_info(gpw->dev, "other power management IRQ\n");67		break;68	}69 70	return IRQ_HANDLED;71}72 73static int gemini_poweroff(struct sys_off_data *data)74{75	struct gemini_powercon *gpw = data->cb_data;76	u32 val;77 78	dev_crit(gpw->dev, "Gemini power off\n");79	val = readl(gpw->base + GEMINI_PWC_CTRLREG);80	val |= GEMINI_CTRL_ENABLE | GEMINI_CTRL_IRQ_CLR;81	writel(val, gpw->base + GEMINI_PWC_CTRLREG);82 83	val &= ~GEMINI_CTRL_ENABLE;84	val |= GEMINI_CTRL_SHUTDOWN;85	writel(val, gpw->base + GEMINI_PWC_CTRLREG);86 87	return NOTIFY_DONE;88}89 90static int gemini_poweroff_probe(struct platform_device *pdev)91{92	struct device *dev = &pdev->dev;93	struct gemini_powercon *gpw;94	u32 val;95	int irq;96	int ret;97 98	gpw = devm_kzalloc(dev, sizeof(*gpw), GFP_KERNEL);99	if (!gpw)100		return -ENOMEM;101 102	gpw->base = devm_platform_ioremap_resource(pdev, 0);103	if (IS_ERR(gpw->base))104		return PTR_ERR(gpw->base);105 106	irq = platform_get_irq(pdev, 0);107	if (irq < 0)108		return irq;109 110	gpw->dev = dev;111 112	val = readl(gpw->base + GEMINI_PWC_IDREG);113	val &= 0xFFFFFF00U;114	if (val != GEMINI_PWC_ID) {115		dev_err(dev, "wrong power controller ID: %08x\n",116			val);117		return -ENODEV;118	}119 120	/*121	 * Enable the power controller. This is crucial on Gemini122	 * systems: if this is not done, pressing the power button123	 * will result in unconditional poweroff without any warning.124	 * This makes the kernel handle the poweroff.125	 */126	val = readl(gpw->base + GEMINI_PWC_CTRLREG);127	val |= GEMINI_CTRL_ENABLE;128	writel(val, gpw->base + GEMINI_PWC_CTRLREG);129 130	/* Clear the IRQ */131	val = readl(gpw->base + GEMINI_PWC_CTRLREG);132	val |= GEMINI_CTRL_IRQ_CLR;133	writel(val, gpw->base + GEMINI_PWC_CTRLREG);134 135	/* Wait for this to clear */136	val = readl(gpw->base + GEMINI_PWC_STATREG);137	while (val & 0x70U)138		val = readl(gpw->base + GEMINI_PWC_STATREG);139 140	/* Clear the IRQ again */141	val = readl(gpw->base + GEMINI_PWC_CTRLREG);142	val |= GEMINI_CTRL_IRQ_CLR;143	writel(val, gpw->base + GEMINI_PWC_CTRLREG);144 145	ret = devm_request_irq(dev, irq, gemini_powerbutton_interrupt, 0,146			       "poweroff", gpw);147	if (ret)148		return ret;149 150	ret = devm_register_sys_off_handler(dev, SYS_OFF_MODE_POWER_OFF,151					    SYS_OFF_PRIO_DEFAULT,152					    gemini_poweroff, gpw);153	if (ret)154		return ret;155 156	dev_info(dev, "Gemini poweroff driver registered\n");157 158	return 0;159}160 161static const struct of_device_id gemini_poweroff_of_match[] = {162	{163		.compatible = "cortina,gemini-power-controller",164	},165	{}166};167 168static struct platform_driver gemini_poweroff_driver = {169	.probe = gemini_poweroff_probe,170	.driver = {171		.name = "gemini-poweroff",172		.of_match_table = gemini_poweroff_of_match,173	},174};175builtin_platform_driver(gemini_poweroff_driver);176