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 *ULPICarKitControlDisableUTMIafterclearingPOWER_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);
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 *notgetadisconnectirq...
*/ 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 *isremoved)SRP.responsesaretimecritical: *-turnonVBUS(withsilicon-specificmechanism) *-gothroughA_WAIT_VRISE *-...toA_WAIT_BCON. *a_wait_vrise_tmouttriggersVBUS_ERRORtransitions
*/
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 *currentspikescausingvoltagedrop,becauseofcable *andperipheralcapacitancecombinedwithvbusdraw. *(So:lesscommonwithtrulyself-powereddevices,where *vbusdoesn'tactlikeapowersupply.) * *Suchspikesareshort;usuallylessthan~500usec,max *of~2msec.Thatis,they'renotsustainedovercurrent *errors,thoughthey'rereportedusingVBUSERRORirqs. * *Workarounds:(a)hardware:useselfpowereddevices. *(b)software:ignorenon-repeatedVBUSerrors. * *REVISIT:dodelaysfromlotsofDEBUG_KERNELchecks *maketroublehere,keepingVBUS<4.4V?
*/ switch (musb_get_state(musb)) { case OTG_STATE_A_HOST: /* recovery is dicey once we've gotten past the *initialstagesofenumeration,butifVBUS *stayedokattheotherendofthelink,and *anotherresetisdue(atleastforhighspeed, *toredothechirpetc),itmightworkOK...
*/ 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 *thissilicondoesn'treportID-no-longer-grounded. * *WedependonT(a_wait_bcon)toshutusdown,and *hopeusersdon'tdoanythingdiceyduringthis *undesireddetourthroughA_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;
}
}
/* in host mode, the peripheral may issue remote wakeup. *inperipheralmode,thehostmayresumethelink. *spuriousRESUMEirqshappentoo,pairedwithSUSPEND.
*/ 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 (!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);
}
/* "modprobe ... use_dma=0" etc */
module_param(use_dma, bool, 0644);
MODULE_PARM_DESC(use_dma, "enable/disable use of DMA");
void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit)
{
/* called with controller lock already held */
if (!epnum) {
if (!is_cppi_enabled(musb)) {
/* endpoint 0 */
if (is_host_active(musb))
musb_h_ep0_irq(musb);
else
musb_g_ep0_irq(musb);
}
} else {
/* endpoints 1..15 */
if (transmit) {
if (is_host_active(musb))
musb_host_tx(musb, epnum);
else
musb_g_tx(musb, epnum);
} else {
/* receive */
if (is_host_active(musb))
musb_host_rx(musb, epnum);
else
musb_g_rx(musb, epnum);
}
}
}
EXPORT_SYMBOL_GPL(musb_dma_completion);
#else
#define use_dma 0
#endif
static int (*musb_phy_callback)(enum musb_vbus_id_status status);
/*
* 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);
spin_lock_irqsave(&musb->lock, flags);
if (sysfs_streq(buf, "host"))
status = musb_platform_set_mode(musb, MUSB_HOST);
else if (sysfs_streq(buf, "peripheral"))
status = musb_platform_set_mode(musb, MUSB_PERIPHERAL);
else if (sysfs_streq(buf, "otg"))
status = musb_platform_set_mode(musb, MUSB_OTG);
else
status = -EINVAL;
spin_unlock_irqrestore(&musb->lock, flags);
/* Gadget drivers can't know that a host is connected so they might want
* to start SRP, but users can. This allows userspace to trigger SRP.
*/
static ssize_t srp_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t n)
{
struct musb *musb = dev_to_musb(dev);
unsigned short srp;
if (sscanf(buf, "%hu", &srp) != 1
|| (srp != 1)) {
dev_err(dev, "SRP: Value must be 1\n");
return -EINVAL;
}
/*
* 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.
*/
static void 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 */
static void 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);
static void 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;
}
static void 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);
}
#ifdef CONFIG_PM
/*
* Called from musb_runtime_resume(), musb_resume(), and
* musb_queue_resume_work(). Callers must take musb->lock.
*/
static int musb_run_resume_work(struct musb *musb)
{
struct musb_pending_work *w, *_w;
unsigned long flags;
int error = 0;
/*
* 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;
unsigned long 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
*/
static int
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;
#ifndef CONFIG_MUSB_PIO_ONLY
if (!musb->ops->dma_init || !musb->ops->dma_exit) {
dev_err(dev, "DMA controller not set\n");
status = -ENODEV;
goto fail2;
}
musb_dma_controller_create = musb->ops->dma_init;
musb_dma_controller_destroy = musb->ops->dma_exit;
#endif
if (musb->ops->read_fifo)
musb->io.read_fifo = musb->ops->read_fifo;
else
musb->io.read_fifo = musb_default_read_fifo;
if (musb->ops->write_fifo)
musb->io.write_fifo = musb->ops->write_fifo;
else
musb->io.write_fifo = musb_default_write_fifo;
if (musb->ops->get_toggle)
musb->io.get_toggle = musb->ops->get_toggle;
else
musb->io.get_toggle = musb_default_get_toggle;
if (musb->ops->set_toggle)
musb->io.set_toggle = musb->ops->set_toggle;
else
musb->io.set_toggle = musb_default_set_toggle;
if (musb->ops->phy_callback)
musb_phy_callback = musb->ops->phy_callback;
/*
* We need musb_read/write functions initialized for PM.
* Note that at least 2430 glue needs autosuspend delay
* somewhere above 300 ms for the hardware to idle properly
* after disconnecting the cable in host mode. Let's use
* 500 ms for some margin.
*/
pm_runtime_use_autosuspend(musb->controller);
pm_runtime_set_autosuspend_delay(musb->controller, 500);
pm_runtime_enable(musb->controller);
pm_runtime_get_sync(musb->controller);
status = usb_phy_init(musb->xceiv);
if (status < 0)
goto err_usb_phy_init;
if (use_dma && dev->dma_mask) {
musb->dma_controller =
musb_dma_controller_create(musb, musb->mregs);
if (IS_ERR(musb->dma_controller)) {
status = PTR_ERR(musb->dma_controller);
goto fail2_5;
}
}
/* be sure interrupts are disabled before connecting ISR */
musb_platform_disable(musb);
musb_disable_interrupts(musb);
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
/* MUSB_POWER_SOFTCONN might be already set, JZ4740 does this. */
musb_writeb(musb->mregs, MUSB_POWER, 0);
/* setup musb parts of the core (especially endpoints) */
status = musb_core_init(plat->config->multipoint
? MUSB_CONTROLLER_MHDRC
: MUSB_CONTROLLER_HDRC, musb);
if (status < 0)
goto fail3;
/* attach to the IRQ */
if (request_irq(nIrq, musb->isr, IRQF_SHARED, dev_name(dev), musb)) {
dev_err(dev, "request_irq %d failed!\n", nIrq);
status = -ENODEV;
goto fail3;
}
musb->nIrq = nIrq;
/* FIXME this handles wakeup irqs wrong */
if (enable_irq_wake(nIrq) == 0) {
musb->irq_wake = 1;
device_init_wakeup(dev, 1);
} else {
musb->irq_wake = 0;
}
/* program PHY to use external vBus if required */
if (plat->extvbus) {
u8 busctl = musb_readb(musb->mregs, MUSB_ULPI_BUSCONTROL);
busctl |= MUSB_ULPI_USE_EXTVBUS;
musb_writeb(musb->mregs, MUSB_ULPI_BUSCONTROL, busctl);
}
switch (musb->port_mode) {
case MUSB_HOST:
status = musb_host_setup(musb, plat->power);
if (status < 0)
goto fail3;
status = musb_platform_set_mode(musb, MUSB_HOST);
break;
case MUSB_PERIPHERAL:
status = musb_gadget_setup(musb);
if (status < 0)
goto fail3;
status = musb_platform_set_mode(musb, MUSB_PERIPHERAL);
break;
case MUSB_OTG:
status = musb_host_setup(musb, plat->power);
if (status < 0)
goto fail3;
status = musb_gadget_setup(musb);
if (status) {
musb_host_cleanup(musb);
goto fail3;
}
status = musb_platform_set_mode(musb, MUSB_OTG);
break;
default:
dev_err(dev, "unsupported port mode %d\n", musb->port_mode);
break;
}
/* all implementations (PCI bridge to FPGA, VLYNQ, etc) should just
* bridge to a platform device; this driver then suffices.
*/
static int musb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int irq = platform_get_irq_byname(pdev, "mc");
void __iomem *base;
if (irq < 0)
return irq;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
/* this gets called on rmmod.
* - Host mode: host may still be active
* - Peripheral mode: peripheral is deactivated (or never-activated)
* - OTG mode: both roles are deactivated (or never-activated)
*/
musb_exit_debugfs(musb);
musb->context.index_regs[i].rxinterval);
musb_write_txfunaddr(musb, i,
musb->context.index_regs[i].txfunaddr);
musb_write_txhubaddr(musb, i,
musb->context.index_regs[i].txhubaddr);
musb_write_txhubport(musb, i,
musb->context.index_regs[i].txhubport);
musb_write_rxfunaddr(musb, i,
musb->context.index_regs[i].rxfunaddr);
musb_write_rxhubaddr(musb, i,
musb->context.index_regs[i].rxhubaddr);
musb_write_rxhubport(musb, i,
musb->context.index_regs[i].rxhubport);
}
musb_writeb(musb_base, MUSB_INDEX, musb->context.index);
}
static int musb_suspend(struct device *dev)
{
struct musb *musb = dev_to_musb(dev);
unsigned long flags;
int ret;
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
pm_runtime_put_noidle(dev);
return ret;
}
musb->flush_irq_work = true;
while (flush_delayed_work(&musb->irq_work))
;
musb->flush_irq_work = false;
if (!(musb->ops->quirks & MUSB_PRESERVE_SESSION))
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
WARN_ON(!list_empty(&musb->pending_list));
spin_lock_irqsave(&musb->lock, flags);
if (is_peripheral_active(musb)) {
/* FIXME force disconnect unless we know USB will wake
* the system up quickly enough to respond ...
*/
} else if (is_host_active(musb)) {
/* we know all the children are suspended; sometimes
* they will even be wakeup-enabled.
*/
}
static int musb_resume(struct device *dev)
{
struct musb *musb = dev_to_musb(dev);
unsigned long flags;
int error;
u8 devctl;
u8 mask;
/*
* For static cmos like DaVinci, register values were preserved
* unless for some reason the whole soc powered down or the USB
* module got reset through the PSC (vs just being disabled).
*
* For the DSPS glue layer though, a full register restore has to
* be done. As it shouldn't harm other platforms, we do it
* unconditionally.
*/
static int musb_runtime_resume(struct device *dev)
{
struct musb *musb = dev_to_musb(dev);
unsigned long flags;
int error;
/*
* When pm_runtime_get_sync called for the first time in driver
* init, some of the structure is still not initialized which is
* used in restore function. But clock needs to be
* enabled before any register access, so
* pm_runtime_get_sync has to be called.
* Also context restore without save does not make
* any sense
*/
if (!musb->is_initialized)
return 0;
musb_restore_context(musb);
spin_lock_irqsave(&musb->lock, flags);
error = musb_run_resume_work(musb);
if (error)
dev_err(musb->controller, "resume work failed with %i\n",
error);
musb->is_runtime_suspended = 0;
spin_unlock_irqrestore(&musb->lock, flags);
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.82Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-11)
¤
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.