On Sun, Mar 30, 2014 at 08:20:07PM +0800, Li Jun wrote: > Add OTG HNP and SRP operation functions implementation: > - charge vbus > - drive vbus > - connection signaling > - drive sof > - start data pulse > - add fsm timer > - delete fsm timer > - start host > - start gadget > > Signed-off-by: Li Jun <b47624@xxxxxxxxxxxxx> > --- > drivers/usb/chipidea/bits.h | 9 ++ > drivers/usb/chipidea/ci.h | 1 + > drivers/usb/chipidea/otg_fsm.c | 191 ++++++++++++++++++++++++++++++++++++++++ > drivers/usb/chipidea/otg_fsm.h | 25 ++++++ > 4 files changed, 226 insertions(+) > > diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h > index 83d06c1..44882c8 100644 > --- a/drivers/usb/chipidea/bits.h > +++ b/drivers/usb/chipidea/bits.h > @@ -44,9 +44,14 @@ > #define DEVICEADDR_USBADR (0x7FUL << 25) > > /* PORTSC */ > +#define PORTSC_CCS BIT(0) > +#define PORTSC_CSC BIT(1) > +#define PORTSC_PEC BIT(3) > +#define PORTSC_OCC BIT(5) > #define PORTSC_FPR BIT(6) > #define PORTSC_SUSP BIT(7) > #define PORTSC_HSP BIT(9) > +#define PORTSC_PP BIT(12) > #define PORTSC_PTC (0x0FUL << 16) > #define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23)) > /* PTS and PTW for non lpm version only */ > @@ -56,6 +61,9 @@ > #define PORTSC_PTW BIT(28) > #define PORTSC_STS BIT(29) > > +#define PORTSC_W1C_BITS \ > + (PORTSC_CSC | PORTSC_PEC | PORTSC_OCC) > + > /* DEVLC */ > #define DEVLC_PFSC BIT(23) > #define DEVLC_PSPD (0x03UL << 25) > @@ -72,6 +80,7 @@ > > /* OTGSC */ > #define OTGSC_IDPU BIT(5) > +#define OTGSC_HADP BIT(6) > #define OTGSC_ID BIT(8) > #define OTGSC_AVV BIT(9) > #define OTGSC_ASV BIT(10) > diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h > index 0bd16f6..344e435 100644 > --- a/drivers/usb/chipidea/ci.h > +++ b/drivers/usb/chipidea/ci.h > @@ -176,6 +176,7 @@ struct ci_hdrc { > enum ci_role role; > bool is_otg; > struct otg_fsm fsm; > + struct ci_otg_fsm_timer_list *fsm_timer; > struct work_struct work; > struct workqueue_struct *wq; > > diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c > index 35748a9..2fc395c 100644 > --- a/drivers/usb/chipidea/otg_fsm.c > +++ b/drivers/usb/chipidea/otg_fsm.c > @@ -18,12 +18,202 @@ > #include <linux/usb/otg.h> > #include <linux/usb/gadget.h> > #include <linux/usb/chipidea.h> > +#include <linux/regulator/consumer.h> > > #include "ci.h" > #include "bits.h" > #include "otg.h" > #include "otg_fsm.h" > > +/* > + * Add timer to active timer list > + */ > +static void ci_otg_add_timer(struct ci_hdrc *ci, enum ci_otg_fsm_timer_index t) > +{ > + struct ci_otg_fsm_timer *tmp_timer; > + struct ci_otg_fsm_timer *timer = ci->fsm_timer->timer_list[t]; > + struct list_head *active_timers = &ci->fsm_timer->active_timers; > + > + if (t >= NUM_CI_OTG_FSM_TIMERS) > + return; > + > + /* > + * Check if the timer is already in the active list, > + * if so update timer count > + */ > + list_for_each_entry(tmp_timer, active_timers, list) > + if (tmp_timer == timer) { > + timer->count = timer->expires; > + return; > + } > + > + timer->count = timer->expires; > + list_add_tail(&timer->list, active_timers); > + > + /* Enable 1ms irq */ > + if (!(hw_read_otgsc(ci, OTGSC_1MSIE))) > + hw_write_otgsc(ci, OTGSC_1MSIE, OTGSC_1MSIE); > +} > + > +/* > + * Remove timer from active timer list > + */ > +static void ci_otg_del_timer(struct ci_hdrc *ci, enum ci_otg_fsm_timer_index t) > +{ > + 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; > + > + if (t >= NUM_CI_OTG_FSM_TIMERS) > + return; > + > + list_for_each_entry_safe(tmp_timer, del_tmp, active_timers, list) > + if (tmp_timer == timer) > + list_del(&timer->list); > + > + /* Disable 1ms irq if there is no any active timer */ > + if (list_empty(active_timers)) > + hw_write_otgsc(ci, OTGSC_1MSIE, 0); > +} > + > +/* -------------------------------------------------------------*/ > +/* Operations that will be called from OTG Finite State Machine */ > +/* -------------------------------------------------------------*/ > +static void ci_otg_fsm_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer t) > +{ > + struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm); > + > + if (t < NUM_OTG_FSM_TIMERS) > + ci_otg_add_timer(ci, t); > + return; > +} > + > +static void ci_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t) > +{ > + struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm); > + > + if (t < NUM_OTG_FSM_TIMERS) > + ci_otg_del_timer(ci, t); > + return; > +} > + > +/* > + * A-device drive vbus: turn on vbus regulator and enable port power > + * Data pulse irq should be disabled while vbus is on. > + */ > +static void ci_otg_drv_vbus(struct otg_fsm *fsm, int on) > +{ > + int ret; > + struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm); > + > + if (on) { > + /* Enable power power */ > + hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP, > + PORTSC_PP); > + if (ci->platdata->reg_vbus) { > + ret = regulator_enable(ci->platdata->reg_vbus); > + if (ret) { > + dev_err(ci->dev, > + "Failed to enable vbus regulator, ret=%d\n", > + ret); > + return; > + } > + } > + /* Disable data pulse irq */ > + hw_write_otgsc(ci, OTGSC_DPIE, 0); > + } else { > + if (ci->platdata->reg_vbus) > + regulator_disable(ci->platdata->reg_vbus); > + /* Disable port power */ > + hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP, 0); > + } > +} > + > +/* > + * Control data line by Run Stop bit. > + */ > +static void ci_otg_loc_conn(struct otg_fsm *fsm, int on) > +{ > + struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm); > + > + if (on) > + hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); > + else > + hw_write(ci, OP_USBCMD, USBCMD_RS, 0); > +} > + > +/* > + * Generate SOF by host. > + * This is controlled through suspend/resume the port. > + * In host mode, controller will automatically send SOF. > + * Suspend will block the data on the port. > + */ > +static void ci_otg_loc_sof(struct otg_fsm *fsm, int on) > +{ > + struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm); > + > + if (on) > + hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_FPR, > + PORTSC_FPR); > + else > + hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_SUSP, > + PORTSC_SUSP); > +} > + > +/* > + * Start SRP pulsing by data-line pulsing, > + * no v-bus pulsing followed > + */ > +static void ci_otg_start_pulse(struct otg_fsm *fsm) > +{ > + struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm); > + > + /* Hardware Assistant Data pulse */ > + hw_write_otgsc(ci, OTGSC_HADP, OTGSC_HADP); > + > + ci_otg_add_timer(ci, B_DATA_PLS); > +} > + > +static int ci_otg_start_host(struct otg_fsm *fsm, int on) > +{ > + struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm); > + > + mutex_unlock(&fsm->lock); > + if (on) { > + ci_role_stop(ci); > + ci_role_start(ci, CI_ROLE_HOST); > + } else { > + ci_role_stop(ci); > + ci_role_start(ci, CI_ROLE_GADGET); > + } > + mutex_lock(&fsm->lock); > + return 0; > +} > + > +static int ci_otg_start_gadget(struct otg_fsm *fsm, int on) > +{ > + struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm); > + > + mutex_unlock(&fsm->lock); > + if (on) > + usb_gadget_vbus_connect(&ci->gadget); > + else > + usb_gadget_vbus_disconnect(&ci->gadget); extra TAB? > + mutex_lock(&fsm->lock); > + return 0; > +} > + > +static struct otg_fsm_ops ci_otg_ops = { > + .drv_vbus = ci_otg_drv_vbus, > + .loc_conn = ci_otg_loc_conn, > + .loc_sof = ci_otg_loc_sof, > + .start_pulse = ci_otg_start_pulse, > + .add_timer = ci_otg_fsm_add_timer, > + .del_timer = ci_otg_fsm_del_timer, > + .start_host = ci_otg_start_host, > + .start_gadget = ci_otg_start_gadget, > +}; > + > int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci) > { > if (!ci_otg_is_fsm_mode(ci)) > @@ -43,6 +233,7 @@ int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci) > ci->fsm.otg->phy = ci->transceiver; > ci->fsm.otg->gadget = &ci->gadget; > ci->transceiver->state = OTG_STATE_UNDEFINED; > + ci->fsm.ops = &ci_otg_ops; > > mutex_init(&ci->fsm.lock); > > diff --git a/drivers/usb/chipidea/otg_fsm.h b/drivers/usb/chipidea/otg_fsm.h > index bf20a85..4d0dfe6 100644 > --- a/drivers/usb/chipidea/otg_fsm.h > +++ b/drivers/usb/chipidea/otg_fsm.h > @@ -13,6 +13,31 @@ > > #include <linux/usb/otg-fsm.h> > > +enum ci_otg_fsm_timer_index { > + /* > + * CI specific timers, start from the end > + * of standard and auxiliary OTG timers > + */ > + B_DATA_PLS = NUM_OTG_FSM_TIMERS, > + B_SSEND_SRP, > + B_SESS_VLD, > + > + NUM_CI_OTG_FSM_TIMERS, > +}; > + > +struct ci_otg_fsm_timer { > + unsigned long expires; /* Number of count increase to timeout */ > + unsigned long count; /* Tick counter */ > + void (*function)(void *, unsigned long); /* Timeout function */ > + unsigned long data; /* Data passed to function */ > + struct list_head list; > +}; > + > +struct ci_otg_fsm_timer_list { > + struct ci_otg_fsm_timer *timer_list[NUM_CI_OTG_FSM_TIMERS]; > + struct list_head active_timers; > +}; > + > #ifdef CONFIG_USB_OTG_FSM > > int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci); > -- > 1.7.9.5 > > -- Best Regards, Peter Chen -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html