1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22#include <linux/kernel.h>
23#include <linux/module.h>
24#include <linux/err.h>
25#include <linux/init.h>
26#include <linux/types.h>
27#include <linux/device.h>
28#include <linux/sysfs.h>
29#include <linux/platform_device.h>
30#include <linux/power_supply.h>
31#include <linux/delay.h>
32#include <linux/of.h>
33
34#include <linux/gpio/consumer.h>
35#include <linux/usb/otg.h>
36#include <linux/usb/ulpi.h>
37#include <linux/usb/ch9.h>
38#include <linux/usb/gadget.h>
39
40
41#define ISP1704_PWR_CTRL 0x3d
42#define ISP1704_PWR_CTRL_SWCTRL (1 << 0)
43#define ISP1704_PWR_CTRL_DET_COMP (1 << 1)
44#define ISP1704_PWR_CTRL_BVALID_RISE (1 << 2)
45#define ISP1704_PWR_CTRL_BVALID_FALL (1 << 3)
46#define ISP1704_PWR_CTRL_DP_WKPU_EN (1 << 4)
47#define ISP1704_PWR_CTRL_VDAT_DET (1 << 5)
48#define ISP1704_PWR_CTRL_DPVSRC_EN (1 << 6)
49#define ISP1704_PWR_CTRL_HWDETECT (1 << 7)
50
51#define NXP_VENDOR_ID 0x04cc
52
53static u16 isp170x_id[] = {
54 0x1704,
55 0x1707,
56};
57
58struct isp1704_charger {
59 struct device *dev;
60 struct power_supply *psy;
61 struct power_supply_desc psy_desc;
62 struct gpio_desc *enable_gpio;
63 struct usb_phy *phy;
64 struct notifier_block nb;
65 struct work_struct work;
66
67
68 char model[8];
69 unsigned present:1;
70 unsigned online:1;
71 unsigned current_max;
72};
73
74static inline int isp1704_read(struct isp1704_charger *isp, u32 reg)
75{
76 return usb_phy_io_read(isp->phy, reg);
77}
78
79static inline int isp1704_write(struct isp1704_charger *isp, u32 reg, u32 val)
80{
81 return usb_phy_io_write(isp->phy, val, reg);
82}
83
84static void isp1704_charger_set_power(struct isp1704_charger *isp, bool on)
85{
86 gpiod_set_value(isp->enable_gpio, on);
87}
88
89
90
91
92
93
94
95
96static inline int isp1704_charger_type(struct isp1704_charger *isp)
97{
98 u8 reg;
99 u8 func_ctrl;
100 u8 otg_ctrl;
101 int type = POWER_SUPPLY_TYPE_USB_DCP;
102
103 func_ctrl = isp1704_read(isp, ULPI_FUNC_CTRL);
104 otg_ctrl = isp1704_read(isp, ULPI_OTG_CTRL);
105
106
107 reg = ULPI_OTG_CTRL_DM_PULLDOWN | ULPI_OTG_CTRL_DP_PULLDOWN;
108 isp1704_write(isp, ULPI_CLR(ULPI_OTG_CTRL), reg);
109
110
111 isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL),
112 ULPI_FUNC_CTRL_XCVRSEL_MASK);
113 isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL),
114 ULPI_FUNC_CTRL_FULL_SPEED);
115
116
117 reg = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET;
118 isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL), reg);
119 usleep_range(1000, 2000);
120
121 reg = isp1704_read(isp, ULPI_DEBUG);
122 if ((reg & 3) != 3)
123 type = POWER_SUPPLY_TYPE_USB_CDP;
124
125
126 isp1704_write(isp, ULPI_FUNC_CTRL, func_ctrl);
127 isp1704_write(isp, ULPI_OTG_CTRL, otg_ctrl);
128
129 return type;
130}
131
132
133
134
135
136static inline int isp1704_charger_verify(struct isp1704_charger *isp)
137{
138 int ret = 0;
139 u8 r;
140
141
142 r = isp1704_read(isp, ULPI_FUNC_CTRL);
143 r |= ULPI_FUNC_CTRL_RESET;
144 isp1704_write(isp, ULPI_FUNC_CTRL, r);
145 usleep_range(1000, 2000);
146
147
148 r &= ~(ULPI_FUNC_CTRL_RESET | ULPI_FUNC_CTRL_OPMODE_MASK);
149 isp1704_write(isp, ULPI_FUNC_CTRL, r);
150
151
152 r = ULPI_OTG_CTRL_DP_PULLDOWN | ULPI_OTG_CTRL_DM_PULLDOWN;
153 isp1704_write(isp, ULPI_CLR(ULPI_OTG_CTRL), r);
154
155
156 r = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET;
157 isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL), r);
158 usleep_range(1000, 2000);
159
160
161 if (!isp1704_read(isp, ULPI_DEBUG)) {
162
163 isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL),
164 ULPI_FUNC_CTRL_TERMSELECT);
165 return 1;
166 }
167
168
169
170
171 isp1704_write(isp, ULPI_SET(ISP1704_PWR_CTRL),
172 ISP1704_PWR_CTRL_DP_WKPU_EN);
173
174
175 isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL),
176 ULPI_FUNC_CTRL_TERMSELECT);
177
178
179 isp1704_write(isp, ULPI_SET(ULPI_OTG_CTRL),
180 ULPI_OTG_CTRL_DM_PULLDOWN);
181
182
183 if (!(isp1704_read(isp, ULPI_DEBUG)))
184 ret = 1;
185
186
187 isp1704_write(isp, ULPI_CLR(ISP1704_PWR_CTRL),
188 ISP1704_PWR_CTRL_DP_WKPU_EN);
189
190 return ret;
191}
192
193static inline int isp1704_charger_detect(struct isp1704_charger *isp)
194{
195 unsigned long timeout;
196 u8 pwr_ctrl;
197 int ret = 0;
198
199 pwr_ctrl = isp1704_read(isp, ISP1704_PWR_CTRL);
200
201
202 isp1704_write(isp, ISP1704_PWR_CTRL,
203 ISP1704_PWR_CTRL_SWCTRL);
204
205
206 isp1704_write(isp, ULPI_SET(ISP1704_PWR_CTRL),
207 ISP1704_PWR_CTRL_SWCTRL
208 | ISP1704_PWR_CTRL_DPVSRC_EN);
209 usleep_range(1000, 2000);
210
211 timeout = jiffies + msecs_to_jiffies(300);
212 do {
213
214 if (isp1704_read(isp, ISP1704_PWR_CTRL)
215 & ISP1704_PWR_CTRL_VDAT_DET) {
216 ret = isp1704_charger_verify(isp);
217 break;
218 }
219 } while (!time_after(jiffies, timeout) && isp->online);
220
221
222 isp1704_write(isp, ISP1704_PWR_CTRL, pwr_ctrl);
223
224 return ret;
225}
226
227static inline int isp1704_charger_detect_dcp(struct isp1704_charger *isp)
228{
229 if (isp1704_charger_detect(isp) &&
230 isp1704_charger_type(isp) == POWER_SUPPLY_TYPE_USB_DCP)
231 return true;
232 else
233 return false;
234}
235
236static void isp1704_charger_work(struct work_struct *data)
237{
238 struct isp1704_charger *isp =
239 container_of(data, struct isp1704_charger, work);
240 static DEFINE_MUTEX(lock);
241
242 mutex_lock(&lock);
243
244 switch (isp->phy->last_event) {
245 case USB_EVENT_VBUS:
246
247 if (!isp->present) {
248 isp->online = true;
249 isp->present = 1;
250 isp1704_charger_set_power(isp, 1);
251
252
253 if (isp1704_charger_detect_dcp(isp)) {
254 isp->psy_desc.type = POWER_SUPPLY_TYPE_USB_DCP;
255 isp->current_max = 1800;
256 } else {
257 isp->psy_desc.type = POWER_SUPPLY_TYPE_USB;
258 isp->current_max = 500;
259 }
260
261
262 if (isp->phy->otg->gadget)
263 usb_gadget_connect(isp->phy->otg->gadget);
264 }
265
266 if (isp->psy_desc.type != POWER_SUPPLY_TYPE_USB_DCP) {
267
268
269
270
271 if (isp->current_max > 500)
272 isp->current_max = 500;
273
274 if (isp->current_max > 100)
275 isp->psy_desc.type = POWER_SUPPLY_TYPE_USB_CDP;
276 }
277 break;
278 case USB_EVENT_NONE:
279 isp->online = false;
280 isp->present = 0;
281 isp->current_max = 0;
282 isp->psy_desc.type = POWER_SUPPLY_TYPE_USB;
283
284
285
286
287
288
289
290
291
292 if (isp->phy->otg->gadget)
293 usb_gadget_disconnect(isp->phy->otg->gadget);
294
295 isp1704_charger_set_power(isp, 0);
296 break;
297 default:
298 goto out;
299 }
300
301 power_supply_changed(isp->psy);
302out:
303 mutex_unlock(&lock);
304}
305
306static int isp1704_notifier_call(struct notifier_block *nb,
307 unsigned long val, void *v)
308{
309 struct isp1704_charger *isp =
310 container_of(nb, struct isp1704_charger, nb);
311
312 schedule_work(&isp->work);
313
314 return NOTIFY_OK;
315}
316
317static int isp1704_charger_get_property(struct power_supply *psy,
318 enum power_supply_property psp,
319 union power_supply_propval *val)
320{
321 struct isp1704_charger *isp = power_supply_get_drvdata(psy);
322
323 switch (psp) {
324 case POWER_SUPPLY_PROP_PRESENT:
325 val->intval = isp->present;
326 break;
327 case POWER_SUPPLY_PROP_ONLINE:
328 val->intval = isp->online;
329 break;
330 case POWER_SUPPLY_PROP_CURRENT_MAX:
331 val->intval = isp->current_max;
332 break;
333 case POWER_SUPPLY_PROP_MODEL_NAME:
334 val->strval = isp->model;
335 break;
336 case POWER_SUPPLY_PROP_MANUFACTURER:
337 val->strval = "NXP";
338 break;
339 default:
340 return -EINVAL;
341 }
342 return 0;
343}
344
345static enum power_supply_property power_props[] = {
346 POWER_SUPPLY_PROP_PRESENT,
347 POWER_SUPPLY_PROP_ONLINE,
348 POWER_SUPPLY_PROP_CURRENT_MAX,
349 POWER_SUPPLY_PROP_MODEL_NAME,
350 POWER_SUPPLY_PROP_MANUFACTURER,
351};
352
353static inline int isp1704_test_ulpi(struct isp1704_charger *isp)
354{
355 int vendor;
356 int product;
357 int i;
358 int ret = -ENODEV;
359
360
361 ret = isp1704_write(isp, ULPI_SCRATCH, 0xaa);
362 if (ret < 0)
363 return ret;
364
365 ret = isp1704_read(isp, ULPI_SCRATCH);
366 if (ret < 0)
367 return ret;
368
369 if (ret != 0xaa)
370 return -ENODEV;
371
372
373 vendor = isp1704_read(isp, ULPI_VENDOR_ID_LOW);
374 vendor |= isp1704_read(isp, ULPI_VENDOR_ID_HIGH) << 8;
375 if (vendor != NXP_VENDOR_ID)
376 return -ENODEV;
377
378 product = isp1704_read(isp, ULPI_PRODUCT_ID_LOW);
379 product |= isp1704_read(isp, ULPI_PRODUCT_ID_HIGH) << 8;
380
381 for (i = 0; i < ARRAY_SIZE(isp170x_id); i++) {
382 if (product == isp170x_id[i]) {
383 sprintf(isp->model, "isp%x", product);
384 return product;
385 }
386 }
387
388 dev_err(isp->dev, "product id %x not matching known ids", product);
389
390 return -ENODEV;
391}
392
393static int isp1704_charger_probe(struct platform_device *pdev)
394{
395 struct isp1704_charger *isp;
396 int ret = -ENODEV;
397 struct power_supply_config psy_cfg = {};
398
399 isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL);
400 if (!isp)
401 return -ENOMEM;
402
403 isp->enable_gpio = devm_gpiod_get(&pdev->dev, "nxp,enable",
404 GPIOD_OUT_HIGH);
405 if (IS_ERR(isp->enable_gpio)) {
406 ret = PTR_ERR(isp->enable_gpio);
407 dev_err(&pdev->dev, "Could not get reset gpio: %d\n", ret);
408 return ret;
409 }
410
411 if (pdev->dev.of_node)
412 isp->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
413 else
414 isp->phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
415
416 if (IS_ERR(isp->phy)) {
417 ret = PTR_ERR(isp->phy);
418 dev_err(&pdev->dev, "usb_get_phy failed\n");
419 goto fail0;
420 }
421
422 isp->dev = &pdev->dev;
423 platform_set_drvdata(pdev, isp);
424
425 isp1704_charger_set_power(isp, 1);
426
427 ret = isp1704_test_ulpi(isp);
428 if (ret < 0) {
429 dev_err(&pdev->dev, "isp1704_test_ulpi failed\n");
430 goto fail1;
431 }
432
433 isp->psy_desc.name = "isp1704";
434 isp->psy_desc.type = POWER_SUPPLY_TYPE_USB;
435 isp->psy_desc.properties = power_props;
436 isp->psy_desc.num_properties = ARRAY_SIZE(power_props);
437 isp->psy_desc.get_property = isp1704_charger_get_property;
438
439 psy_cfg.drv_data = isp;
440
441 isp->psy = power_supply_register(isp->dev, &isp->psy_desc, &psy_cfg);
442 if (IS_ERR(isp->psy)) {
443 ret = PTR_ERR(isp->psy);
444 dev_err(&pdev->dev, "power_supply_register failed\n");
445 goto fail1;
446 }
447
448
449
450
451
452 INIT_WORK(&isp->work, isp1704_charger_work);
453
454 isp->nb.notifier_call = isp1704_notifier_call;
455
456 ret = usb_register_notifier(isp->phy, &isp->nb);
457 if (ret) {
458 dev_err(&pdev->dev, "usb_register_notifier failed\n");
459 goto fail2;
460 }
461
462 dev_info(isp->dev, "registered with product id %s\n", isp->model);
463
464
465
466
467
468
469
470
471 if (isp->phy->otg->gadget)
472 usb_gadget_disconnect(isp->phy->otg->gadget);
473
474 if (isp->phy->last_event == USB_EVENT_NONE)
475 isp1704_charger_set_power(isp, 0);
476
477
478 if (isp->phy->last_event == USB_EVENT_VBUS &&
479 !isp->phy->otg->default_a)
480 schedule_work(&isp->work);
481
482 return 0;
483fail2:
484 power_supply_unregister(isp->psy);
485fail1:
486 isp1704_charger_set_power(isp, 0);
487fail0:
488 dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret);
489
490 return ret;
491}
492
493static int isp1704_charger_remove(struct platform_device *pdev)
494{
495 struct isp1704_charger *isp = platform_get_drvdata(pdev);
496
497 usb_unregister_notifier(isp->phy, &isp->nb);
498 power_supply_unregister(isp->psy);
499 isp1704_charger_set_power(isp, 0);
500
501 return 0;
502}
503
504#ifdef CONFIG_OF
505static const struct of_device_id omap_isp1704_of_match[] = {
506 { .compatible = "nxp,isp1704", },
507 { .compatible = "nxp,isp1707", },
508 {},
509};
510MODULE_DEVICE_TABLE(of, omap_isp1704_of_match);
511#endif
512
513static struct platform_driver isp1704_charger_driver = {
514 .driver = {
515 .name = "isp1704_charger",
516 .of_match_table = of_match_ptr(omap_isp1704_of_match),
517 },
518 .probe = isp1704_charger_probe,
519 .remove = isp1704_charger_remove,
520};
521
522module_platform_driver(isp1704_charger_driver);
523
524MODULE_ALIAS("platform:isp1704_charger");
525MODULE_AUTHOR("Nokia Corporation");
526MODULE_DESCRIPTION("ISP170x USB Charger driver");
527MODULE_LICENSE("GPL");
528