162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Onkey driver for Actions Semi ATC260x PMICs. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/bitfield.h> 962306a36Sopenharmony_ci#include <linux/input.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/mfd/atc260x/core.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/regmap.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* <2s for short press, >2s for long press */ 1862306a36Sopenharmony_ci#define KEY_PRESS_TIME_SEC 2 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* Driver internals */ 2162306a36Sopenharmony_cienum atc260x_onkey_reset_status { 2262306a36Sopenharmony_ci KEY_RESET_HW_DEFAULT, 2362306a36Sopenharmony_ci KEY_RESET_DISABLED, 2462306a36Sopenharmony_ci KEY_RESET_USER_SEL, 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct atc260x_onkey_params { 2862306a36Sopenharmony_ci u32 reg_int_ctl; 2962306a36Sopenharmony_ci u32 kdwn_state_bm; 3062306a36Sopenharmony_ci u32 long_int_pnd_bm; 3162306a36Sopenharmony_ci u32 short_int_pnd_bm; 3262306a36Sopenharmony_ci u32 kdwn_int_pnd_bm; 3362306a36Sopenharmony_ci u32 press_int_en_bm; 3462306a36Sopenharmony_ci u32 kdwn_int_en_bm; 3562306a36Sopenharmony_ci u32 press_time_bm; 3662306a36Sopenharmony_ci u32 reset_en_bm; 3762306a36Sopenharmony_ci u32 reset_time_bm; 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct atc260x_onkey { 4162306a36Sopenharmony_ci struct atc260x *atc260x; 4262306a36Sopenharmony_ci const struct atc260x_onkey_params *params; 4362306a36Sopenharmony_ci struct input_dev *input_dev; 4462306a36Sopenharmony_ci struct delayed_work work; 4562306a36Sopenharmony_ci int irq; 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic const struct atc260x_onkey_params atc2603c_onkey_params = { 4962306a36Sopenharmony_ci .reg_int_ctl = ATC2603C_PMU_SYS_CTL2, 5062306a36Sopenharmony_ci .long_int_pnd_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_LONG_PRESS, 5162306a36Sopenharmony_ci .short_int_pnd_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_SHORT_PRESS, 5262306a36Sopenharmony_ci .kdwn_int_pnd_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_PD, 5362306a36Sopenharmony_ci .press_int_en_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_INT_EN, 5462306a36Sopenharmony_ci .kdwn_int_en_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_INT_EN, 5562306a36Sopenharmony_ci .kdwn_state_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS, 5662306a36Sopenharmony_ci .press_time_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_TIME, 5762306a36Sopenharmony_ci .reset_en_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_RESET_EN, 5862306a36Sopenharmony_ci .reset_time_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL, 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic const struct atc260x_onkey_params atc2609a_onkey_params = { 6262306a36Sopenharmony_ci .reg_int_ctl = ATC2609A_PMU_SYS_CTL2, 6362306a36Sopenharmony_ci .long_int_pnd_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_LONG_PRESS, 6462306a36Sopenharmony_ci .short_int_pnd_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_SHORT_PRESS, 6562306a36Sopenharmony_ci .kdwn_int_pnd_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_PD, 6662306a36Sopenharmony_ci .press_int_en_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_LSP_INT_EN, 6762306a36Sopenharmony_ci .kdwn_int_en_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_INT_EN, 6862306a36Sopenharmony_ci .kdwn_state_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS, 6962306a36Sopenharmony_ci .press_time_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_TIME, 7062306a36Sopenharmony_ci .reset_en_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_EN, 7162306a36Sopenharmony_ci .reset_time_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL, 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic int atc2603x_onkey_hw_init(struct atc260x_onkey *onkey, 7562306a36Sopenharmony_ci enum atc260x_onkey_reset_status reset_status, 7662306a36Sopenharmony_ci u32 reset_time, u32 press_time) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci u32 reg_bm, reg_val; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci reg_bm = onkey->params->long_int_pnd_bm | 8162306a36Sopenharmony_ci onkey->params->short_int_pnd_bm | 8262306a36Sopenharmony_ci onkey->params->kdwn_int_pnd_bm | 8362306a36Sopenharmony_ci onkey->params->press_int_en_bm | 8462306a36Sopenharmony_ci onkey->params->kdwn_int_en_bm; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci reg_val = reg_bm | press_time; 8762306a36Sopenharmony_ci reg_bm |= onkey->params->press_time_bm; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (reset_status == KEY_RESET_DISABLED) { 9062306a36Sopenharmony_ci reg_bm |= onkey->params->reset_en_bm; 9162306a36Sopenharmony_ci } else if (reset_status == KEY_RESET_USER_SEL) { 9262306a36Sopenharmony_ci reg_bm |= onkey->params->reset_en_bm | 9362306a36Sopenharmony_ci onkey->params->reset_time_bm; 9462306a36Sopenharmony_ci reg_val |= onkey->params->reset_en_bm | reset_time; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci return regmap_update_bits(onkey->atc260x->regmap, 9862306a36Sopenharmony_ci onkey->params->reg_int_ctl, reg_bm, reg_val); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic void atc260x_onkey_query(struct atc260x_onkey *onkey) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci u32 reg_bits; 10462306a36Sopenharmony_ci int ret, key_down; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci ret = regmap_read(onkey->atc260x->regmap, 10762306a36Sopenharmony_ci onkey->params->reg_int_ctl, &key_down); 10862306a36Sopenharmony_ci if (ret) { 10962306a36Sopenharmony_ci key_down = 1; 11062306a36Sopenharmony_ci dev_err(onkey->atc260x->dev, 11162306a36Sopenharmony_ci "Failed to read onkey status: %d\n", ret); 11262306a36Sopenharmony_ci } else { 11362306a36Sopenharmony_ci key_down &= onkey->params->kdwn_state_bm; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* 11762306a36Sopenharmony_ci * The hardware generates interrupt only when the onkey pin is 11862306a36Sopenharmony_ci * asserted. Hence, the deassertion of the pin is simulated through 11962306a36Sopenharmony_ci * work queue. 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci if (key_down) { 12262306a36Sopenharmony_ci schedule_delayed_work(&onkey->work, msecs_to_jiffies(200)); 12362306a36Sopenharmony_ci return; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* 12762306a36Sopenharmony_ci * The key-down status bit is cleared when the On/Off button 12862306a36Sopenharmony_ci * is released. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ci input_report_key(onkey->input_dev, KEY_POWER, 0); 13162306a36Sopenharmony_ci input_sync(onkey->input_dev); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci reg_bits = onkey->params->long_int_pnd_bm | 13462306a36Sopenharmony_ci onkey->params->short_int_pnd_bm | 13562306a36Sopenharmony_ci onkey->params->kdwn_int_pnd_bm | 13662306a36Sopenharmony_ci onkey->params->press_int_en_bm | 13762306a36Sopenharmony_ci onkey->params->kdwn_int_en_bm; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Clear key press pending events and enable key press interrupts. */ 14062306a36Sopenharmony_ci regmap_update_bits(onkey->atc260x->regmap, onkey->params->reg_int_ctl, 14162306a36Sopenharmony_ci reg_bits, reg_bits); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void atc260x_onkey_work(struct work_struct *work) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct atc260x_onkey *onkey = container_of(work, struct atc260x_onkey, 14762306a36Sopenharmony_ci work.work); 14862306a36Sopenharmony_ci atc260x_onkey_query(onkey); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic irqreturn_t atc260x_onkey_irq(int irq, void *data) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct atc260x_onkey *onkey = data; 15462306a36Sopenharmony_ci int ret; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* Disable key press interrupts. */ 15762306a36Sopenharmony_ci ret = regmap_update_bits(onkey->atc260x->regmap, 15862306a36Sopenharmony_ci onkey->params->reg_int_ctl, 15962306a36Sopenharmony_ci onkey->params->press_int_en_bm | 16062306a36Sopenharmony_ci onkey->params->kdwn_int_en_bm, 0); 16162306a36Sopenharmony_ci if (ret) 16262306a36Sopenharmony_ci dev_err(onkey->atc260x->dev, 16362306a36Sopenharmony_ci "Failed to disable interrupts: %d\n", ret); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci input_report_key(onkey->input_dev, KEY_POWER, 1); 16662306a36Sopenharmony_ci input_sync(onkey->input_dev); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci atc260x_onkey_query(onkey); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return IRQ_HANDLED; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int atc260x_onkey_open(struct input_dev *dev) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct atc260x_onkey *onkey = input_get_drvdata(dev); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci enable_irq(onkey->irq); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return 0; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic void atc260x_onkey_close(struct input_dev *dev) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct atc260x_onkey *onkey = input_get_drvdata(dev); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci disable_irq(onkey->irq); 18762306a36Sopenharmony_ci cancel_delayed_work_sync(&onkey->work); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int atc260x_onkey_probe(struct platform_device *pdev) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct atc260x *atc260x = dev_get_drvdata(pdev->dev.parent); 19362306a36Sopenharmony_ci struct atc260x_onkey *onkey; 19462306a36Sopenharmony_ci struct input_dev *input_dev; 19562306a36Sopenharmony_ci enum atc260x_onkey_reset_status reset_status; 19662306a36Sopenharmony_ci u32 press_time = KEY_PRESS_TIME_SEC, reset_time = 0; 19762306a36Sopenharmony_ci int val, error; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci onkey = devm_kzalloc(&pdev->dev, sizeof(*onkey), GFP_KERNEL); 20062306a36Sopenharmony_ci if (!onkey) 20162306a36Sopenharmony_ci return -ENOMEM; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci error = device_property_read_u32(pdev->dev.parent, 20462306a36Sopenharmony_ci "reset-time-sec", &val); 20562306a36Sopenharmony_ci if (error) { 20662306a36Sopenharmony_ci reset_status = KEY_RESET_HW_DEFAULT; 20762306a36Sopenharmony_ci } else if (val) { 20862306a36Sopenharmony_ci if (val < 6 || val > 12) { 20962306a36Sopenharmony_ci dev_err(&pdev->dev, "reset-time-sec out of range\n"); 21062306a36Sopenharmony_ci return -EINVAL; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci reset_status = KEY_RESET_USER_SEL; 21462306a36Sopenharmony_ci reset_time = (val - 6) / 2; 21562306a36Sopenharmony_ci } else { 21662306a36Sopenharmony_ci reset_status = KEY_RESET_DISABLED; 21762306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Disabled reset on long-press\n"); 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci switch (atc260x->ic_type) { 22162306a36Sopenharmony_ci case ATC2603C: 22262306a36Sopenharmony_ci onkey->params = &atc2603c_onkey_params; 22362306a36Sopenharmony_ci press_time = FIELD_PREP(ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_TIME, 22462306a36Sopenharmony_ci press_time); 22562306a36Sopenharmony_ci reset_time = FIELD_PREP(ATC2603C_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL, 22662306a36Sopenharmony_ci reset_time); 22762306a36Sopenharmony_ci break; 22862306a36Sopenharmony_ci case ATC2609A: 22962306a36Sopenharmony_ci onkey->params = &atc2609a_onkey_params; 23062306a36Sopenharmony_ci press_time = FIELD_PREP(ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_TIME, 23162306a36Sopenharmony_ci press_time); 23262306a36Sopenharmony_ci reset_time = FIELD_PREP(ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL, 23362306a36Sopenharmony_ci reset_time); 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci default: 23662306a36Sopenharmony_ci dev_err(&pdev->dev, 23762306a36Sopenharmony_ci "OnKey not supported for ATC260x PMIC type: %u\n", 23862306a36Sopenharmony_ci atc260x->ic_type); 23962306a36Sopenharmony_ci return -EINVAL; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci input_dev = devm_input_allocate_device(&pdev->dev); 24362306a36Sopenharmony_ci if (!input_dev) { 24462306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to allocate input device\n"); 24562306a36Sopenharmony_ci return -ENOMEM; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci onkey->input_dev = input_dev; 24962306a36Sopenharmony_ci onkey->atc260x = atc260x; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci input_dev->name = "atc260x-onkey"; 25262306a36Sopenharmony_ci input_dev->phys = "atc260x-onkey/input0"; 25362306a36Sopenharmony_ci input_dev->open = atc260x_onkey_open; 25462306a36Sopenharmony_ci input_dev->close = atc260x_onkey_close; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci input_set_capability(input_dev, EV_KEY, KEY_POWER); 25762306a36Sopenharmony_ci input_set_drvdata(input_dev, onkey); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci INIT_DELAYED_WORK(&onkey->work, atc260x_onkey_work); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci onkey->irq = platform_get_irq(pdev, 0); 26262306a36Sopenharmony_ci if (onkey->irq < 0) 26362306a36Sopenharmony_ci return onkey->irq; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci error = devm_request_threaded_irq(&pdev->dev, onkey->irq, NULL, 26662306a36Sopenharmony_ci atc260x_onkey_irq, IRQF_ONESHOT, 26762306a36Sopenharmony_ci dev_name(&pdev->dev), onkey); 26862306a36Sopenharmony_ci if (error) { 26962306a36Sopenharmony_ci dev_err(&pdev->dev, 27062306a36Sopenharmony_ci "Failed to register IRQ %d: %d\n", onkey->irq, error); 27162306a36Sopenharmony_ci return error; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* Keep IRQ disabled until atc260x_onkey_open() is called. */ 27562306a36Sopenharmony_ci disable_irq(onkey->irq); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci error = input_register_device(input_dev); 27862306a36Sopenharmony_ci if (error) { 27962306a36Sopenharmony_ci dev_err(&pdev->dev, 28062306a36Sopenharmony_ci "Failed to register input device: %d\n", error); 28162306a36Sopenharmony_ci return error; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci error = atc2603x_onkey_hw_init(onkey, reset_status, 28562306a36Sopenharmony_ci reset_time, press_time); 28662306a36Sopenharmony_ci if (error) 28762306a36Sopenharmony_ci return error; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci device_init_wakeup(&pdev->dev, true); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return 0; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic struct platform_driver atc260x_onkey_driver = { 29562306a36Sopenharmony_ci .probe = atc260x_onkey_probe, 29662306a36Sopenharmony_ci .driver = { 29762306a36Sopenharmony_ci .name = "atc260x-onkey", 29862306a36Sopenharmony_ci }, 29962306a36Sopenharmony_ci}; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cimodule_platform_driver(atc260x_onkey_driver); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ciMODULE_DESCRIPTION("Onkey driver for ATC260x PMICs"); 30462306a36Sopenharmony_ciMODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>"); 30562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 306