cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

gemini-poweroff.c (4400B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Gemini power management controller
      4 * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
      5 *
      6 * Inspired by code from the SL3516 board support by Jason Lee
      7 * 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		0x00010500
     19#define	GEMINI_PWC_IDREG	0x00
     20#define	GEMINI_PWC_CTRLREG	0x04
     21#define	GEMINI_PWC_STATREG	0x08
     22
     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 infrared
     52		 * controller so it can cause spurious poweroff
     53		 * 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
     73/* This callback needs this static local as it has void as argument */
     74static struct gemini_powercon *gpw_poweroff;
     75
     76static void gemini_poweroff(void)
     77{
     78	struct gemini_powercon *gpw = gpw_poweroff;
     79	u32 val;
     80
     81	dev_crit(gpw->dev, "Gemini power off\n");
     82	val = readl(gpw->base + GEMINI_PWC_CTRLREG);
     83	val |= GEMINI_CTRL_ENABLE | GEMINI_CTRL_IRQ_CLR;
     84	writel(val, gpw->base + GEMINI_PWC_CTRLREG);
     85
     86	val &= ~GEMINI_CTRL_ENABLE;
     87	val |= GEMINI_CTRL_SHUTDOWN;
     88	writel(val, gpw->base + GEMINI_PWC_CTRLREG);
     89}
     90
     91static int gemini_poweroff_probe(struct platform_device *pdev)
     92{
     93	struct device *dev = &pdev->dev;
     94	struct resource *res;
     95	struct gemini_powercon *gpw;
     96	u32 val;
     97	int irq;
     98	int ret;
     99
    100	gpw = devm_kzalloc(dev, sizeof(*gpw), GFP_KERNEL);
    101	if (!gpw)
    102		return -ENOMEM;
    103
    104	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    105	gpw->base = devm_ioremap_resource(dev, res);
    106	if (IS_ERR(gpw->base))
    107		return PTR_ERR(gpw->base);
    108
    109	irq = platform_get_irq(pdev, 0);
    110	if (irq < 0)
    111		return irq;
    112
    113	gpw->dev = dev;
    114
    115	val = readl(gpw->base + GEMINI_PWC_IDREG);
    116	val &= 0xFFFFFF00U;
    117	if (val != GEMINI_PWC_ID) {
    118		dev_err(dev, "wrong power controller ID: %08x\n",
    119			val);
    120		return -ENODEV;
    121	}
    122
    123	/*
    124	 * Enable the power controller. This is crucial on Gemini
    125	 * systems: if this is not done, pressing the power button
    126	 * will result in unconditional poweroff without any warning.
    127	 * This makes the kernel handle the poweroff.
    128	 */
    129	val = readl(gpw->base + GEMINI_PWC_CTRLREG);
    130	val |= GEMINI_CTRL_ENABLE;
    131	writel(val, gpw->base + GEMINI_PWC_CTRLREG);
    132
    133	/* Clear the IRQ */
    134	val = readl(gpw->base + GEMINI_PWC_CTRLREG);
    135	val |= GEMINI_CTRL_IRQ_CLR;
    136	writel(val, gpw->base + GEMINI_PWC_CTRLREG);
    137
    138	/* Wait for this to clear */
    139	val = readl(gpw->base + GEMINI_PWC_STATREG);
    140	while (val & 0x70U)
    141		val = readl(gpw->base + GEMINI_PWC_STATREG);
    142
    143	/* Clear the IRQ again */
    144	val = readl(gpw->base + GEMINI_PWC_CTRLREG);
    145	val |= GEMINI_CTRL_IRQ_CLR;
    146	writel(val, gpw->base + GEMINI_PWC_CTRLREG);
    147
    148	ret = devm_request_irq(dev, irq, gemini_powerbutton_interrupt, 0,
    149			       "poweroff", gpw);
    150	if (ret)
    151		return ret;
    152
    153	pm_power_off = gemini_poweroff;
    154	gpw_poweroff = gpw;
    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);