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 31 struct gemini_powercon { 32 struct device *dev; 33 void __iomem *base; 34 }; 35 36 static 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 */ 74 static struct gemini_powercon *gpw_poweroff; 75 76 static 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 91 static int gemini_poweroff_probe(struct platform_device *pdev) 92 { 93 struct device *dev = &pdev->dev; 94 struct gemini_powercon *gpw; 95 u32 val; 96 int irq; 97 int ret; 98 99 gpw = devm_kzalloc(dev, sizeof(*gpw), GFP_KERNEL); 100 if (!gpw) 101 return -ENOMEM; 102 103 gpw->base = devm_platform_ioremap_resource(pdev, 0); 104 if (IS_ERR(gpw->base)) 105 return PTR_ERR(gpw->base); 106 107 irq = platform_get_irq(pdev, 0); 108 if (irq < 0) 109 return irq; 110 111 gpw->dev = dev; 112 113 val = readl(gpw->base + GEMINI_PWC_IDREG); 114 val &= 0xFFFFFF00U; 115 if (val != GEMINI_PWC_ID) { 116 dev_err(dev, "wrong power controller ID: %08x\n", 117 val); 118 return -ENODEV; 119 } 120 121 /* 122 * Enable the power controller. This is crucial on Gemini 123 * systems: if this is not done, pressing the power button 124 * will result in unconditional poweroff without any warning. 125 * This makes the kernel handle the poweroff. 126 */ 127 val = readl(gpw->base + GEMINI_PWC_CTRLREG); 128 val |= GEMINI_CTRL_ENABLE; 129 writel(val, gpw->base + GEMINI_PWC_CTRLREG); 130 131 /* Clear the IRQ */ 132 val = readl(gpw->base + GEMINI_PWC_CTRLREG); 133 val |= GEMINI_CTRL_IRQ_CLR; 134 writel(val, gpw->base + GEMINI_PWC_CTRLREG); 135 136 /* Wait for this to clear */ 137 val = readl(gpw->base + GEMINI_PWC_STATREG); 138 while (val & 0x70U) 139 val = readl(gpw->base + GEMINI_PWC_STATREG); 140 141 /* Clear the IRQ again */ 142 val = readl(gpw->base + GEMINI_PWC_CTRLREG); 143 val |= GEMINI_CTRL_IRQ_CLR; 144 writel(val, gpw->base + GEMINI_PWC_CTRLREG); 145 146 ret = devm_request_irq(dev, irq, gemini_powerbutton_interrupt, 0, 147 "poweroff", gpw); 148 if (ret) 149 return ret; 150 151 pm_power_off = gemini_poweroff; 152 gpw_poweroff = gpw; 153 154 dev_info(dev, "Gemini poweroff driver registered\n"); 155 156 return 0; 157 } 158 159 static const struct of_device_id gemini_poweroff_of_match[] = { 160 { 161 .compatible = "cortina,gemini-power-controller", 162 }, 163 {} 164 }; 165 166 static struct platform_driver gemini_poweroff_driver = { 167 .probe = gemini_poweroff_probe, 168 .driver = { 169 .name = "gemini-poweroff", 170 .of_match_table = gemini_poweroff_of_match, 171 }, 172 }; 173 builtin_platform_driver(gemini_poweroff_driver); 174