| 1 | |
| 2 | |
| 3 | * Copyright (c) 2010-2011, 2020-2021, The Linux Foundation. All rights reserved.
|
| 4 | * Copyright (c) 2014, Sony Mobile Communications Inc.
|
| 5 |
|
| 6 |
|
| 7 | #include <linux/delay.h>
|
| 8 | #include <linux/errno.h>
|
| 9 | #include <linux/input.h>
|
| 10 | #include <linux/interrupt.h>
|
| 11 | #include <linux/kernel.h>
|
| 12 | #include <linux/ktime.h>
|
| 13 | #include <linux/log2.h>
|
| 14 | #include <linux/module.h>
|
| 15 | #include <linux/of.h>
|
| 16 | #include <linux/of_address.h>
|
| 17 | #include <linux/platform_device.h>
|
| 18 | #include <linux/reboot.h>
|
| 19 | #include <linux/regmap.h>
|
| 20 |
|
| 21 | #define PON_REV2 0x01
|
| 22 |
|
| 23 | #define PON_SUBTYPE 0x05
|
| 24 |
|
| 25 | #define PON_SUBTYPE_PRIMARY 0x01
|
| 26 | #define PON_SUBTYPE_SECONDARY 0x02
|
| 27 | #define PON_SUBTYPE_1REG 0x03
|
| 28 | #define PON_SUBTYPE_GEN2_PRIMARY 0x04
|
| 29 | #define PON_SUBTYPE_GEN2_SECONDARY 0x05
|
| 30 | #define PON_SUBTYPE_GEN3_PBS 0x08
|
| 31 | #define PON_SUBTYPE_GEN3_HLOS 0x09
|
| 32 |
|
| 33 | #define PON_RT_STS 0x10
|
| 34 | #define PON_KPDPWR_N_SET BIT(0)
|
| 35 | #define PON_RESIN_N_SET BIT(1)
|
| 36 | #define PON_GEN3_RESIN_N_SET BIT(6)
|
| 37 | #define PON_GEN3_KPDPWR_N_SET BIT(7)
|
| 38 |
|
| 39 | #define PON_PS_HOLD_RST_CTL 0x5a
|
| 40 | #define PON_PS_HOLD_RST_CTL2 0x5b
|
| 41 | #define PON_PS_HOLD_ENABLE BIT(7)
|
| 42 | #define PON_PS_HOLD_TYPE_MASK 0x0f
|
| 43 | #define PON_PS_HOLD_TYPE_WARM_RESET 1
|
| 44 | #define PON_PS_HOLD_TYPE_SHUTDOWN 4
|
| 45 | #define PON_PS_HOLD_TYPE_HARD_RESET 7
|
| 46 |
|
| 47 | #define PON_PULL_CTL 0x70
|
| 48 | #define PON_KPDPWR_PULL_UP BIT(1)
|
| 49 | #define PON_RESIN_PULL_UP BIT(0)
|
| 50 |
|
| 51 | #define PON_DBC_CTL 0x71
|
| 52 | #define PON_DBC_DELAY_MASK_GEN1 0x7
|
| 53 | #define PON_DBC_DELAY_MASK_GEN2 0xf
|
| 54 | #define PON_DBC_SHIFT_GEN1 6
|
| 55 | #define PON_DBC_SHIFT_GEN2 14
|
| 56 |
|
| 57 | struct pm8941_data {
|
| 58 | unsigned int pull_up_bit;
|
| 59 | unsigned int status_bit;
|
| 60 | bool supports_ps_hold_poff_config;
|
| 61 | bool supports_debounce_config;
|
| 62 | bool has_pon_pbs;
|
| 63 | bool wakeup_source_default;
|
| 64 | const char *name;
|
| 65 | const char *phys;
|
| 66 | };
|
| 67 |
|
| 68 | struct pm8941_pwrkey {
|
| 69 | struct device *dev;
|
| 70 | int irq;
|
| 71 | u32 baseaddr;
|
| 72 | u32 pon_pbs_baseaddr;
|
| 73 | struct regmap *regmap;
|
| 74 | struct input_dev *input;
|
| 75 |
|
| 76 | unsigned int revision;
|
| 77 | unsigned int subtype;
|
| 78 | struct notifier_block reboot_notifier;
|
| 79 |
|
| 80 | u32 code;
|
| 81 | u32 sw_debounce_time_us;
|
| 82 | ktime_t sw_debounce_end_time;
|
| 83 | bool last_status;
|
| 84 | const struct pm8941_data *data;
|
| 85 | };
|
| 86 |
|
| 87 | static int pm8941_reboot_notify(struct notifier_block *nb,
|
| 88 | unsigned long code, void *unused)
|
| 89 | {
|
| 90 | struct pm8941_pwrkey *pwrkey = container_of(nb, struct pm8941_pwrkey,
|
| 91 | reboot_notifier);
|
| 92 | unsigned int enable_reg;
|
| 93 | unsigned int reset_type;
|
| 94 | int error;
|
| 95 |
|
| 96 |
|
| 97 | if (pwrkey->revision == 0)
|
| 98 | enable_reg = PON_PS_HOLD_RST_CTL;
|
| 99 | else
|
| 100 | enable_reg = PON_PS_HOLD_RST_CTL2;
|
| 101 |
|
| 102 | error = regmap_update_bits(pwrkey->regmap,
|
| 103 | pwrkey->baseaddr + enable_reg,
|
| 104 | PON_PS_HOLD_ENABLE,
|
| 105 | 0);
|
| 106 | if (error)
|
| 107 | dev_err(pwrkey->dev,
|
| 108 | "unable to clear ps hold reset enable: %d\n",
|
| 109 | error);
|
| 110 |
|
| 111 | |
| 112 | * Updates of PON_PS_HOLD_ENABLE requires 3 sleep cycles between
|
| 113 | * writes.
|
| 114 |
|
| 115 | usleep_range(100, 1000);
|
| 116 |
|
| 117 | switch (code) {
|
| 118 | case SYS_HALT:
|
| 119 | case SYS_POWER_OFF:
|
| 120 | reset_type = PON_PS_HOLD_TYPE_SHUTDOWN;
|
| 121 | break;
|
| 122 | case SYS_RESTART:
|
| 123 | default:
|
| 124 | if (reboot_mode == REBOOT_WARM)
|
| 125 | reset_type = PON_PS_HOLD_TYPE_WARM_RESET;
|
| 126 | else
|
| 127 | reset_type = PON_PS_HOLD_TYPE_HARD_RESET;
|
| 128 | break;
|
| 129 | }
|
| 130 |
|
| 131 | error = regmap_update_bits(pwrkey->regmap,
|
| 132 | pwrkey->baseaddr + PON_PS_HOLD_RST_CTL,
|
| 133 | PON_PS_HOLD_TYPE_MASK,
|
| 134 | reset_type);
|
| 135 | if (error)
|
| 136 | dev_err(pwrkey->dev, "unable to set ps hold reset type: %d\n",
|
| 137 | error);
|
| 138 |
|
| 139 | error = regmap_update_bits(pwrkey->regmap,
|
| 140 | pwrkey->baseaddr + enable_reg,
|
| 141 | PON_PS_HOLD_ENABLE,
|
| 142 | PON_PS_HOLD_ENABLE);
|
| 143 | if (error)
|
| 144 | dev_err(pwrkey->dev, "unable to re-set enable: %d\n", error);
|
| 145 |
|
| 146 | return NOTIFY_DONE;
|
| 147 | }
|
| 148 |
|
| 149 | static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data)
|
| 150 | {
|
| 151 | struct pm8941_pwrkey *pwrkey = _data;
|
| 152 | unsigned int sts;
|
| 153 | int err;
|
| 154 |
|
| 155 | if (pwrkey->sw_debounce_time_us) {
|
| 156 | if (ktime_before(ktime_get(), pwrkey->sw_debounce_end_time)) {
|
| 157 | dev_dbg(pwrkey->dev,
|
| 158 | "ignoring key event received before debounce end %lld us\n",
|
| 159 | ktime_to_us(pwrkey->sw_debounce_end_time));
|
| 160 | return IRQ_HANDLED;
|
| 161 | }
|
| 162 | }
|
| 163 |
|
| 164 | err = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_RT_STS, &sts);
|
| 165 | if (err)
|
| 166 | return IRQ_HANDLED;
|
| 167 |
|
| 168 | sts &= pwrkey->data->status_bit;
|
| 169 |
|
| 170 | if (pwrkey->sw_debounce_time_us && !sts)
|
| 171 | pwrkey->sw_debounce_end_time = ktime_add_us(ktime_get(),
|
| 172 | pwrkey->sw_debounce_time_us);
|
| 173 |
|
| 174 | |
| 175 | * Simulate a press event in case a release event occurred without a
|
| 176 | * corresponding press event.
|
| 177 |
|
| 178 | if (!pwrkey->last_status && !sts) {
|
| 179 | input_report_key(pwrkey->input, pwrkey->code, 1);
|
| 180 | input_sync(pwrkey->input);
|
| 181 | }
|
| 182 | pwrkey->last_status = sts;
|
| 183 |
|
| 184 | input_report_key(pwrkey->input, pwrkey->code, sts);
|
| 185 | input_sync(pwrkey->input);
|
| 186 |
|
| 187 | return IRQ_HANDLED;
|
| 188 | }
|
| 189 |
|
| 190 | static int pm8941_pwrkey_sw_debounce_init(struct pm8941_pwrkey *pwrkey)
|
| 191 | {
|
| 192 | unsigned int val, addr, mask;
|
| 193 | int error;
|
| 194 |
|
| 195 | if (pwrkey->data->has_pon_pbs && !pwrkey->pon_pbs_baseaddr) {
|
| 196 | dev_err(pwrkey->dev,
|
| 197 | "PON_PBS address missing, can't read HW debounce time\n");
|
| 198 | return 0;
|
| 199 | }
|
| 200 |
|
| 201 | if (pwrkey->pon_pbs_baseaddr)
|
| 202 | addr = pwrkey->pon_pbs_baseaddr + PON_DBC_CTL;
|
| 203 | else
|
| 204 | addr = pwrkey->baseaddr + PON_DBC_CTL;
|
| 205 | error = regmap_read(pwrkey->regmap, addr, &val);
|
| 206 | if (error)
|
| 207 | return error;
|
| 208 |
|
| 209 | if (pwrkey->subtype >= PON_SUBTYPE_GEN2_PRIMARY)
|
| 210 | mask = 0xf;
|
| 211 | else
|
| 212 | mask = 0x7;
|
| 213 |
|
| 214 | pwrkey->sw_debounce_time_us =
|
| 215 | 2 * USEC_PER_SEC / (1 << (mask - (val & mask)));
|
| 216 |
|
| 217 | dev_dbg(pwrkey->dev, "SW debounce time = %u us\n",
|
| 218 | pwrkey->sw_debounce_time_us);
|
| 219 |
|
| 220 | return 0;
|
| 221 | }
|
| 222 |
|
| 223 | static int pm8941_pwrkey_suspend(struct device *dev)
|
| 224 | {
|
| 225 | struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev);
|
| 226 |
|
| 227 | if (device_may_wakeup(dev))
|
| 228 | enable_irq_wake(pwrkey->irq);
|
| 229 |
|
| 230 | return 0;
|
| 231 | }
|
| 232 |
|
| 233 | static int pm8941_pwrkey_resume(struct device *dev)
|
| 234 | {
|
| 235 | struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev);
|
| 236 |
|
| 237 | if (device_may_wakeup(dev))
|
| 238 | disable_irq_wake(pwrkey->irq);
|
| 239 |
|
| 240 | return 0;
|
| 241 | }
|
| 242 |
|
| 243 | static DEFINE_SIMPLE_DEV_PM_OPS(pm8941_pwr_key_pm_ops,
|
| 244 | pm8941_pwrkey_suspend, pm8941_pwrkey_resume);
|
| 245 |
|
| 246 | static int pm8941_pwrkey_probe(struct platform_device *pdev)
|
| 247 | {
|
| 248 | struct pm8941_pwrkey *pwrkey;
|
| 249 | bool pull_up, wakeup;
|
| 250 | struct device *parent;
|
| 251 | struct device_node *regmap_node;
|
| 252 | const __be32 *addr;
|
| 253 | u32 req_delay, mask, delay_shift;
|
| 254 | int error;
|
| 255 |
|
| 256 | if (of_property_read_u32(pdev->dev.of_node, "debounce", &req_delay))
|
| 257 | req_delay = 15625;
|
| 258 |
|
| 259 | if (req_delay > 2000000 || req_delay == 0) {
|
| 260 | dev_err(&pdev->dev, "invalid debounce time: %u\n", req_delay);
|
| 261 | return -EINVAL;
|
| 262 | }
|
| 263 |
|
| 264 | pull_up = of_property_read_bool(pdev->dev.of_node, "bias-pull-up");
|
| 265 |
|
| 266 | pwrkey = devm_kzalloc(&pdev->dev, sizeof(*pwrkey), GFP_KERNEL);
|
| 267 | if (!pwrkey)
|
| 268 | return -ENOMEM;
|
| 269 |
|
| 270 | pwrkey->dev = &pdev->dev;
|
| 271 | pwrkey->data = of_device_get_match_data(&pdev->dev);
|
| 272 |
|
| 273 | parent = pdev->dev.parent;
|
| 274 | regmap_node = pdev->dev.of_node;
|
| 275 | pwrkey->regmap = dev_get_regmap(parent, NULL);
|
| 276 | if (!pwrkey->regmap) {
|
| 277 | regmap_node = parent->of_node;
|
| 278 | |
| 279 | * We failed to get regmap for parent. Let's see if we are
|
| 280 | * a child of pon node and read regmap and reg from its
|
| 281 | * parent.
|
| 282 |
|
| 283 | pwrkey->regmap = dev_get_regmap(parent->parent, NULL);
|
| 284 | if (!pwrkey->regmap) {
|
| 285 | dev_err(&pdev->dev, "failed to locate regmap\n");
|
| 286 | return -ENODEV;
|
| 287 | }
|
| 288 | }
|
| 289 |
|
| 290 | addr = of_get_address(regmap_node, 0, NULL, NULL);
|
| 291 | if (!addr) {
|
| 292 | dev_err(&pdev->dev, "reg property missing\n");
|
| 293 | return -EINVAL;
|
| 294 | }
|
| 295 | pwrkey->baseaddr = be32_to_cpup(addr);
|
| 296 |
|
| 297 | if (pwrkey->data->has_pon_pbs) {
|
| 298 |
|
| 299 | addr = of_get_address(regmap_node, 1, NULL, NULL);
|
| 300 | if (addr)
|
| 301 | pwrkey->pon_pbs_baseaddr = be32_to_cpup(addr);
|
| 302 | }
|
| 303 |
|
| 304 | pwrkey->irq = platform_get_irq(pdev, 0);
|
| 305 | if (pwrkey->irq < 0)
|
| 306 | return pwrkey->irq;
|
| 307 |
|
| 308 | error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_REV2,
|
| 309 | &pwrkey->revision);
|
| 310 | if (error) {
|
| 311 | dev_err(&pdev->dev, "failed to read revision: %d\n", error);
|
| 312 | return error;
|
| 313 | }
|
| 314 |
|
| 315 | error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_SUBTYPE,
|
| 316 | &pwrkey->subtype);
|
| 317 | if (error) {
|
| 318 | dev_err(&pdev->dev, "failed to read subtype: %d\n", error);
|
| 319 | return error;
|
| 320 | }
|
| 321 |
|
| 322 | error = of_property_read_u32(pdev->dev.of_node, "linux,code",
|
| 323 | &pwrkey->code);
|
| 324 | if (error) {
|
| 325 | dev_dbg(&pdev->dev,
|
| 326 | "no linux,code assuming power (%d)\n", error);
|
| 327 | pwrkey->code = KEY_POWER;
|
| 328 | }
|
| 329 |
|
| 330 | pwrkey->input = devm_input_allocate_device(&pdev->dev);
|
| 331 | if (!pwrkey->input) {
|
| 332 | dev_dbg(&pdev->dev, "unable to allocate input device\n");
|
| 333 | return -ENOMEM;
|
| 334 | }
|
| 335 |
|
| 336 | input_set_capability(pwrkey->input, EV_KEY, pwrkey->code);
|
| 337 |
|
| 338 | pwrkey->input->name = pwrkey->data->name;
|
| 339 | pwrkey->input->phys = pwrkey->data->phys;
|
| 340 |
|
| 341 | if (pwrkey->data->supports_debounce_config) {
|
| 342 | if (pwrkey->subtype >= PON_SUBTYPE_GEN2_PRIMARY) {
|
| 343 | mask = PON_DBC_DELAY_MASK_GEN2;
|
| 344 | delay_shift = PON_DBC_SHIFT_GEN2;
|
| 345 | } else {
|
| 346 | mask = PON_DBC_DELAY_MASK_GEN1;
|
| 347 | delay_shift = PON_DBC_SHIFT_GEN1;
|
| 348 | }
|
| 349 |
|
| 350 | req_delay = (req_delay << delay_shift) / USEC_PER_SEC;
|
| 351 | req_delay = ilog2(req_delay);
|
| 352 |
|
| 353 | error = regmap_update_bits(pwrkey->regmap,
|
| 354 | pwrkey->baseaddr + PON_DBC_CTL,
|
| 355 | mask,
|
| 356 | req_delay);
|
| 357 | if (error) {
|
| 358 | dev_err(&pdev->dev, "failed to set debounce: %d\n",
|
| 359 | error);
|
| 360 | return error;
|
| 361 | }
|
| 362 | }
|
| 363 |
|
| 364 | error = pm8941_pwrkey_sw_debounce_init(pwrkey);
|
| 365 | if (error)
|
| 366 | return error;
|
| 367 |
|
| 368 | if (pwrkey->data->pull_up_bit) {
|
| 369 | error = regmap_update_bits(pwrkey->regmap,
|
| 370 | pwrkey->baseaddr + PON_PULL_CTL,
|
| 371 | pwrkey->data->pull_up_bit,
|
| 372 | pull_up ? pwrkey->data->pull_up_bit :
|
| 373 | 0);
|
| 374 | if (error) {
|
| 375 | dev_err(&pdev->dev, "failed to set pull: %d\n", error);
|
| 376 | return error;
|
| 377 | }
|
| 378 | }
|
| 379 |
|
| 380 | error = devm_request_threaded_irq(&pdev->dev, pwrkey->irq,
|
| 381 | NULL, pm8941_pwrkey_irq,
|
| 382 | IRQF_ONESHOT,
|
| 383 | pwrkey->data->name, pwrkey);
|
| 384 | if (error) {
|
| 385 | dev_err(&pdev->dev, "failed requesting IRQ: %d\n", error);
|
| 386 | return error;
|
| 387 | }
|
| 388 |
|
| 389 | error = input_register_device(pwrkey->input);
|
| 390 | if (error) {
|
| 391 | dev_err(&pdev->dev, "failed to register input device: %d\n",
|
| 392 | error);
|
| 393 | return error;
|
| 394 | }
|
| 395 |
|
| 396 | if (pwrkey->data->supports_ps_hold_poff_config) {
|
| 397 | pwrkey->reboot_notifier.notifier_call = pm8941_reboot_notify;
|
| 398 | error = register_reboot_notifier(&pwrkey->reboot_notifier);
|
| 399 | if (error) {
|
| 400 | dev_err(&pdev->dev, "failed to register reboot notifier: %d\n",
|
| 401 | error);
|
| 402 | return error;
|
| 403 | }
|
| 404 | }
|
| 405 |
|
| 406 | wakeup = pwrkey->data->wakeup_source_default ||
|
| 407 | of_property_read_bool(pdev->dev.of_node, "wakeup-source");
|
| 408 |
|
| 409 | platform_set_drvdata(pdev, pwrkey);
|
| 410 | device_init_wakeup(&pdev->dev, wakeup);
|
| 411 |
|
| 412 | return 0;
|
| 413 | }
|
| 414 |
|
| 415 | static void pm8941_pwrkey_remove(struct platform_device *pdev)
|
| 416 | {
|
| 417 | struct pm8941_pwrkey *pwrkey = platform_get_drvdata(pdev);
|
| 418 |
|
| 419 | if (pwrkey->data->supports_ps_hold_poff_config)
|
| 420 | unregister_reboot_notifier(&pwrkey->reboot_notifier);
|
| 421 | }
|
| 422 |
|
| 423 | static const struct pm8941_data pwrkey_data = {
|
| 424 | .pull_up_bit = PON_KPDPWR_PULL_UP,
|
| 425 | .status_bit = PON_KPDPWR_N_SET,
|
| 426 | .name = "pm8941_pwrkey",
|
| 427 | .phys = "pm8941_pwrkey/input0",
|
| 428 | .supports_ps_hold_poff_config = true,
|
| 429 | .supports_debounce_config = true,
|
| 430 | .has_pon_pbs = false,
|
| 431 | .wakeup_source_default = true,
|
| 432 | };
|
| 433 |
|
| 434 | static const struct pm8941_data resin_data = {
|
| 435 | .pull_up_bit = PON_RESIN_PULL_UP,
|
| 436 | .status_bit = PON_RESIN_N_SET,
|
| 437 | .name = "pm8941_resin",
|
| 438 | .phys = "pm8941_resin/input0",
|
| 439 | .supports_ps_hold_poff_config = true,
|
| 440 | .supports_debounce_config = true,
|
| 441 | .has_pon_pbs = false,
|
| 442 | .wakeup_source_default = false,
|
| 443 | };
|
| 444 |
|
| 445 | static const struct pm8941_data pon_gen3_pwrkey_data = {
|
| 446 | .status_bit = PON_GEN3_KPDPWR_N_SET,
|
| 447 | .name = "pmic_pwrkey",
|
| 448 | .phys = "pmic_pwrkey/input0",
|
| 449 | .supports_ps_hold_poff_config = false,
|
| 450 | .supports_debounce_config = false,
|
| 451 | .has_pon_pbs = true,
|
| 452 | .wakeup_source_default = true,
|
| 453 | };
|
| 454 |
|
| 455 | static const struct pm8941_data pon_gen3_resin_data = {
|
| 456 | .status_bit = PON_GEN3_RESIN_N_SET,
|
| 457 | .name = "pmic_resin",
|
| 458 | .phys = "pmic_resin/input0",
|
| 459 | .supports_ps_hold_poff_config = false,
|
| 460 | .supports_debounce_config = false,
|
| 461 | .has_pon_pbs = true,
|
| 462 | .wakeup_source_default = false,
|
| 463 | };
|
| 464 |
|
| 465 | static const struct of_device_id pm8941_pwr_key_id_table[] = {
|
| 466 | { .compatible = "qcom,pm8941-pwrkey", .data = &pwrkey_data },
|
| 467 | { .compatible = "qcom,pm8941-resin", .data = &resin_data },
|
| 468 | { .compatible = "qcom,pmk8350-pwrkey", .data = &pon_gen3_pwrkey_data },
|
| 469 | { .compatible = "qcom,pmk8350-resin", .data = &pon_gen3_resin_data },
|
| 470 | { }
|
| 471 | };
|
| 472 | MODULE_DEVICE_TABLE(of, pm8941_pwr_key_id_table);
|
| 473 |
|
| 474 | static struct platform_driver pm8941_pwrkey_driver = {
|
| 475 | .probe = pm8941_pwrkey_probe,
|
| 476 | .remove = pm8941_pwrkey_remove,
|
| 477 | .driver = {
|
| 478 | .name = "pm8941-pwrkey",
|
| 479 | .pm = pm_sleep_ptr(&pm8941_pwr_key_pm_ops),
|
| 480 | .of_match_table = of_match_ptr(pm8941_pwr_key_id_table),
|
| 481 | },
|
| 482 | };
|
| 483 | module_platform_driver(pm8941_pwrkey_driver);
|
| 484 |
|
| 485 | MODULE_DESCRIPTION("PM8941 Power Key driver");
|
| 486 | MODULE_LICENSE("GPL v2");
|