/* * Inventra (Multipoint) Dual-Role Controller Driver for Linux. * * This consists of a Host Controller Driver (HCD) and a peripheral * controller driver implementing the "Gadget" API; OTG support is * in the works. These are normal Linux-USB controller drivers which * use IRQs and have no dedicated thread. * * This version of the driver has only been used with products from * Texas Instruments. Those products integrate the Inventra logic * with other DMA, IRQ, and bus modules, as well as other logic that * needs to be reflected in this driver. * * * NOTE: the original Mentor code here was pretty much a collection * of mechanisms that don't seem to have been fully integrated/working * for *any* Linux kernel version. This version aims at Linux 2.6.now, * Key open issues include: * * - Lack of host-side transaction scheduling, for all transfer types. * The hardware doesn't do it; instead, software must. * * This is not an issue for OTG devices that don't support external * hubs, but for more "normal" USB hosts it's a user issue that the * "multipoint" support doesn't scale in the expected ways. That * includes DaVinci EVM in a common non-OTG mode. * * * Control and bulk use dedicated endpoints, and there's as * yet no mechanism to either (a) reclaim the hardware when * peripherals are NAKing, which gets complicated with bulk * endpoints, or (b) use more than a single bulk endpoint in * each direction. * * RESULT: one device may be perceived as blocking another one. * * * Interrupt and isochronous will dynamically allocate endpoint * hardware, but (a) there's no record keeping for bandwidth; * (b) in the common case that few endpoints are available, there * is no mechanism to reuse endpoints to talk to multiple devices. * * RESULT: At one extreme, bandwidth can be overcommitted in * some hardware configurations, no faults will be reported. * At the other extreme, the bandwidth capabilities which do * exist tend to be severely undercommitted. You can't yet hook * up both a keyboard and a mouse to an external USB hub.
*/
/* * This gets many kinds of configuration information: * - Kconfig for everything user-configurable * - platform_device for addressing, irq, and platform_data * - platform_data is mostly for board-specific information * (plus recentrly, SOC or family details) * * Most of the conditional compilation will (someday) vanish.
*/
staticint musb_ulpi_read(struct usb_phy *phy, u32 reg)
{ void __iomem *addr = phy->io_priv; int i = 0;
u8 r;
u8 power; int ret;
pm_runtime_get_sync(phy->io_dev);
/* Make sure the transceiver is not in low power mode */
power = musb_readb(addr, MUSB_POWER);
power &= ~MUSB_POWER_SUSPENDM;
musb_writeb(addr, MUSB_POWER, power);
/* REVISIT: musbhdrc_ulpi_an.pdf recommends setting the * ULPICarKitControlDisableUTMI after clearing POWER_SUSPENDM.
*/
while (!(musb_readb(addr, MUSB_ULPI_REG_CONTROL)
& MUSB_ULPI_REG_CMPLT)) {
i++; if (i == 10000) {
ret = -ETIMEDOUT; goto out;
}
}
r = musb_readb(addr, MUSB_ULPI_REG_CONTROL);
r &= ~MUSB_ULPI_REG_CMPLT;
musb_writeb(addr, MUSB_ULPI_REG_CONTROL, r);
ret = musb_readb(addr, MUSB_ULPI_REG_DATA);
out:
pm_runtime_put(phy->io_dev);
return ret;
}
staticint musb_ulpi_write(struct usb_phy *phy, u32 val, u32 reg)
{ void __iomem *addr = phy->io_priv; int i = 0;
u8 r = 0;
u8 power; int ret = 0;
pm_runtime_get_sync(phy->io_dev);
/* Make sure the transceiver is not in low power mode */
power = musb_readb(addr, MUSB_POWER);
power &= ~MUSB_POWER_SUSPENDM;
musb_writeb(addr, MUSB_POWER, power);
/** * musb_set_host - set and initialize host mode * @musb: musb controller driver data * * At least some musb revisions need to enable devctl session bit in * peripheral mode to switch to host mode. Initializes things to host * mode and sets A_IDLE. SoC glue needs to advance state further * based on phy provided VBUS state. * * Note that the SoC glue code may need to wait for musb to settle * on enable before calling this to avoid babble.
*/ int musb_set_host(struct musb *musb)
{ int error = 0;
u8 devctl;
if (!musb) return -EINVAL;
devctl = musb_read_devctl(musb); if (!(devctl & MUSB_DEVCTL_BDEVICE)) {
trace_musb_state(musb, devctl, "Already in host mode"); goto init_data;
}
/** * musb_set_peripheral - set and initialize peripheral mode * @musb: musb controller driver data * * Clears devctl session bit and initializes things for peripheral * mode and sets B_IDLE. SoC glue needs to advance state further * based on phy provided VBUS state.
*/ int musb_set_peripheral(struct musb *musb)
{ int error = 0;
u8 devctl;
if (!musb) return -EINVAL;
devctl = musb_read_devctl(musb); if (devctl & MUSB_DEVCTL_BDEVICE) {
trace_musb_state(musb, devctl, "Already in peripheral mode"); goto init_data;
}
spin_lock_irqsave(&musb->lock, flags); switch (musb_get_state(musb)) { case OTG_STATE_B_WAIT_ACON:
musb_dbg(musb, "HNP: b_wait_acon timeout; back to b_peripheral");
musb_g_disconnect(musb);
musb_set_state(musb, OTG_STATE_B_PERIPHERAL);
musb->is_active = 0; break; case OTG_STATE_A_SUSPEND: case OTG_STATE_A_WAIT_BCON:
musb_dbg(musb, "HNP: %s timeout",
musb_otg_state_string(musb));
musb_platform_set_vbus(musb, 0);
musb_set_state(musb, OTG_STATE_A_WAIT_VFALL); break; default:
musb_dbg(musb, "HNP: Unhandled mode %s",
musb_otg_state_string(musb));
}
spin_unlock_irqrestore(&musb->lock, flags);
}
/* * Stops the HNP transition. Caller must take care of locking.
*/ void musb_hnp_stop(struct musb *musb)
{ struct usb_hcd *hcd = musb->hcd; void __iomem *mbase = musb->mregs;
u8 reg;
musb_dbg(musb, "HNP: stop from %s", musb_otg_state_string(musb));
switch (musb_get_state(musb)) { case OTG_STATE_A_PERIPHERAL:
musb_g_disconnect(musb);
musb_dbg(musb, "HNP: back to %s", musb_otg_state_string(musb)); break; case OTG_STATE_B_HOST:
musb_dbg(musb, "HNP: Disabling HR"); if (hcd)
hcd->self.is_b_host = 0;
musb_set_state(musb, OTG_STATE_B_PERIPHERAL);
MUSB_DEV_MODE(musb);
reg = musb_readb(mbase, MUSB_POWER);
reg |= MUSB_POWER_SUSPENDM;
musb_writeb(mbase, MUSB_POWER, reg); /* REVISIT: Start SESSION_REQUEST here? */ break; default:
musb_dbg(musb, "HNP: Stopping in unknown state %s",
musb_otg_state_string(musb));
}
/* * When returning to A state after HNP, avoid hub_port_rebounce(), * which cause occasional OPT A "Did not receive reset after connect" * errors.
*/
musb->port1_status &= ~(USB_PORT_STAT_C_CONNECTION << 16);
}
if (devctl & MUSB_DEVCTL_HM) { switch (musb_get_state(musb)) { case OTG_STATE_A_SUSPEND: /* remote wakeup? */
musb->port1_status |=
(USB_PORT_STAT_C_SUSPEND << 16)
| MUSB_PORT_STAT_RESUME;
musb->rh_timer = jiffies
+ msecs_to_jiffies(USB_RESUME_TIMEOUT);
musb_set_state(musb, OTG_STATE_A_HOST);
musb->is_active = 1;
musb_host_resume_root_hub(musb);
schedule_delayed_work(&musb->finish_resume_work,
msecs_to_jiffies(USB_RESUME_TIMEOUT)); break; case OTG_STATE_B_WAIT_ACON:
musb_set_state(musb, OTG_STATE_B_PERIPHERAL);
musb->is_active = 1;
MUSB_DEV_MODE(musb); break; default:
WARNING("bogus %s RESUME (%s)\n", "host",
musb_otg_state_string(musb));
}
} else { switch (musb_get_state(musb)) { case OTG_STATE_A_SUSPEND: /* possibly DISCONNECT is upcoming */
musb_set_state(musb, OTG_STATE_A_HOST);
musb_host_resume_root_hub(musb); break; case OTG_STATE_B_WAIT_ACON: case OTG_STATE_B_PERIPHERAL: /* disconnect while suspended? we may * not get a disconnect irq...
*/ if ((devctl & MUSB_DEVCTL_VBUS)
!= (3 << MUSB_DEVCTL_VBUS_SHIFT)
) {
musb->int_usb |= MUSB_INTR_DISCONNECT;
musb->int_usb &= ~MUSB_INTR_SUSPEND; break;
}
musb_g_resume(musb); break; case OTG_STATE_B_IDLE:
musb->int_usb &= ~MUSB_INTR_SUSPEND; break; default:
WARNING("bogus %s RESUME (%s)\n", "peripheral",
musb_otg_state_string(musb));
}
}
}
/* return IRQ_HANDLED to tell the caller to return immediately */ static irqreturn_t musb_handle_intr_sessreq(struct musb *musb, u8 devctl)
{ void __iomem *mbase = musb->mregs;
if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS
&& (devctl & MUSB_DEVCTL_BDEVICE)) {
musb_dbg(musb, "SessReq while on B state"); return IRQ_HANDLED;
}
/* IRQ arrives from ID pin sense or (later, if VBUS power * is removed) SRP. responses are time critical: * - turn on VBUS (with silicon-specific mechanism) * - go through A_WAIT_VRISE * - ... to A_WAIT_BCON. * a_wait_vrise_tmout triggers VBUS_ERROR transitions
*/
musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
musb->ep0_stage = MUSB_EP0_START;
musb_set_state(musb, OTG_STATE_A_IDLE);
MUSB_HST_MODE(musb);
musb_platform_set_vbus(musb, 1);
/* During connection as an A-Device, we may see a short * current spikes causing voltage drop, because of cable * and peripheral capacitance combined with vbus draw. * (So: less common with truly self-powered devices, where * vbus doesn't act like a power supply.) * * Such spikes are short; usually less than ~500 usec, max * of ~2 msec. That is, they're not sustained overcurrent * errors, though they're reported using VBUSERROR irqs. * * Workarounds: (a) hardware: use self powered devices. * (b) software: ignore non-repeated VBUS errors. * * REVISIT: do delays from lots of DEBUG_KERNEL checks * make trouble here, keeping VBUS < 4.4V ?
*/ switch (musb_get_state(musb)) { case OTG_STATE_A_HOST: /* recovery is dicey once we've gotten past the * initial stages of enumeration, but if VBUS * stayed ok at the other end of the link, and * another reset is due (at least for high speed, * to redo the chirp etc), it might work OK...
*/ case OTG_STATE_A_WAIT_BCON: case OTG_STATE_A_WAIT_VRISE: if (musb->vbuserr_retry) { void __iomem *mbase = musb->mregs;
switch (musb_get_state(musb)) { case OTG_STATE_A_PERIPHERAL: /* We also come here if the cable is removed, since * this silicon doesn't report ID-no-longer-grounded. * * We depend on T(a_wait_bcon) to shut us down, and * hope users don't do anything dicey during this * undesired detour through A_WAIT_BCON.
*/
musb_hnp_stop(musb);
musb_host_resume_root_hub(musb);
musb_root_disconnect(musb);
musb_platform_try_idle(musb, jiffies
+ msecs_to_jiffies(musb->a_wait_bcon
? : OTG_TIME_A_WAIT_BCON));
break; case OTG_STATE_B_IDLE: if (!musb->is_active) break;
fallthrough; case OTG_STATE_B_PERIPHERAL:
musb_g_suspend(musb);
musb->is_active = musb->g.b_hnp_enable; if (musb->is_active) {
musb_set_state(musb, OTG_STATE_B_WAIT_ACON);
musb_dbg(musb, "HNP: Setting timer for b_ase0_brst");
mod_timer(&musb->otg_timer, jiffies
+ msecs_to_jiffies(
OTG_TIME_B_ASE0_BRST));
} break; case OTG_STATE_A_WAIT_BCON: if (musb->a_wait_bcon != 0)
musb_platform_try_idle(musb, jiffies
+ msecs_to_jiffies(musb->a_wait_bcon)); break; case OTG_STATE_A_HOST:
musb_set_state(musb, OTG_STATE_A_SUSPEND);
musb->is_active = musb->hcd->self.b_hnp_enable; break; case OTG_STATE_B_HOST: /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */
musb_dbg(musb, "REVISIT: SUSPEND as B_HOST"); break; default: /* "should not happen" */
musb->is_active = 0; break;
}
}
switch (musb_get_state(musb)) { case OTG_STATE_A_HOST: case OTG_STATE_A_SUSPEND:
musb_host_resume_root_hub(musb);
musb_root_disconnect(musb); if (musb->a_wait_bcon != 0)
musb_platform_try_idle(musb, jiffies
+ msecs_to_jiffies(musb->a_wait_bcon)); break; case OTG_STATE_B_HOST: /* REVISIT this behaves for "real disconnect" * cases; make sure the other transitions from * from B_HOST act right too. The B_HOST code * in hnp_stop() is currently not used...
*/
musb_root_disconnect(musb); if (musb->hcd)
musb->hcd->self.is_b_host = 0;
musb_set_state(musb, OTG_STATE_B_PERIPHERAL);
MUSB_DEV_MODE(musb);
musb_g_disconnect(musb); break; case OTG_STATE_A_PERIPHERAL:
musb_hnp_stop(musb);
musb_root_disconnect(musb);
fallthrough; case OTG_STATE_B_WAIT_ACON: case OTG_STATE_B_PERIPHERAL: case OTG_STATE_B_IDLE:
musb_g_disconnect(musb); break; default:
WARNING("unhandled DISCONNECT transition (%s)\n",
musb_otg_state_string(musb)); break;
}
}
/* * mentor saves a bit: bus reset and babble share the same irq. * only host sees babble; only peripheral sees bus reset.
*/ staticvoid musb_handle_intr_reset(struct musb *musb)
{ if (is_host_active(musb)) { /* * When BABBLE happens what we can depends on which * platform MUSB is running, because some platforms * implemented proprietary means for 'recovering' from * Babble conditions. One such platform is AM335x. In * most cases, however, the only thing we can do is * drop the session.
*/
dev_err(musb->controller, "Babble\n");
musb_recover_from_babble(musb);
} else {
musb_dbg(musb, "BUS RESET as %s", musb_otg_state_string(musb)); switch (musb_get_state(musb)) { case OTG_STATE_A_SUSPEND:
musb_g_reset(musb);
fallthrough; case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */ /* never use invalid T(a_wait_bcon) */
musb_dbg(musb, "HNP: in %s, %d msec timeout",
musb_otg_state_string(musb),
TA_WAIT_BCON(musb));
mod_timer(&musb->otg_timer, jiffies
+ msecs_to_jiffies(TA_WAIT_BCON(musb))); break; case OTG_STATE_A_PERIPHERAL:
timer_delete(&musb->otg_timer);
musb_g_reset(musb); break; case OTG_STATE_B_WAIT_ACON:
musb_dbg(musb, "HNP: RESET (%s), to b_peripheral",
musb_otg_state_string(musb));
musb_set_state(musb, OTG_STATE_B_PERIPHERAL);
musb_g_reset(musb); break; case OTG_STATE_B_IDLE:
musb_set_state(musb, OTG_STATE_B_PERIPHERAL);
fallthrough; case OTG_STATE_B_PERIPHERAL:
musb_g_reset(musb); break; default:
musb_dbg(musb, "Unhandled BUS RESET as %s",
musb_otg_state_string(musb));
}
}
}
/* * Interrupt Service Routine to record USB "global" interrupts. * Since these do not happen often and signify things of * paramount importance, it seems OK to check them individually; * the order of the tests is specified in the manual * * @param musb instance pointer * @param int_usb register contents * @param devctl
*/
/* in host mode, the peripheral may issue remote wakeup. * in peripheral mode, the host may resume the link. * spurious RESUME irqs happen too, paired with SUSPEND.
*/ if (int_usb & MUSB_INTR_RESUME) {
musb_handle_intr_resume(musb, devctl);
handled = IRQ_HANDLED;
}
/* see manual for the order of the tests */ if (int_usb & MUSB_INTR_SESSREQ) { if (musb_handle_intr_sessreq(musb, devctl)) return IRQ_HANDLED;
handled = IRQ_HANDLED;
}
if (int_usb & MUSB_INTR_RESET) {
musb_handle_intr_reset(musb);
handled = IRQ_HANDLED;
}
#if 0 /* REVISIT ... this would be for multiplexing periodic endpoints, or * supporting transfer phasing to prevent exceeding ISO bandwidth * limits of a given frame or microframe. * * It's not needed for peripheral side, which dedicates endpoints; * though it _might_ use SOF irqs for other purposes. * * And it's not currently needed for host side, which also dedicates * endpoints, relies on TX/RX interval registers, and isn't claimed * to support ISO transfers yet.
*/ if (int_usb & MUSB_INTR_SOF) { void __iomem *mbase = musb->mregs; struct musb_hw_ep *ep;
u8 epnum;
u16 frame;
power = MUSB_POWER_ISOUPDATE; /* * treating UNKNOWN as unspecified maximum speed, in which case * we will default to high-speed.
*/ if (musb->config->maximum_speed == USB_SPEED_HIGH ||
musb->config->maximum_speed == USB_SPEED_UNKNOWN)
power |= MUSB_POWER_HSENAB;
musb_writeb(regs, MUSB_POWER, power);
/* * Make the HDRC stop (disable interrupts, etc.); * reversible by musb_start * called on gadget driver unregister * with controller locked, irqs blocked * acts as a NOP unless some role activated the hardware
*/ void musb_stop(struct musb *musb)
{ /* stop IRQs, timers, ... */
musb_platform_disable(musb);
musb_disable_interrupts(musb);
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
/* FIXME * - mark host and/or peripheral drivers unusable/inactive * - disable DMA (and enable it in HdrcStart) * - make sure we can musb_start() after musb_stop(); with * OTG mode, gadget driver module rmmod/modprobe cycles that * - ...
*/
musb_platform_try_idle(musb, 0);
}
/* * The silicon either has hard-wired endpoint configurations, or else * "dynamic fifo" sizing. The driver has support for both, though at this * writing only the dynamic sizing is very well tested. Since we switched * away from compile-time hardware parameters, we can no longer rely on * dead code elimination to leave only the relevant one in the object file. * * We don't currently use dynamic fifo setup capability to do anything * more than selecting one of a bunch of predefined configurations.
*/ static ushort fifo_mode;
/* * configure a fifo; for non-shared endpoints, this may be called * once for a tx fifo and once for an rx fifo. * * returns negative errno or offset for next fifo.
*/ staticint
fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep, conststruct musb_fifo_cfg *cfg, u16 offset)
{ void __iomem *mbase = musb->mregs; int size = 0;
u16 maxpacket = cfg->maxpacket;
u16 c_off = offset >> 3;
u8 c_size;
/* expect hw_ep has already been zero-initialized */
ret = musb_read_fifosize(musb, hw_ep, epnum); if (ret < 0) break;
/* FIXME set up hw_ep->{rx,tx}_double_buffered */
/* pick an RX/TX endpoint for bulk */ if (hw_ep->max_packet_sz_tx < 512
|| hw_ep->max_packet_sz_rx < 512) continue;
/* REVISIT: this algorithm is lazy, we should at least * try to pick a double buffered endpoint.
*/ if (musb->bulk_ep) continue;
musb->bulk_ep = hw_ep;
}
if (!musb->bulk_ep) {
pr_debug("%s: missing bulk\n", musb_driver_name); return -EINVAL;
}
/* Initialize MUSB (M)HDRC part of the USB hardware subsystem; * configure endpoints, or take their config from silicon
*/ staticint musb_core_init(u16 musb_type, struct musb *musb)
{
u8 reg; char *type; char aInfo[90]; void __iomem *mbase = musb->mregs; int status = 0; int i;
/* * handle all the irqs defined by the HDRC core. for now we expect: other * irq sources (phy, dma, etc) will be handled first, musb->int_* values * will be assigned, and the irq will already have been acked. * * called in irq context with spinlock held, irqs blocked
*/
irqreturn_t musb_interrupt(struct musb *musb)
{
irqreturn_t retval = IRQ_NONE; unsignedlong status; unsignedlong epnum;
u8 devctl;
if (!musb->int_usb && !musb->int_tx && !musb->int_rx) return IRQ_NONE;
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
trace_musb_isr(musb);
/** * According to Mentor Graphics' documentation, flowchart on page 98, * IRQ should be handled as follows: * * . Resume IRQ * . Session Request IRQ * . VBUS Error IRQ * . Suspend IRQ * . Connect IRQ * . Disconnect IRQ * . Reset/Babble IRQ * . SOF IRQ (we're not using this one) * . Endpoint 0 IRQ * . TX Endpoints * . RX Endpoints * * We will be following that flowchart in order to avoid any problems * that might arise with internal Finite State Machine.
*/
if (musb->int_usb)
retval |= musb_stage0_irq(musb, musb->int_usb, devctl);
if (musb->int_tx & 1) { if (is_host_active(musb))
retval |= musb_h_ep0_irq(musb); else
retval |= musb_g_ep0_irq(musb);
/* we have just handled endpoint 0 IRQ, clear it */
musb->int_tx &= ~BIT(0);
}
/* * musb_mailbox - optional phy notifier function * @status phy state change * * Optionally gets called from the USB PHY. Note that the USB PHY must be * disabled at the point the phy_callback is registered or unregistered.
*/ int musb_mailbox(enum musb_vbus_id_status status)
{ if (musb_phy_callback) return musb_phy_callback(status);
/* * Check the musb devctl session bit to determine if we want to * allow PM runtime for the device. In general, we want to keep things * active when the session bit is set except after host disconnect. * * Only called from musb_irq_work. If this ever needs to get called * elsewhere, proper locking must be implemented for musb->session.
*/ staticvoid musb_pm_runtime_check_session(struct musb *musb)
{
u8 devctl, s; int error;
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
/* Handle session status quirks first */
s = MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV |
MUSB_DEVCTL_HR; switch (devctl & ~s) { case MUSB_QUIRK_B_DISCONNECT_99:
musb_state_needs_recheck(musb, devctl, "Poll devctl in case of suspend after disconnect"); break; case MUSB_QUIRK_B_INVALID_VBUS_91: if (musb_state_needs_recheck(musb, devctl, "Poll devctl on invalid vbus, assume no session")) return;
fallthrough; case MUSB_QUIRK_A_DISCONNECT_19: if (musb_state_needs_recheck(musb, devctl, "Poll devctl on possible host mode disconnect")) return; if (!musb->session) break;
trace_musb_state(musb, devctl, "Allow PM on possible host mode disconnect");
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
musb->session = false; return; default: break;
}
/* No need to do anything if session has not changed */
s = devctl & MUSB_DEVCTL_SESSION; if (s == musb->session) return;
/* Block PM or allow PM? */ if (s) {
trace_musb_state(musb, devctl, "Block PM on active session");
error = pm_runtime_get_sync(musb->controller); if (error < 0)
dev_err(musb->controller, "Could not enable: %i\n",
error);
musb->quirk_retries = 3;
/* * We can get a spurious MUSB_INTR_SESSREQ interrupt on start-up * in B-peripheral mode with nothing connected and the session * bit clears silently. Check status again in 3 seconds.
*/ if (devctl & MUSB_DEVCTL_BDEVICE)
schedule_delayed_work(&musb->irq_work,
msecs_to_jiffies(3000));
} else {
trace_musb_state(musb, devctl, "Allow PM with no session");
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
}
musb->session = s;
}
/* Only used to provide driver mode change events */ staticvoid musb_irq_work(struct work_struct *data)
{ struct musb *musb = container_of(data, struct musb, irq_work.work); int error;
error = pm_runtime_resume_and_get(musb->controller); if (error < 0) {
dev_err(musb->controller, "Could not enable: %i\n", error);
staticvoid musb_recover_from_babble(struct musb *musb)
{ int ret;
u8 devctl;
musb_disable_interrupts(musb);
/* * wait at least 320 cycles of 60MHz clock. That's 5.3us, we will give * it some slack and wait for 10us.
*/
udelay(10);
ret = musb_platform_recover(musb); if (ret) {
musb_enable_interrupts(musb); return;
}
/* drop session bit */
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
devctl &= ~MUSB_DEVCTL_SESSION;
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
/* tell usbcore about it */
musb_root_disconnect(musb);
/* * When a babble condition occurs, the musb controller * removes the session bit and the endpoint config is lost.
*/ if (musb->dyn_fifo)
ret = ep_config_from_table(musb); else
ret = ep_config_from_hw(musb);
/* restart session */ if (ret == 0)
musb_start(musb);
}
/* -------------------------------------------------------------------------- * Init support
*/
ret = musb_host_alloc(musb); if (ret < 0) goto err_free;
dev_set_drvdata(dev, musb);
return musb;
err_free: return NULL;
}
staticvoid musb_free(struct musb *musb)
{ /* this has multiple entry modes. it handles fault cleanup after * probe(), where things may be partially set up, as well as rmmod * cleanup after everything's been de-activated.
*/
if (musb->nIrq >= 0) { if (musb->irq_wake)
disable_irq_wake(musb->nIrq);
free_irq(musb->nIrq, musb);
}
/* * Called to run work if device is active or else queue the work to happen * on resume. Caller must take musb->lock and must hold an RPM reference. * * Note that we cowardly refuse queuing work after musb PM runtime * resume is done calling musb_run_resume_work() and return -EINPROGRESS * instead.
*/ int musb_queue_resume_work(struct musb *musb, int (*callback)(struct musb *musb, void *data), void *data)
{ struct musb_pending_work *w; unsignedlong flags; bool is_suspended; int error;
if (musb->port1_status & USB_PORT_STAT_RESET)
musb_port_reset(musb, false);
spin_unlock_irqrestore(&musb->lock, flags);
}
/* * Perform generic per-controller initialization. * * @dev: the controller (already clocked, etc) * @nIrq: IRQ number * @ctrl: virtual address of controller registers, * not yet corrected for platform-specific offsets
*/ staticint
musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
{ int status; struct musb *musb; struct musb_hdrc_platform_data *plat = dev_get_platdata(dev);
/* The driver might handle more features than the board; OK. * Fail when the board needs a feature that's not enabled.
*/ if (!plat) {
dev_err(dev, "no platform_data?\n");
status = -ENODEV; goto fail0;
}
/* allocate */
musb = allocate_instance(dev, plat->config, ctrl); if (!musb) {
status = -ENOMEM; goto fail0;
}
/* * Initialize the default IO functions. At least omap2430 needs * these early. We initialize the platform specific IO functions * later on.
*/
musb_readb = musb_default_readb;
musb_writeb = musb_default_writeb;
musb_readw = musb_default_readw;
musb_writew = musb_default_writew;
/* The musb_platform_init() call: * - adjusts musb->mregs * - sets the musb->isr * - may initialize an integrated transceiver * - initializes musb->xceiv, usually by otg_get_phy() * - stops powering VBUS * * There are various transceiver configurations. * DaVinci, TUSB60x0, and others integrate them. OMAP3 uses * external/discrete ones in various flavors (twl4030 family, * isp1504, non-OTG, etc) mostly hooking up through ULPI.
*/
status = musb_platform_init(musb); if (status < 0) goto fail1;
if (!musb->isr) {
status = -ENODEV; goto fail2;
}
/* Most devices use indexed offset or flat offset */ if (musb->ops->quirks & MUSB_INDEXED_EP) {
musb->io.ep_offset = musb_indexed_ep_offset;
musb->io.ep_select = musb_indexed_ep_select;
} else {
musb->io.ep_offset = musb_flat_ep_offset;
musb->io.ep_select = musb_flat_ep_select;
}
if (musb->ops->quirks & MUSB_G_NO_SKB_RESERVE)
musb->g.quirk_avoids_skb_reserve = 1;
/* At least tusb6010 has its own offsets */ if (musb->ops->ep_offset)
musb->io.ep_offset = musb->ops->ep_offset; if (musb->ops->ep_select)
musb->io.ep_select = musb->ops->ep_select;
if (musb->ops->fifo_mode)
fifo_mode = musb->ops->fifo_mode; else
fifo_mode = 4;
if (musb->ops->fifo_offset)
musb->io.fifo_offset = musb->ops->fifo_offset; else
musb->io.fifo_offset = musb_default_fifo_offset;
if (musb->ops->busctl_offset)
musb->io.busctl_offset = musb->ops->busctl_offset; else
musb->io.busctl_offset = musb_default_busctl_offset;
if (musb->ops->readb)
musb_readb = musb->ops->readb; if (musb->ops->writeb)
musb_writeb = musb->ops->writeb; if (musb->ops->clearb)
musb_clearb = musb->ops->clearb; else
musb_clearb = musb_readb;
if (musb->ops->readw)
musb_readw = musb->ops->readw; if (musb->ops->writew)
musb_writew = musb->ops->writew; if (musb->ops->clearw)
musb_clearw = musb->ops->clearw; else
musb_clearw = musb_readw;
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.