Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 1 | /* |
| 2 | * axp20x power button driver. |
| 3 | * |
| 4 | * Copyright (C) 2013 Carlo Caione <carlo@caione.org> |
| 5 | * |
| 6 | * This file is subject to the terms and conditions of the GNU General |
| 7 | * Public License. See the file "COPYING" in the main directory of this |
| 8 | * archive for more details. |
| 9 | * |
| 10 | * This program is distributed in the hope that it will be useful, |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | * GNU General Public License for more details. |
| 14 | */ |
| 15 | |
Hans de Goede | 9b13a4c | 2017-03-09 09:55:49 -0800 | [diff] [blame] | 16 | #include <linux/acpi.h> |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 17 | #include <linux/errno.h> |
| 18 | #include <linux/irq.h> |
| 19 | #include <linux/init.h> |
| 20 | #include <linux/input.h> |
| 21 | #include <linux/interrupt.h> |
| 22 | #include <linux/kernel.h> |
| 23 | #include <linux/mfd/axp20x.h> |
| 24 | #include <linux/module.h> |
Hans de Goede | 5ecc1e9 | 2021-10-18 16:33:24 +0200 | [diff] [blame] | 25 | #include <linux/platform_data/x86/soc.h> |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 26 | #include <linux/platform_device.h> |
| 27 | #include <linux/regmap.h> |
| 28 | #include <linux/slab.h> |
| 29 | |
| 30 | #define AXP20X_PEK_STARTUP_MASK (0xc0) |
| 31 | #define AXP20X_PEK_SHUTDOWN_MASK (0x03) |
| 32 | |
Quentin Schulz | fbc1b32 | 2017-08-14 22:16:27 -0700 | [diff] [blame] | 33 | struct axp20x_info { |
| 34 | const struct axp20x_time *startup_time; |
| 35 | unsigned int startup_mask; |
| 36 | const struct axp20x_time *shutdown_time; |
| 37 | unsigned int shutdown_mask; |
| 38 | }; |
| 39 | |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 40 | struct axp20x_pek { |
| 41 | struct axp20x_dev *axp20x; |
| 42 | struct input_dev *input; |
Quentin Schulz | fbc1b32 | 2017-08-14 22:16:27 -0700 | [diff] [blame] | 43 | struct axp20x_info *info; |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 44 | int irq_dbr; |
| 45 | int irq_dbf; |
| 46 | }; |
| 47 | |
| 48 | struct axp20x_time { |
| 49 | unsigned int time; |
| 50 | unsigned int idx; |
| 51 | }; |
| 52 | |
| 53 | static const struct axp20x_time startup_time[] = { |
| 54 | { .time = 128, .idx = 0 }, |
| 55 | { .time = 1000, .idx = 2 }, |
| 56 | { .time = 3000, .idx = 1 }, |
| 57 | { .time = 2000, .idx = 3 }, |
| 58 | }; |
| 59 | |
Quentin Schulz | c3cc944 | 2017-08-14 22:19:42 -0700 | [diff] [blame] | 60 | static const struct axp20x_time axp221_startup_time[] = { |
| 61 | { .time = 128, .idx = 0 }, |
| 62 | { .time = 1000, .idx = 1 }, |
| 63 | { .time = 2000, .idx = 2 }, |
| 64 | { .time = 3000, .idx = 3 }, |
| 65 | }; |
| 66 | |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 67 | static const struct axp20x_time shutdown_time[] = { |
| 68 | { .time = 4000, .idx = 0 }, |
| 69 | { .time = 6000, .idx = 1 }, |
| 70 | { .time = 8000, .idx = 2 }, |
| 71 | { .time = 10000, .idx = 3 }, |
| 72 | }; |
| 73 | |
Quentin Schulz | fbc1b32 | 2017-08-14 22:16:27 -0700 | [diff] [blame] | 74 | static const struct axp20x_info axp20x_info = { |
| 75 | .startup_time = startup_time, |
| 76 | .startup_mask = AXP20X_PEK_STARTUP_MASK, |
| 77 | .shutdown_time = shutdown_time, |
| 78 | .shutdown_mask = AXP20X_PEK_SHUTDOWN_MASK, |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 79 | }; |
| 80 | |
Quentin Schulz | c3cc944 | 2017-08-14 22:19:42 -0700 | [diff] [blame] | 81 | static const struct axp20x_info axp221_info = { |
| 82 | .startup_time = axp221_startup_time, |
| 83 | .startup_mask = AXP20X_PEK_STARTUP_MASK, |
| 84 | .shutdown_time = shutdown_time, |
| 85 | .shutdown_mask = AXP20X_PEK_SHUTDOWN_MASK, |
| 86 | }; |
| 87 | |
Quentin Schulz | fbc1b32 | 2017-08-14 22:16:27 -0700 | [diff] [blame] | 88 | static ssize_t axp20x_show_attr(struct device *dev, |
| 89 | const struct axp20x_time *time, |
| 90 | unsigned int mask, char *buf) |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 91 | { |
| 92 | struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 93 | unsigned int val; |
| 94 | int ret, i; |
| 95 | |
| 96 | ret = regmap_read(axp20x_pek->axp20x->regmap, AXP20X_PEK_KEY, &val); |
| 97 | if (ret != 0) |
| 98 | return ret; |
| 99 | |
Quentin Schulz | fbc1b32 | 2017-08-14 22:16:27 -0700 | [diff] [blame] | 100 | val &= mask; |
| 101 | val >>= ffs(mask) - 1; |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 102 | |
| 103 | for (i = 0; i < 4; i++) |
Quentin Schulz | fbc1b32 | 2017-08-14 22:16:27 -0700 | [diff] [blame] | 104 | if (val == time[i].idx) |
| 105 | val = time[i].time; |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 106 | |
| 107 | return sprintf(buf, "%u\n", val); |
| 108 | } |
| 109 | |
Quentin Schulz | fbc1b32 | 2017-08-14 22:16:27 -0700 | [diff] [blame] | 110 | static ssize_t axp20x_show_attr_startup(struct device *dev, |
| 111 | struct device_attribute *attr, |
| 112 | char *buf) |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 113 | { |
| 114 | struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); |
Quentin Schulz | fbc1b32 | 2017-08-14 22:16:27 -0700 | [diff] [blame] | 115 | |
| 116 | return axp20x_show_attr(dev, axp20x_pek->info->startup_time, |
| 117 | axp20x_pek->info->startup_mask, buf); |
| 118 | } |
| 119 | |
| 120 | static ssize_t axp20x_show_attr_shutdown(struct device *dev, |
| 121 | struct device_attribute *attr, |
| 122 | char *buf) |
| 123 | { |
| 124 | struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); |
| 125 | |
| 126 | return axp20x_show_attr(dev, axp20x_pek->info->shutdown_time, |
| 127 | axp20x_pek->info->shutdown_mask, buf); |
| 128 | } |
| 129 | |
| 130 | static ssize_t axp20x_store_attr(struct device *dev, |
| 131 | const struct axp20x_time *time, |
| 132 | unsigned int mask, const char *buf, |
| 133 | size_t count) |
| 134 | { |
| 135 | struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 136 | char val_str[20]; |
| 137 | size_t len; |
| 138 | int ret, i; |
| 139 | unsigned int val, idx = 0; |
| 140 | unsigned int best_err = UINT_MAX; |
| 141 | |
| 142 | val_str[sizeof(val_str) - 1] = '\0'; |
| 143 | strncpy(val_str, buf, sizeof(val_str) - 1); |
| 144 | len = strlen(val_str); |
| 145 | |
| 146 | if (len && val_str[len - 1] == '\n') |
| 147 | val_str[len - 1] = '\0'; |
| 148 | |
| 149 | ret = kstrtouint(val_str, 10, &val); |
| 150 | if (ret) |
| 151 | return ret; |
| 152 | |
| 153 | for (i = 3; i >= 0; i--) { |
| 154 | unsigned int err; |
| 155 | |
Quentin Schulz | fbc1b32 | 2017-08-14 22:16:27 -0700 | [diff] [blame] | 156 | err = abs(time[i].time - val); |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 157 | if (err < best_err) { |
| 158 | best_err = err; |
Quentin Schulz | fbc1b32 | 2017-08-14 22:16:27 -0700 | [diff] [blame] | 159 | idx = time[i].idx; |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 160 | } |
| 161 | |
| 162 | if (!err) |
| 163 | break; |
| 164 | } |
| 165 | |
Quentin Schulz | fbc1b32 | 2017-08-14 22:16:27 -0700 | [diff] [blame] | 166 | idx <<= ffs(mask) - 1; |
| 167 | ret = regmap_update_bits(axp20x_pek->axp20x->regmap, AXP20X_PEK_KEY, |
| 168 | mask, idx); |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 169 | if (ret != 0) |
| 170 | return -EINVAL; |
Dmitry Torokhov | b388de8 | 2014-12-29 15:13:06 -0800 | [diff] [blame] | 171 | |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 172 | return count; |
| 173 | } |
| 174 | |
Quentin Schulz | fbc1b32 | 2017-08-14 22:16:27 -0700 | [diff] [blame] | 175 | static ssize_t axp20x_store_attr_startup(struct device *dev, |
| 176 | struct device_attribute *attr, |
| 177 | const char *buf, size_t count) |
| 178 | { |
| 179 | struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 180 | |
Quentin Schulz | fbc1b32 | 2017-08-14 22:16:27 -0700 | [diff] [blame] | 181 | return axp20x_store_attr(dev, axp20x_pek->info->startup_time, |
| 182 | axp20x_pek->info->startup_mask, buf, count); |
| 183 | } |
| 184 | |
| 185 | static ssize_t axp20x_store_attr_shutdown(struct device *dev, |
| 186 | struct device_attribute *attr, |
| 187 | const char *buf, size_t count) |
| 188 | { |
| 189 | struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); |
| 190 | |
| 191 | return axp20x_store_attr(dev, axp20x_pek->info->shutdown_time, |
| 192 | axp20x_pek->info->shutdown_mask, buf, count); |
| 193 | } |
| 194 | |
Ben Dooks (Codethink) | cbe821a | 2019-12-19 11:13:13 -0800 | [diff] [blame] | 195 | static DEVICE_ATTR(startup, 0644, axp20x_show_attr_startup, |
| 196 | axp20x_store_attr_startup); |
| 197 | static DEVICE_ATTR(shutdown, 0644, axp20x_show_attr_shutdown, |
| 198 | axp20x_store_attr_shutdown); |
Dmitry Torokhov | b388de8 | 2014-12-29 15:13:06 -0800 | [diff] [blame] | 199 | |
Greg Kroah-Hartman | d99995a | 2019-08-11 23:42:31 -0700 | [diff] [blame] | 200 | static struct attribute *axp20x_attrs[] = { |
Quentin Schulz | fbc1b32 | 2017-08-14 22:16:27 -0700 | [diff] [blame] | 201 | &dev_attr_startup.attr, |
| 202 | &dev_attr_shutdown.attr, |
Dmitry Torokhov | b388de8 | 2014-12-29 15:13:06 -0800 | [diff] [blame] | 203 | NULL, |
| 204 | }; |
Greg Kroah-Hartman | d99995a | 2019-08-11 23:42:31 -0700 | [diff] [blame] | 205 | ATTRIBUTE_GROUPS(axp20x); |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 206 | |
| 207 | static irqreturn_t axp20x_pek_irq(int irq, void *pwr) |
| 208 | { |
Hans de Goede | 9747070 | 2020-05-05 19:06:32 -0700 | [diff] [blame] | 209 | struct axp20x_pek *axp20x_pek = pwr; |
| 210 | struct input_dev *idev = axp20x_pek->input; |
| 211 | |
| 212 | if (!idev) |
| 213 | return IRQ_HANDLED; |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 214 | |
Hans de Goede | eeeee40 | 2015-06-24 14:25:54 -0700 | [diff] [blame] | 215 | /* |
| 216 | * The power-button is connected to ground so a falling edge (dbf) |
| 217 | * means it is pressed. |
| 218 | */ |
| 219 | if (irq == axp20x_pek->irq_dbf) |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 220 | input_report_key(idev, KEY_POWER, true); |
Hans de Goede | eeeee40 | 2015-06-24 14:25:54 -0700 | [diff] [blame] | 221 | else if (irq == axp20x_pek->irq_dbr) |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 222 | input_report_key(idev, KEY_POWER, false); |
| 223 | |
| 224 | input_sync(idev); |
| 225 | |
| 226 | return IRQ_HANDLED; |
| 227 | } |
| 228 | |
Hans de Goede | f2bd5a9 | 2017-03-09 09:47:24 -0800 | [diff] [blame] | 229 | static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek, |
| 230 | struct platform_device *pdev) |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 231 | { |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 232 | struct input_dev *idev; |
| 233 | int error; |
| 234 | |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 235 | axp20x_pek->input = devm_input_allocate_device(&pdev->dev); |
| 236 | if (!axp20x_pek->input) |
| 237 | return -ENOMEM; |
| 238 | |
| 239 | idev = axp20x_pek->input; |
| 240 | |
| 241 | idev->name = "axp20x-pek"; |
| 242 | idev->phys = "m1kbd/input2"; |
| 243 | idev->dev.parent = &pdev->dev; |
| 244 | |
| 245 | input_set_capability(idev, EV_KEY, KEY_POWER); |
| 246 | |
| 247 | input_set_drvdata(idev, axp20x_pek); |
| 248 | |
Hans de Goede | f2bd5a9 | 2017-03-09 09:47:24 -0800 | [diff] [blame] | 249 | error = input_register_device(idev); |
| 250 | if (error) { |
| 251 | dev_err(&pdev->dev, "Can't register input device: %d\n", |
| 252 | error); |
| 253 | return error; |
| 254 | } |
| 255 | |
| 256 | return 0; |
| 257 | } |
| 258 | |
Hans de Goede | 5ecc1e9 | 2021-10-18 16:33:24 +0200 | [diff] [blame] | 259 | static bool axp20x_pek_should_register_input(struct axp20x_pek *axp20x_pek) |
Hans de Goede | 8d4b313 | 2017-06-02 17:18:47 -0700 | [diff] [blame] | 260 | { |
Hans de Goede | 8d4b313 | 2017-06-02 17:18:47 -0700 | [diff] [blame] | 261 | if (IS_ENABLED(CONFIG_INPUT_SOC_BUTTON_ARRAY) && |
| 262 | axp20x_pek->axp20x->variant == AXP288_ID) { |
Hans de Goede | 8d4b313 | 2017-06-02 17:18:47 -0700 | [diff] [blame] | 263 | /* |
| 264 | * On Cherry Trail platforms (hrv == 3), do not register the |
Hans de Goede | 0fd5f22 | 2017-06-02 17:50:22 -0700 | [diff] [blame] | 265 | * input device if there is an "INTCFD9" or "ACPI0011" gpio |
Hans de Goede | 8d4b313 | 2017-06-02 17:18:47 -0700 | [diff] [blame] | 266 | * button ACPI device, as that handles the power button too, |
| 267 | * and otherwise we end up reporting all presses twice. |
| 268 | */ |
Hans de Goede | 5ecc1e9 | 2021-10-18 16:33:24 +0200 | [diff] [blame] | 269 | if (soc_intel_is_cht() && |
| 270 | (acpi_dev_present("INTCFD9", NULL, -1) || |
Hans de Goede | 0fd5f22 | 2017-06-02 17:50:22 -0700 | [diff] [blame] | 271 | acpi_dev_present("ACPI0011", NULL, -1))) |
Hans de Goede | 8d4b313 | 2017-06-02 17:18:47 -0700 | [diff] [blame] | 272 | return false; |
Hans de Goede | 8d4b313 | 2017-06-02 17:18:47 -0700 | [diff] [blame] | 273 | } |
| 274 | |
| 275 | return true; |
| 276 | } |
Hans de Goede | 8d4b313 | 2017-06-02 17:18:47 -0700 | [diff] [blame] | 277 | |
Hans de Goede | f2bd5a9 | 2017-03-09 09:47:24 -0800 | [diff] [blame] | 278 | static int axp20x_pek_probe(struct platform_device *pdev) |
| 279 | { |
| 280 | struct axp20x_pek *axp20x_pek; |
Quentin Schulz | fbc1b32 | 2017-08-14 22:16:27 -0700 | [diff] [blame] | 281 | const struct platform_device_id *match = platform_get_device_id(pdev); |
Hans de Goede | f2bd5a9 | 2017-03-09 09:47:24 -0800 | [diff] [blame] | 282 | int error; |
| 283 | |
Quentin Schulz | fbc1b32 | 2017-08-14 22:16:27 -0700 | [diff] [blame] | 284 | if (!match) { |
| 285 | dev_err(&pdev->dev, "Failed to get platform_device_id\n"); |
| 286 | return -EINVAL; |
| 287 | } |
| 288 | |
Hans de Goede | f2bd5a9 | 2017-03-09 09:47:24 -0800 | [diff] [blame] | 289 | axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek), |
| 290 | GFP_KERNEL); |
| 291 | if (!axp20x_pek) |
| 292 | return -ENOMEM; |
| 293 | |
| 294 | axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent); |
| 295 | |
Hans de Goede | 9747070 | 2020-05-05 19:06:32 -0700 | [diff] [blame] | 296 | axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR"); |
| 297 | if (axp20x_pek->irq_dbr < 0) |
| 298 | return axp20x_pek->irq_dbr; |
| 299 | axp20x_pek->irq_dbr = regmap_irq_get_virq( |
| 300 | axp20x_pek->axp20x->regmap_irqc, axp20x_pek->irq_dbr); |
| 301 | |
| 302 | axp20x_pek->irq_dbf = platform_get_irq_byname(pdev, "PEK_DBF"); |
| 303 | if (axp20x_pek->irq_dbf < 0) |
| 304 | return axp20x_pek->irq_dbf; |
| 305 | axp20x_pek->irq_dbf = regmap_irq_get_virq( |
| 306 | axp20x_pek->axp20x->regmap_irqc, axp20x_pek->irq_dbf); |
| 307 | |
Hans de Goede | 5ecc1e9 | 2021-10-18 16:33:24 +0200 | [diff] [blame] | 308 | if (axp20x_pek_should_register_input(axp20x_pek)) { |
Hans de Goede | 9b13a4c | 2017-03-09 09:55:49 -0800 | [diff] [blame] | 309 | error = axp20x_pek_probe_input_device(axp20x_pek, pdev); |
| 310 | if (error) |
| 311 | return error; |
| 312 | } |
Hans de Goede | f2bd5a9 | 2017-03-09 09:47:24 -0800 | [diff] [blame] | 313 | |
Quentin Schulz | fbc1b32 | 2017-08-14 22:16:27 -0700 | [diff] [blame] | 314 | axp20x_pek->info = (struct axp20x_info *)match->driver_data; |
| 315 | |
Hans de Goede | 9747070 | 2020-05-05 19:06:32 -0700 | [diff] [blame] | 316 | error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbr, |
| 317 | axp20x_pek_irq, 0, |
| 318 | "axp20x-pek-dbr", axp20x_pek); |
| 319 | if (error < 0) { |
| 320 | dev_err(&pdev->dev, "Failed to request dbr IRQ#%d: %d\n", |
| 321 | axp20x_pek->irq_dbr, error); |
| 322 | return error; |
| 323 | } |
| 324 | |
| 325 | error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbf, |
| 326 | axp20x_pek_irq, 0, |
| 327 | "axp20x-pek-dbf", axp20x_pek); |
| 328 | if (error < 0) { |
| 329 | dev_err(&pdev->dev, "Failed to request dbf IRQ#%d: %d\n", |
| 330 | axp20x_pek->irq_dbf, error); |
| 331 | return error; |
| 332 | } |
| 333 | |
| 334 | device_init_wakeup(&pdev->dev, true); |
| 335 | |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 336 | platform_set_drvdata(pdev, axp20x_pek); |
| 337 | |
| 338 | return 0; |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 339 | } |
| 340 | |
Samuel Holland | fe77f9b | 2020-01-21 22:03:14 -0800 | [diff] [blame] | 341 | static int __maybe_unused axp20x_pek_suspend(struct device *dev) |
| 342 | { |
| 343 | struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); |
| 344 | |
| 345 | /* |
| 346 | * As nested threaded IRQs are not automatically disabled during |
| 347 | * suspend, we must explicitly disable non-wakeup IRQs. |
| 348 | */ |
| 349 | if (device_may_wakeup(dev)) { |
| 350 | enable_irq_wake(axp20x_pek->irq_dbf); |
| 351 | enable_irq_wake(axp20x_pek->irq_dbr); |
| 352 | } else { |
| 353 | disable_irq(axp20x_pek->irq_dbf); |
| 354 | disable_irq(axp20x_pek->irq_dbr); |
| 355 | } |
| 356 | |
| 357 | return 0; |
| 358 | } |
| 359 | |
| 360 | static int __maybe_unused axp20x_pek_resume(struct device *dev) |
| 361 | { |
| 362 | struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); |
| 363 | |
| 364 | if (device_may_wakeup(dev)) { |
| 365 | disable_irq_wake(axp20x_pek->irq_dbf); |
| 366 | disable_irq_wake(axp20x_pek->irq_dbr); |
| 367 | } else { |
| 368 | enable_irq(axp20x_pek->irq_dbf); |
| 369 | enable_irq(axp20x_pek->irq_dbr); |
| 370 | } |
| 371 | |
| 372 | return 0; |
| 373 | } |
| 374 | |
Hans de Goede | 58be768 | 2017-06-02 17:51:42 -0700 | [diff] [blame] | 375 | static int __maybe_unused axp20x_pek_resume_noirq(struct device *dev) |
| 376 | { |
| 377 | struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); |
| 378 | |
| 379 | if (axp20x_pek->axp20x->variant != AXP288_ID) |
| 380 | return 0; |
| 381 | |
| 382 | /* |
| 383 | * Clear interrupts from button presses during suspend, to avoid |
| 384 | * a wakeup power-button press getting reported to userspace. |
| 385 | */ |
| 386 | regmap_write(axp20x_pek->axp20x->regmap, |
| 387 | AXP20X_IRQ1_STATE + AXP288_IRQ_POKN / 8, |
| 388 | BIT(AXP288_IRQ_POKN % 8)); |
| 389 | |
| 390 | return 0; |
| 391 | } |
| 392 | |
| 393 | static const struct dev_pm_ops axp20x_pek_pm_ops = { |
Samuel Holland | fe77f9b | 2020-01-21 22:03:14 -0800 | [diff] [blame] | 394 | SET_SYSTEM_SLEEP_PM_OPS(axp20x_pek_suspend, axp20x_pek_resume) |
Hans de Goede | 58be768 | 2017-06-02 17:51:42 -0700 | [diff] [blame] | 395 | #ifdef CONFIG_PM_SLEEP |
| 396 | .resume_noirq = axp20x_pek_resume_noirq, |
| 397 | #endif |
| 398 | }; |
| 399 | |
Quentin Schulz | fbc1b32 | 2017-08-14 22:16:27 -0700 | [diff] [blame] | 400 | static const struct platform_device_id axp_pek_id_match[] = { |
| 401 | { |
| 402 | .name = "axp20x-pek", |
| 403 | .driver_data = (kernel_ulong_t)&axp20x_info, |
| 404 | }, |
Quentin Schulz | c3cc944 | 2017-08-14 22:19:42 -0700 | [diff] [blame] | 405 | { |
| 406 | .name = "axp221-pek", |
| 407 | .driver_data = (kernel_ulong_t)&axp221_info, |
| 408 | }, |
Quentin Schulz | fbc1b32 | 2017-08-14 22:16:27 -0700 | [diff] [blame] | 409 | { /* sentinel */ } |
| 410 | }; |
Hans de Goede | 481c209 | 2017-10-19 15:38:50 -0700 | [diff] [blame] | 411 | MODULE_DEVICE_TABLE(platform, axp_pek_id_match); |
Quentin Schulz | fbc1b32 | 2017-08-14 22:16:27 -0700 | [diff] [blame] | 412 | |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 413 | static struct platform_driver axp20x_pek_driver = { |
| 414 | .probe = axp20x_pek_probe, |
Quentin Schulz | fbc1b32 | 2017-08-14 22:16:27 -0700 | [diff] [blame] | 415 | .id_table = axp_pek_id_match, |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 416 | .driver = { |
| 417 | .name = "axp20x-pek", |
Hans de Goede | 58be768 | 2017-06-02 17:51:42 -0700 | [diff] [blame] | 418 | .pm = &axp20x_pek_pm_ops, |
Greg Kroah-Hartman | d99995a | 2019-08-11 23:42:31 -0700 | [diff] [blame] | 419 | .dev_groups = axp20x_groups, |
Carlo Caione | 5b6c26a | 2014-12-29 11:20:54 -0800 | [diff] [blame] | 420 | }, |
| 421 | }; |
| 422 | module_platform_driver(axp20x_pek_driver); |
| 423 | |
| 424 | MODULE_DESCRIPTION("axp20x Power Button"); |
| 425 | MODULE_AUTHOR("Carlo Caione <carlo@caione.org>"); |
| 426 | MODULE_LICENSE("GPL"); |