Merge tag 'selinux-pr-20190801' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-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[] = {
110 { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_AIRMONT, X86_FEATURE_ANY },
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",
124 NULL,
125};
126
127/*
128 * Decode and log the given "reset source indicator" (rsi)
129 * register and then clear it.
130 */
131static void axp288_extcon_log_rsi(struct axp288_extcon_info *info)
132{
5b4e64be 133 const char * const *rsi;
f0312378
RP
134 unsigned int val, i, clear_mask = 0;
135 int ret;
136
137 ret = regmap_read(info->regmap, AXP288_PS_BOOT_REASON_REG, &val);
138 for (i = 0, rsi = axp288_pwr_up_down_info; *rsi; rsi++, i++) {
139 if (val & BIT(i)) {
140 dev_dbg(info->dev, "%s\n", *rsi);
141 clear_mask |= BIT(i);
142 }
143 }
144
145 /* Clear the register value for next reboot (write 1 to clear bit) */
146 regmap_write(info->regmap, AXP288_PS_BOOT_REASON_REG, clear_mask);
147}
148
d54f063c
HG
149/*
150 * The below code to control the USB role-switch on devices with an AXP288
151 * may seem out of place, but there are 2 reasons why this is the best place
152 * to control the USB role-switch on such devices:
153 * 1) On many devices the USB role is controlled by AML code, but the AML code
154 * only switches between the host and none roles, because of Windows not
155 * really using device mode. To make device mode work we need to toggle
156 * between the none/device roles based on Vbus presence, and this driver
157 * gets interrupts on Vbus insertion / removal.
158 * 2) In order for our BC1.2 charger detection to work properly the role
159 * mux must be properly set to device mode before we do the detection.
160 */
161
162/* Returns the id-pin value, note pulled low / false == host-mode */
163static bool axp288_get_id_pin(struct axp288_extcon_info *info)
f0312378 164{
d54f063c
HG
165 enum usb_role role;
166
167 if (info->id_extcon)
168 return extcon_get_state(info->id_extcon, EXTCON_USB_HOST) <= 0;
169
170 /* We cannot access the id-pin, see what mode the AML code has set */
171 role = usb_role_switch_get_role(info->role_sw);
172 return role != USB_ROLE_HOST;
173}
174
175static void axp288_usb_role_work(struct work_struct *work)
176{
177 struct axp288_extcon_info *info =
178 container_of(work, struct axp288_extcon_info, role_work);
179 enum usb_role role;
180 bool id_pin;
181 int ret;
182
183 id_pin = axp288_get_id_pin(info);
184 if (!id_pin)
185 role = USB_ROLE_HOST;
186 else if (info->vbus_attach)
187 role = USB_ROLE_DEVICE;
188 else
189 role = USB_ROLE_NONE;
190
191 ret = usb_role_switch_set_role(info->role_sw, role);
192 if (ret)
193 dev_err(info->dev, "failed to set role: %d\n", ret);
194}
195
196static bool axp288_get_vbus_attach(struct axp288_extcon_info *info)
197{
198 int ret, pwr_stat;
f0312378
RP
199
200 ret = regmap_read(info->regmap, AXP288_PS_STAT_REG, &pwr_stat);
201 if (ret < 0) {
202 dev_err(info->dev, "failed to read vbus status\n");
d54f063c 203 return false;
f0312378
RP
204 }
205
d54f063c
HG
206 return !!(pwr_stat & PS_STAT_VBUS_VALID);
207}
208
209static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
210{
211 int ret, stat, cfg;
212 u8 chrg_type;
213 unsigned int cable = info->previous_cable;
214 bool vbus_attach = false;
215
216 vbus_attach = axp288_get_vbus_attach(info);
f0312378 217 if (!vbus_attach)
3fe1e0e2 218 goto no_vbus;
f0312378
RP
219
220 /* Check charger detection completion status */
221 ret = regmap_read(info->regmap, AXP288_BC_GLOBAL_REG, &cfg);
222 if (ret < 0)
223 goto dev_det_ret;
224 if (cfg & BC_GLOBAL_DET_STAT) {
225 dev_dbg(info->dev, "can't complete the charger detection\n");
226 goto dev_det_ret;
227 }
228
229 ret = regmap_read(info->regmap, AXP288_BC_DET_STAT_REG, &stat);
230 if (ret < 0)
231 goto dev_det_ret;
232
233 chrg_type = (stat & DET_STAT_MASK) >> DET_STAT_SHIFT;
234
235 switch (chrg_type) {
236 case DET_STAT_SDP:
525867db 237 dev_dbg(info->dev, "sdp cable is connected\n");
11eecf91 238 cable = EXTCON_CHG_USB_SDP;
f0312378
RP
239 break;
240 case DET_STAT_CDP:
525867db 241 dev_dbg(info->dev, "cdp cable is connected\n");
11eecf91 242 cable = EXTCON_CHG_USB_CDP;
f0312378
RP
243 break;
244 case DET_STAT_DCP:
525867db 245 dev_dbg(info->dev, "dcp cable is connected\n");
11eecf91 246 cable = EXTCON_CHG_USB_DCP;
f0312378
RP
247 break;
248 default:
ca90a64d
HG
249 dev_warn(info->dev, "unknown (reserved) bc detect result\n");
250 cable = EXTCON_CHG_USB_SDP;
f0312378
RP
251 }
252
3fe1e0e2 253no_vbus:
5d2199ea 254 extcon_set_state_sync(info->edev, info->previous_cable, false);
5298b836
BW
255 if (info->previous_cable == EXTCON_CHG_USB_SDP)
256 extcon_set_state_sync(info->edev, EXTCON_USB, false);
257
5d2199ea
HG
258 if (vbus_attach) {
259 extcon_set_state_sync(info->edev, cable, vbus_attach);
5298b836
BW
260 if (cable == EXTCON_CHG_USB_SDP)
261 extcon_set_state_sync(info->edev, EXTCON_USB,
262 vbus_attach);
263
5d2199ea
HG
264 info->previous_cable = cable;
265 }
f0312378 266
d54f063c
HG
267 if (info->role_sw && info->vbus_attach != vbus_attach) {
268 info->vbus_attach = vbus_attach;
269 /* Setting the role can take a while */
270 queue_work(system_long_wq, &info->role_work);
271 }
272
f0312378
RP
273 return 0;
274
275dev_det_ret:
276 if (ret < 0)
277 dev_err(info->dev, "failed to detect BC Mod\n");
278
279 return ret;
280}
281
d54f063c
HG
282static int axp288_extcon_id_evt(struct notifier_block *nb,
283 unsigned long event, void *param)
284{
285 struct axp288_extcon_info *info =
286 container_of(nb, struct axp288_extcon_info, id_nb);
287
288 /* We may not sleep and setting the role can take a while */
289 queue_work(system_long_wq, &info->role_work);
290
291 return NOTIFY_OK;
292}
293
f0312378
RP
294static irqreturn_t axp288_extcon_isr(int irq, void *data)
295{
296 struct axp288_extcon_info *info = data;
297 int ret;
298
299 ret = axp288_handle_chrg_det_event(info);
300 if (ret < 0)
301 dev_err(info->dev, "failed to handle the interrupt\n");
302
303 return IRQ_HANDLED;
304}
305
d82e233c 306static void axp288_extcon_enable(struct axp288_extcon_info *info)
f0312378 307{
f0312378
RP
308 regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG,
309 BC_GLOBAL_RUN, 0);
f0312378
RP
310 /* Enable the charger detection logic */
311 regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG,
312 BC_GLOBAL_RUN, BC_GLOBAL_RUN);
313}
314
d54f063c
HG
315static void axp288_put_role_sw(void *data)
316{
317 struct axp288_extcon_info *info = data;
318
319 cancel_work_sync(&info->role_work);
320 usb_role_switch_put(info->role_sw);
321}
322
f0312378
RP
323static int axp288_extcon_probe(struct platform_device *pdev)
324{
325 struct axp288_extcon_info *info;
326 struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
d54f063c 327 struct device *dev = &pdev->dev;
0cf064db 328 struct acpi_device *adev;
9bf317e9 329 int ret, i, pirq;
f0312378
RP
330
331 info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
332 if (!info)
333 return -ENOMEM;
334
335 info->dev = &pdev->dev;
336 info->regmap = axp20x->regmap;
337 info->regmap_irqc = axp20x->regmap_irqc;
5d2199ea 338 info->previous_cable = EXTCON_NONE;
d54f063c
HG
339 INIT_WORK(&info->role_work, axp288_usb_role_work);
340 info->id_nb.notifier_call = axp288_extcon_id_evt;
76884241 341
f0312378
RP
342 platform_set_drvdata(pdev, info);
343
d54f063c
HG
344 info->role_sw = usb_role_switch_get(dev);
345 if (IS_ERR(info->role_sw))
346 return PTR_ERR(info->role_sw);
347 if (info->role_sw) {
348 ret = devm_add_action_or_reset(dev, axp288_put_role_sw, info);
349 if (ret)
350 return ret;
351
0cf064db
AS
352 adev = acpi_dev_get_first_match_dev("INT3496", NULL, -1);
353 if (adev) {
354 info->id_extcon = extcon_get_extcon_dev(acpi_dev_name(adev));
355 put_device(&adev->dev);
d54f063c
HG
356 if (!info->id_extcon)
357 return -EPROBE_DEFER;
358
359 dev_info(dev, "controlling USB role\n");
360 } else {
361 dev_info(dev, "controlling USB role based on Vbus presence\n");
362 }
363 }
364
365 info->vbus_attach = axp288_get_vbus_attach(info);
366
f0312378
RP
367 axp288_extcon_log_rsi(info);
368
369 /* Initialize extcon device */
370 info->edev = devm_extcon_dev_allocate(&pdev->dev,
371 axp288_extcon_cables);
372 if (IS_ERR(info->edev)) {
373 dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
374 return PTR_ERR(info->edev);
375 }
376
377 /* Register extcon device */
378 ret = devm_extcon_dev_register(&pdev->dev, info->edev);
379 if (ret) {
380 dev_err(&pdev->dev, "failed to register extcon device\n");
381 return ret;
382 }
383
f0312378
RP
384 for (i = 0; i < EXTCON_IRQ_END; i++) {
385 pirq = platform_get_irq(pdev, i);
01e1429b
AY
386 if (pirq < 0)
387 return pirq;
388
f0312378
RP
389 info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq);
390 if (info->irq[i] < 0) {
391 dev_err(&pdev->dev,
392 "failed to get virtual interrupt=%d\n", pirq);
393 ret = info->irq[i];
4bf27b70 394 return ret;
f0312378
RP
395 }
396
397 ret = devm_request_threaded_irq(&pdev->dev, info->irq[i],
398 NULL, axp288_extcon_isr,
399 IRQF_ONESHOT | IRQF_NO_SUSPEND,
400 pdev->name, info);
401 if (ret) {
402 dev_err(&pdev->dev, "failed to request interrupt=%d\n",
403 info->irq[i]);
4bf27b70 404 return ret;
f0312378
RP
405 }
406 }
407
d54f063c
HG
408 if (info->id_extcon) {
409 ret = devm_extcon_register_notifier_all(dev, info->id_extcon,
410 &info->id_nb);
411 if (ret)
412 return ret;
413 }
414
415 /* Make sure the role-sw is set correctly before doing BC detection */
416 if (info->role_sw) {
417 queue_work(system_long_wq, &info->role_work);
418 flush_work(&info->role_work);
419 }
420
be174952 421 /* Start charger cable type detection */
d82e233c 422 axp288_extcon_enable(info);
f0312378
RP
423
424 return 0;
f0312378
RP
425}
426
dd3a55fc
HG
427static const struct platform_device_id axp288_extcon_table[] = {
428 { .name = "axp288_extcon" },
429 {},
430};
431MODULE_DEVICE_TABLE(platform, axp288_extcon_table);
432
f0312378
RP
433static struct platform_driver axp288_extcon_driver = {
434 .probe = axp288_extcon_probe,
dd3a55fc 435 .id_table = axp288_extcon_table,
f0312378
RP
436 .driver = {
437 .name = "axp288_extcon",
438 },
439};
d54f063c
HG
440
441static struct device_connection axp288_extcon_role_sw_conn = {
442 .endpoint[0] = "axp288_extcon",
443 .endpoint[1] = "intel_xhci_usb_sw-role-switch",
444 .id = "usb-role-switch",
445};
446
447static int __init axp288_extcon_init(void)
448{
449 if (x86_match_cpu(cherry_trail_cpu_ids))
450 device_connection_add(&axp288_extcon_role_sw_conn);
451
452 return platform_driver_register(&axp288_extcon_driver);
453}
454module_init(axp288_extcon_init);
455
456static void __exit axp288_extcon_exit(void)
457{
458 if (x86_match_cpu(cherry_trail_cpu_ids))
459 device_connection_remove(&axp288_extcon_role_sw_conn);
460
461 platform_driver_unregister(&axp288_extcon_driver);
462}
463module_exit(axp288_extcon_exit);
f0312378
RP
464
465MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
d54f063c 466MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
f0312378
RP
467MODULE_DESCRIPTION("X-Powers AXP288 extcon driver");
468MODULE_LICENSE("GPL v2");