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