Example #1
0
/*
 * This interrupt indicates that SUSPEND state has been detected on the USB.
 *
 * For HNP the USB Suspend interrupt signals the change from "a_peripheral"
 * to "a_host".
 *
 * When power management is enabled the core will be put in low power mode.
 */
static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
{
	u32 dsts;

	dev_dbg(hsotg->dev, "USB SUSPEND\n");

	if (dwc2_is_device_mode(hsotg)) {
		/*
		 * Check the Device status register to determine if the Suspend
		 * state is active
		 */
		dsts = readl(hsotg->regs + DSTS);
		dev_dbg(hsotg->dev, "DSTS=0x%0x\n", dsts);
		dev_dbg(hsotg->dev,
			"DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n",
			!!(dsts & DSTS_SUSPSTS),
			hsotg->hw_params.power_optimized);
	} else {
		if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) {
			dev_dbg(hsotg->dev, "a_peripheral->a_host\n");

			/* Clear the a_peripheral flag, back to a_host */
			spin_unlock(&hsotg->lock);
			dwc2_hcd_start(hsotg);
			spin_lock(&hsotg->lock);
			hsotg->op_state = OTG_STATE_A_HOST;
		}
	}

	/* Change to L2 (suspend) state */
	hsotg->lx_state = DWC2_L2;

	/* Clear interrupt */
	writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS);
}
Example #2
0
/*
 * This interrupt indicates that SUSPEND state has been detected on the USB.
 *
 * For HNP the USB Suspend interrupt signals the change from "a_peripheral"
 * to "a_host".
 *
 * When power management is enabled the core will be put in low power mode.
 */
static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
{
    u32 dsts;
    int ret;

    dev_dbg(hsotg->dev, "USB SUSPEND\n");

    if (dwc2_is_device_mode(hsotg)) {
        /*
         * Check the Device status register to determine if the Suspend
         * state is active
         */
        dsts = DWC2_READ_4(hsotg, DSTS);
        dev_dbg(hsotg->dev, "DSTS=0x%0x\n", dsts);
        dev_dbg(hsotg->dev,
                "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n",
                !!(dsts & DSTS_SUSPSTS),
                hsotg->hw_params.power_optimized);
        if ((dsts & DSTS_SUSPSTS) && hsotg->hw_params.power_optimized) {
            /* Ignore suspend request before enumeration */
            if (!dwc2_is_device_connected(hsotg)) {
                dev_dbg(hsotg->dev,
                        "ignore suspend request before enumeration\n");
                goto clear_int;
            }

            ret = dwc2_enter_hibernation(hsotg);
            if (ret) {
                if (ret != -ENOTSUPP)
                    dev_err(hsotg->dev,
                            "enter hibernation failed\n");
                goto skip_power_saving;
            }

            udelay(100);

            /* Ask phy to be suspended */
            if (!IS_ERR_OR_NULL(hsotg->uphy))
                usb_phy_set_suspend(hsotg->uphy, true);
skip_power_saving:
            /*
             * Change to L2 (suspend) state before releasing
             * spinlock
             */
            hsotg->lx_state = DWC2_L2;

            /* Call gadget suspend callback */
            call_gadget(hsotg, suspend);
        }
    } else {
        if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) {
            dev_dbg(hsotg->dev, "a_peripheral->a_host\n");

            /* Change to L2 (suspend) state */
            hsotg->lx_state = DWC2_L2;
            /* Clear the a_peripheral flag, back to a_host */
            spin_unlock(&hsotg->lock);
            dwc2_hcd_start(hsotg);
            spin_lock(&hsotg->lock);
            hsotg->op_state = OTG_STATE_A_HOST;
        }
    }

clear_int:
    /* Clear interrupt */
    DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_USBSUSP);
}
Example #3
0
/**
 * dwc2_handle_otg_intr() - Handles the OTG Interrupts. It reads the OTG
 * Interrupt Register (GOTGINT) to determine what interrupt has occurred.
 *
 * @hsotg: Programming view of DWC_otg controller
 */
static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
{
    u32 gotgint;
    u32 gotgctl;
    u32 gintmsk;

    gotgint = DWC2_READ_4(hsotg, GOTGINT);
    gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
    dev_dbg(hsotg->dev, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint,
            dwc2_op_state_str(hsotg));

    if (gotgint & GOTGINT_SES_END_DET) {
        dev_dbg(hsotg->dev,
                " ++OTG Interrupt: Session End Detected++ (%s)\n",
                dwc2_op_state_str(hsotg));
        gotgctl = DWC2_READ_4(hsotg, GOTGCTL);

        if (dwc2_is_device_mode(hsotg))
            s3c_hsotg_disconnect(hsotg);

        if (hsotg->op_state == OTG_STATE_B_HOST) {
            hsotg->op_state = OTG_STATE_B_PERIPHERAL;
        } else {
            /*
             * If not B_HOST and Device HNP still set, HNP did
             * not succeed!
             */
            if (gotgctl & GOTGCTL_DEVHNPEN) {
                dev_dbg(hsotg->dev, "Session End Detected\n");
                dev_err(hsotg->dev,
                        "Device Not Connected/Responding!\n");
            }

            /*
             * If Session End Detected the B-Cable has been
             * disconnected
             */
            /* Reset to a clean state */
            hsotg->lx_state = DWC2_L0;
        }

        gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
        gotgctl &= ~GOTGCTL_DEVHNPEN;
        DWC2_WRITE_4(hsotg, GOTGCTL, gotgctl);
    }

    if (gotgint & GOTGINT_SES_REQ_SUC_STS_CHNG) {
        dev_dbg(hsotg->dev,
                " ++OTG Interrupt: Session Request Success Status Change++\n");
        gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
        if (gotgctl & GOTGCTL_SESREQSCS) {
            if (hsotg->core_params->phy_type ==
                    DWC2_PHY_TYPE_PARAM_FS
                    && hsotg->core_params->i2c_enable > 0) {
                hsotg->srp_success = 1;
            } else {
                /* Clear Session Request */
                gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
                gotgctl &= ~GOTGCTL_SESREQ;
                DWC2_WRITE_4(hsotg, GOTGCTL, gotgctl);
            }
        }
    }

    if (gotgint & GOTGINT_HST_NEG_SUC_STS_CHNG) {
        /*
         * Print statements during the HNP interrupt handling
         * can cause it to fail
         */
        gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
        /*
         * WA for 3.00a- HW is not setting cur_mode, even sometimes
         * this does not help
         */
        if (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a)
            udelay(100);
        if (gotgctl & GOTGCTL_HSTNEGSCS) {
            if (dwc2_is_host_mode(hsotg)) {
                hsotg->op_state = OTG_STATE_B_HOST;
                /*
                 * Need to disable SOF interrupt immediately.
                 * When switching from device to host, the PCD
                 * interrupt handler won't handle the interrupt
                 * if host mode is already set. The HCD
                 * interrupt handler won't get called if the
                 * HCD state is HALT. This means that the
                 * interrupt does not get handled and Linux
                 * complains loudly.
                 */
                gintmsk = DWC2_READ_4(hsotg, GINTMSK);
                gintmsk &= ~GINTSTS_SOF;
                DWC2_WRITE_4(hsotg, GINTMSK, gintmsk);

                /*
                 * Call callback function with spin lock
                 * released
                 */
                spin_unlock(&hsotg->lock);

                /* Initialize the Core for Host mode */
                dwc2_hcd_start(hsotg);
                spin_lock(&hsotg->lock);
                hsotg->op_state = OTG_STATE_B_HOST;
            }
        } else {
            gotgctl = DWC2_READ_4(hsotg, GOTGCTL);
            gotgctl &= ~(GOTGCTL_HNPREQ | GOTGCTL_DEVHNPEN);
            DWC2_WRITE_4(hsotg, GOTGCTL, gotgctl);
            dev_dbg(hsotg->dev, "HNP Failed\n");
            dev_err(hsotg->dev,
                    "Device Not Connected/Responding\n");
        }
    }

    if (gotgint & GOTGINT_HST_NEG_DET) {
        /*
         * The disconnect interrupt is set at the same time as
         * Host Negotiation Detected. During the mode switch all
         * interrupts are cleared so the disconnect interrupt
         * handler will not get executed.
         */
        dev_dbg(hsotg->dev,
                " ++OTG Interrupt: Host Negotiation Detected++ (%s)\n",
                (dwc2_is_host_mode(hsotg) ? "Host" : "Device"));
        if (dwc2_is_device_mode(hsotg)) {
            dev_dbg(hsotg->dev, "a_suspend->a_peripheral (%d)\n",
                    hsotg->op_state);
            spin_unlock(&hsotg->lock);
            dwc2_hcd_disconnect(hsotg);
            spin_lock(&hsotg->lock);
            hsotg->op_state = OTG_STATE_A_PERIPHERAL;
        } else {
            /* Need to disable SOF interrupt immediately */
            gintmsk = DWC2_READ_4(hsotg, GINTMSK);
            gintmsk &= ~GINTSTS_SOF;
            DWC2_WRITE_4(hsotg, GINTMSK, gintmsk);
            spin_unlock(&hsotg->lock);
            dwc2_hcd_start(hsotg);
            spin_lock(&hsotg->lock);
            hsotg->op_state = OTG_STATE_A_HOST;
        }
    }

    if (gotgint & GOTGINT_A_DEV_TOUT_CHG)
        dev_dbg(hsotg->dev,
                " ++OTG Interrupt: A-Device Timeout Change++\n");
    if (gotgint & GOTGINT_DBNCE_DONE)
        dev_dbg(hsotg->dev, " ++OTG Interrupt: Debounce Done++\n");

    /* Clear GOTGINT */
    DWC2_WRITE_4(hsotg, GOTGINT, gotgint);
}