// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2007,2008 Freescale semiconductor, Inc. * * Author: Li Yang <LeoLi@freescale.com> * Jerry Huang <Chang-Ming.Huang@freescale.com> * * Initialization based on code from Shlomi Gridish.
*/
/* * Pull-up D+, signalling connect by periperal. Also used in * data-line pulsing in SRP
*/ void fsl_otg_loc_conn(struct otg_fsm *fsm, int on)
{
u32 tmp;
if (on)
tmp |= OTGSC_CTRL_DATA_PULSING; else
tmp &= ~OTGSC_CTRL_DATA_PULSING;
fsl_writel(tmp, &usb_dr_regs->otgsc);
}
/* * 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.
*/ void fsl_otg_loc_sof(struct otg_fsm *fsm, int on)
{
u32 tmp;
/* * As USB3300 using the same a_sess_vld and b_sess_vld voltage * we need to discharge the bus for a while to distinguish * residual voltage of vbus pulsing and A device pull up
*/
fsl_otg_dischrg_vbus(1);
fsl_otg_add_timer(&fsl_otg_dev->fsm, b_srp_wait_tmr);
}
if ((fsl_otg_dev->phy.otg->state == OTG_STATE_B_SRP_INIT) &&
fsl_otg_dev->fsm.b_sess_vld)
fsl_otg_dev->fsm.b_srp_done = 1;
}
/* * Workaround for a_host suspending too fast. When a_bus_req=0, * a_host will start by SRP. It needs to set b_hnp_enable before * actually suspending to start HNP
*/ void a_wait_enum(unsignedlong foo)
{
VDBG("a_wait_enum timeout\n"); if (!fsl_otg_dev->phy.otg->host->b_hnp_enable)
fsl_otg_add_timer(&fsl_otg_dev->fsm, a_wait_enum_tmr); else
otg_statemachine(&fsl_otg_dev->fsm);
}
/* The timeout callback function to set time out bit */ void set_tmout(unsignedlong indicator)
{
*(int *)indicator = 1;
}
/* Initialize timers */ int fsl_otg_init_timers(struct otg_fsm *fsm)
{ /* FSM used timers */
a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE,
(unsignedlong)&fsm->a_wait_vrise_tmout); if (!a_wait_vrise_tmr) return -ENOMEM;
a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON,
(unsignedlong)&fsm->a_wait_bcon_tmout); if (!a_wait_bcon_tmr) return -ENOMEM;
a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS,
(unsignedlong)&fsm->a_aidl_bdis_tmout); if (!a_aidl_bdis_tmr) return -ENOMEM;
b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST,
(unsignedlong)&fsm->b_ase0_brst_tmout); if (!b_ase0_brst_tmr) return -ENOMEM;
b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP,
(unsignedlong)&fsm->b_se0_srp); if (!b_se0_srp_tmr) return -ENOMEM;
b_srp_fail_tmr = otg_timer_initializer(&set_tmout, TB_SRP_FAIL,
(unsignedlong)&fsm->b_srp_done); if (!b_srp_fail_tmr) return -ENOMEM;
a_wait_enum_tmr = otg_timer_initializer(&a_wait_enum, 10,
(unsignedlong)&fsm); if (!a_wait_enum_tmr) return -ENOMEM;
/* device driver used timers */
b_srp_wait_tmr = otg_timer_initializer(&b_srp_end, TB_SRP_WAIT, 0); if (!b_srp_wait_tmr) return -ENOMEM;
b_data_pulse_tmr = otg_timer_initializer(&b_data_pulse_end,
TB_DATA_PLS, 0); if (!b_data_pulse_tmr) return -ENOMEM;
b_vbus_pulse_tmr = otg_timer_initializer(&b_vbus_pulse_end,
TB_VBUS_PLS, 0); if (!b_vbus_pulse_tmr) return -ENOMEM;
/* * 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);
}
if (!otg->host) return -ENODEV;
dev = otg->host->controller;
/* * Update a_vbus_vld state as a_vbus_vld int is disabled * in device mode
*/
fsm->a_vbus_vld =
!!(fsl_readl(&usb_dr_regs->otgsc) & OTGSC_STS_A_VBUS_VALID); if (on) { /* start fsl usb host controller */ if (otg_dev->host_working) goto end; else {
otg_reset_controller();
VDBG("host on......\n"); if (dev->driver->pm && dev->driver->pm->resume) {
retval = dev->driver->pm->resume(dev); if (fsm->id) { /* default-b */
fsl_otg_drv_vbus(fsm, 1); /* * Workaround: b_host can't driver * vbus, but PP in PORTSC needs to * be 1 for host to work. * So we set drv_vbus bit in * transceiver to 0 thru ULPI.
*/
write_ulpi(0x0c, 0x20);
}
}
/* * Call suspend and resume function in udc driver * to stop and start udc driver.
*/ int fsl_otg_start_gadget(struct otg_fsm *fsm, int on)
{ struct usb_otg *otg = fsm->otg; struct device *dev;
if (!otg->gadget || !otg->gadget->dev.parent) return -ENODEV;
VDBG("gadget %s\n", str_on_off(on));
dev = otg->gadget->dev.parent;
if (on) { if (dev->driver->resume)
dev->driver->resume(dev);
} else { if (dev->driver->suspend)
dev->driver->suspend(dev, otg_suspend_state);
}
return 0;
}
/* * Called by initialization code of host driver. Register host controller * to the OTG. Suspend host for OTG role detection.
*/ staticint fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
{ struct fsl_otg *otg_dev;
otg->host->otg_port = fsl_otg_initdata.otg_port;
otg->host->is_b_host = otg_dev->fsm.id; /* * must leave time for hub_wq to finish its thing * before yanking the host driver out from under it, * so suspend the host after a short delay.
*/
otg_dev->host_working = 1;
schedule_delayed_work(&otg_dev->otg_event, 100); return 0;
} else { /* host driver going away */ if (!(fsl_readl(&otg_dev->dr_mem_map->otgsc) &
OTGSC_STS_USB_ID)) { /* Mini-A cable connected */ struct otg_fsm *fsm = &otg_dev->fsm;
/* start the gadget right away if the ID pin says Mini-B */
pr_debug("ID pin=%d\n", otg_dev->fsm.id); if (otg_dev->fsm.id == 1) {
fsl_otg_start_host(&otg_dev->fsm, 0);
otg_drv_vbus(&otg_dev->fsm, 0);
fsl_otg_start_gadget(&otg_dev->fsm, 1);
}
return 0;
}
/* * Delayed pin detect interrupt processing. * * When the Mini-A cable is disconnected from the board, * the pin-detect interrupt happens before the disconnect * interrupts for the connected device(s). In order to * process the disconnect interrupt(s) prior to switching * roles, the pin-detect interrupts are delayed, and handled * by this routine.
*/ staticvoid fsl_otg_event(struct work_struct *work)
{ struct fsl_otg *og = container_of(work, struct fsl_otg, otg_event.work); struct otg_fsm *fsm = &og->fsm;
if (fsm->id) { /* switch to gadget */
fsl_otg_start_host(fsm, 0);
otg_drv_vbus(fsm, 0);
fsl_otg_start_gadget(fsm, 1);
}
}
/* clear a_bus_req to enter a_suspend state */
otg_dev->fsm.a_bus_req = 0;
otg_statemachine(&otg_dev->fsm);
return 0;
}
/* * Interrupt handler. OTG/host/peripheral share the same int line. * OTG driver clears OTGSC interrupts and leaves USB interrupts * intact. It needs to have knowledge of some USB interrupts * such as port change.
*/
irqreturn_t fsl_otg_isr(int irq, void *dev_id)
{ struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm; struct usb_otg *otg = ((struct fsl_otg *)dev_id)->phy.otg;
u32 otg_int_src, otg_sc;
/* Initialize the global variable fsl_otg_dev and request IRQ for OTG */ staticint fsl_otg_conf(struct platform_device *pdev)
{ struct fsl_otg *fsl_otg_tc; int status;
if (fsl_otg_dev) return 0;
/* allocate space to fsl otg device */
fsl_otg_tc = kzalloc(sizeof(struct fsl_otg), GFP_KERNEL); if (!fsl_otg_tc) return -ENOMEM;
/* Store the otg transceiver */
status = usb_add_phy(&fsl_otg_tc->phy, USB_PHY_TYPE_USB2); if (status) {
pr_warn(FSL_OTG_NAME ": unable to register OTG transceiver.\n"); goto err;
}
if (pdata->have_sysif_regs) { /* configure control enable IO output, big endian register */
temp = __raw_readl(&p_otg->dr_mem_map->control);
temp |= USB_CTRL_IOENB;
__raw_writel(temp, &p_otg->dr_mem_map->control);
}
/* disable all interrupt and clear all OTGSC status */
temp = fsl_readl(&p_otg->dr_mem_map->otgsc);
temp &= ~OTGSC_INTERRUPT_ENABLE_BITS_MASK;
temp |= OTGSC_INTERRUPT_STATUS_BITS_MASK | OTGSC_CTRL_VBUS_DISCHARGE;
fsl_writel(temp, &p_otg->dr_mem_map->otgsc);
/* * The identification (id) input is FALSE when a Mini-A plug is inserted * in the devices Mini-AB receptacle. Otherwise, this input is TRUE. * Also: record initial state of ID pin
*/ if (fsl_readl(&p_otg->dr_mem_map->otgsc) & OTGSC_STS_USB_ID) {
p_otg->phy.otg->state = OTG_STATE_UNDEFINED;
p_otg->fsm.id = 1;
} else {
p_otg->phy.otg->state = OTG_STATE_A_IDLE;
p_otg->fsm.id = 0;
}
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.