axp20x-pek.c (10677B)
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 16#include <linux/acpi.h> 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> 25#include <linux/platform_data/x86/soc.h> 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 33struct 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 40struct axp20x_pek { 41 struct axp20x_dev *axp20x; 42 struct input_dev *input; 43 struct axp20x_info *info; 44 int irq_dbr; 45 int irq_dbf; 46}; 47 48struct axp20x_time { 49 unsigned int time; 50 unsigned int idx; 51}; 52 53static 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 60static 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 67static 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 74static 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, 79}; 80 81static 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 88static ssize_t axp20x_show_attr(struct device *dev, 89 const struct axp20x_time *time, 90 unsigned int mask, char *buf) 91{ 92 struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); 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 100 val &= mask; 101 val >>= ffs(mask) - 1; 102 103 for (i = 0; i < 4; i++) 104 if (val == time[i].idx) 105 val = time[i].time; 106 107 return sprintf(buf, "%u\n", val); 108} 109 110static ssize_t axp20x_show_attr_startup(struct device *dev, 111 struct device_attribute *attr, 112 char *buf) 113{ 114 struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); 115 116 return axp20x_show_attr(dev, axp20x_pek->info->startup_time, 117 axp20x_pek->info->startup_mask, buf); 118} 119 120static 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 130static 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); 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 156 err = abs(time[i].time - val); 157 if (err < best_err) { 158 best_err = err; 159 idx = time[i].idx; 160 } 161 162 if (!err) 163 break; 164 } 165 166 idx <<= ffs(mask) - 1; 167 ret = regmap_update_bits(axp20x_pek->axp20x->regmap, AXP20X_PEK_KEY, 168 mask, idx); 169 if (ret != 0) 170 return -EINVAL; 171 172 return count; 173} 174 175static 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); 180 181 return axp20x_store_attr(dev, axp20x_pek->info->startup_time, 182 axp20x_pek->info->startup_mask, buf, count); 183} 184 185static 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 195static DEVICE_ATTR(startup, 0644, axp20x_show_attr_startup, 196 axp20x_store_attr_startup); 197static DEVICE_ATTR(shutdown, 0644, axp20x_show_attr_shutdown, 198 axp20x_store_attr_shutdown); 199 200static struct attribute *axp20x_attrs[] = { 201 &dev_attr_startup.attr, 202 &dev_attr_shutdown.attr, 203 NULL, 204}; 205ATTRIBUTE_GROUPS(axp20x); 206 207static irqreturn_t axp20x_pek_irq(int irq, void *pwr) 208{ 209 struct input_dev *idev = pwr; 210 struct axp20x_pek *axp20x_pek = input_get_drvdata(idev); 211 212 /* 213 * The power-button is connected to ground so a falling edge (dbf) 214 * means it is pressed. 215 */ 216 if (irq == axp20x_pek->irq_dbf) 217 input_report_key(idev, KEY_POWER, true); 218 else if (irq == axp20x_pek->irq_dbr) 219 input_report_key(idev, KEY_POWER, false); 220 221 input_sync(idev); 222 223 return IRQ_HANDLED; 224} 225 226static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek, 227 struct platform_device *pdev) 228{ 229 struct axp20x_dev *axp20x = axp20x_pek->axp20x; 230 struct input_dev *idev; 231 int error; 232 233 axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR"); 234 if (axp20x_pek->irq_dbr < 0) 235 return axp20x_pek->irq_dbr; 236 axp20x_pek->irq_dbr = regmap_irq_get_virq(axp20x->regmap_irqc, 237 axp20x_pek->irq_dbr); 238 239 axp20x_pek->irq_dbf = platform_get_irq_byname(pdev, "PEK_DBF"); 240 if (axp20x_pek->irq_dbf < 0) 241 return axp20x_pek->irq_dbf; 242 axp20x_pek->irq_dbf = regmap_irq_get_virq(axp20x->regmap_irqc, 243 axp20x_pek->irq_dbf); 244 245 axp20x_pek->input = devm_input_allocate_device(&pdev->dev); 246 if (!axp20x_pek->input) 247 return -ENOMEM; 248 249 idev = axp20x_pek->input; 250 251 idev->name = "axp20x-pek"; 252 idev->phys = "m1kbd/input2"; 253 idev->dev.parent = &pdev->dev; 254 255 input_set_capability(idev, EV_KEY, KEY_POWER); 256 257 input_set_drvdata(idev, axp20x_pek); 258 259 error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbr, 260 axp20x_pek_irq, 0, 261 "axp20x-pek-dbr", idev); 262 if (error < 0) { 263 dev_err(&pdev->dev, "Failed to request dbr IRQ#%d: %d\n", 264 axp20x_pek->irq_dbr, error); 265 return error; 266 } 267 268 error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbf, 269 axp20x_pek_irq, 0, 270 "axp20x-pek-dbf", idev); 271 if (error < 0) { 272 dev_err(&pdev->dev, "Failed to request dbf IRQ#%d: %d\n", 273 axp20x_pek->irq_dbf, error); 274 return error; 275 } 276 277 error = input_register_device(idev); 278 if (error) { 279 dev_err(&pdev->dev, "Can't register input device: %d\n", 280 error); 281 return error; 282 } 283 284 device_init_wakeup(&pdev->dev, true); 285 286 return 0; 287} 288 289static bool axp20x_pek_should_register_input(struct axp20x_pek *axp20x_pek) 290{ 291 if (IS_ENABLED(CONFIG_INPUT_SOC_BUTTON_ARRAY) && 292 axp20x_pek->axp20x->variant == AXP288_ID) { 293 /* 294 * On Cherry Trail platforms (hrv == 3), do not register the 295 * input device if there is an "INTCFD9" or "ACPI0011" gpio 296 * button ACPI device, as that handles the power button too, 297 * and otherwise we end up reporting all presses twice. 298 */ 299 if (soc_intel_is_cht() && 300 (acpi_dev_present("INTCFD9", NULL, -1) || 301 acpi_dev_present("ACPI0011", NULL, -1))) 302 return false; 303 } 304 305 return true; 306} 307 308static int axp20x_pek_probe(struct platform_device *pdev) 309{ 310 struct axp20x_pek *axp20x_pek; 311 const struct platform_device_id *match = platform_get_device_id(pdev); 312 int error; 313 314 if (!match) { 315 dev_err(&pdev->dev, "Failed to get platform_device_id\n"); 316 return -EINVAL; 317 } 318 319 axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek), 320 GFP_KERNEL); 321 if (!axp20x_pek) 322 return -ENOMEM; 323 324 axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent); 325 326 if (axp20x_pek_should_register_input(axp20x_pek)) { 327 error = axp20x_pek_probe_input_device(axp20x_pek, pdev); 328 if (error) 329 return error; 330 } 331 332 axp20x_pek->info = (struct axp20x_info *)match->driver_data; 333 334 platform_set_drvdata(pdev, axp20x_pek); 335 336 return 0; 337} 338 339static int __maybe_unused axp20x_pek_suspend(struct device *dev) 340{ 341 struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); 342 343 /* 344 * As nested threaded IRQs are not automatically disabled during 345 * suspend, we must explicitly disable non-wakeup IRQs. 346 */ 347 if (device_may_wakeup(dev)) { 348 enable_irq_wake(axp20x_pek->irq_dbf); 349 enable_irq_wake(axp20x_pek->irq_dbr); 350 } else { 351 disable_irq(axp20x_pek->irq_dbf); 352 disable_irq(axp20x_pek->irq_dbr); 353 } 354 355 return 0; 356} 357 358static int __maybe_unused axp20x_pek_resume(struct device *dev) 359{ 360 struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); 361 362 if (device_may_wakeup(dev)) { 363 disable_irq_wake(axp20x_pek->irq_dbf); 364 disable_irq_wake(axp20x_pek->irq_dbr); 365 } else { 366 enable_irq(axp20x_pek->irq_dbf); 367 enable_irq(axp20x_pek->irq_dbr); 368 } 369 370 return 0; 371} 372 373static int __maybe_unused axp20x_pek_resume_noirq(struct device *dev) 374{ 375 struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); 376 377 if (axp20x_pek->axp20x->variant != AXP288_ID) 378 return 0; 379 380 /* 381 * Clear interrupts from button presses during suspend, to avoid 382 * a wakeup power-button press getting reported to userspace. 383 */ 384 regmap_write(axp20x_pek->axp20x->regmap, 385 AXP20X_IRQ1_STATE + AXP288_IRQ_POKN / 8, 386 BIT(AXP288_IRQ_POKN % 8)); 387 388 return 0; 389} 390 391static const struct dev_pm_ops axp20x_pek_pm_ops = { 392 SET_SYSTEM_SLEEP_PM_OPS(axp20x_pek_suspend, axp20x_pek_resume) 393#ifdef CONFIG_PM_SLEEP 394 .resume_noirq = axp20x_pek_resume_noirq, 395#endif 396}; 397 398static const struct platform_device_id axp_pek_id_match[] = { 399 { 400 .name = "axp20x-pek", 401 .driver_data = (kernel_ulong_t)&axp20x_info, 402 }, 403 { 404 .name = "axp221-pek", 405 .driver_data = (kernel_ulong_t)&axp221_info, 406 }, 407 { /* sentinel */ } 408}; 409MODULE_DEVICE_TABLE(platform, axp_pek_id_match); 410 411static struct platform_driver axp20x_pek_driver = { 412 .probe = axp20x_pek_probe, 413 .id_table = axp_pek_id_match, 414 .driver = { 415 .name = "axp20x-pek", 416 .pm = &axp20x_pek_pm_ops, 417 .dev_groups = axp20x_groups, 418 }, 419}; 420module_platform_driver(axp20x_pek_driver); 421 422MODULE_DESCRIPTION("axp20x Power Button"); 423MODULE_AUTHOR("Carlo Caione <carlo@caione.org>"); 424MODULE_LICENSE("GPL");