예제 #1
0
/**
 * dwc2_handle_conn_id_status_change_intr() - Handles the Connector ID Status
 * Change Interrupt
 *
 * @hsotg: Programming view of DWC_otg controller
 *
 * Reads the OTG Interrupt Register (GOTCTL) to determine whether this is a
 * Device to Host Mode transition or a Host to Device Mode transition. This only
 * occurs when the cable is connected/removed from the PHY connector.
 */
static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
{
    u32 gintmsk = DWC2_READ_4(hsotg, GINTMSK);

    /* Need to disable SOF interrupt immediately */
    gintmsk &= ~GINTSTS_SOF;
    DWC2_WRITE_4(hsotg, GINTMSK, gintmsk);

    dev_dbg(hsotg->dev, " ++Connector ID Status Change Interrupt++  (%s)\n",
            dwc2_is_host_mode(hsotg) ? "Host" : "Device");

    /*
     * Need to schedule a work, as there are possible DELAY function calls.
     * Release lock before scheduling workq as it holds spinlock during
     * scheduling.
     */
    if (hsotg->wq_otg) {
        spin_unlock(&hsotg->lock);
        workqueue_enqueue(hsotg->wq_otg, &hsotg->wf_otg, NULL);
        spin_lock(&hsotg->lock);
    }

    /* Clear interrupt */
    DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_CONIDSTSCHNG);
}
예제 #2
0
/**
 * dwc2_handle_usb_port_intr - handles OTG PRTINT interrupts.
 * When the PRTINT interrupt fires, there are certain status bits in the Host
 * Port that needs to get cleared.
 *
 * @hsotg: Programming view of DWC_otg controller
 */
static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
{
    u32 hprt0 = DWC2_READ_4(hsotg, HPRT0);

    if (hprt0 & HPRT0_ENACHG) {
        hprt0 &= ~HPRT0_ENA;
        DWC2_WRITE_4(hsotg, HPRT0, hprt0);
    }

    /* Clear interrupt */
    DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_PRTINT);
}
예제 #3
0
/*
 * This function returns the Core Interrupt register
 */
static u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg)
{
    u32 gintsts;
    u32 gintmsk;
    u32 gahbcfg;
    u32 gintmsk_common = GINTMSK_COMMON;

    gintsts = DWC2_READ_4(hsotg, GINTSTS);
    gintmsk = DWC2_READ_4(hsotg, GINTMSK);
    gahbcfg = DWC2_READ_4(hsotg, GAHBCFG);

    /* If any common interrupts set */
    if (gintsts & gintmsk_common)
        dev_dbg(hsotg->dev, "gintsts=%08x  gintmsk=%08x\n",
                gintsts, gintmsk);

    if (gahbcfg & GAHBCFG_GLBL_INTR_EN)
        return gintsts & gintmsk & gintmsk_common;
    else
        return 0;
}
예제 #4
0
/*
 * This interrupt indicates that the DWC_otg controller has detected a
 * resume or remote wakeup sequence. If the DWC_otg controller is in
 * low power mode, the handler must brings the controller out of low
 * power mode. The controller automatically begins resume signaling.
 * The handler schedules a time to stop resume signaling.
 */
static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
{
    int ret;
    dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n");
    dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state);

    if (dwc2_is_device_mode(hsotg)) {
        dev_dbg(hsotg->dev, "DSTS=0x%0x\n", DWC2_READ_4(hsotg, DSTS));
        if (hsotg->lx_state == DWC2_L2) {
            u32 dctl = DWC2_READ_4(hsotg, DCTL);

            /* Clear Remote Wakeup Signaling */
            dctl &= ~DCTL_RMTWKUPSIG;
            DWC2_WRITE_4(hsotg, DCTL, dctl);
            ret = dwc2_exit_hibernation(hsotg, true);
            if (ret && (ret != -ENOTSUPP))
                dev_err(hsotg->dev, "exit hibernation failed\n");

            call_gadget(hsotg, resume);
        }
        /* Change to L0 state */
        hsotg->lx_state = DWC2_L0;
    } else {
        if (hsotg->lx_state != DWC2_L1) {
            u32 pcgcctl = DWC2_READ_4(hsotg, PCGCTL);

            /* Restart the Phy Clock */
            pcgcctl &= ~PCGCTL_STOPPCLK;
            DWC2_WRITE_4(hsotg, PCGCTL, pcgcctl);
            callout_reset(&hsotg->wkp_timer, mstohz(71),
                          dwc2_wakeup_detected, hsotg);
        } else {
            /* Change to L0 state */
            hsotg->lx_state = DWC2_L0;
        }
    }

    /* Clear interrupt */
    DWC2_WRITE_4(hsotg, GINTSTS, GINTSTS_WKUPINT);
}
예제 #5
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);
}
예제 #6
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);
}
예제 #7
0
static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
			 struct dwc2_hcd_urb *urb)
{
	int dev_speed, hub_addr, hub_port;

	dev_vdbg(hsotg->dev, "%s()\n", __func__);

	/* Initialize QH */
	qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
	qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;

	qh->data_toggle = DWC2_HC_PID_DATA0;
	qh->maxp = dwc2_hcd_get_mps(&urb->pipe_info);
	INIT_LIST_HEAD(&qh->qtd_list);
	INIT_LIST_HEAD(&qh->qh_list_entry);

	/* FS/LS Endpoint on HS Hub, NOT virtual root hub */
	dev_speed = dwc2_host_get_speed(hsotg, urb->priv);

	dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port);
	qh->nak_frame = 0xffff;

	if ((dev_speed == USB_SPEED_LOW || dev_speed == USB_SPEED_FULL) &&
	    hub_addr != 0 && hub_addr != 1) {
		dev_vdbg(hsotg->dev,
			 "QH init: EP %d: TT found at hub addr %d, for port %d\n",
			 dwc2_hcd_get_ep_num(&urb->pipe_info), hub_addr,
			 hub_port);
		qh->do_split = 1;
	}

	if (qh->ep_type == USB_ENDPOINT_XFER_INT ||
	    qh->ep_type == USB_ENDPOINT_XFER_ISOC) {
		/* Compute scheduling parameters once and save them */
		u32 hprt, prtspd;

		/* Todo: Account for split transfers in the bus time */
		int bytecount =
			dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp);

		qh->usecs = dwc2_calc_bus_time(hsotg, qh->do_split ?
				USB_SPEED_HIGH : dev_speed, qh->ep_is_in,
				qh->ep_type == USB_ENDPOINT_XFER_ISOC,
				bytecount);

		/* Ensure frame_number corresponds to the reality */
		hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
		/* Start in a slightly future (micro)frame */
		qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number,
						     SCHEDULE_SLOP);
		qh->interval = urb->interval;
#if 0
		/* Increase interrupt polling rate for debugging */
		if (qh->ep_type == USB_ENDPOINT_XFER_INT)
			qh->interval = 8;
#endif
		hprt = DWC2_READ_4(hsotg, HPRT0);
		prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
		if (prtspd == HPRT0_SPD_HIGH_SPEED &&
		    (dev_speed == USB_SPEED_LOW ||
		     dev_speed == USB_SPEED_FULL)) {
			qh->interval *= 8;
			qh->sched_frame |= 0x7;
			qh->start_split_frame = qh->sched_frame;
		}
		dev_dbg(hsotg->dev, "interval=%d\n", qh->interval);
	}