This penwell_otg transceiver driver patch enables
MFLD USB OTG SRP function per OTG 2.0 spec requirement.

Signed-off-by: Hao Wu <[email protected]>
---
 drivers/usb/otg/penwell_otg.c   |  156 +++++++++++++++++++++++++++++++-------
 include/linux/usb/penwell_otg.h |   13 +++-
 2 files changed, 138 insertions(+), 31 deletions(-)

diff --git a/drivers/usb/otg/penwell_otg.c b/drivers/usb/otg/penwell_otg.c
index 229a1ee..0b29710 100644
--- a/drivers/usb/otg/penwell_otg.c
+++ b/drivers/usb/otg/penwell_otg.c
@@ -62,6 +62,9 @@ static int penwell_otg_set_host(struct otg_transceiver *otg,
 static int penwell_otg_set_peripheral(struct otg_transceiver *otg,
                                struct usb_gadget *gadget);
 static int penwell_otg_start_srp(struct otg_transceiver *otg);
+static void penwell_otg_mon_bus(unsigned long indicator);
+static void penwell_otg_mon_bus_fn(unsigned long indicator);
+
 static int penwell_otg_msic_write(u16 addr, u8 data);
 
 static const char *state_string(enum usb_otg_state state)
@@ -517,6 +520,86 @@ static enum msic_vendor penwell_otg_check_msic(void)
        return MSIC_VD_TI;
 }
 
+/* Monitor function check if SRP initial conditions. Use polling on current
+ * status for b_ssend_srp, b_se0_srp */
+static void penwell_otg_mon_bus(unsigned long indicator)
+{
+       struct penwell_otg *pnw = the_transceiver;
+       unsigned long j = jiffies;
+
+       switch (indicator) {
+       case BUS_MON_START:
+               /* reset all parameters when start monitoring */
+               pnw->b_ssend_srp_time = 0;
+               pnw->b_se0_srp_time = 0;
+               pnw->iotg.hsm.b_ssend_srp = 0;
+               pnw->iotg.hsm.b_se0_srp = 0;
+       case BUS_MON_CONTINUE:
+               pnw->bus_mon_timer.data = indicator;
+               pnw->bus_mon_timer.function = penwell_otg_mon_bus_fn;
+               pnw->bus_mon_timer.expires = j + SRP_MON_INVAL * HZ / 1000;
+
+               add_timer(&pnw->bus_mon_timer);
+               break;
+       case BUS_MON_STOP:
+               del_timer_sync(&pnw->bus_mon_timer);
+               break;
+       default:
+               dev_dbg(pnw->dev, "unknown bus monitor action\n");
+               break;
+       }
+}
+
+/* monitor function to see if srp initial conditions are met or not */
+static void penwell_otg_mon_bus_fn(unsigned long indicator)
+{
+       struct penwell_otg      *pnw = the_transceiver;
+       u32                     val;
+
+       /* For b_ssend_srp, poll OTGSC.BSE bit to
+        * make sure in session end state */
+       val = readl(pnw->iotg.base + CI_OTGSC);
+       if (val & OTGSC_BSE)
+               pnw->b_ssend_srp_time += SRP_MON_INVAL;
+       else {
+               pnw->b_ssend_srp_time = 0;
+               pnw->iotg.hsm.b_ssend_srp = 0;
+               dev_dbg(pnw->dev, "b_ssend_srp = 0\n");
+       }
+
+       if (pnw->b_ssend_srp_time >= TB_SSEND_SRP) {
+               pnw->b_ssend_srp_time = TB_SSEND_SRP;
+               if (pnw->iotg.hsm.b_ssend_srp != 1) {
+                       dev_dbg(pnw->dev, "b_ssend_srp = 1\n");
+                       pnw->iotg.hsm.b_ssend_srp = 1;
+                       penwell_update_transceiver();
+               }
+       }
+
+       /* For b_se0_srp, poll PORTSC.LS bits to
+        * make sure in data line se0 state */
+       val = readl(pnw->iotg.base + CI_PORTSC1);
+       if (val & PORTSC_LS) {
+               pnw->b_se0_srp_time = 0;
+               pnw->iotg.hsm.b_se0_srp = 0;
+               dev_dbg(pnw->dev, "b_se0_srp = 0\n");
+       } else {
+               /* LS = SE0 */
+               pnw->b_se0_srp_time += SRP_MON_INVAL;
+       }
+
+       if (pnw->b_se0_srp_time >= TB_SE0_SRP) {
+               pnw->b_se0_srp_time = TB_SE0_SRP;
+               if (pnw->iotg.hsm.b_se0_srp != 1) {
+                       dev_dbg(pnw->dev, "b_se0_srp = 1\n");
+                       pnw->iotg.hsm.b_se0_srp = 1;
+                       penwell_update_transceiver();
+               }
+       }
+
+       penwell_otg_mon_bus(BUS_MON_CONTINUE);
+}
+
 /* Start SRP function */
 static int penwell_otg_start_srp(struct otg_transceiver *otg)
 {
@@ -1043,7 +1126,7 @@ static void init_hsm(void)
        iotg->hsm.a_bus_req = 1;
        iotg->hsm.a_bus_drop = 0;
        /* init hsm means power_up case */
-       iotg->hsm.power_up = 0;
+       iotg->hsm.power_up = 1;
        /* defautly don't request bus as B device */
        iotg->hsm.b_bus_req = 0;
        /* no system error */
@@ -1051,6 +1134,8 @@ static void init_hsm(void)
 
        penwell_otg_phy_low_power(1);
 
+       if (iotg->otg.state == OTG_STATE_B_IDLE)
+               penwell_otg_mon_bus(BUS_MON_START);
 }
 
 static void update_hsm(void)
@@ -1217,11 +1302,17 @@ static int penwell_otg_iotg_notify(struct 
notifier_block *nb,
                break;
        case MID_OTG_NOTIFY_HSUSPEND:
                dev_dbg(pnw->dev, "PNW OTG Notify Host Bus suspend Event\n");
-               flag = 0;
+               if (iotg->otg.default_a == 1)
+                       iotg->hsm.a_bus_req = 0;
+               else
+                       iotg->hsm.b_bus_req = 0;
+               flag = 1;
                break;
        case MID_OTG_NOTIFY_HRESUME:
                dev_dbg(pnw->dev, "PNW OTG Notify Host Bus resume Event\n");
-               flag = 0;
+               if (iotg->otg.default_a == 1)
+                       iotg->hsm.a_bus_req = 1;
+               flag = 1;
                break;
        case MID_OTG_NOTIFY_CSUSPEND:
                dev_dbg(pnw->dev, "PNW OTG Notify Client Bus suspend Event\n");
@@ -1353,7 +1444,8 @@ static void penwell_otg_work(struct work_struct *work)
                        iotg->otg.default_a = 1;
                        hsm->a_srp_det = 0;
                        set_host_mode();
-                       penwell_otg_phy_low_power(1);
+                       penwell_otg_phy_low_power(0);
+                       penwell_otg_mon_bus(BUS_MON_STOP);
 
                        /* Always set a_bus_req to 1, in case no ADP */
                        hsm->a_bus_req = 1;
@@ -1388,8 +1480,7 @@ static void penwell_otg_work(struct work_struct *work)
                        }
 
                        /* Clear power_up */
-                       if (hsm->power_up)
-                               hsm->power_up = 0;
+                       hsm->power_up = 0;
 
                        /* Move to B_PERIPHERAL state, Session Valid */
 
@@ -1400,6 +1491,7 @@ static void penwell_otg_work(struct work_struct *work)
                        hsm->a_bus_suspend = 0;
 
                        /* Start USB Battery charger detection flow */
+                       penwell_otg_mon_bus(BUS_MON_STOP);
 
                        mutex_lock(&pnw->msic_mutex);
                        /* Enable data contact detection */
@@ -1464,14 +1556,10 @@ static void penwell_otg_work(struct work_struct *work)
 
                } 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) ||
-                                       hsm->adp_change || hsm->power_up) {
+                       if (hsm->b_ssend_srp && hsm->b_se0_srp) {
 
-                               if (hsm->power_up)
-                                       hsm->power_up = 0;
-
-                               if (hsm->adp_change)
-                                       hsm->adp_change = 0;
+                               hsm->power_up = 0;
+                               hsm->adp_change = 0;
 
                                /* clear the PHCD before start srp */
                                penwell_otg_phy_low_power(0);
@@ -1535,9 +1623,8 @@ static void penwell_otg_work(struct work_struct *work)
                                dev_dbg(pnw->dev,
                                        "client driver has been removed.\n");
 
-                       hsm->b_ssend_srp = 1;
-                       hsm->b_se0_srp = 1;
                        penwell_otg_phy_low_power(1);
+                       penwell_otg_mon_bus(BUS_MON_START);
 
                        iotg->otg.state = OTG_STATE_B_IDLE;
                } else if (hsm->b_bus_req && hsm->b_hnp_enable
@@ -1624,9 +1711,7 @@ static void penwell_otg_work(struct work_struct *work)
 
                        set_client_mode();
                        penwell_otg_phy_low_power(1);
-
-                       hsm->b_ssend_srp = 1;
-                       hsm->b_se0_srp = 1;
+                       penwell_otg_mon_bus(BUS_MON_START);
 
                        iotg->otg.state = OTG_STATE_B_IDLE;
                } else if (hsm->a_conn) {
@@ -1716,9 +1801,7 @@ static void penwell_otg_work(struct work_struct *work)
 
                        set_client_mode();
                        penwell_otg_phy_low_power(1);
-
-                       hsm->b_ssend_srp = 1;
-                       hsm->b_se0_srp = 1;
+                       penwell_otg_mon_bus(BUS_MON_START);
 
                        iotg->otg.state = OTG_STATE_B_IDLE;
                } else if (!hsm->b_bus_req || !hsm->a_conn
@@ -1758,9 +1841,7 @@ static void penwell_otg_work(struct work_struct *work)
                        set_client_mode();
                        msleep(5);
                        penwell_otg_phy_low_power(1);
-
-                       hsm->b_ssend_srp = 1;
-                       hsm->b_se0_srp = 1;
+                       penwell_otg_mon_bus(BUS_MON_START);
 
                        iotg->otg.state = OTG_STATE_B_IDLE;
                        penwell_update_transceiver();
@@ -1788,7 +1869,7 @@ static void penwell_otg_work(struct work_struct *work)
                        }
                        iotg->otg.state = OTG_STATE_A_WAIT_BCON;
                } else if (!hsm->a_bus_drop && (hsm->power_up || hsm->a_bus_req
-                               || hsm->power_up || hsm->adp_change)) {
+                               || hsm->a_srp_det || hsm->adp_change)) {
                        /* power up / adp changes / srp detection should be
                         * cleared at once after handled. */
                        if (hsm->power_up)
@@ -1808,7 +1889,8 @@ static void penwell_otg_work(struct work_struct *work)
 
                        penwell_update_transceiver();
                } else if (hsm->b_sess_end || hsm->a_sess_vld ||
-                                               !hsm->b_sess_vld) {
+                               hsm->a_srp_det || !hsm->b_sess_vld) {
+                       hsm->a_srp_det = 0;
                        dev_dbg(pnw->dev,
                                "reconfig...PHCD bit for PHY low power mode\n");
                        penwell_otg_phy_low_power(1);
@@ -1829,7 +1911,8 @@ static void penwell_otg_work(struct work_struct *work)
 
                        penwell_otg_add_timer(TA_WAIT_VFALL_TMR);
                        iotg->otg.state = OTG_STATE_A_WAIT_VFALL;
-               } else if (hsm->a_wait_vrise_tmout || hsm->id == ID_ACA_A) {
+               } else if (hsm->a_vbus_vld || hsm->a_wait_vrise_tmout
+                                               || hsm->id == ID_ACA_A) {
                        /* Move to A_WAIT_BCON state, a vbus vld */
                        /* Delete current timer and clear flags */
                        penwell_otg_del_timer(TA_WAIT_VRISE_TMR);
@@ -1851,6 +1934,7 @@ static void penwell_otg_work(struct work_struct *work)
                                                        CHRG_CURR_ACA);
                        }
 
+                       hsm->a_bus_req = 1;
                        hsm->b_conn = 0;
                        hsm->hnp_poll_enable = 0;
 
@@ -2159,6 +2243,8 @@ static void penwell_otg_work(struct work_struct *work)
                break;
        case OTG_STATE_A_WAIT_VFALL:
                if (hsm->a_wait_vfall_tmout) {
+                       hsm->a_srp_det = 0;
+
                        /* Move to A_IDLE state, vbus falls */
                        penwell_otg_phy_low_power(1);
 
@@ -2668,6 +2754,7 @@ static int penwell_otg_probe(struct pci_dev *pdev,
        pnw->iotg.ulpi_ops.write = penwell_otg_ulpi_write;
 
        init_timer(&pnw->hsm_timer);
+       init_timer(&pnw->bus_mon_timer);
        init_timer(&pnw->hnp_poll_timer);
        init_completion(&pnw->adp.adp_comp);
 
@@ -2794,9 +2881,9 @@ static void penwell_otg_remove(struct pci_dev *pdev)
 
 static void transceiver_suspend(struct pci_dev *pdev)
 {
+       penwell_otg_phy_low_power(1);
        pci_save_state(pdev);
        pci_set_power_state(pdev, PCI_D3hot);
-       penwell_otg_phy_low_power(1);
 }
 
 static int penwell_otg_suspend(struct pci_dev *pdev, pm_message_t message)
@@ -2821,10 +2908,13 @@ static int penwell_otg_suspend(struct pci_dev *pdev, 
pm_message_t message)
        case OTG_STATE_A_WAIT_VFALL:
                iotg->otg.state = OTG_STATE_A_IDLE;
        case OTG_STATE_A_IDLE:
-       case OTG_STATE_B_IDLE:
        case OTG_STATE_A_VBUS_ERR:
                transceiver_suspend(pdev);
                break;
+       case OTG_STATE_B_IDLE:
+               penwell_otg_mon_bus(BUS_MON_STOP);
+               transceiver_suspend(pdev);
+               break;
        case OTG_STATE_A_WAIT_VRISE:
                penwell_otg_del_timer(TA_WAIT_VRISE_TMR);
                iotg->hsm.a_srp_det = 0;
@@ -2955,6 +3045,14 @@ static int penwell_otg_resume(struct pci_dev *pdev)
                goto error;
        }
 
+       switch (pnw->iotg.otg.state) {
+       case OTG_STATE_B_IDLE:
+               penwell_otg_mon_bus(BUS_MON_START);
+               break;
+       default:
+               break;
+       }
+
        /* enable OTG interrupts */
        penwell_otg_intr(1);
 
diff --git a/include/linux/usb/penwell_otg.h b/include/linux/usb/penwell_otg.h
index f4e8d5b..3361d01 100644
--- a/include/linux/usb/penwell_otg.h
+++ b/include/linux/usb/penwell_otg.h
@@ -239,9 +239,9 @@ enum penwell_otg_timer_type {
 #define TA_BIDL_ADIS           300
 #define TA_WAIT_VFALL          950
 #define TB_ASE0_BRST           300
-#define TB_SE0_SRP             1800
+#define TB_SE0_SRP             1200
 #define TB_SSEND_SRP           1800
-#      define SRP_MON_INVAL    200
+#      define SRP_MON_INVAL    300
 #define TB_SRP_FAIL            5500
 #define TB_BUS_SUSPEND         500
 #define THOS_REQ_POL           1500
@@ -282,6 +282,11 @@ struct otg_bc_cap {
 #define CHRG_CURR_ACA  1500
 };
 
+/* Bus monitor action for b_ssend_srp/b_se0_srp */
+#define BUS_MON_STOP           0
+#define BUS_MON_START          1
+#define BUS_MON_CONTINUE       2
+
 /* define event ids to notify battery driver */
 #define USBCHRG_EVENT_CONNECT  1
 #define USBCHRG_EVENT_DISCONN  2
@@ -302,6 +307,10 @@ struct penwell_otg {
 
        struct timer_list               hsm_timer;
        struct timer_list               hnp_poll_timer;
+       struct timer_list               bus_mon_timer;
+
+       unsigned long                   b_se0_srp_time;
+       unsigned long                   b_ssend_srp_time;
 
        struct mutex                    msic_mutex;
        enum msic_vendor                msic;
-- 
1.6.0.6

_______________________________________________
MeeGo-kernel mailing list
[email protected]
http://lists.meego.com/listinfo/meego-kernel

Reply via email to