Add HNP polling support in penwell_otg transceiver driver.

Signed-off-by: Hao Wu <[email protected]>
---
 drivers/usb/otg/penwell_otg.c     |  175 +++++++++++++++++++++++++++++++++----
 include/linux/usb/intel_mid_otg.h |    7 ++
 2 files changed, 165 insertions(+), 17 deletions(-)

diff --git a/drivers/usb/otg/penwell_otg.c b/drivers/usb/otg/penwell_otg.c
index 0b29710..5eb4948 100644
--- a/drivers/usb/otg/penwell_otg.c
+++ b/drivers/usb/otg/penwell_otg.c
@@ -600,6 +600,96 @@ static void penwell_otg_mon_bus_fn(unsigned long indicator)
        penwell_otg_mon_bus(BUS_MON_CONTINUE);
 }
 
+/* HNP polling function */
+/* The timeout callback function which polls the host request flag for HNP */
+static void penwell_otg_hnp_poll_fn(unsigned long indicator)
+{
+       struct penwell_otg      *pnw = the_transceiver;
+
+       queue_work(pnw->qwork, &pnw->hnp_poll_work);
+}
+
+/* Start HNP polling */
+/* Call this function with iotg->hnp_poll_lock held */
+static int penwell_otg_add_hnp_poll_timer(struct intel_mid_otg_xceiv *iotg,
+                                       unsigned long delay)
+{
+       struct penwell_otg      *pnw = the_transceiver;
+       unsigned long           j = jiffies;
+
+       pnw->hnp_poll_timer.data = 1;
+       pnw->hnp_poll_timer.function = penwell_otg_hnp_poll_fn;
+       pnw->hnp_poll_timer.expires = j + msecs_to_jiffies(delay);
+
+       add_timer(&pnw->hnp_poll_timer);
+
+       return 0;
+}
+
+static int penwell_otg_start_hnp_poll(struct intel_mid_otg_xceiv *iotg)
+{
+       struct penwell_otg      *pnw = the_transceiver;
+       unsigned long           flags;
+
+       spin_lock_irqsave(&pnw->iotg.hnp_poll_lock, flags);
+
+       if (pnw->iotg.hsm.hnp_poll_enable) {
+               dev_dbg(pnw->dev, "HNP polling is already enabled\n");
+               spin_unlock_irqrestore(&pnw->iotg.hnp_poll_lock, flags);
+               return 0;
+       }
+
+       /* mark HNP polling enabled and start HNP polling in 50ms */
+       pnw->iotg.hsm.hnp_poll_enable = 1;
+       penwell_otg_add_hnp_poll_timer(&pnw->iotg, 50);
+
+       spin_unlock_irqrestore(&pnw->iotg.hnp_poll_lock, flags);
+
+       return 0;
+}
+
+static int penwell_otg_continue_hnp_poll(struct intel_mid_otg_xceiv *iotg)
+{
+       struct penwell_otg      *pnw = the_transceiver;
+       unsigned long           flags;
+
+       spin_lock_irqsave(&pnw->iotg.hnp_poll_lock, flags);
+
+       if (!pnw->iotg.hsm.hnp_poll_enable) {
+               dev_dbg(pnw->dev, "HNP polling is disabled, stop polling\n");
+               spin_unlock_irqrestore(&pnw->iotg.hnp_poll_lock, flags);
+               return 0;
+       }
+
+       penwell_otg_add_hnp_poll_timer(&pnw->iotg, THOS_REQ_POL);
+
+       spin_unlock_irqrestore(&pnw->iotg.hnp_poll_lock, flags);
+
+       return 0;
+}
+
+/* Stop HNP polling */
+static int penwell_otg_stop_hnp_poll(struct intel_mid_otg_xceiv *iotg)
+{
+       struct penwell_otg      *pnw = the_transceiver;
+       unsigned long           flags;
+
+       spin_lock_irqsave(&pnw->iotg.hnp_poll_lock, flags);
+
+       if (!pnw->iotg.hsm.hnp_poll_enable) {
+               dev_dbg(pnw->dev, "HNP polling is already disabled\n");
+               spin_unlock_irqrestore(&pnw->iotg.hnp_poll_lock, flags);
+               return 0;
+       }
+
+       pnw->iotg.hsm.hnp_poll_enable = 0;
+       del_timer_sync(&pnw->hnp_poll_timer);
+
+       spin_unlock_irqrestore(&pnw->iotg.hnp_poll_lock, flags);
+
+       return 0;
+}
+
 /* Start SRP function */
 static int penwell_otg_start_srp(struct otg_transceiver *otg)
 {
@@ -623,14 +713,6 @@ static int penwell_otg_start_srp(struct otg_transceiver 
*otg)
        return 0;
 }
 
-/* The timeout callback function to poll the host request flag */
-static void penwell_otg_hnp_poll_fn(unsigned long indicator)
-{
-       struct penwell_otg *pnw = the_transceiver;
-
-       queue_work(pnw->qwork, &pnw->hnp_poll_work);
-}
-
 /* stop SOF via bus_suspend */
 static void penwell_otg_loc_sof(int on)
 {
@@ -1365,7 +1447,6 @@ static void penwell_otg_hnp_poll_work(struct work_struct 
*work)
        struct penwell_otg              *pnw = the_transceiver;
        struct intel_mid_otg_xceiv      *iotg = &pnw->iotg;
        struct usb_device               *udev;
-       unsigned long                   j = jiffies;
        int                             err = 0;
        u8                              data;
 
@@ -1397,7 +1478,9 @@ static void penwell_otg_hnp_poll_work(struct work_struct 
*work)
        }
 
        if (data & HOST_REQUEST_FLAG) {
-               /* set a_bus_req = 0 */
+               /* start HNP sequence to switch role */
+               dev_dbg(pnw->dev, "host_request_flag = 1\n");
+
                if (iotg->hsm.id == ID_B) {
                        dev_dbg(pnw->dev,
                                "Device B host - start HNP - b_bus_req = 0\n");
@@ -1409,12 +1492,8 @@ static void penwell_otg_hnp_poll_work(struct work_struct 
*work)
                }
                penwell_update_transceiver();
        } else {
-               pnw->hnp_poll_timer.data = 1;
-               pnw->hnp_poll_timer.function = penwell_otg_hnp_poll_fn;
-               pnw->hnp_poll_timer.expires = j + THOS_REQ_POL * HZ / 1000;
-               add_timer(&pnw->hnp_poll_timer);
-
-               dev_dbg(pnw->dev, "HNP Polling - continue\n");
+               dev_dbg(pnw->dev, "host_request_flag = 0\n");
+               penwell_otg_continue_hnp_poll(&pnw->iotg);
        }
 }
 
@@ -1522,6 +1601,10 @@ static void penwell_otg_work(struct work_struct *work)
                                penwell_otg_update_chrg_cap(CHRG_CDP,
                                                        CHRG_CURR_CDP);
 
+                               /* Clear HNP polling flag */
+                               if (iotg->otg.gadget)
+                                       iotg->otg.gadget->host_request_flag = 0;
+
                                if (iotg->start_peripheral) {
                                        iotg->start_peripheral(iotg);
                                } else {
@@ -1536,6 +1619,10 @@ static void penwell_otg_work(struct work_struct *work)
                                penwell_otg_update_chrg_cap(CHRG_SDP,
                                                        pnw->charging_cap.mA);
 
+                               /* Clear HNP polling flag */
+                               if (iotg->otg.gadget)
+                                       iotg->otg.gadget->host_request_flag = 0;
+
                                if (iotg->start_peripheral) {
                                        iotg->start_peripheral(iotg);
                                } else {
@@ -1764,6 +1851,9 @@ static void penwell_otg_work(struct work_struct *work)
                        iotg->otg.default_a = 1;
                        hsm->a_srp_det = 0;
 
+                       /* Stop HNP polling */
+                       iotg->stop_hnp_poll(iotg);
+
                        if (iotg->stop_host)
                                iotg->stop_host(iotg);
                        else
@@ -1793,6 +1883,9 @@ static void penwell_otg_work(struct work_struct *work)
                        hsm->b_hnp_enable = 0;
                        hsm->b_bus_req = 0;
 
+                       /* Stop HNP polling */
+                       iotg->stop_hnp_poll(iotg);
+
                        if (iotg->stop_host)
                                iotg->stop_host(iotg);
                        else
@@ -1808,6 +1901,9 @@ static void penwell_otg_work(struct work_struct *work)
                                        || hsm->test_device) {
                        hsm->b_bus_req = 0;
 
+                       /* Stop HNP polling */
+                       iotg->stop_hnp_poll(iotg);
+
                        if (iotg->stop_host)
                                iotg->stop_host(iotg);
                        else
@@ -1816,6 +1912,10 @@ static void penwell_otg_work(struct work_struct *work)
 
                        hsm->a_bus_suspend = 0;
 
+                       /* Clear HNP polling flag */
+                       if (iotg->otg.gadget)
+                               iotg->otg.gadget->host_request_flag = 0;
+
                        if (iotg->start_peripheral)
                                iotg->start_peripheral(iotg);
                        else
@@ -1994,6 +2094,9 @@ static void penwell_otg_work(struct work_struct *work)
                        /* Delete current timer and disable host function */
                        penwell_otg_del_timer(TA_WAIT_BCON_TMR);
 
+                       /* Start HNP polling */
+                       iotg->start_hnp_poll(iotg);
+
                        iotg->otg.state = OTG_STATE_A_HOST;
 
                        if (!hsm->a_bus_req)
@@ -2029,6 +2132,9 @@ static void penwell_otg_work(struct work_struct *work)
                } else if (!hsm->a_vbus_vld) {
                        /* Move to A_VBUS_ERR state */
 
+                       /* Stop HNP polling */
+                       iotg->stop_hnp_poll(iotg);
+
                        if (iotg->stop_host)
                                iotg->stop_host(iotg);
                        else
@@ -2044,6 +2150,9 @@ static void penwell_otg_work(struct work_struct *work)
                } else if (!hsm->a_bus_req) {
                        /* Move to A_SUSPEND state */
 
+                       /* Stop HNP polling */
+                       iotg->stop_hnp_poll(iotg);
+
                        penwell_otg_loc_sof(0);
 
                        if (iotg->otg.host->b_hnp_enable) {
@@ -2053,7 +2162,9 @@ static void penwell_otg_work(struct work_struct *work)
 
                        iotg->otg.state = OTG_STATE_A_SUSPEND;
                } else if (!hsm->b_conn) {
-                       hsm->hnp_poll_enable = 0;
+                       /* Stop HNP polling */
+                       iotg->stop_hnp_poll(iotg);
+
                        /* add kernel timer */
                        iotg->otg.state = OTG_STATE_A_WAIT_BCON;
                } else if (hsm->id == ID_ACA_A) {
@@ -2079,6 +2190,9 @@ static void penwell_otg_work(struct work_struct *work)
                                penwell_otg_update_chrg_cap(CHRG_ACA,
                                                        CHRG_CURR_ACA);
 
+                       /* Stop HNP polling */
+                       iotg->stop_hnp_poll(iotg);
+
                        if (iotg->stop_host)
                                iotg->stop_host(iotg);
                        else
@@ -2123,6 +2237,9 @@ static void penwell_otg_work(struct work_struct *work)
                        /* Delete current timer and clear flags */
                        penwell_otg_del_timer(TA_AIDL_BDIS_TMR);
 
+                       /* Stop HNP polling */
+                       iotg->stop_hnp_poll(iotg);
+
                        if (iotg->stop_host)
                                iotg->stop_host(iotg);
                        else
@@ -2131,6 +2248,10 @@ static void penwell_otg_work(struct work_struct *work)
 
                        hsm->b_bus_suspend = 0;
 
+                       /* Clear HNP polling flag */
+                       if (iotg->otg.gadget)
+                               iotg->otg.gadget->host_request_flag = 0;
+
                        if (iotg->start_peripheral)
                                iotg->start_peripheral(iotg);
                        else
@@ -2145,6 +2266,9 @@ static void penwell_otg_work(struct work_struct *work)
                        /* Delete current timer and clear flags */
                        penwell_otg_del_timer(TA_AIDL_BDIS_TMR);
 
+                       /* Start HNP polling */
+                       iotg->start_hnp_poll(iotg);
+
                        penwell_otg_loc_sof(1);
                        iotg->otg.state = OTG_STATE_A_HOST;
                } else if (hsm->id == ID_ACA_A) {
@@ -2564,10 +2688,17 @@ set_b_bus_req(struct device *dev, struct 
device_attribute *attr,
        if (buf[0] == '0') {
                iotg->hsm.b_bus_req = 0;
                dev_dbg(pnw->dev, "b_bus_req = 0\n");
+
+               if (iotg->otg.gadget)
+                       iotg->otg.gadget->host_request_flag = 0;
        } else if (buf[0] == '1') {
                iotg->hsm.b_bus_req = 1;
                dev_dbg(pnw->dev, "b_bus_req = 1\n");
+
                if (iotg->otg.state == OTG_STATE_B_PERIPHERAL) {
+                       if (iotg->otg.gadget)
+                               iotg->otg.gadget->host_request_flag = 1;
+
                        dev_warn(pnw->dev, "Role switch will be "
                                "performed soon, if connected OTG device "
                                "supports role switch request.\n");
@@ -2743,6 +2874,8 @@ static int penwell_otg_probe(struct pci_dev *pdev,
        pnw->iotg.otg.start_srp = penwell_otg_start_srp;
        pnw->iotg.set_adp_probe = NULL;
        pnw->iotg.set_adp_sense = NULL;
+       pnw->iotg.start_hnp_poll = penwell_otg_start_hnp_poll;
+       pnw->iotg.stop_hnp_poll = penwell_otg_stop_hnp_poll;
        pnw->iotg.otg.state = OTG_STATE_UNDEFINED;
        if (otg_set_transceiver(&pnw->iotg.otg)) {
                dev_dbg(pnw->dev, "can't set transceiver\n");
@@ -2753,6 +2886,8 @@ 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;
 
+       spin_lock_init(&pnw->iotg.hnp_poll_lock);
+
        init_timer(&pnw->hsm_timer);
        init_timer(&pnw->bus_mon_timer);
        init_timer(&pnw->hnp_poll_timer);
@@ -2939,6 +3074,9 @@ static int penwell_otg_suspend(struct pci_dev *pdev, 
pm_message_t message)
                transceiver_suspend(pdev);
                break;
        case OTG_STATE_A_HOST:
+               /* Stop HNP polling */
+               iotg->stop_hnp_poll(iotg);
+
                if (pnw->iotg.stop_host)
                        pnw->iotg.stop_host(&pnw->iotg);
                else
@@ -2981,6 +3119,9 @@ static int penwell_otg_suspend(struct pci_dev *pdev, 
pm_message_t message)
                transceiver_suspend(pdev);
                break;
        case OTG_STATE_B_HOST:
+               /* Stop HNP polling */
+               iotg->stop_hnp_poll(iotg);
+
                if (pnw->iotg.stop_host)
                        pnw->iotg.stop_host(&pnw->iotg);
                else
diff --git a/include/linux/usb/intel_mid_otg.h 
b/include/linux/usb/intel_mid_otg.h
index 62db841..ca2c4a6 100644
--- a/include/linux/usb/intel_mid_otg.h
+++ b/include/linux/usb/intel_mid_otg.h
@@ -120,6 +120,9 @@ struct intel_mid_otg_xceiv {
        /* atomic notifier for interrupt context */
        struct atomic_notifier_head     iotg_notifier;
 
+       /* hnp poll lock */
+       spinlock_t                      hnp_poll_lock;
+
        /* start/stop USB Host function */
        int     (*start_host)(struct intel_mid_otg_xceiv *iotg);
        int     (*stop_host)(struct intel_mid_otg_xceiv *iotg);
@@ -134,6 +137,10 @@ struct intel_mid_otg_xceiv {
        int     (*set_adp_sense)(struct intel_mid_otg_xceiv *iotg,
                                        bool enabled);
 
+       /* start/stop HNP Polling function */
+       int     (*start_hnp_poll)(struct intel_mid_otg_xceiv *iotg);
+       int     (*stop_hnp_poll)(struct intel_mid_otg_xceiv *iotg);
+
 #ifdef CONFIG_PM
        /* suspend/resume USB host function */
        int     (*suspend_host)(struct intel_mid_otg_xceiv *iotg,
-- 
1.6.0.6

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

Reply via email to