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