atc260x-onkey.c (8371B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Onkey driver for Actions Semi ATC260x PMICs. 4 * 5 * Copyright (c) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com> 6 */ 7 8#include <linux/bitfield.h> 9#include <linux/input.h> 10#include <linux/interrupt.h> 11#include <linux/mfd/atc260x/core.h> 12#include <linux/module.h> 13#include <linux/of.h> 14#include <linux/platform_device.h> 15#include <linux/regmap.h> 16 17/* <2s for short press, >2s for long press */ 18#define KEY_PRESS_TIME_SEC 2 19 20/* Driver internals */ 21enum atc260x_onkey_reset_status { 22 KEY_RESET_HW_DEFAULT, 23 KEY_RESET_DISABLED, 24 KEY_RESET_USER_SEL, 25}; 26 27struct atc260x_onkey_params { 28 u32 reg_int_ctl; 29 u32 kdwn_state_bm; 30 u32 long_int_pnd_bm; 31 u32 short_int_pnd_bm; 32 u32 kdwn_int_pnd_bm; 33 u32 press_int_en_bm; 34 u32 kdwn_int_en_bm; 35 u32 press_time_bm; 36 u32 reset_en_bm; 37 u32 reset_time_bm; 38}; 39 40struct atc260x_onkey { 41 struct atc260x *atc260x; 42 const struct atc260x_onkey_params *params; 43 struct input_dev *input_dev; 44 struct delayed_work work; 45 int irq; 46}; 47 48static const struct atc260x_onkey_params atc2603c_onkey_params = { 49 .reg_int_ctl = ATC2603C_PMU_SYS_CTL2, 50 .long_int_pnd_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_LONG_PRESS, 51 .short_int_pnd_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_SHORT_PRESS, 52 .kdwn_int_pnd_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_PD, 53 .press_int_en_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_INT_EN, 54 .kdwn_int_en_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_INT_EN, 55 .kdwn_state_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS, 56 .press_time_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_TIME, 57 .reset_en_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_RESET_EN, 58 .reset_time_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL, 59}; 60 61static const struct atc260x_onkey_params atc2609a_onkey_params = { 62 .reg_int_ctl = ATC2609A_PMU_SYS_CTL2, 63 .long_int_pnd_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_LONG_PRESS, 64 .short_int_pnd_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_SHORT_PRESS, 65 .kdwn_int_pnd_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_PD, 66 .press_int_en_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_LSP_INT_EN, 67 .kdwn_int_en_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_INT_EN, 68 .kdwn_state_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS, 69 .press_time_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_TIME, 70 .reset_en_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_EN, 71 .reset_time_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL, 72}; 73 74static int atc2603x_onkey_hw_init(struct atc260x_onkey *onkey, 75 enum atc260x_onkey_reset_status reset_status, 76 u32 reset_time, u32 press_time) 77{ 78 u32 reg_bm, reg_val; 79 80 reg_bm = onkey->params->long_int_pnd_bm | 81 onkey->params->short_int_pnd_bm | 82 onkey->params->kdwn_int_pnd_bm | 83 onkey->params->press_int_en_bm | 84 onkey->params->kdwn_int_en_bm; 85 86 reg_val = reg_bm | press_time; 87 reg_bm |= onkey->params->press_time_bm; 88 89 if (reset_status == KEY_RESET_DISABLED) { 90 reg_bm |= onkey->params->reset_en_bm; 91 } else if (reset_status == KEY_RESET_USER_SEL) { 92 reg_bm |= onkey->params->reset_en_bm | 93 onkey->params->reset_time_bm; 94 reg_val |= onkey->params->reset_en_bm | reset_time; 95 } 96 97 return regmap_update_bits(onkey->atc260x->regmap, 98 onkey->params->reg_int_ctl, reg_bm, reg_val); 99} 100 101static void atc260x_onkey_query(struct atc260x_onkey *onkey) 102{ 103 u32 reg_bits; 104 int ret, key_down; 105 106 ret = regmap_read(onkey->atc260x->regmap, 107 onkey->params->reg_int_ctl, &key_down); 108 if (ret) { 109 key_down = 1; 110 dev_err(onkey->atc260x->dev, 111 "Failed to read onkey status: %d\n", ret); 112 } else { 113 key_down &= onkey->params->kdwn_state_bm; 114 } 115 116 /* 117 * The hardware generates interrupt only when the onkey pin is 118 * asserted. Hence, the deassertion of the pin is simulated through 119 * work queue. 120 */ 121 if (key_down) { 122 schedule_delayed_work(&onkey->work, msecs_to_jiffies(200)); 123 return; 124 } 125 126 /* 127 * The key-down status bit is cleared when the On/Off button 128 * is released. 129 */ 130 input_report_key(onkey->input_dev, KEY_POWER, 0); 131 input_sync(onkey->input_dev); 132 133 reg_bits = onkey->params->long_int_pnd_bm | 134 onkey->params->short_int_pnd_bm | 135 onkey->params->kdwn_int_pnd_bm | 136 onkey->params->press_int_en_bm | 137 onkey->params->kdwn_int_en_bm; 138 139 /* Clear key press pending events and enable key press interrupts. */ 140 regmap_update_bits(onkey->atc260x->regmap, onkey->params->reg_int_ctl, 141 reg_bits, reg_bits); 142} 143 144static void atc260x_onkey_work(struct work_struct *work) 145{ 146 struct atc260x_onkey *onkey = container_of(work, struct atc260x_onkey, 147 work.work); 148 atc260x_onkey_query(onkey); 149} 150 151static irqreturn_t atc260x_onkey_irq(int irq, void *data) 152{ 153 struct atc260x_onkey *onkey = data; 154 int ret; 155 156 /* Disable key press interrupts. */ 157 ret = regmap_update_bits(onkey->atc260x->regmap, 158 onkey->params->reg_int_ctl, 159 onkey->params->press_int_en_bm | 160 onkey->params->kdwn_int_en_bm, 0); 161 if (ret) 162 dev_err(onkey->atc260x->dev, 163 "Failed to disable interrupts: %d\n", ret); 164 165 input_report_key(onkey->input_dev, KEY_POWER, 1); 166 input_sync(onkey->input_dev); 167 168 atc260x_onkey_query(onkey); 169 170 return IRQ_HANDLED; 171} 172 173static int atc260x_onkey_open(struct input_dev *dev) 174{ 175 struct atc260x_onkey *onkey = input_get_drvdata(dev); 176 177 enable_irq(onkey->irq); 178 179 return 0; 180} 181 182static void atc260x_onkey_close(struct input_dev *dev) 183{ 184 struct atc260x_onkey *onkey = input_get_drvdata(dev); 185 186 disable_irq(onkey->irq); 187 cancel_delayed_work_sync(&onkey->work); 188} 189 190static int atc260x_onkey_probe(struct platform_device *pdev) 191{ 192 struct atc260x *atc260x = dev_get_drvdata(pdev->dev.parent); 193 struct atc260x_onkey *onkey; 194 struct input_dev *input_dev; 195 enum atc260x_onkey_reset_status reset_status; 196 u32 press_time = KEY_PRESS_TIME_SEC, reset_time = 0; 197 int val, error; 198 199 onkey = devm_kzalloc(&pdev->dev, sizeof(*onkey), GFP_KERNEL); 200 if (!onkey) 201 return -ENOMEM; 202 203 error = device_property_read_u32(pdev->dev.parent, 204 "reset-time-sec", &val); 205 if (error) { 206 reset_status = KEY_RESET_HW_DEFAULT; 207 } else if (val) { 208 if (val < 6 || val > 12) { 209 dev_err(&pdev->dev, "reset-time-sec out of range\n"); 210 return -EINVAL; 211 } 212 213 reset_status = KEY_RESET_USER_SEL; 214 reset_time = (val - 6) / 2; 215 } else { 216 reset_status = KEY_RESET_DISABLED; 217 dev_dbg(&pdev->dev, "Disabled reset on long-press\n"); 218 } 219 220 switch (atc260x->ic_type) { 221 case ATC2603C: 222 onkey->params = &atc2603c_onkey_params; 223 press_time = FIELD_PREP(ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_TIME, 224 press_time); 225 reset_time = FIELD_PREP(ATC2603C_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL, 226 reset_time); 227 break; 228 case ATC2609A: 229 onkey->params = &atc2609a_onkey_params; 230 press_time = FIELD_PREP(ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_TIME, 231 press_time); 232 reset_time = FIELD_PREP(ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL, 233 reset_time); 234 break; 235 default: 236 dev_err(&pdev->dev, 237 "OnKey not supported for ATC260x PMIC type: %u\n", 238 atc260x->ic_type); 239 return -EINVAL; 240 } 241 242 input_dev = devm_input_allocate_device(&pdev->dev); 243 if (!input_dev) { 244 dev_err(&pdev->dev, "Failed to allocate input device\n"); 245 return -ENOMEM; 246 } 247 248 onkey->input_dev = input_dev; 249 onkey->atc260x = atc260x; 250 251 input_dev->name = "atc260x-onkey"; 252 input_dev->phys = "atc260x-onkey/input0"; 253 input_dev->open = atc260x_onkey_open; 254 input_dev->close = atc260x_onkey_close; 255 256 input_set_capability(input_dev, EV_KEY, KEY_POWER); 257 input_set_drvdata(input_dev, onkey); 258 259 INIT_DELAYED_WORK(&onkey->work, atc260x_onkey_work); 260 261 onkey->irq = platform_get_irq(pdev, 0); 262 if (onkey->irq < 0) 263 return onkey->irq; 264 265 error = devm_request_threaded_irq(&pdev->dev, onkey->irq, NULL, 266 atc260x_onkey_irq, IRQF_ONESHOT, 267 dev_name(&pdev->dev), onkey); 268 if (error) { 269 dev_err(&pdev->dev, 270 "Failed to register IRQ %d: %d\n", onkey->irq, error); 271 return error; 272 } 273 274 /* Keep IRQ disabled until atc260x_onkey_open() is called. */ 275 disable_irq(onkey->irq); 276 277 error = input_register_device(input_dev); 278 if (error) { 279 dev_err(&pdev->dev, 280 "Failed to register input device: %d\n", error); 281 return error; 282 } 283 284 error = atc2603x_onkey_hw_init(onkey, reset_status, 285 reset_time, press_time); 286 if (error) 287 return error; 288 289 device_init_wakeup(&pdev->dev, true); 290 291 return 0; 292} 293 294static struct platform_driver atc260x_onkey_driver = { 295 .probe = atc260x_onkey_probe, 296 .driver = { 297 .name = "atc260x-onkey", 298 }, 299}; 300 301module_platform_driver(atc260x_onkey_driver); 302 303MODULE_DESCRIPTION("Onkey driver for ATC260x PMICs"); 304MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>"); 305MODULE_LICENSE("GPL");