static irqreturn_t da8xx_musb_interrupt(int irq, void *hci)
{
struct musb *musb = hci;
void __iomem *reg_base = musb->ctrl_base;
struct usb_otg *otg = musb->xceiv->otg;
unsigned long flags;
irqreturn_t ret = IRQ_NONE;
u32 status;
spin_lock_irqsave(&musb->lock, flags);
/*
* NOTE: DA8XX shadows the Mentor IRQs. Don't manage them through
* the Mentor registers (except for setup), use the TI ones and EOI.
*/
/* Acknowledge and handle non-CPPI interrupts */
status = musb_readl(reg_base, DA8XX_USB_INTR_SRC_MASKED_REG);
if (!status)
goto eoi;
musb_writel(reg_base, DA8XX_USB_INTR_SRC_CLEAR_REG, status);
dev_dbg(musb->controller, "USB IRQ %08x\n", status);
musb->int_rx = (status & DA8XX_INTR_RX_MASK) >> DA8XX_INTR_RX_SHIFT;
musb->int_tx = (status & DA8XX_INTR_TX_MASK) >> DA8XX_INTR_TX_SHIFT;
musb->int_usb = (status & DA8XX_INTR_USB_MASK) >> DA8XX_INTR_USB_SHIFT;
/*
* DRVVBUS IRQs are the only proxy we have (a very poor one!) for
* DA8xx's missing ID change IRQ. We need an ID change IRQ to
* switch appropriately between halves of the OTG state machine.
* Managing DEVCTL.Session per Mentor docs requires that we know its
* value but DEVCTL.BDevice is invalid without DEVCTL.Session set.
* Also, DRVVBUS pulses for SRP (but not at 5 V)...
*/
if (status & (DA8XX_INTR_DRVVBUS << DA8XX_INTR_USB_SHIFT)) {
int drvvbus = musb_readl(reg_base, DA8XX_USB_STAT_REG);
void __iomem *mregs = musb->mregs;
u8 devctl = musb_readb(mregs, MUSB_DEVCTL);
int err;
err = is_host_enabled(musb) && (musb->int_usb &
MUSB_INTR_VBUSERROR);
if (err) {
/*
* The Mentor core doesn't debounce VBUS as needed
* to cope with device connect current spikes. This
* means it's not uncommon for bus-powered devices
* to get VBUS errors during enumeration.
*
* This is a workaround, but newer RTL from Mentor
* seems to allow a better one: "re"-starting sessions
* without waiting for VBUS to stop registering in
* devctl.
*/
musb->int_usb &= ~MUSB_INTR_VBUSERROR;
musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
WARNING("VBUS error workaround (delay coming)\n");
} else if (is_host_enabled(musb) && drvvbus) {
MUSB_HST_MODE(musb);
otg->default_a = 1;
musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
portstate(musb->port1_status |= USB_PORT_STAT_POWER);
del_timer(&otg_workaround);
} else {
musb->is_active = 0;
MUSB_DEV_MODE(musb);
otg->default_a = 0;
musb->xceiv->state = OTG_STATE_B_IDLE;
portstate(musb->port1_status &= ~USB_PORT_STAT_POWER);
}
dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n",
drvvbus ? "on" : "off",
otg_state_string(musb->xceiv->state),
err ? " ERROR" : "",
devctl);
ret = IRQ_HANDLED;
}
if (musb->int_tx || musb->int_rx || musb->int_usb)
ret |= musb_interrupt(musb);
eoi:
/* EOI needs to be written for the IRQ to be re-asserted. */
if (ret == IRQ_HANDLED || status)
musb_writel(reg_base, DA8XX_USB_END_OF_INTR_REG, 0);
/* Poll for ID change */
if (is_otg_enabled(musb) && musb->xceiv->state == OTG_STATE_B_IDLE)
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
spin_unlock_irqrestore(&musb->lock, flags);
return ret;
}
/**
* dwc3_otg_sm_work - workqueue function.
*
* @w: Pointer to the dwc3 otg workqueue
*
* NOTE: After any change in phy->state,
* we must reschdule the state machine.
*/
static void dwc3_otg_sm_work(struct work_struct *w)
{
struct dwc3_otg *dotg = container_of(w, struct dwc3_otg, sm_work.work);
struct usb_phy *phy = dotg->otg.phy;
struct dwc3_charger *charger = dotg->charger;
bool work = 0;
int ret = 0;
unsigned long delay = 0;
pm_runtime_resume(phy->dev);
dev_dbg(phy->dev, "%s state\n", otg_state_string(phy->state));
/* Check OTG state */
switch (phy->state) {
case OTG_STATE_UNDEFINED:
dwc3_otg_init_sm(dotg);
if (!dotg->psy) {
dotg->psy = power_supply_get_by_name("usb");
if (!dotg->psy)
dev_err(phy->dev,
"couldn't get usb power supply\n");
}
/* Switch to A or B-Device according to ID / BSV */
if (!test_bit(ID, &dotg->inputs)) {
dev_dbg(phy->dev, "!id\n");
phy->state = OTG_STATE_A_IDLE;
work = 1;
} else if (test_bit(B_SESS_VLD, &dotg->inputs)) {
dev_dbg(phy->dev, "b_sess_vld\n");
phy->state = OTG_STATE_B_IDLE;
work = 1;
} else {
phy->state = OTG_STATE_B_IDLE;
dev_dbg(phy->dev, "No device, trying to suspend\n");
pm_runtime_put_sync(phy->dev);
}
break;
case OTG_STATE_B_IDLE:
if (!test_bit(ID, &dotg->inputs)) {
dev_dbg(phy->dev, "!id\n");
phy->state = OTG_STATE_A_IDLE;
work = 1;
dotg->charger_retry_count = 0;
if (charger) {
if (charger->chg_type == DWC3_INVALID_CHARGER)
charger->start_detection(dotg->charger,
false);
else
charger->chg_type =
DWC3_INVALID_CHARGER;
}
} else if (test_bit(B_SESS_VLD, &dotg->inputs)) {
dev_dbg(phy->dev, "b_sess_vld\n");
if (charger) {
/* Has charger been detected? If no detect it */
switch (charger->chg_type) {
case DWC3_DCP_CHARGER:
case DWC3_PROPRIETARY_CHARGER:
dev_dbg(phy->dev, "lpm, DCP charger\n");
dwc3_otg_set_power(phy,
DWC3_IDEV_CHG_MAX);
pm_runtime_put_sync(phy->dev);
break;
case DWC3_CDP_CHARGER:
dwc3_otg_set_power(phy,
DWC3_IDEV_CHG_MAX);
dwc3_otg_start_peripheral(&dotg->otg,
1);
phy->state = OTG_STATE_B_PERIPHERAL;
work = 1;
break;
case DWC3_SDP_CHARGER:
dwc3_otg_start_peripheral(&dotg->otg,
1);
phy->state = OTG_STATE_B_PERIPHERAL;
work = 1;
break;
case DWC3_FLOATED_CHARGER:
#ifdef CONFIG_ZTEMT_COMM_CHARGE
dev_dbg(phy->dev, "lpm, FLOATED charger\n");
dwc3_otg_set_power(phy,
DWC3_IDEV_CHG_MAX);
pm_runtime_put_sync(phy->dev);
#else
if (dotg->charger_retry_count <
max_chgr_retry_count)
dotg->charger_retry_count++;
/*
* In case of floating charger, if
//.........这里部分代码省略.........
static void check_state(struct isp1301 *isp, const char *tag)
{
enum usb_otg_state state = OTG_STATE_UNDEFINED;
u8 fsm = omap_readw(OTG_TEST) & 0x0ff;
unsigned extra = 0;
switch (fsm) {
/* default-b */
case 0x0:
state = OTG_STATE_B_IDLE;
break;
case 0x3:
case 0x7:
extra = 1;
case 0x1:
state = OTG_STATE_B_PERIPHERAL;
break;
case 0x11:
state = OTG_STATE_B_SRP_INIT;
break;
/* extra dual-role default-b states */
case 0x12:
case 0x13:
case 0x16:
extra = 1;
case 0x17:
state = OTG_STATE_B_WAIT_ACON;
break;
case 0x34:
state = OTG_STATE_B_HOST;
break;
/* default-a */
case 0x36:
state = OTG_STATE_A_IDLE;
break;
case 0x3c:
state = OTG_STATE_A_WAIT_VFALL;
break;
case 0x7d:
state = OTG_STATE_A_VBUS_ERR;
break;
case 0x9e:
case 0x9f:
extra = 1;
case 0x89:
state = OTG_STATE_A_PERIPHERAL;
break;
case 0xb7:
state = OTG_STATE_A_WAIT_VRISE;
break;
case 0xb8:
state = OTG_STATE_A_WAIT_BCON;
break;
case 0xb9:
state = OTG_STATE_A_HOST;
break;
case 0xba:
state = OTG_STATE_A_SUSPEND;
break;
default:
break;
}
if (isp->otg.state == state && !extra)
return;
pr_debug("otg: %s FSM %s/%02x, %s, %06x\n", tag,
otg_state_string(state), fsm, state_name(isp),
omap_readl(OTG_CTRL));
}
/**
* dwc3_otg_sm_work - workqueue function.
*
* @w: Pointer to the dwc3 otg workqueue
*
* NOTE: After any change in phy->state,
* we must reschdule the state machine.
*/
static void dwc3_otg_sm_work(struct work_struct *w)
{
struct dwc3_otg *dotg = container_of(w, struct dwc3_otg, sm_work.work);
struct usb_phy *phy = dotg->otg.phy;
struct dwc3_charger *charger = dotg->charger;
bool work = 0;
int ret = 0;
unsigned long delay = 0;
pm_runtime_resume(phy->dev);
dev_dbg(phy->dev, "%s state\n", otg_state_string(phy->state));
/* Check OTG state */
switch (phy->state) {
case OTG_STATE_UNDEFINED:
dwc3_otg_init_sm(dotg);
if (!dotg->psy) {
dotg->psy = power_supply_get_by_name("usb");
if (!dotg->psy)
dev_err(phy->dev,
"couldn't get usb power supply\n");
}
/* Switch to A or B-Device according to ID / BSV */
if (!test_bit(ID, &dotg->inputs)) {
dev_dbg(phy->dev, "!id\n");
phy->state = OTG_STATE_A_IDLE;
work = 1;
} else if (test_bit(B_SESS_VLD, &dotg->inputs)) {
dev_dbg(phy->dev, "b_sess_vld\n");
phy->state = OTG_STATE_B_IDLE;
work = 1;
} else {
phy->state = OTG_STATE_B_IDLE;
dev_dbg(phy->dev, "No device, trying to suspend\n");
pm_runtime_put_sync(phy->dev);
}
break;
case OTG_STATE_B_IDLE:
if (!test_bit(ID, &dotg->inputs)) {
dev_dbg(phy->dev, "!id\n");
phy->state = OTG_STATE_A_IDLE;
work = 1;
dotg->charger_retry_count = 0;
if (charger) {
if (charger->chg_type == DWC3_INVALID_CHARGER)
charger->start_detection(dotg->charger,
false);
else
charger->chg_type =
DWC3_INVALID_CHARGER;
}
} else if (test_bit(B_SESS_VLD, &dotg->inputs)) {
dev_dbg(phy->dev, "b_sess_vld\n");
if (charger) {
/* Has charger been detected? If no detect it */
switch (charger->chg_type) {
case DWC3_DCP_CHARGER:
case DWC3_PROPRIETARY_CHARGER:
dev_dbg(phy->dev, "lpm, DCP charger\n");
dwc3_otg_set_power(phy,
DWC3_IDEV_CHG_MAX);
pm_runtime_put_sync(phy->dev);
break;
case DWC3_CDP_CHARGER:
dwc3_otg_set_power(phy,
DWC3_IDEV_CHG_MAX);
dwc3_otg_start_peripheral(&dotg->otg,
1);
phy->state = OTG_STATE_B_PERIPHERAL;
work = 1;
break;
case DWC3_SDP_CHARGER:
dwc3_otg_start_peripheral(&dotg->otg,
1);
phy->state = OTG_STATE_B_PERIPHERAL;
pr_info("DWC3_SDP_CHARGER\n");
work = 1;
break;
case DWC3_FLOATED_CHARGER:
if (dotg->charger_retry_count <
max_chgr_retry_count)
dotg->charger_retry_count++;
/*
* In case of floating charger, if
* retry count equal to max retry count
* notify PMIC about floating charger
* and put Hw in low power mode. Else
* perform charger detection again by
* calling start_detection() with false
//.........这里部分代码省略.........
/**
* dwc3_otg_sm_work - workqueue function.
*
* @w: Pointer to the dwc3 otg workqueue
*
* NOTE: After any change in phy->state,
* we must reschdule the state machine.
*/
static void dwc3_otg_sm_work(struct work_struct *w)
{
struct dwc3_otg *dotg = container_of(w, struct dwc3_otg, sm_work);
struct usb_phy *phy = dotg->otg.phy;
struct dwc3_charger *charger = dotg->charger;
bool work = 0;
dev_dbg(phy->dev, "%s state\n", otg_state_string(phy->state));
/* Check OTG state */
switch (phy->state) {
case OTG_STATE_UNDEFINED:
dwc3_otg_init_sm(dotg);
/* Switch to A or B-Device according to ID / BSV */
if (!test_bit(ID, &dotg->inputs) && phy->otg->host) {
dev_dbg(phy->dev, "!id\n");
phy->state = OTG_STATE_A_IDLE;
work = 1;
} else if (test_bit(B_SESS_VLD, &dotg->inputs)) {
dev_dbg(phy->dev, "b_sess_vld\n");
phy->state = OTG_STATE_B_IDLE;
work = 1;
} else {
phy->state = OTG_STATE_B_IDLE;
/* TODO: Enter low power state */
}
break;
case OTG_STATE_B_IDLE:
if (!test_bit(ID, &dotg->inputs) && phy->otg->host) {
dev_dbg(phy->dev, "!id\n");
phy->state = OTG_STATE_A_IDLE;
work = 1;
if (charger) {
if (charger->chg_type == DWC3_INVALID_CHARGER)
charger->start_detection(dotg->charger,
false);
else
charger->chg_type =
DWC3_INVALID_CHARGER;
}
} else if (test_bit(B_SESS_VLD, &dotg->inputs)) {
dev_dbg(phy->dev, "b_sess_vld\n");
if (charger) {
/* Has charger been detected? If no detect it */
switch (charger->chg_type) {
case DWC3_DCP_CHARGER:
/* TODO: initiate LPM */
break;
case DWC3_CDP_CHARGER:
dwc3_otg_start_peripheral(&dotg->otg,
1);
phy->state = OTG_STATE_B_PERIPHERAL;
work = 1;
break;
case DWC3_SDP_CHARGER:
dwc3_otg_start_peripheral(&dotg->otg,
1);
phy->state = OTG_STATE_B_PERIPHERAL;
work = 1;
break;
default:
dev_dbg(phy->dev, "chg_det started\n");
charger->start_detection(charger, true);
break;
}
} else {
/* no charger registered, start peripheral */
if (dwc3_otg_start_peripheral(&dotg->otg, 1)) {
/*
* Probably set_peripheral not called
* yet. We will re-try as soon as it
* will be called
*/
dev_err(phy->dev,
"unable to start B-device\n");
phy->state = OTG_STATE_UNDEFINED;
return;
}
}
} else {
if (charger) {
if (charger->chg_type == DWC3_INVALID_CHARGER)
charger->start_detection(dotg->charger,
false);
else
charger->chg_type =
DWC3_INVALID_CHARGER;
}
/* TODO: Enter low power state */
}
break;
//.........这里部分代码省略.........
请发表评论