[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