[Meego-kernel] [PATCH 2/10] usb: penwell_otg: add charging current negotiation and notification support
Wu, Hao
hao.wu
Mon Sep 27 03:45:19 PDT 2010
>From 5419cc696ce824e9b2b9d1333396971503e174bc Mon Sep 17 00:00:00 2001
From: Hao Wu <hao.wu at intel.com>
Date: Mon, 27 Sep 2010 15:42:05 +0800
Subject: [PATCH] usb: penwell_otg: add charging current negotiation and notification support.
This patch adds charging current negotiation in Transceiver driver for
SDP/CDP/DCP cases and it also provides notification/query interfaces to
EM(Battery driver) for charging current information notification.
A debug sysfs interface "chargers" is added to check current charging
capability.
Please notice:
- It requires device controller/gadget driver modification to fully
support SDP/CDP cases.
Signed-off-by: Hao Wu <hao.wu at intel.com>
---
drivers/usb/otg/penwell_otg.c | 458 +++++++++++++++++++++++++++++++------
include/linux/usb/intel_mid_otg.h | 2 +
include/linux/usb/penwell_otg.h | 10 +
3 files changed, 402 insertions(+), 68 deletions(-)
diff --git a/drivers/usb/otg/penwell_otg.c b/drivers/usb/otg/penwell_otg.c
index 58f9708..47ead31 100644
--- a/drivers/usb/otg/penwell_otg.c
+++ b/drivers/usb/otg/penwell_otg.c
@@ -115,6 +115,22 @@ static const char *state_string(enum usb_otg_state state)
}
}
+static const char *charger_string(enum usb_charger_type charger)
+{
+ switch (charger) {
+ case CHRG_SDP:
+ return "Standard Downstream Port";
+ case CHRG_CDP:
+ return "Charging Downstream Port";
+ case CHRG_DCP:
+ return "Dedicated Charging Port";
+ case CHRG_UNKNOWN:
+ return "Unknown";
+ default:
+ return "Undefined";
+ }
+}
+
static struct penwell_otg *the_transceiver;
void penwell_update_transceiver(void)
@@ -145,15 +161,225 @@ static int penwell_otg_set_peripheral(struct otg_transceiver *otg,
return 0;
}
+static void penwell_otg_set_charger(enum usb_charger_type charger)
+{
+ struct penwell_otg *pnw = the_transceiver;
+
+ dev_dbg(pnw->dev, "%s ---> %s\n", __func__,
+ charger_string(charger));
+
+ switch (charger) {
+ case CHRG_SDP:
+ case CHRG_DCP:
+ case CHRG_CDP:
+ case CHRG_ACA:
+ case CHRG_UNKNOWN:
+ pnw->charging_cap.chrg_type = charger;
+ break;
+ default:
+ dev_warn(pnw->dev, "undefined charger type\n");
+ break;
+ }
+
+ dev_dbg(pnw->dev, "%s <---\n", __func__);
+}
+
+static void penwell_otg_update_chrg_cap(enum usb_charger_type charger,
+ unsigned mA)
+{
+ struct penwell_otg *pnw = the_transceiver;
+ int flag = 0;
+ int event, retval;
+
+ dev_dbg(pnw->dev, "%s = %s, %d --->\n", __func__,
+ charger_string(charger), mA);
+
+ spin_lock(&pnw->bc_lock);
+
+ /* set charger type */
+ penwell_otg_set_charger(charger);
+
+ /* Don't notify EM if no current change needed */
+ if (pnw->charging_cap.mA == mA) {
+ spin_unlock(&pnw->bc_lock);
+ return ;
+ }
+
+ /* set current */
+ switch (pnw->charging_cap.chrg_type) {
+ case CHRG_SDP:
+ if ((pnw->charging_cap.mA == CHRG_CURR_DISCONN
+ || pnw->charging_cap.mA == CHRG_CURR_SDP_LOW
+ || pnw->charging_cap.mA == CHRG_CURR_SDP_HIGH)
+ && mA == CHRG_CURR_SDP_SUSP) {
+ /* SDP event: enter suspend state */
+ event = USBCHRG_EVENT_SUSPEND;
+ flag = 1;
+ } else if (pnw->charging_cap.mA == CHRG_CURR_DISCONN
+ && (mA == CHRG_CURR_SDP_LOW
+ || mA == CHRG_CURR_SDP_HIGH)) {
+ /* SDP event: charger connect */
+ event = USBCHRG_EVENT_CONNECT;
+ flag = 1;
+ } else if (pnw->charging_cap.mA == CHRG_CURR_SDP_SUSP
+ && (mA == CHRG_CURR_SDP_LOW
+ || mA == CHRG_CURR_SDP_HIGH)) {
+ /* SDP event: resume from suspend state */
+ event = USBCHRG_EVENT_RESUME;
+ flag = 1;
+ } else if (pnw->charging_cap.mA == CHRG_CURR_SDP_LOW
+ && mA == CHRG_CURR_SDP_HIGH) {
+ /* SDP event: configuration update */
+ event = USBCHRG_EVENT_UPDATE;
+ flag = 1;
+ } else if (pnw->charging_cap.mA == CHRG_CURR_SDP_HIGH
+ && mA == CHRG_CURR_SDP_LOW) {
+ /* SDP event: configuration update */
+ event = USBCHRG_EVENT_UPDATE;
+ flag = 1;
+ } else
+ dev_dbg(pnw->dev, "SDP: no need to update EM\n");
+ break;
+ case CHRG_DCP:
+ if (mA == CHRG_CURR_DCP) {
+ /* DCP event: charger connect */
+ event = USBCHRG_EVENT_CONNECT;
+ flag = 1;
+ } else
+ dev_dbg(pnw->dev, "DCP: no need to update EM\n");
+ break;
+ case CHRG_CDP:
+ if (pnw->charging_cap.mA == CHRG_CURR_DISCONN
+ && mA == CHRG_CURR_CDP) {
+ /* CDP event: charger connect */
+ event = USBCHRG_EVENT_CONNECT;
+ flag = 1;
+ } else if (pnw->charging_cap.mA == CHRG_CURR_CDP
+ && mA == CHRG_CURR_CDP_HS) {
+ /* CDP event: mode update */
+ event = USBCHRG_EVENT_UPDATE;
+ flag = 1;
+ } else if (pnw->charging_cap.mA == CHRG_CURR_CDP_HS
+ && mA == CHRG_CURR_CDP) {
+ /* CDP event: mode update */
+ event = USBCHRG_EVENT_UPDATE;
+ flag = 1;
+ } else
+ dev_dbg(pnw->dev, "CDP: no need to update EM\n");
+ break;
+ case CHRG_UNKNOWN:
+ if (mA == CHRG_CURR_DISCONN) {
+ /* event: chargers disconnect */
+ event = USBCHRG_EVENT_DISCONN;
+ flag = 1;
+ } else
+ dev_dbg(pnw->dev, "UNKNOWN: no need to update EM\n");
+ break;
+ default:
+ break;
+ }
+
+ if (flag) {
+ pnw->charging_cap.mA = mA;
+
+ /* Notify EM the charging current update */
+ dev_dbg(pnw->dev, "Notify EM charging capability change\n");
+ dev_dbg(pnw->dev, "%s event = %d mA = %d\n",
+ charger_string(pnw->charging_cap.chrg_type), event, mA);
+
+ if (pnw->bc_callback) {
+ retval = pnw->bc_callback(pnw->bc_arg, event,
+ &pnw->charging_cap);
+ if (retval)
+ dev_dbg(pnw->dev,
+ "bc callback return %d\n", retval);
+ }
+ }
+
+ spin_unlock(&pnw->bc_lock);
+
+ dev_dbg(pnw->dev, "%s <---\n", __func__);
+}
+
static int penwell_otg_set_power(struct otg_transceiver *otg,
unsigned mA)
{
struct penwell_otg *pnw = the_transceiver;
- dev_dbg(pnw->dev, "set_power = %d\n", mA);
+ dev_dbg(pnw->dev, "%s --->\n", __func__);
+
+ if (pnw->charging_cap.chrg_type != CHRG_SDP)
+ return 0;
+
+ /* Only current is updated */
+ penwell_otg_update_chrg_cap(CHRG_SDP, mA);
+
+ dev_dbg(pnw->dev, "%s <---\n", __func__);
+
+ return 0;
+}
+
+int penwell_otg_query_charging_cap(struct otg_bc_cap *cap)
+{
+ struct penwell_otg *pnw = the_transceiver;
+
+ dev_dbg(pnw->dev, "%s --->\n", __func__);
+
+ if (pnw == NULL)
+ return -ENODEV;
+
+ if (cap == NULL)
+ return -EINVAL;
+
+ spin_lock(&pnw->bc_lock);
+ cap->chrg_type = pnw->charging_cap.chrg_type;
+ cap->mA = pnw->charging_cap.mA;
+ spin_unlock(&pnw->bc_lock);
+
+ dev_dbg(pnw->dev, "%s <---\n", __func__);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(penwell_otg_query_charging_cap);
+
+/* Register/unregister battery driver callback */
+void *penwell_otg_register_bc_callback(
+ int (*cb)(void *, int, struct otg_bc_cap *), void *arg)
+{
+ struct penwell_otg *pnw = the_transceiver;
+
+ if (pnw == NULL)
+ return pnw;
+
+ if (pnw->bc_callback != NULL)
+ dev_dbg(pnw->dev, "callback has already registered\n");
+
+ spin_lock(&pnw->bc_lock);
+ pnw->bc_callback = cb;
+ pnw->bc_arg = arg;
+ spin_unlock(&pnw->bc_lock);
+
+ return pnw;
+}
+EXPORT_SYMBOL_GPL(penwell_otg_register_bc_callback);
+
+int penwell_otg_unregister_bc_callback(void *handler)
+{
+ struct penwell_otg *pnw = the_transceiver;
+
+ if (pnw == NULL)
+ return -ENODEV;
+
+ if (pnw != handler)
+ return -EINVAL;
+
+ spin_lock(&pnw->bc_lock);
+ pnw->bc_callback = NULL;
+ pnw->bc_arg = NULL;
+ spin_unlock(&pnw->bc_lock);
return 0;
}
+EXPORT_SYMBOL_GPL(penwell_otg_unregister_bc_callback);
/* After probe, it should enable the power of USB PHY */
static void penwell_otg_phy_enable(int on)
@@ -1013,6 +1239,20 @@ static int penwell_otg_iotg_notify(struct notifier_block *nb,
dev_dbg(pnw->dev, "PNW OTG Nofity Client Driver remove\n");
flag = 1;
break;
+ case MID_OTG_NOTIFY_CLIENTFS:
+ if (pnw->charging_cap.chrg_type == CHRG_CDP) {
+ dev_dbg(pnw->dev, "PNW OTG Notfiy Client FullSpeed\n");
+ penwell_otg_update_chrg_cap(CHRG_CDP, CHRG_CURR_CDP);
+ }
+ flag = 0;
+ break;
+ case MID_OTG_NOTIFY_CLIENTHS:
+ if (pnw->charging_cap.chrg_type == CHRG_CDP) {
+ dev_dbg(pnw->dev, "PNW OTG Notfiy Client HighSpeed\n");
+ penwell_otg_update_chrg_cap(CHRG_CDP, CHRG_CURR_CDP_HS);
+ }
+ flag = 0;
+ break;
default:
dev_dbg(pnw->dev, "PNW OTG Nofity unknown notify message\n");
return NOTIFY_DONE;
@@ -1126,16 +1366,16 @@ static void penwell_otg_work(struct work_struct *work)
/* Check it is caused by ACA attachment */
if (hsm->id == ID_ACA_B) {
/* in this case, update current limit*/
- if (iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 1500);
+ penwell_otg_update_chrg_cap(CHRG_ACA,
+ CHRG_CURR_ACA);
/* make sure PHY low power state */
penwell_otg_phy_low_power(1);
break;
} else if (hsm->id == ID_ACA_C) {
/* in this case, update current limit*/
- if (iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 1500);
+ penwell_otg_update_chrg_cap(CHRG_ACA,
+ CHRG_CURR_ACA);
}
/* Clear power_up */
@@ -1160,24 +1400,52 @@ static void penwell_otg_work(struct work_struct *work)
if (charger_type == CHRG_DCP) {
dev_info(pnw->dev, "DCP detected\n");
+
+ /* DCP: set charger type, current, notify EM */
+ penwell_otg_update_chrg_cap(CHRG_DCP,
+ CHRG_CURR_DCP);
penwell_otg_phy_low_power(1);
- iotg->otg.set_power(&iotg->otg, CHRG_CURR_DCP);
break;
+
} else if (charger_type == CHRG_CDP) {
dev_info(pnw->dev, "CDP detected\n");
- iotg->otg.set_power(&iotg->otg, CHRG_CURR_CDP);
- } else if (charger_type == CHRG_SDP)
+
+ /* CDP: set charger type, current, notify EM */
+ penwell_otg_update_chrg_cap(CHRG_CDP,
+ CHRG_CURR_CDP);
+
+ if (iotg->start_peripheral) {
+ iotg->start_peripheral(iotg);
+ } else {
+ dev_dbg(pnw->dev,
+ "client driver not support\n");
+ break;
+ }
+ } else if (charger_type == CHRG_SDP) {
dev_info(pnw->dev, "SDP detected\n");
- else if (charger_type == CHRG_UNKNOWN)
+
+ /* SDP: set charger type */
+ penwell_otg_update_chrg_cap(CHRG_SDP,
+ pnw->charging_cap.mA);
+
+ if (iotg->start_peripheral) {
+ iotg->start_peripheral(iotg);
+ } else {
+ dev_dbg(pnw->dev,
+ "client driver not support\n");
+ break;
+ }
+ } else if (charger_type == CHRG_UNKNOWN) {
dev_info(pnw->dev, "Unknown Charger Found\n");
- if (iotg->start_peripheral) {
- iotg->start_peripheral(iotg);
- iotg->otg.state = OTG_STATE_B_PERIPHERAL;
- } else {
- dev_dbg(pnw->dev, "client driver not loaded\n");
- break;
+ /* Unknown: set charger type */
+ penwell_otg_update_chrg_cap(CHRG_UNKNOWN,
+ pnw->charging_cap.mA);
+ penwell_otg_phy_low_power(1);
}
+
+ iotg->otg.state = OTG_STATE_B_PERIPHERAL;
+
} else if ((hsm->b_bus_req || hsm->power_up ||
hsm->adp_change) && !hsm->b_srp_fail_tmr) {
if ((hsm->b_ssend_srp && hsm->b_se0_srp) ||
@@ -1205,9 +1473,9 @@ static void penwell_otg_work(struct work_struct *work)
"BUS is active, try SRP later\n");
}
} else if (!hsm->b_sess_vld && hsm->id == ID_B) {
- if (iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 0);
- penwell_otg_phy_low_power(1);
+ /* Notify EM charger remove event */
+ penwell_otg_update_chrg_cap(CHRG_UNKNOWN,
+ CHRG_CURR_DISCONN);
}
break;
@@ -1232,10 +1500,16 @@ static void penwell_otg_work(struct work_struct *work)
iotg->otg.state = OTG_STATE_A_IDLE;
penwell_update_transceiver();
} else if (!hsm->b_sess_vld || hsm->id == ID_ACA_B) {
- if (hsm->id == ID_ACA_B && iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 1500);
- else if (hsm->id == ID_B && iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 0);
+ /* Move to B_IDLE state, VBUS off/ACA */
+
+ if (hsm->id == ID_ACA_B)
+ penwell_otg_update_chrg_cap(CHRG_ACA,
+ CHRG_CURR_ACA);
+ else if (hsm->id == ID_B) {
+ /* Notify EM charger remove event */
+ penwell_otg_update_chrg_cap(CHRG_UNKNOWN,
+ CHRG_CURR_DISCONN);
+ }
hsm->b_hnp_enable = 0;
@@ -1273,11 +1547,12 @@ static void penwell_otg_work(struct work_struct *work)
penwell_otg_add_timer(TB_ASE0_BRST_TMR);
} else if (hsm->id == ID_ACA_C) {
/* Make sure current limit updated */
- if (iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 1500);
+ penwell_otg_update_chrg_cap(CHRG_ACA, CHRG_CURR_ACA);
+#if 0
} else if (hsm->id == ID_B) {
if (iotg->otg.set_power)
iotg->otg.set_power(&iotg->otg, 100);
+#endif
}
break;
@@ -1307,12 +1582,16 @@ static void penwell_otg_work(struct work_struct *work)
iotg->otg.state = OTG_STATE_A_IDLE;
penwell_update_transceiver();
} else if (!hsm->b_sess_vld || hsm->id == ID_ACA_B) {
- /* Move to B_IDLE state, VBUS off */
-
- if (hsm->id == ID_ACA_B && iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 1500);
- else if (hsm->id == ID_B)
- iotg->otg.set_power(&iotg->otg, 0);
+ /* Move to B_IDLE state, VBUS off/ACA */
+
+ if (hsm->id == ID_ACA_B)
+ penwell_otg_update_chrg_cap(CHRG_ACA,
+ CHRG_CURR_ACA);
+ else if (hsm->id == ID_B) {
+ /* Notify EM charger remove event */
+ penwell_otg_update_chrg_cap(CHRG_UNKNOWN,
+ CHRG_CURR_DISCONN);
+ }
/* Delete current timer */
penwell_otg_del_timer(TB_ASE0_BRST_TMR);
@@ -1369,12 +1648,13 @@ static void penwell_otg_work(struct work_struct *work)
iotg->otg.state = OTG_STATE_B_PERIPHERAL;
} else if (hsm->id == ID_ACA_C) {
/* Make sure current limit updated */
- if (iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 1500);
+ penwell_otg_update_chrg_cap(CHRG_ACA, CHRG_CURR_ACA);
} else if (hsm->id == ID_B) {
+#if 0
/* only set 2mA due to client function stopped */
if (iotg->otg.set_power)
iotg->otg.set_power(&iotg->otg, 2);
+#endif
}
break;
@@ -1398,10 +1678,16 @@ static void penwell_otg_work(struct work_struct *work)
iotg->otg.state = OTG_STATE_A_IDLE;
penwell_update_transceiver();
} else if (!hsm->b_sess_vld || hsm->id == ID_ACA_B) {
- if (hsm->id == ID_ACA_B && iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 1500);
- else if (hsm->id == ID_B && iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 0);
+ /* Move to B_IDLE state, VBUS off/ACA */
+
+ if (hsm->id == ID_ACA_B)
+ penwell_otg_update_chrg_cap(CHRG_ACA,
+ CHRG_CURR_ACA);
+ else if (hsm->id == ID_B) {
+ /* Notify EM charger remove event */
+ penwell_otg_update_chrg_cap(CHRG_UNKNOWN,
+ CHRG_CURR_DISCONN);
+ }
hsm->b_hnp_enable = 0;
hsm->b_bus_req = 0;
@@ -1440,11 +1726,12 @@ static void penwell_otg_work(struct work_struct *work)
iotg->otg.state = OTG_STATE_B_PERIPHERAL;
} else if (hsm->id == ID_ACA_C) {
/* Make sure current limit updated */
- if (iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 1500);
+ penwell_otg_update_chrg_cap(CHRG_ACA, CHRG_CURR_ACA);
} else if (hsm->id == ID_B) {
+#if 0
if (iotg->otg.set_power)
iotg->otg.set_power(&iotg->otg, 100);
+#endif
}
break;
@@ -1453,8 +1740,9 @@ static void penwell_otg_work(struct work_struct *work)
pnw->iotg.otg.default_a = 0;
hsm->b_bus_req = 0;
- if (hsm->id == ID_ACA_B && iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 1500);
+ if (hsm->id == ID_ACA_B)
+ penwell_otg_update_chrg_cap(CHRG_ACA,
+ CHRG_CURR_ACA);
set_client_mode();
penwell_otg_phy_low_power(1);
@@ -1466,8 +1754,7 @@ static void penwell_otg_work(struct work_struct *work)
penwell_update_transceiver();
} else if (hsm->id == ID_ACA_A) {
- if (iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 1500);
+ penwell_otg_update_chrg_cap(CHRG_ACA, CHRG_CURR_ACA);
if (hsm->power_up)
hsm->power_up = 0;
@@ -1547,8 +1834,9 @@ static void penwell_otg_work(struct work_struct *work)
if (hsm->id == ID_ACA_A) {
if (iotg->otg.set_vbus)
iotg->otg.set_vbus(&iotg->otg, false);
- if (iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 1500);
+
+ penwell_otg_update_chrg_cap(CHRG_ACA,
+ CHRG_CURR_ACA);
}
hsm->b_conn = 0;
@@ -1592,9 +1880,6 @@ static void penwell_otg_work(struct work_struct *work)
/* Delete current timer and disable host function */
penwell_otg_del_timer(TA_WAIT_BCON_TMR);
- if (iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 0);
-
if (iotg->stop_host)
iotg->stop_host(iotg);
else
@@ -1618,8 +1903,7 @@ static void penwell_otg_work(struct work_struct *work)
if (!hsm->a_bus_req)
hsm->a_bus_req = 1;
} else if (hsm->id == ID_ACA_A) {
- if (iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 1500);
+ penwell_otg_update_chrg_cap(CHRG_ACA, CHRG_CURR_ACA);
/* Turn off VBUS */
if (iotg->otg.set_vbus)
@@ -1630,8 +1914,9 @@ static void penwell_otg_work(struct work_struct *work)
case OTG_STATE_A_HOST:
if (hsm->id == ID_B || hsm->id == ID_ACA_B || hsm->a_bus_drop) {
/* Move to A_WAIT_VFALL state, timeout/user request */
- if (hsm->id == ID_ACA_B && iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 1500);
+ if (hsm->id == ID_ACA_B)
+ penwell_otg_update_chrg_cap(CHRG_ACA,
+ CHRG_CURR_ACA);
if (iotg->stop_host)
iotg->stop_host(iotg);
@@ -1648,9 +1933,6 @@ static void penwell_otg_work(struct work_struct *work)
} else if (!hsm->a_vbus_vld) {
/* Move to A_VBUS_ERR state */
- if (iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 0);
-
if (iotg->stop_host)
iotg->stop_host(iotg);
else
@@ -1679,8 +1961,7 @@ static void penwell_otg_work(struct work_struct *work)
/* add kernel timer */
iotg->otg.state = OTG_STATE_A_WAIT_BCON;
} else if (hsm->id == ID_ACA_A) {
- if (iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 1500);
+ penwell_otg_update_chrg_cap(CHRG_ACA, CHRG_CURR_ACA);
/* Turn off VBUS */
if (iotg->otg.set_vbus)
@@ -1698,8 +1979,9 @@ static void penwell_otg_work(struct work_struct *work)
hsm->a_aidl_bdis_tmout = 0;
penwell_otg_del_timer(TA_AIDL_BDIS_TMR);
- if (hsm->id == ID_ACA_B && iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 1500);
+ if (hsm->id == ID_ACA_B)
+ penwell_otg_update_chrg_cap(CHRG_ACA,
+ CHRG_CURR_ACA);
if (iotg->stop_host)
iotg->stop_host(iotg);
@@ -1719,9 +2001,6 @@ static void penwell_otg_work(struct work_struct *work)
/* Delete current timer and clear flags */
penwell_otg_del_timer(TA_AIDL_BDIS_TMR);
- if (iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 0);
-
if (iotg->stop_host)
iotg->stop_host(iotg);
else
@@ -1773,8 +2052,9 @@ static void penwell_otg_work(struct work_struct *work)
penwell_otg_loc_sof(1);
iotg->otg.state = OTG_STATE_A_HOST;
} else if (hsm->id == ID_ACA_A) {
- if (hsm->id == ID_ACA_A && iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 1500);
+ if (hsm->id == ID_ACA_A)
+ penwell_otg_update_chrg_cap(CHRG_ACA,
+ CHRG_CURR_ACA);
/* Turn off VBUS */
if (iotg->otg.set_vbus)
@@ -1808,9 +2088,6 @@ static void penwell_otg_work(struct work_struct *work)
/* Delete current timer and disable client function */
penwell_otg_del_timer(TA_BIDL_ADIS_TMR);
- if (iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 0);
-
if (iotg->stop_peripheral)
iotg->stop_peripheral(iotg);
else
@@ -1853,8 +2130,9 @@ static void penwell_otg_work(struct work_struct *work)
if (!timer_pending(&pnw->hsm_timer))
penwell_otg_add_timer(TA_BIDL_ADIS_TMR);
} else if (hsm->id == ID_ACA_A) {
- if (hsm->id == ID_ACA_A && iotg->otg.set_power)
- iotg->otg.set_power(&iotg->otg, 1500);
+ if (hsm->id == ID_ACA_A)
+ penwell_otg_update_chrg_cap(CHRG_ACA,
+ CHRG_CURR_ACA);
/* Turn off VBUS */
if (iotg->otg.set_vbus)
@@ -2027,6 +2305,37 @@ show_hsm(struct device *_dev, struct device_attribute *attr, char *buf)
static DEVICE_ATTR(hsm, S_IRUGO, show_hsm, NULL);
static ssize_t
+show_chargers(struct device *_dev, struct device_attribute *attr, char *buf)
+{
+ struct penwell_otg *pnw = the_transceiver;
+ char *next;
+ unsigned size, t;
+ enum usb_charger_type type;
+ unsigned int mA;
+
+ next = buf;
+ size = PAGE_SIZE;
+
+ spin_lock(&pnw->bc_lock);
+ type = pnw->charging_cap.chrg_type;
+ mA = pnw->charging_cap.mA;
+ spin_unlock(&pnw->bc_lock);
+
+ t = scnprintf(next, size,
+ "USB Battery Charging Capability\n"
+ "\tUSB Charger Type: %s\n"
+ "\tMax Charging Current: %u\n",
+ charger_string(type),
+ mA
+ );
+ size -= t;
+ next += t;
+
+ return PAGE_SIZE - size;
+}
+static DEVICE_ATTR(chargers, S_IRUGO, show_chargers, NULL);
+
+static ssize_t
get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
{
struct penwell_otg *pnw = the_transceiver;
@@ -2299,6 +2608,11 @@ static int penwell_otg_probe(struct pci_dev *pdev,
pnw->iotg.ulpi_ops.read = penwell_otg_ulpi_read;
pnw->iotg.ulpi_ops.write = penwell_otg_ulpi_write;
+ /* Battery Charging part */
+ spin_lock_init(&pnw->bc_lock);
+ pnw->charging_cap.mA = CHRG_CURR_DISCONN;
+ pnw->charging_cap.chrg_type = CHRG_UNKNOWN;
+
init_timer(&pnw->hsm_timer);
init_timer(&pnw->hnp_poll_timer);
init_completion(&pnw->adp.adp_comp);
@@ -2346,6 +2660,13 @@ static int penwell_otg_probe(struct pci_dev *pdev,
goto err;
}
+ retval = device_create_file(&pdev->dev, &dev_attr_chargers);
+ if (retval < 0) {
+ dev_dbg(pnw->dev,
+ "Can't chargers sysfs attribute: %d\n", retval);
+ goto err;
+ }
+
retval = sysfs_create_group(&pdev->dev.kobj, &debug_dev_attr_group);
if (retval < 0) {
dev_dbg(pnw->dev,
@@ -2390,6 +2711,7 @@ static void penwell_otg_remove(struct pci_dev *pdev)
otg_set_transceiver(NULL);
pci_disable_device(pdev);
sysfs_remove_group(&pdev->dev.kobj, &debug_dev_attr_group);
+ device_remove_file(&pdev->dev, &dev_attr_chargers);
device_remove_file(&pdev->dev, &dev_attr_hsm);
device_remove_file(&pdev->dev, &dev_attr_registers);
kfree(pnw);
diff --git a/include/linux/usb/intel_mid_otg.h b/include/linux/usb/intel_mid_otg.h
index a0ccf79..c9ec416 100644
--- a/include/linux/usb/intel_mid_otg.h
+++ b/include/linux/usb/intel_mid_otg.h
@@ -162,6 +162,8 @@ struct intel_mid_otg_xceiv *otg_to_mid_xceiv(struct otg_transceiver *otg)
#define MID_OTG_NOTIFY_HOSTREMOVE 0x0008
#define MID_OTG_NOTIFY_CLIENTADD 0x0009
#define MID_OTG_NOTIFY_CLIENTREMOVE 0x000a
+#define MID_OTG_NOTIFY_CLIENTFS 0x000b
+#define MID_OTG_NOTIFY_CLIENTHS 0x000c
static inline int
intel_mid_otg_register_notifier(struct intel_mid_otg_xceiv *iotg,
diff --git a/include/linux/usb/penwell_otg.h b/include/linux/usb/penwell_otg.h
index c09dd14..23a004e 100644
--- a/include/linux/usb/penwell_otg.h
+++ b/include/linux/usb/penwell_otg.h
@@ -303,6 +303,11 @@ struct penwell_otg {
struct notifier_block iotg_notifier;
struct adp_status adp;
+
+ spinlock_t bc_lock;
+ struct otg_bc_cap charging_cap;
+ int (*bc_callback)(void *arg, int event, struct otg_bc_cap *cap);
+ void *bc_arg;
};
static inline
@@ -311,4 +316,9 @@ struct penwell_otg *iotg_to_penwell(struct intel_mid_otg_xceiv *iotg)
return container_of(iotg, struct penwell_otg, iotg);
}
+extern int penwell_otg_query_charging_cap(struct otg_bc_cap *cap);
+extern void *penwell_otg_register_bc_callback(
+ int (*cb)(void *, int, struct otg_bc_cap *), void *arg);
+extern int penwell_otg_unregister_bc_callback(void *handler);
+
#endif /* __PENWELL_OTG_H__ */
--
1.5.5
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0002-usb-penwell_otg-add-charging-current-negotiation-a.patch
Type: application/octet-stream
Size: 23311 bytes
Desc: 0002-usb-penwell_otg-add-charging-current-negotiation-a.patch
URL: <http://lists.meego.com/pipermail/meego-kernel/attachments/20100927/7e3a19e2/attachment-0001.obj>
More information about the Meego-kernel
mailing list