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