Merge tag 'for-5.10-rc3-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave...
[linux-block.git] / drivers / extcon / extcon-axp288.c
CommitLineData
1802d0be 1// SPDX-License-Identifier: GPL-2.0-only
f0312378
RP
2/*
3 * extcon-axp288.c - X-Power AXP288 PMIC extcon cable detection driver
4 *
d54f063c 5 * Copyright (c) 2017-2018 Hans de Goede <hdegoede@redhat.com>
f0312378
RP
6 * Copyright (C) 2015 Intel Corporation
7 * Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
f0312378
RP
8 */
9
d54f063c 10#include <linux/acpi.h>
f0312378
RP
11#include <linux/module.h>
12#include <linux/kernel.h>
13#include <linux/io.h>
14#include <linux/slab.h>
15#include <linux/interrupt.h>
16#include <linux/platform_device.h>
17#include <linux/property.h>
f0312378 18#include <linux/notifier.h>
176aa360 19#include <linux/extcon-provider.h>
f0312378 20#include <linux/regmap.h>
f0312378 21#include <linux/mfd/axp20x.h>
d54f063c
HG
22#include <linux/usb/role.h>
23#include <linux/workqueue.h>
24
25#include <asm/cpu_device_id.h>
26#include <asm/intel-family.h>
f0312378
RP
27
28/* Power source status register */
29#define PS_STAT_VBUS_TRIGGER BIT(0)
30#define PS_STAT_BAT_CHRG_DIR BIT(2)
31#define PS_STAT_VBUS_ABOVE_VHOLD BIT(3)
32#define PS_STAT_VBUS_VALID BIT(4)
33#define PS_STAT_VBUS_PRESENT BIT(5)
34
35/* BC module global register */
36#define BC_GLOBAL_RUN BIT(0)
37#define BC_GLOBAL_DET_STAT BIT(2)
38#define BC_GLOBAL_DBP_TOUT BIT(3)
39#define BC_GLOBAL_VLGC_COM_SEL BIT(4)
40#define BC_GLOBAL_DCD_TOUT_MASK (BIT(6)|BIT(5))
41#define BC_GLOBAL_DCD_TOUT_300MS 0
42#define BC_GLOBAL_DCD_TOUT_100MS 1
43#define BC_GLOBAL_DCD_TOUT_500MS 2
44#define BC_GLOBAL_DCD_TOUT_900MS 3
45#define BC_GLOBAL_DCD_DET_SEL BIT(7)
46
47/* BC module vbus control and status register */
48#define VBUS_CNTL_DPDM_PD_EN BIT(4)
49#define VBUS_CNTL_DPDM_FD_EN BIT(5)
50#define VBUS_CNTL_FIRST_PO_STAT BIT(6)
51
52/* BC USB status register */
53#define USB_STAT_BUS_STAT_MASK (BIT(3)|BIT(2)|BIT(1)|BIT(0))
54#define USB_STAT_BUS_STAT_SHIFT 0
55#define USB_STAT_BUS_STAT_ATHD 0
56#define USB_STAT_BUS_STAT_CONN 1
57#define USB_STAT_BUS_STAT_SUSP 2
58#define USB_STAT_BUS_STAT_CONF 3
59#define USB_STAT_USB_SS_MODE BIT(4)
60#define USB_STAT_DEAD_BAT_DET BIT(6)
61#define USB_STAT_DBP_UNCFG BIT(7)
62
63/* BC detect status register */
64#define DET_STAT_MASK (BIT(7)|BIT(6)|BIT(5))
65#define DET_STAT_SHIFT 5
66#define DET_STAT_SDP 1
67#define DET_STAT_CDP 2
68#define DET_STAT_DCP 3
69
f0312378
RP
70enum axp288_extcon_reg {
71 AXP288_PS_STAT_REG = 0x00,
72 AXP288_PS_BOOT_REASON_REG = 0x02,
73 AXP288_BC_GLOBAL_REG = 0x2c,
74 AXP288_BC_VBUS_CNTL_REG = 0x2d,
75 AXP288_BC_USB_STAT_REG = 0x2e,
76 AXP288_BC_DET_STAT_REG = 0x2f,
f0312378
RP
77};
78
f0312378
RP
79enum axp288_extcon_irq {
80 VBUS_FALLING_IRQ = 0,
81 VBUS_RISING_IRQ,
82 MV_CHNG_IRQ,
83 BC_USB_CHNG_IRQ,
84 EXTCON_IRQ_END,
85};
86
73b6ecdb 87static const unsigned int axp288_extcon_cables[] = {
11eecf91
CC
88 EXTCON_CHG_USB_SDP,
89 EXTCON_CHG_USB_CDP,
90 EXTCON_CHG_USB_DCP,
5298b836 91 EXTCON_USB,
2a9de9c0 92 EXTCON_NONE,
f0312378
RP
93};
94
95struct axp288_extcon_info {
96 struct device *dev;
97 struct regmap *regmap;
98 struct regmap_irq_chip_data *regmap_irqc;
d54f063c
HG
99 struct usb_role_switch *role_sw;
100 struct work_struct role_work;
f0312378
RP
101 int irq[EXTCON_IRQ_END];
102 struct extcon_dev *edev;
d54f063c
HG
103 struct extcon_dev *id_extcon;
104 struct notifier_block id_nb;
5d2199ea 105 unsigned int previous_cable;
d54f063c
HG
106 bool vbus_attach;
107};
108
109static const struct x86_cpu_id cherry_trail_cpu_ids[] = {
20d320c6 110 X86_MATCH_INTEL_FAM6_MODEL(ATOM_AIRMONT, NULL),
d54f063c 111 {}
f0312378
RP
112};
113
114/* Power up/down reason string array */
5b4e64be 115static const char * const axp288_pwr_up_down_info[] = {
f0312378
RP
116 "Last wake caused by user pressing the power button",
117 "Last wake caused by a charger insertion",
118 "Last wake caused by a battery insertion",
119 "Last wake caused by SOC initiated global reset",
120 "Last wake caused by cold reset",
121 "Last shutdown caused by PMIC UVLO threshold",
122 "Last shutdown caused by SOC initiated cold off",
123 "Last shutdown caused by user pressing the power button",
f0312378
RP
124};
125
126/*
127 * Decode and log the given "reset source indicator" (rsi)
128 * register and then clear it.
129 */
130static void axp288_extcon_log_rsi(struct axp288_extcon_info *info)
131{
f0312378 132 unsigned int val, i, clear_mask = 0;
21be848e 133 unsigned long bits;
f0312378
RP
134 int ret;
135
136 ret = regmap_read(info->regmap, AXP288_PS_BOOT_REASON_REG, &val);
d72e3dc7
AS
137 if (ret < 0) {
138 dev_err(info->dev, "failed to read reset source indicator\n");
139 return;
140 }
141
21be848e
AS
142 bits = val & GENMASK(ARRAY_SIZE(axp288_pwr_up_down_info) - 1, 0);
143 for_each_set_bit(i, &bits, ARRAY_SIZE(axp288_pwr_up_down_info))
144 dev_dbg(info->dev, "%s\n", axp288_pwr_up_down_info[i]);
145 clear_mask = bits;
f0312378
RP
146
147 /* Clear the register value for next reboot (write 1 to clear bit) */
148 regmap_write(info->regmap, AXP288_PS_BOOT_REASON_REG, clear_mask);
149}
150
d54f063c
HG
151/*
152 * The below code to control the USB role-switch on devices with an AXP288
153 * may seem out of place, but there are 2 reasons why this is the best place
154 * to control the USB role-switch on such devices:
155 * 1) On many devices the USB role is controlled by AML code, but the AML code
156 * only switches between the host and none roles, because of Windows not
157 * really using device mode. To make device mode work we need to toggle
158 * between the none/device roles based on Vbus presence, and this driver
159 * gets interrupts on Vbus insertion / removal.
160 * 2) In order for our BC1.2 charger detection to work properly the role
161 * mux must be properly set to device mode before we do the detection.
162 */
163
164/* Returns the id-pin value, note pulled low / false == host-mode */
165static bool axp288_get_id_pin(struct axp288_extcon_info *info)
f0312378 166{
d54f063c
HG
167 enum usb_role role;
168
169 if (info->id_extcon)
170 return extcon_get_state(info->id_extcon, EXTCON_USB_HOST) <= 0;
171
172 /* We cannot access the id-pin, see what mode the AML code has set */
173 role = usb_role_switch_get_role(info->role_sw);
174 return role != USB_ROLE_HOST;
175}
176
177static void axp288_usb_role_work(struct work_struct *work)
178{
179 struct axp288_extcon_info *info =
180 container_of(work, struct axp288_extcon_info, role_work);
181 enum usb_role role;
182 bool id_pin;
183 int ret;
184
185 id_pin = axp288_get_id_pin(info);
186 if (!id_pin)
187 role = USB_ROLE_HOST;
188 else if (info->vbus_attach)
189 role = USB_ROLE_DEVICE;
190 else
191 role = USB_ROLE_NONE;
192
193 ret = usb_role_switch_set_role(info->role_sw, role);
194 if (ret)
195 dev_err(info->dev, "failed to set role: %d\n", ret);
196}
197
198static bool axp288_get_vbus_attach(struct axp288_extcon_info *info)
199{
200 int ret, pwr_stat;
f0312378
RP
201
202 ret = regmap_read(info->regmap, AXP288_PS_STAT_REG, &pwr_stat);
203 if (ret < 0) {
204 dev_err(info->dev, "failed to read vbus status\n");
d54f063c 205 return false;
f0312378
RP
206 }
207
d54f063c
HG
208 return !!(pwr_stat & PS_STAT_VBUS_VALID);
209}
210
211static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
212{
213 int ret, stat, cfg;
214 u8 chrg_type;
215 unsigned int cable = info->previous_cable;
216 bool vbus_attach = false;
217
218 vbus_attach = axp288_get_vbus_attach(info);
f0312378 219 if (!vbus_attach)
3fe1e0e2 220 goto no_vbus;
f0312378
RP
221
222 /* Check charger detection completion status */
223 ret = regmap_read(info->regmap, AXP288_BC_GLOBAL_REG, &cfg);
224 if (ret < 0)
225 goto dev_det_ret;
226 if (cfg & BC_GLOBAL_DET_STAT) {
227 dev_dbg(info->dev, "can't complete the charger detection\n");
228 goto dev_det_ret;
229 }
230
231 ret = regmap_read(info->regmap, AXP288_BC_DET_STAT_REG, &stat);
232 if (ret < 0)
233 goto dev_det_ret;
234
235 chrg_type = (stat & DET_STAT_MASK) >> DET_STAT_SHIFT;
236
237 switch (chrg_type) {
238 case DET_STAT_SDP:
525867db 239 dev_dbg(info->dev, "sdp cable is connected\n");
11eecf91 240 cable = EXTCON_CHG_USB_SDP;
f0312378
RP
241 break;
242 case DET_STAT_CDP:
525867db 243 dev_dbg(info->dev, "cdp cable is connected\n");
11eecf91 244 cable = EXTCON_CHG_USB_CDP;
f0312378
RP
245 break;
246 case DET_STAT_DCP:
525867db 247 dev_dbg(info->dev, "dcp cable is connected\n");
11eecf91 248 cable = EXTCON_CHG_USB_DCP;
f0312378
RP
249 break;
250 default:
ca90a64d
HG
251 dev_warn(info->dev, "unknown (reserved) bc detect result\n");
252 cable = EXTCON_CHG_USB_SDP;
f0312378
RP
253 }
254
3fe1e0e2 255no_vbus:
5d2199ea 256 extcon_set_state_sync(info->edev, info->previous_cable, false);
5298b836
BW
257 if (info->previous_cable == EXTCON_CHG_USB_SDP)
258 extcon_set_state_sync(info->edev, EXTCON_USB, false);
259
5d2199ea
HG
260 if (vbus_attach) {
261 extcon_set_state_sync(info->edev, cable, vbus_attach);
5298b836
BW
262 if (cable == EXTCON_CHG_USB_SDP)
263 extcon_set_state_sync(info->edev, EXTCON_USB,
264 vbus_attach);
265
5d2199ea
HG
266 info->previous_cable = cable;
267 }
f0312378 268
d54f063c
HG
269 if (info->role_sw && info->vbus_attach != vbus_attach) {
270 info->vbus_attach = vbus_attach;
271 /* Setting the role can take a while */
272 queue_work(system_long_wq, &info->role_work);
273 }
274
f0312378
RP
275 return 0;
276
277dev_det_ret:
278 if (ret < 0)
279 dev_err(info->dev, "failed to detect BC Mod\n");
280
281 return ret;
282}
283
d54f063c
HG
284static int axp288_extcon_id_evt(struct notifier_block *nb,
285 unsigned long event, void *param)
286{
287 struct axp288_extcon_info *info =
288 container_of(nb, struct axp288_extcon_info, id_nb);
289
290 /* We may not sleep and setting the role can take a while */
291 queue_work(system_long_wq, &info->role_work);
292
293 return NOTIFY_OK;
294}
295
f0312378
RP
296static irqreturn_t axp288_extcon_isr(int irq, void *data)
297{
298 struct axp288_extcon_info *info = data;
299 int ret;
300
301 ret = axp288_handle_chrg_det_event(info);
302 if (ret < 0)
303 dev_err(info->dev, "failed to handle the interrupt\n");
304
305 return IRQ_HANDLED;
306}
307
d82e233c 308static void axp288_extcon_enable(struct axp288_extcon_info *info)
f0312378 309{
f0312378
RP
310 regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG,
311 BC_GLOBAL_RUN, 0);
f0312378
RP
312 /* Enable the charger detection logic */
313 regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG,
314 BC_GLOBAL_RUN, BC_GLOBAL_RUN);
315}
316
d54f063c
HG
317static void axp288_put_role_sw(void *data)
318{
319 struct axp288_extcon_info *info = data;
320
321 cancel_work_sync(&info->role_work);
322 usb_role_switch_put(info->role_sw);
323}
324
a69dff99
HK
325static int axp288_extcon_find_role_sw(struct axp288_extcon_info *info)
326{
327 const struct software_node *swnode;
328 struct fwnode_handle *fwnode;
329
330 if (!x86_match_cpu(cherry_trail_cpu_ids))
331 return 0;
332
333 swnode = software_node_find_by_name(NULL, "intel-xhci-usb-sw");
334 if (!swnode)
335 return -EPROBE_DEFER;
336
337 fwnode = software_node_fwnode(swnode);
338 info->role_sw = usb_role_switch_find_by_fwnode(fwnode);
339 fwnode_handle_put(fwnode);
340
341 return info->role_sw ? 0 : -EPROBE_DEFER;
342}
343
f0312378
RP
344static int axp288_extcon_probe(struct platform_device *pdev)
345{
346 struct axp288_extcon_info *info;
347 struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
d54f063c 348 struct device *dev = &pdev->dev;
0cf064db 349 struct acpi_device *adev;
9bf317e9 350 int ret, i, pirq;
f0312378
RP
351
352 info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
353 if (!info)
354 return -ENOMEM;
355
356 info->dev = &pdev->dev;
357 info->regmap = axp20x->regmap;
358 info->regmap_irqc = axp20x->regmap_irqc;
5d2199ea 359 info->previous_cable = EXTCON_NONE;
d54f063c
HG
360 INIT_WORK(&info->role_work, axp288_usb_role_work);
361 info->id_nb.notifier_call = axp288_extcon_id_evt;
76884241 362
f0312378
RP
363 platform_set_drvdata(pdev, info);
364
a69dff99
HK
365 ret = axp288_extcon_find_role_sw(info);
366 if (ret)
367 return ret;
368
d54f063c
HG
369 if (info->role_sw) {
370 ret = devm_add_action_or_reset(dev, axp288_put_role_sw, info);
371 if (ret)
372 return ret;
373
0cf064db
AS
374 adev = acpi_dev_get_first_match_dev("INT3496", NULL, -1);
375 if (adev) {
376 info->id_extcon = extcon_get_extcon_dev(acpi_dev_name(adev));
377 put_device(&adev->dev);
d54f063c
HG
378 if (!info->id_extcon)
379 return -EPROBE_DEFER;
380
381 dev_info(dev, "controlling USB role\n");
382 } else {
383 dev_info(dev, "controlling USB role based on Vbus presence\n");
384 }
385 }
386
387 info->vbus_attach = axp288_get_vbus_attach(info);
388
f0312378
RP
389 axp288_extcon_log_rsi(info);
390
391 /* Initialize extcon device */
392 info->edev = devm_extcon_dev_allocate(&pdev->dev,
393 axp288_extcon_cables);
394 if (IS_ERR(info->edev)) {
395 dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
396 return PTR_ERR(info->edev);
397 }
398
399 /* Register extcon device */
400 ret = devm_extcon_dev_register(&pdev->dev, info->edev);
401 if (ret) {
402 dev_err(&pdev->dev, "failed to register extcon device\n");
403 return ret;
404 }
405
f0312378
RP
406 for (i = 0; i < EXTCON_IRQ_END; i++) {
407 pirq = platform_get_irq(pdev, i);
01e1429b
AY
408 if (pirq < 0)
409 return pirq;
410
f0312378
RP
411 info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq);
412 if (info->irq[i] < 0) {
413 dev_err(&pdev->dev,
414 "failed to get virtual interrupt=%d\n", pirq);
415 ret = info->irq[i];
4bf27b70 416 return ret;
f0312378
RP
417 }
418
419 ret = devm_request_threaded_irq(&pdev->dev, info->irq[i],
420 NULL, axp288_extcon_isr,
421 IRQF_ONESHOT | IRQF_NO_SUSPEND,
422 pdev->name, info);
423 if (ret) {
424 dev_err(&pdev->dev, "failed to request interrupt=%d\n",
425 info->irq[i]);
4bf27b70 426 return ret;
f0312378
RP
427 }
428 }
429
d54f063c
HG
430 if (info->id_extcon) {
431 ret = devm_extcon_register_notifier_all(dev, info->id_extcon,
432 &info->id_nb);
433 if (ret)
434 return ret;
435 }
436
437 /* Make sure the role-sw is set correctly before doing BC detection */
438 if (info->role_sw) {
439 queue_work(system_long_wq, &info->role_work);
440 flush_work(&info->role_work);
441 }
442
be174952 443 /* Start charger cable type detection */
d82e233c 444 axp288_extcon_enable(info);
f0312378 445
9c945530
HG
446 device_init_wakeup(dev, true);
447 platform_set_drvdata(pdev, info);
448
449 return 0;
450}
451
452static int __maybe_unused axp288_extcon_suspend(struct device *dev)
453{
454 struct axp288_extcon_info *info = dev_get_drvdata(dev);
455
456 if (device_may_wakeup(dev))
457 enable_irq_wake(info->irq[VBUS_RISING_IRQ]);
458
f0312378 459 return 0;
f0312378
RP
460}
461
9c945530
HG
462static int __maybe_unused axp288_extcon_resume(struct device *dev)
463{
464 struct axp288_extcon_info *info = dev_get_drvdata(dev);
465
466 /*
467 * Wakeup when a charger is connected to do charger-type
468 * connection and generate an extcon event which makes the
469 * axp288 charger driver set the input current limit.
470 */
471 if (device_may_wakeup(dev))
472 disable_irq_wake(info->irq[VBUS_RISING_IRQ]);
473
474 return 0;
475}
476
477static SIMPLE_DEV_PM_OPS(axp288_extcon_pm_ops, axp288_extcon_suspend,
478 axp288_extcon_resume);
479
dd3a55fc
HG
480static const struct platform_device_id axp288_extcon_table[] = {
481 { .name = "axp288_extcon" },
482 {},
483};
484MODULE_DEVICE_TABLE(platform, axp288_extcon_table);
485
f0312378
RP
486static struct platform_driver axp288_extcon_driver = {
487 .probe = axp288_extcon_probe,
dd3a55fc 488 .id_table = axp288_extcon_table,
f0312378
RP
489 .driver = {
490 .name = "axp288_extcon",
9c945530 491 .pm = &axp288_extcon_pm_ops,
f0312378
RP
492 },
493};
dbc88807 494module_platform_driver(axp288_extcon_driver);
f0312378
RP
495
496MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
d54f063c 497MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
f0312378
RP
498MODULE_DESCRIPTION("X-Powers AXP288 extcon driver");
499MODULE_LICENSE("GPL v2");