|
@@ -2,6 +2,7 @@
|
|
|
* ISP1704 USB Charger Detection driver
|
|
|
*
|
|
|
* Copyright (C) 2010 Nokia Corporation
|
|
|
+ * Copyright (C) 2012 - 2013 Pali Rohár <pali.rohar@gmail.com>
|
|
|
*
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
@@ -65,10 +66,6 @@ struct isp1704_charger {
|
|
|
unsigned present:1;
|
|
|
unsigned online:1;
|
|
|
unsigned current_max;
|
|
|
-
|
|
|
- /* temp storage variables */
|
|
|
- unsigned long event;
|
|
|
- unsigned max_power;
|
|
|
};
|
|
|
|
|
|
static inline int isp1704_read(struct isp1704_charger *isp, u32 reg)
|
|
@@ -231,56 +228,59 @@ static inline int isp1704_charger_detect(struct isp1704_charger *isp)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static inline int isp1704_charger_detect_dcp(struct isp1704_charger *isp)
|
|
|
+{
|
|
|
+ if (isp1704_charger_detect(isp) &&
|
|
|
+ isp1704_charger_type(isp) == POWER_SUPPLY_TYPE_USB_DCP)
|
|
|
+ return true;
|
|
|
+ else
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
static void isp1704_charger_work(struct work_struct *data)
|
|
|
{
|
|
|
- int detect;
|
|
|
- unsigned long event;
|
|
|
- unsigned power;
|
|
|
struct isp1704_charger *isp =
|
|
|
container_of(data, struct isp1704_charger, work);
|
|
|
static DEFINE_MUTEX(lock);
|
|
|
|
|
|
- event = isp->event;
|
|
|
- power = isp->max_power;
|
|
|
-
|
|
|
mutex_lock(&lock);
|
|
|
|
|
|
- if (event != USB_EVENT_NONE)
|
|
|
- isp1704_charger_set_power(isp, 1);
|
|
|
-
|
|
|
- switch (event) {
|
|
|
+ switch (isp->phy->last_event) {
|
|
|
case USB_EVENT_VBUS:
|
|
|
- isp->online = true;
|
|
|
+ /* do not call wall charger detection more times */
|
|
|
+ if (!isp->present) {
|
|
|
+ isp->online = true;
|
|
|
+ isp->present = 1;
|
|
|
+ isp1704_charger_set_power(isp, 1);
|
|
|
+
|
|
|
+ /* detect wall charger */
|
|
|
+ if (isp1704_charger_detect_dcp(isp)) {
|
|
|
+ isp->psy.type = POWER_SUPPLY_TYPE_USB_DCP;
|
|
|
+ isp->current_max = 1800;
|
|
|
+ } else {
|
|
|
+ isp->psy.type = POWER_SUPPLY_TYPE_USB;
|
|
|
+ isp->current_max = 500;
|
|
|
+ }
|
|
|
|
|
|
- /* detect charger */
|
|
|
- detect = isp1704_charger_detect(isp);
|
|
|
-
|
|
|
- if (detect) {
|
|
|
- isp->present = detect;
|
|
|
- isp->psy.type = isp1704_charger_type(isp);
|
|
|
+ /* enable data pullups */
|
|
|
+ if (isp->phy->otg->gadget)
|
|
|
+ usb_gadget_connect(isp->phy->otg->gadget);
|
|
|
}
|
|
|
|
|
|
- switch (isp->psy.type) {
|
|
|
- case POWER_SUPPLY_TYPE_USB_DCP:
|
|
|
- isp->current_max = 1800;
|
|
|
- break;
|
|
|
- case POWER_SUPPLY_TYPE_USB_CDP:
|
|
|
+ if (isp->psy.type != POWER_SUPPLY_TYPE_USB_DCP) {
|
|
|
/*
|
|
|
* Only 500mA here or high speed chirp
|
|
|
* handshaking may break
|
|
|
*/
|
|
|
- isp->current_max = 500;
|
|
|
- /* FALLTHROUGH */
|
|
|
- case POWER_SUPPLY_TYPE_USB:
|
|
|
- default:
|
|
|
- /* enable data pullups */
|
|
|
- if (isp->phy->otg->gadget)
|
|
|
- usb_gadget_connect(isp->phy->otg->gadget);
|
|
|
+ if (isp->current_max > 500)
|
|
|
+ isp->current_max = 500;
|
|
|
+
|
|
|
+ if (isp->current_max > 100)
|
|
|
+ isp->psy.type = POWER_SUPPLY_TYPE_USB_CDP;
|
|
|
}
|
|
|
break;
|
|
|
case USB_EVENT_NONE:
|
|
|
isp->online = false;
|
|
|
- isp->current_max = 0;
|
|
|
isp->present = 0;
|
|
|
isp->current_max = 0;
|
|
|
isp->psy.type = POWER_SUPPLY_TYPE_USB;
|
|
@@ -298,12 +298,6 @@ static void isp1704_charger_work(struct work_struct *data)
|
|
|
|
|
|
isp1704_charger_set_power(isp, 0);
|
|
|
break;
|
|
|
- case USB_EVENT_ENUMERATED:
|
|
|
- if (isp->present)
|
|
|
- isp->current_max = 1800;
|
|
|
- else
|
|
|
- isp->current_max = power;
|
|
|
- break;
|
|
|
default:
|
|
|
goto out;
|
|
|
}
|
|
@@ -314,16 +308,11 @@ out:
|
|
|
}
|
|
|
|
|
|
static int isp1704_notifier_call(struct notifier_block *nb,
|
|
|
- unsigned long event, void *power)
|
|
|
+ unsigned long val, void *v)
|
|
|
{
|
|
|
struct isp1704_charger *isp =
|
|
|
container_of(nb, struct isp1704_charger, nb);
|
|
|
|
|
|
- isp->event = event;
|
|
|
-
|
|
|
- if (power)
|
|
|
- isp->max_power = *((unsigned *)power);
|
|
|
-
|
|
|
schedule_work(&isp->work);
|
|
|
|
|
|
return NOTIFY_OK;
|
|
@@ -462,13 +451,13 @@ static int isp1704_charger_probe(struct platform_device *pdev)
|
|
|
if (isp->phy->otg->gadget)
|
|
|
usb_gadget_disconnect(isp->phy->otg->gadget);
|
|
|
|
|
|
+ if (isp->phy->last_event == USB_EVENT_NONE)
|
|
|
+ isp1704_charger_set_power(isp, 0);
|
|
|
+
|
|
|
/* Detect charger if VBUS is valid (the cable was already plugged). */
|
|
|
- ret = isp1704_read(isp, ULPI_USB_INT_STS);
|
|
|
- isp1704_charger_set_power(isp, 0);
|
|
|
- if ((ret & ULPI_INT_VBUS_VALID) && !isp->phy->otg->default_a) {
|
|
|
- isp->event = USB_EVENT_VBUS;
|
|
|
+ if (isp->phy->last_event == USB_EVENT_VBUS &&
|
|
|
+ !isp->phy->otg->default_a)
|
|
|
schedule_work(&isp->work);
|
|
|
- }
|
|
|
|
|
|
return 0;
|
|
|
fail2:
|