scsi: hisi_sas: Reduce HISI_SAS_SGE_PAGE_CNT in size
[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         struct acpi_device *adev;
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                 adev = acpi_dev_get_first_match_dev("INT3496", NULL, -1);
361                 if (adev) {
362                         info->id_extcon = extcon_get_extcon_dev(acpi_dev_name(adev));
363                         put_device(&adev->dev);
364                         if (!info->id_extcon)
365                                 return -EPROBE_DEFER;
366
367                         dev_info(dev, "controlling USB role\n");
368                 } else {
369                         dev_info(dev, "controlling USB role based on Vbus presence\n");
370                 }
371         }
372
373         info->vbus_attach = axp288_get_vbus_attach(info);
374
375         axp288_extcon_log_rsi(info);
376
377         /* Initialize extcon device */
378         info->edev = devm_extcon_dev_allocate(&pdev->dev,
379                                               axp288_extcon_cables);
380         if (IS_ERR(info->edev)) {
381                 dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
382                 return PTR_ERR(info->edev);
383         }
384
385         /* Register extcon device */
386         ret = devm_extcon_dev_register(&pdev->dev, info->edev);
387         if (ret) {
388                 dev_err(&pdev->dev, "failed to register extcon device\n");
389                 return ret;
390         }
391
392         for (i = 0; i < EXTCON_IRQ_END; i++) {
393                 pirq = platform_get_irq(pdev, i);
394                 if (pirq < 0)
395                         return pirq;
396
397                 info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq);
398                 if (info->irq[i] < 0) {
399                         dev_err(&pdev->dev,
400                                 "failed to get virtual interrupt=%d\n", pirq);
401                         ret = info->irq[i];
402                         return ret;
403                 }
404
405                 ret = devm_request_threaded_irq(&pdev->dev, info->irq[i],
406                                 NULL, axp288_extcon_isr,
407                                 IRQF_ONESHOT | IRQF_NO_SUSPEND,
408                                 pdev->name, info);
409                 if (ret) {
410                         dev_err(&pdev->dev, "failed to request interrupt=%d\n",
411                                                         info->irq[i]);
412                         return ret;
413                 }
414         }
415
416         if (info->id_extcon) {
417                 ret = devm_extcon_register_notifier_all(dev, info->id_extcon,
418                                                         &info->id_nb);
419                 if (ret)
420                         return ret;
421         }
422
423         /* Make sure the role-sw is set correctly before doing BC detection */
424         if (info->role_sw) {
425                 queue_work(system_long_wq, &info->role_work);
426                 flush_work(&info->role_work);
427         }
428
429         /* Start charger cable type detection */
430         axp288_extcon_enable(info);
431
432         return 0;
433 }
434
435 static const struct platform_device_id axp288_extcon_table[] = {
436         { .name = "axp288_extcon" },
437         {},
438 };
439 MODULE_DEVICE_TABLE(platform, axp288_extcon_table);
440
441 static struct platform_driver axp288_extcon_driver = {
442         .probe = axp288_extcon_probe,
443         .id_table = axp288_extcon_table,
444         .driver = {
445                 .name = "axp288_extcon",
446         },
447 };
448
449 static struct device_connection axp288_extcon_role_sw_conn = {
450         .endpoint[0] = "axp288_extcon",
451         .endpoint[1] = "intel_xhci_usb_sw-role-switch",
452         .id = "usb-role-switch",
453 };
454
455 static int __init axp288_extcon_init(void)
456 {
457         if (x86_match_cpu(cherry_trail_cpu_ids))
458                 device_connection_add(&axp288_extcon_role_sw_conn);
459
460         return platform_driver_register(&axp288_extcon_driver);
461 }
462 module_init(axp288_extcon_init);
463
464 static void __exit axp288_extcon_exit(void)
465 {
466         if (x86_match_cpu(cherry_trail_cpu_ids))
467                 device_connection_remove(&axp288_extcon_role_sw_conn);
468
469         platform_driver_unregister(&axp288_extcon_driver);
470 }
471 module_exit(axp288_extcon_exit);
472
473 MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
474 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
475 MODULE_DESCRIPTION("X-Powers AXP288 extcon driver");
476 MODULE_LICENSE("GPL v2");