This patch enables runtime pm support for otg fsm mode.
Signed-off-by: Li Jun <b47624@freescale.com>
#define PORTSC_HSP BIT(9)
#define PORTSC_PP BIT(12)
#define PORTSC_PTC (0x0FUL << 16)
+#define PORTSC_WKCN BIT(20)
#define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23))
/* PTS and PTW for non lpm version only */
#define PORTSC_PFSC BIT(24)
#include "ci.h"
#include "ci_hdrc_imx.h"
+#include "otg.h"
+#include "bits.h"
#define CI_HDRC_IMX_IMX28_WRITE_FIX BIT(0)
#define CI_HDRC_IMX_SUPPORT_RUNTIME_PM BIT(1)
}
#ifdef CONFIG_PM
+/* Prepare wakeup by SRP before suspend */
+static void ci_otg_fsm_suspend_for_srp(struct ci_hdrc *ci)
+{
+ if ((ci->transceiver->state == OTG_STATE_A_IDLE) &&
+ !hw_read_otgsc(ci, OTGSC_ID)) {
+ hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP,
+ PORTSC_PP);
+ hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_WKCN,
+ PORTSC_WKCN);
+ }
+}
+
+/* Handle SRP when wakeup by data pulse */
+static void ci_otg_fsm_wakeup_by_srp(struct ci_hdrc *ci)
+{
+ /*
+ * if a_idle wakeup by data pulse,
+ * handle it like normal SRP
+ */
+ if ((ci->transceiver->state == OTG_STATE_A_IDLE) &&
+ (ci->fsm.a_bus_drop == 1) && (ci->fsm.a_bus_req == 0)) {
+ if (!hw_read_otgsc(ci, OTGSC_ID)) {
+ ci->fsm.a_srp_det = 1;
+ ci->fsm.a_bus_drop = 0;
+ disable_irq_nosync(ci->irq);
+ queue_work(ci->wq, &ci->work);
+ } else {
+ ci->fsm.id = 1;
+ }
+ }
+}
+
static int ci_controller_suspend(struct device *dev)
{
struct ci_hdrc *ci = dev_get_drvdata(dev);
if (ci->in_lpm)
return 0;
+ if (ci_otg_is_fsm_mode(ci))
+ ci_otg_fsm_suspend_for_srp(ci);
+
disable_irq(ci->irq);
if (ci->transceiver)
mod_timer(&ci->timer, jiffies + msecs_to_jiffies(2000));
}
+ if (ci_otg_is_fsm_mode(ci))
+ ci_otg_fsm_wakeup_by_srp(ci);
+
return 0;
}
if (!ci)
return 0;
+ seq_printf(s, "Low Power Mode: %d\n", ci->in_lpm);
+ if (ci->in_lpm)
+ return 0;
+
/* ------ Registers ----- */
tmp_reg = hw_read_intr_enable(ci);
seq_printf(s, "USBINTR reg: %08x\n", tmp_reg);
return;
}
+ if (list_empty(active_timers))
+ pm_runtime_get(ci->dev);
+
timer->count = timer->expires;
list_add_tail(&timer->list, active_timers);
struct ci_otg_fsm_timer *tmp_timer, *del_tmp;
struct ci_otg_fsm_timer *timer = ci->fsm_timer->timer_list[t];
struct list_head *active_timers = &ci->fsm_timer->active_timers;
+ int flag = 0;
if (t >= NUM_CI_OTG_FSM_TIMERS)
return;
list_for_each_entry_safe(tmp_timer, del_tmp, active_timers, list)
- if (tmp_timer == timer)
+ if (tmp_timer == timer) {
list_del(&timer->list);
+ flag = 1;
+ }
/* Disable 1ms irq if there is no any active timer */
- if (list_empty(active_timers))
+ if (list_empty(active_timers) && (flag == 1)) {
hw_write_otgsc(ci, OTGSC_1MSIE, 0);
+ pm_runtime_put(ci->dev);
+ }
}
/*
}
/* disable 1ms irq if there is no any timer active */
- if ((expired == 1) && list_empty(active_timers))
+ if ((expired == 1) && list_empty(active_timers)) {
hw_write_otgsc(ci, OTGSC_1MSIE, 0);
+ pm_runtime_put(ci->dev);
+ }
return expired;
}
return 0;
}
+ pm_runtime_get_sync(ci->dev);
if (otg_statemachine(&ci->fsm)) {
if (ci->transceiver->state == OTG_STATE_A_IDLE) {
/*
}
}
}
+ pm_runtime_put_sync(ci->dev);
return 0;
}
return false;
}
+#ifdef CONFIG_USB_OTG_FSM
+static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
+{
+}
+#else
static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect)
{
void __iomem *base = mxs_phy->phy.io_priv;
__mxs_phy_disconnect_line(mxs_phy, false);
}
+#endif
static void mxs_phy_enable_ldo_in_suspend(struct mxs_phy *mxs_phy, bool on)
{