197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 268c581d5SCourtney Cavin /* 3*2fcbda9aSDavid Collins * Copyright (c) 2010-2011, 2020-2021, The Linux Foundation. All rights reserved. 468c581d5SCourtney Cavin * Copyright (c) 2014, Sony Mobile Communications Inc. 568c581d5SCourtney Cavin */ 668c581d5SCourtney Cavin 768c581d5SCourtney Cavin #include <linux/delay.h> 868c581d5SCourtney Cavin #include <linux/errno.h> 968c581d5SCourtney Cavin #include <linux/input.h> 1068c581d5SCourtney Cavin #include <linux/interrupt.h> 1168c581d5SCourtney Cavin #include <linux/kernel.h> 1268c581d5SCourtney Cavin #include <linux/log2.h> 1368c581d5SCourtney Cavin #include <linux/module.h> 1468c581d5SCourtney Cavin #include <linux/of.h> 152049a9e5SVinod Koul #include <linux/of_device.h> 1668c581d5SCourtney Cavin #include <linux/platform_device.h> 1768c581d5SCourtney Cavin #include <linux/reboot.h> 1868c581d5SCourtney Cavin #include <linux/regmap.h> 1968c581d5SCourtney Cavin 2068c581d5SCourtney Cavin #define PON_REV2 0x01 2168c581d5SCourtney Cavin 2268c581d5SCourtney Cavin #define PON_RT_STS 0x10 2368c581d5SCourtney Cavin #define PON_KPDPWR_N_SET BIT(0) 24955c594eSVinod Koul #define PON_RESIN_N_SET BIT(1) 25*2fcbda9aSDavid Collins #define PON_GEN3_RESIN_N_SET BIT(6) 26*2fcbda9aSDavid Collins #define PON_GEN3_KPDPWR_N_SET BIT(7) 2768c581d5SCourtney Cavin 2868c581d5SCourtney Cavin #define PON_PS_HOLD_RST_CTL 0x5a 2968c581d5SCourtney Cavin #define PON_PS_HOLD_RST_CTL2 0x5b 3068c581d5SCourtney Cavin #define PON_PS_HOLD_ENABLE BIT(7) 3168c581d5SCourtney Cavin #define PON_PS_HOLD_TYPE_MASK 0x0f 3268c581d5SCourtney Cavin #define PON_PS_HOLD_TYPE_SHUTDOWN 4 3368c581d5SCourtney Cavin #define PON_PS_HOLD_TYPE_HARD_RESET 7 3468c581d5SCourtney Cavin 3568c581d5SCourtney Cavin #define PON_PULL_CTL 0x70 3668c581d5SCourtney Cavin #define PON_KPDPWR_PULL_UP BIT(1) 37955c594eSVinod Koul #define PON_RESIN_PULL_UP BIT(0) 3868c581d5SCourtney Cavin 3968c581d5SCourtney Cavin #define PON_DBC_CTL 0x71 4068c581d5SCourtney Cavin #define PON_DBC_DELAY_MASK 0x7 4168c581d5SCourtney Cavin 422049a9e5SVinod Koul struct pm8941_data { 432049a9e5SVinod Koul unsigned int pull_up_bit; 442049a9e5SVinod Koul unsigned int status_bit; 45*2fcbda9aSDavid Collins bool supports_ps_hold_poff_config; 46*2fcbda9aSDavid Collins bool supports_debounce_config; 47*2fcbda9aSDavid Collins const char *name; 48*2fcbda9aSDavid Collins const char *phys; 492049a9e5SVinod Koul }; 5068c581d5SCourtney Cavin 5168c581d5SCourtney Cavin struct pm8941_pwrkey { 5268c581d5SCourtney Cavin struct device *dev; 5368c581d5SCourtney Cavin int irq; 5468c581d5SCourtney Cavin u32 baseaddr; 5568c581d5SCourtney Cavin struct regmap *regmap; 5668c581d5SCourtney Cavin struct input_dev *input; 5768c581d5SCourtney Cavin 5868c581d5SCourtney Cavin unsigned int revision; 5968c581d5SCourtney Cavin struct notifier_block reboot_notifier; 602049a9e5SVinod Koul 612049a9e5SVinod Koul u32 code; 622049a9e5SVinod Koul const struct pm8941_data *data; 6368c581d5SCourtney Cavin }; 6468c581d5SCourtney Cavin 6568c581d5SCourtney Cavin static int pm8941_reboot_notify(struct notifier_block *nb, 6668c581d5SCourtney Cavin unsigned long code, void *unused) 6768c581d5SCourtney Cavin { 6868c581d5SCourtney Cavin struct pm8941_pwrkey *pwrkey = container_of(nb, struct pm8941_pwrkey, 6968c581d5SCourtney Cavin reboot_notifier); 7068c581d5SCourtney Cavin unsigned int enable_reg; 7168c581d5SCourtney Cavin unsigned int reset_type; 7268c581d5SCourtney Cavin int error; 7368c581d5SCourtney Cavin 7468c581d5SCourtney Cavin /* PMICs with revision 0 have the enable bit in same register as ctrl */ 7568c581d5SCourtney Cavin if (pwrkey->revision == 0) 7668c581d5SCourtney Cavin enable_reg = PON_PS_HOLD_RST_CTL; 7768c581d5SCourtney Cavin else 7868c581d5SCourtney Cavin enable_reg = PON_PS_HOLD_RST_CTL2; 7968c581d5SCourtney Cavin 8068c581d5SCourtney Cavin error = regmap_update_bits(pwrkey->regmap, 8168c581d5SCourtney Cavin pwrkey->baseaddr + enable_reg, 8268c581d5SCourtney Cavin PON_PS_HOLD_ENABLE, 8368c581d5SCourtney Cavin 0); 8468c581d5SCourtney Cavin if (error) 8568c581d5SCourtney Cavin dev_err(pwrkey->dev, 8668c581d5SCourtney Cavin "unable to clear ps hold reset enable: %d\n", 8768c581d5SCourtney Cavin error); 8868c581d5SCourtney Cavin 8968c581d5SCourtney Cavin /* 9068c581d5SCourtney Cavin * Updates of PON_PS_HOLD_ENABLE requires 3 sleep cycles between 9168c581d5SCourtney Cavin * writes. 9268c581d5SCourtney Cavin */ 9368c581d5SCourtney Cavin usleep_range(100, 1000); 9468c581d5SCourtney Cavin 9568c581d5SCourtney Cavin switch (code) { 9668c581d5SCourtney Cavin case SYS_HALT: 9768c581d5SCourtney Cavin case SYS_POWER_OFF: 9868c581d5SCourtney Cavin reset_type = PON_PS_HOLD_TYPE_SHUTDOWN; 9968c581d5SCourtney Cavin break; 10068c581d5SCourtney Cavin case SYS_RESTART: 10168c581d5SCourtney Cavin default: 10268c581d5SCourtney Cavin reset_type = PON_PS_HOLD_TYPE_HARD_RESET; 10368c581d5SCourtney Cavin break; 104b9ab471bSJavier Martinez Canillas } 10568c581d5SCourtney Cavin 10668c581d5SCourtney Cavin error = regmap_update_bits(pwrkey->regmap, 10768c581d5SCourtney Cavin pwrkey->baseaddr + PON_PS_HOLD_RST_CTL, 10868c581d5SCourtney Cavin PON_PS_HOLD_TYPE_MASK, 10968c581d5SCourtney Cavin reset_type); 11068c581d5SCourtney Cavin if (error) 11168c581d5SCourtney Cavin dev_err(pwrkey->dev, "unable to set ps hold reset type: %d\n", 11268c581d5SCourtney Cavin error); 11368c581d5SCourtney Cavin 11468c581d5SCourtney Cavin error = regmap_update_bits(pwrkey->regmap, 11568c581d5SCourtney Cavin pwrkey->baseaddr + enable_reg, 11668c581d5SCourtney Cavin PON_PS_HOLD_ENABLE, 11768c581d5SCourtney Cavin PON_PS_HOLD_ENABLE); 11868c581d5SCourtney Cavin if (error) 11968c581d5SCourtney Cavin dev_err(pwrkey->dev, "unable to re-set enable: %d\n", error); 12068c581d5SCourtney Cavin 12168c581d5SCourtney Cavin return NOTIFY_DONE; 12268c581d5SCourtney Cavin } 12368c581d5SCourtney Cavin 12468c581d5SCourtney Cavin static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data) 12568c581d5SCourtney Cavin { 12668c581d5SCourtney Cavin struct pm8941_pwrkey *pwrkey = _data; 12768c581d5SCourtney Cavin unsigned int sts; 12868c581d5SCourtney Cavin int error; 12968c581d5SCourtney Cavin 13068c581d5SCourtney Cavin error = regmap_read(pwrkey->regmap, 13168c581d5SCourtney Cavin pwrkey->baseaddr + PON_RT_STS, &sts); 13268c581d5SCourtney Cavin if (error) 13368c581d5SCourtney Cavin return IRQ_HANDLED; 13468c581d5SCourtney Cavin 1352049a9e5SVinod Koul input_report_key(pwrkey->input, pwrkey->code, 1362049a9e5SVinod Koul sts & pwrkey->data->status_bit); 13768c581d5SCourtney Cavin input_sync(pwrkey->input); 13868c581d5SCourtney Cavin 13968c581d5SCourtney Cavin return IRQ_HANDLED; 14068c581d5SCourtney Cavin } 14168c581d5SCourtney Cavin 14268c581d5SCourtney Cavin static int __maybe_unused pm8941_pwrkey_suspend(struct device *dev) 14368c581d5SCourtney Cavin { 14468c581d5SCourtney Cavin struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev); 14568c581d5SCourtney Cavin 14668c581d5SCourtney Cavin if (device_may_wakeup(dev)) 14768c581d5SCourtney Cavin enable_irq_wake(pwrkey->irq); 14868c581d5SCourtney Cavin 14968c581d5SCourtney Cavin return 0; 15068c581d5SCourtney Cavin } 15168c581d5SCourtney Cavin 15268c581d5SCourtney Cavin static int __maybe_unused pm8941_pwrkey_resume(struct device *dev) 15368c581d5SCourtney Cavin { 15468c581d5SCourtney Cavin struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev); 15568c581d5SCourtney Cavin 15668c581d5SCourtney Cavin if (device_may_wakeup(dev)) 15768c581d5SCourtney Cavin disable_irq_wake(pwrkey->irq); 15868c581d5SCourtney Cavin 15968c581d5SCourtney Cavin return 0; 16068c581d5SCourtney Cavin } 16168c581d5SCourtney Cavin 16268c581d5SCourtney Cavin static SIMPLE_DEV_PM_OPS(pm8941_pwr_key_pm_ops, 16368c581d5SCourtney Cavin pm8941_pwrkey_suspend, pm8941_pwrkey_resume); 16468c581d5SCourtney Cavin 16568c581d5SCourtney Cavin static int pm8941_pwrkey_probe(struct platform_device *pdev) 16668c581d5SCourtney Cavin { 16768c581d5SCourtney Cavin struct pm8941_pwrkey *pwrkey; 16868c581d5SCourtney Cavin bool pull_up; 1692049a9e5SVinod Koul struct device *parent; 17068c581d5SCourtney Cavin u32 req_delay; 17168c581d5SCourtney Cavin int error; 17268c581d5SCourtney Cavin 17368c581d5SCourtney Cavin if (of_property_read_u32(pdev->dev.of_node, "debounce", &req_delay)) 17468c581d5SCourtney Cavin req_delay = 15625; 17568c581d5SCourtney Cavin 17668c581d5SCourtney Cavin if (req_delay > 2000000 || req_delay == 0) { 17768c581d5SCourtney Cavin dev_err(&pdev->dev, "invalid debounce time: %u\n", req_delay); 17868c581d5SCourtney Cavin return -EINVAL; 17968c581d5SCourtney Cavin } 18068c581d5SCourtney Cavin 18168c581d5SCourtney Cavin pull_up = of_property_read_bool(pdev->dev.of_node, "bias-pull-up"); 18268c581d5SCourtney Cavin 18368c581d5SCourtney Cavin pwrkey = devm_kzalloc(&pdev->dev, sizeof(*pwrkey), GFP_KERNEL); 18468c581d5SCourtney Cavin if (!pwrkey) 18568c581d5SCourtney Cavin return -ENOMEM; 18668c581d5SCourtney Cavin 18768c581d5SCourtney Cavin pwrkey->dev = &pdev->dev; 1882049a9e5SVinod Koul pwrkey->data = of_device_get_match_data(&pdev->dev); 18968c581d5SCourtney Cavin 1902049a9e5SVinod Koul parent = pdev->dev.parent; 1912049a9e5SVinod Koul pwrkey->regmap = dev_get_regmap(parent, NULL); 1922049a9e5SVinod Koul if (!pwrkey->regmap) { 1932049a9e5SVinod Koul /* 1942049a9e5SVinod Koul * We failed to get regmap for parent. Let's see if we are 1952049a9e5SVinod Koul * a child of pon node and read regmap and reg from its 1962049a9e5SVinod Koul * parent. 1972049a9e5SVinod Koul */ 1982049a9e5SVinod Koul pwrkey->regmap = dev_get_regmap(parent->parent, NULL); 19968c581d5SCourtney Cavin if (!pwrkey->regmap) { 20068c581d5SCourtney Cavin dev_err(&pdev->dev, "failed to locate regmap\n"); 20168c581d5SCourtney Cavin return -ENODEV; 20268c581d5SCourtney Cavin } 20368c581d5SCourtney Cavin 2042049a9e5SVinod Koul error = of_property_read_u32(parent->of_node, 2052049a9e5SVinod Koul "reg", &pwrkey->baseaddr); 2062049a9e5SVinod Koul } else { 2072049a9e5SVinod Koul error = of_property_read_u32(pdev->dev.of_node, "reg", 2082049a9e5SVinod Koul &pwrkey->baseaddr); 2092049a9e5SVinod Koul } 2102049a9e5SVinod Koul if (error) 2112049a9e5SVinod Koul return error; 2122049a9e5SVinod Koul 21368c581d5SCourtney Cavin pwrkey->irq = platform_get_irq(pdev, 0); 2140bec8b7eSStephen Boyd if (pwrkey->irq < 0) 21568c581d5SCourtney Cavin return pwrkey->irq; 21668c581d5SCourtney Cavin 21768c581d5SCourtney Cavin error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_REV2, 21868c581d5SCourtney Cavin &pwrkey->revision); 21968c581d5SCourtney Cavin if (error) { 22068c581d5SCourtney Cavin dev_err(&pdev->dev, "failed to set debounce: %d\n", error); 22168c581d5SCourtney Cavin return error; 22268c581d5SCourtney Cavin } 22368c581d5SCourtney Cavin 2242049a9e5SVinod Koul error = of_property_read_u32(pdev->dev.of_node, "linux,code", 2252049a9e5SVinod Koul &pwrkey->code); 2262049a9e5SVinod Koul if (error) { 2272049a9e5SVinod Koul dev_dbg(&pdev->dev, 2282049a9e5SVinod Koul "no linux,code assuming power (%d)\n", error); 2292049a9e5SVinod Koul pwrkey->code = KEY_POWER; 2302049a9e5SVinod Koul } 2312049a9e5SVinod Koul 23268c581d5SCourtney Cavin pwrkey->input = devm_input_allocate_device(&pdev->dev); 23368c581d5SCourtney Cavin if (!pwrkey->input) { 23468c581d5SCourtney Cavin dev_dbg(&pdev->dev, "unable to allocate input device\n"); 23568c581d5SCourtney Cavin return -ENOMEM; 23668c581d5SCourtney Cavin } 23768c581d5SCourtney Cavin 2382049a9e5SVinod Koul input_set_capability(pwrkey->input, EV_KEY, pwrkey->code); 23968c581d5SCourtney Cavin 240*2fcbda9aSDavid Collins pwrkey->input->name = pwrkey->data->name; 241*2fcbda9aSDavid Collins pwrkey->input->phys = pwrkey->data->phys; 24268c581d5SCourtney Cavin 243*2fcbda9aSDavid Collins if (pwrkey->data->supports_debounce_config) { 24468c581d5SCourtney Cavin req_delay = (req_delay << 6) / USEC_PER_SEC; 24568c581d5SCourtney Cavin req_delay = ilog2(req_delay); 24668c581d5SCourtney Cavin 24768c581d5SCourtney Cavin error = regmap_update_bits(pwrkey->regmap, 24868c581d5SCourtney Cavin pwrkey->baseaddr + PON_DBC_CTL, 24968c581d5SCourtney Cavin PON_DBC_DELAY_MASK, 25068c581d5SCourtney Cavin req_delay); 25168c581d5SCourtney Cavin if (error) { 252*2fcbda9aSDavid Collins dev_err(&pdev->dev, "failed to set debounce: %d\n", 253*2fcbda9aSDavid Collins error); 25468c581d5SCourtney Cavin return error; 25568c581d5SCourtney Cavin } 256*2fcbda9aSDavid Collins } 25768c581d5SCourtney Cavin 258*2fcbda9aSDavid Collins if (pwrkey->data->pull_up_bit) { 25968c581d5SCourtney Cavin error = regmap_update_bits(pwrkey->regmap, 26068c581d5SCourtney Cavin pwrkey->baseaddr + PON_PULL_CTL, 2612049a9e5SVinod Koul pwrkey->data->pull_up_bit, 262*2fcbda9aSDavid Collins pull_up ? pwrkey->data->pull_up_bit : 263*2fcbda9aSDavid Collins 0); 26468c581d5SCourtney Cavin if (error) { 26568c581d5SCourtney Cavin dev_err(&pdev->dev, "failed to set pull: %d\n", error); 26668c581d5SCourtney Cavin return error; 26768c581d5SCourtney Cavin } 268*2fcbda9aSDavid Collins } 26968c581d5SCourtney Cavin 27068c581d5SCourtney Cavin error = devm_request_threaded_irq(&pdev->dev, pwrkey->irq, 27168c581d5SCourtney Cavin NULL, pm8941_pwrkey_irq, 27268c581d5SCourtney Cavin IRQF_ONESHOT, 273*2fcbda9aSDavid Collins pwrkey->data->name, pwrkey); 27468c581d5SCourtney Cavin if (error) { 27568c581d5SCourtney Cavin dev_err(&pdev->dev, "failed requesting IRQ: %d\n", error); 27668c581d5SCourtney Cavin return error; 27768c581d5SCourtney Cavin } 27868c581d5SCourtney Cavin 27968c581d5SCourtney Cavin error = input_register_device(pwrkey->input); 28068c581d5SCourtney Cavin if (error) { 28168c581d5SCourtney Cavin dev_err(&pdev->dev, "failed to register input device: %d\n", 28268c581d5SCourtney Cavin error); 28368c581d5SCourtney Cavin return error; 28468c581d5SCourtney Cavin } 28568c581d5SCourtney Cavin 286*2fcbda9aSDavid Collins if (pwrkey->data->supports_ps_hold_poff_config) { 28768c581d5SCourtney Cavin pwrkey->reboot_notifier.notifier_call = pm8941_reboot_notify, 28868c581d5SCourtney Cavin error = register_reboot_notifier(&pwrkey->reboot_notifier); 28968c581d5SCourtney Cavin if (error) { 29068c581d5SCourtney Cavin dev_err(&pdev->dev, "failed to register reboot notifier: %d\n", 29168c581d5SCourtney Cavin error); 29268c581d5SCourtney Cavin return error; 29368c581d5SCourtney Cavin } 294*2fcbda9aSDavid Collins } 29568c581d5SCourtney Cavin 29668c581d5SCourtney Cavin platform_set_drvdata(pdev, pwrkey); 29768c581d5SCourtney Cavin device_init_wakeup(&pdev->dev, 1); 29868c581d5SCourtney Cavin 29968c581d5SCourtney Cavin return 0; 30068c581d5SCourtney Cavin } 30168c581d5SCourtney Cavin 30268c581d5SCourtney Cavin static int pm8941_pwrkey_remove(struct platform_device *pdev) 30368c581d5SCourtney Cavin { 30468c581d5SCourtney Cavin struct pm8941_pwrkey *pwrkey = platform_get_drvdata(pdev); 30568c581d5SCourtney Cavin 306*2fcbda9aSDavid Collins if (pwrkey->data->supports_ps_hold_poff_config) 30768c581d5SCourtney Cavin unregister_reboot_notifier(&pwrkey->reboot_notifier); 30868c581d5SCourtney Cavin 30968c581d5SCourtney Cavin return 0; 31068c581d5SCourtney Cavin } 31168c581d5SCourtney Cavin 3122049a9e5SVinod Koul static const struct pm8941_data pwrkey_data = { 3132049a9e5SVinod Koul .pull_up_bit = PON_KPDPWR_PULL_UP, 3142049a9e5SVinod Koul .status_bit = PON_KPDPWR_N_SET, 315*2fcbda9aSDavid Collins .name = "pm8941_pwrkey", 316*2fcbda9aSDavid Collins .phys = "pm8941_pwrkey/input0", 317*2fcbda9aSDavid Collins .supports_ps_hold_poff_config = true, 318*2fcbda9aSDavid Collins .supports_debounce_config = true, 3192049a9e5SVinod Koul }; 3202049a9e5SVinod Koul 321955c594eSVinod Koul static const struct pm8941_data resin_data = { 322955c594eSVinod Koul .pull_up_bit = PON_RESIN_PULL_UP, 323955c594eSVinod Koul .status_bit = PON_RESIN_N_SET, 324*2fcbda9aSDavid Collins .name = "pm8941_resin", 325*2fcbda9aSDavid Collins .phys = "pm8941_resin/input0", 326*2fcbda9aSDavid Collins .supports_ps_hold_poff_config = true, 327*2fcbda9aSDavid Collins .supports_debounce_config = true, 328*2fcbda9aSDavid Collins }; 329*2fcbda9aSDavid Collins 330*2fcbda9aSDavid Collins static const struct pm8941_data pon_gen3_pwrkey_data = { 331*2fcbda9aSDavid Collins .status_bit = PON_GEN3_KPDPWR_N_SET, 332*2fcbda9aSDavid Collins .name = "pmic_pwrkey", 333*2fcbda9aSDavid Collins .phys = "pmic_pwrkey/input0", 334*2fcbda9aSDavid Collins .supports_ps_hold_poff_config = false, 335*2fcbda9aSDavid Collins .supports_debounce_config = false, 336*2fcbda9aSDavid Collins }; 337*2fcbda9aSDavid Collins 338*2fcbda9aSDavid Collins static const struct pm8941_data pon_gen3_resin_data = { 339*2fcbda9aSDavid Collins .status_bit = PON_GEN3_RESIN_N_SET, 340*2fcbda9aSDavid Collins .name = "pmic_resin", 341*2fcbda9aSDavid Collins .phys = "pmic_resin/input0", 342*2fcbda9aSDavid Collins .supports_ps_hold_poff_config = false, 343*2fcbda9aSDavid Collins .supports_debounce_config = false, 344955c594eSVinod Koul }; 345955c594eSVinod Koul 34668c581d5SCourtney Cavin static const struct of_device_id pm8941_pwr_key_id_table[] = { 3472049a9e5SVinod Koul { .compatible = "qcom,pm8941-pwrkey", .data = &pwrkey_data }, 348955c594eSVinod Koul { .compatible = "qcom,pm8941-resin", .data = &resin_data }, 349*2fcbda9aSDavid Collins { .compatible = "qcom,pmk8350-pwrkey", .data = &pon_gen3_pwrkey_data }, 350*2fcbda9aSDavid Collins { .compatible = "qcom,pmk8350-resin", .data = &pon_gen3_resin_data }, 35168c581d5SCourtney Cavin { } 35268c581d5SCourtney Cavin }; 35368c581d5SCourtney Cavin MODULE_DEVICE_TABLE(of, pm8941_pwr_key_id_table); 35468c581d5SCourtney Cavin 35568c581d5SCourtney Cavin static struct platform_driver pm8941_pwrkey_driver = { 35668c581d5SCourtney Cavin .probe = pm8941_pwrkey_probe, 35768c581d5SCourtney Cavin .remove = pm8941_pwrkey_remove, 35868c581d5SCourtney Cavin .driver = { 35968c581d5SCourtney Cavin .name = "pm8941-pwrkey", 36068c581d5SCourtney Cavin .pm = &pm8941_pwr_key_pm_ops, 36168c581d5SCourtney Cavin .of_match_table = of_match_ptr(pm8941_pwr_key_id_table), 36268c581d5SCourtney Cavin }, 36368c581d5SCourtney Cavin }; 36468c581d5SCourtney Cavin module_platform_driver(pm8941_pwrkey_driver); 36568c581d5SCourtney Cavin 36668c581d5SCourtney Cavin MODULE_DESCRIPTION("PM8941 Power Key driver"); 36768c581d5SCourtney Cavin MODULE_LICENSE("GPL v2"); 368