天天看點

MSM8X60 USB控制器流程分析

MSM8X60 USB控制器流程分析,其中去掉了沒有用到的代碼,以便把握整個主線不被幹擾。

static int msm_otg_set_peripheral(struct otg_transceiver *xceiv,

            struct usb_gadget *gadget)

{

    struct msm_otg *dev = container_of(xceiv, struct msm_otg, otg);

    ............

    dev->otg.gadget = gadget;

    pr_info("peripheral driver registered w/ tranceiver\n");

    wake_lock(&dev->wlock);//鎖定wakelock,防止睡眠

    queue_work(dev->wq, &dev->sm_work);//排程work

    return 0;

}

static irqreturn_t msm_otg_irq(int irq, void *data)

{

    struct msm_otg *dev = data;

    u32 otgsc, sts, pc, sts_mask;

    int work = 0;

    enum usb_otg_state state;

    if (atomic_read(&dev->in_lpm)) {//插入usb時,如果處于睡眠,則喚醒

        disable_irq_nosync(dev->irq);

        wake_lock(&dev->wlock);//鎖定wakelock

        queue_work(dev->wq, &dev->otg_resume_work);//進入喚醒流程

        goto out;

    }

    otgsc = readl(USB_OTGSC);

    sts = readl(USB_USBSTS);

    sts_mask = (otgsc & OTGSC_INTR_MASK) >> 8;//該寄存器的中斷使能掩碼

    if (!((otgsc & sts_mask) || (sts & STS_PCI))) {//如果沒有變化,則迅速退出,因為該中斷為共享中斷

        ret = IRQ_NONE;

        goto out;

    }

    state = dev->otg.state;//取出上次的狀态

    if ((otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) {//檢測到id引腳有變換

        if (otgsc & OTGSC_ID) {//id引腳值為1,則設定id位,為client

            pr_debug("Id set\n");

            set_bit(ID, &dev->inputs);

        } else {//id引腳值為0,則清除id位,host

            pr_debug("Id clear\n");

            set_bit(A_BUS_REQ, &dev->inputs);

            clear_bit(ID, &dev->inputs);

        }

        writel(otgsc, USB_OTGSC);//清除該寄存器,以便下次接收中斷

        work = 1;

    } else if (otgsc & OTGSC_BSVIS) {//檢測到VBUS有變換

        writel(otgsc, USB_OTGSC);//清除該寄存器,以便下次接收中斷

        if ((state >= OTG_STATE_A_IDLE) &&

            !test_bit(ID_A, &dev->inputs))

            goto out;

        if (otgsc & OTGSC_BSV) {//表示usb cable插入

            pr_debug("BSV set\n");

            set_bit(B_SESS_VLD, &dev->inputs);

        } else {//表示usb cable移除

            pr_debug("BSV clear\n");

            clear_bit(B_SESS_VLD, &dev->inputs);

        }

        work = 1;

    } else if (sts & STS_PCI) {//表示端口變化中斷

        pc = readl(USB_PORTSC);

        pr_debug("portsc = %x\n", pc);

        ret = IRQ_NONE;

        work = 1;

        switch (state) {

        ..............

        default:

            PR_DEBUG_ON

            work = 0;

            break;

        }

    }

    if (work) {//work為1,則排程work,并鎖定wakelock

        wake_lock(&dev->wlock);

        queue_work(dev->wq, &dev->sm_work);

    }

out:

    return ret;

}

排程的work如下:

static void msm_otg_sm_work(struct work_struct *w)

{

    struct msm_otg    *dev = container_of(w, struct msm_otg, sm_work);

    int work = 0;

    enum usb_otg_state state;

    if (atomic_read(&dev->in_lpm))//進入runtime喚醒流程

        msm_otg_set_suspend(&dev->otg, 0);

    state = dev->otg.state;//第一次狀态沒有初始化,預設為0

    switch (state) {

    case OTG_STATE_UNDEFINED:

        otg_reset(&dev->otg, 1);//第一次所走路線

        if (!dev->otg.host || !is_host())//第一次所走路線

            set_bit(ID, &dev->inputs);

        if (dev->otg.gadget && is_b_sess_vld())

            set_bit(B_SESS_VLD, &dev->inputs);

        if ((test_bit(ID, &dev->inputs)) &&

                !test_bit(ID_A, &dev->inputs)) {

            dev->otg.state = OTG_STATE_B_IDLE;//第一次所走路線

        } else {

            set_bit(A_BUS_REQ, &dev->inputs);

            dev->otg.state = OTG_STATE_A_IDLE;

        }

        work = 1;//排程work

        break;

    case OTG_STATE_B_IDLE:

        dev->otg.default_a = 0;

        if (test_bit(B_SESS_VLD, &dev->inputs) &&

                !test_bit(ID_B, &dev->inputs)) {//第二次所走路線

            pr_debug("b_sess_vld\n");

            spin_lock_irqsave(&dev->lock, flags);

            dev->otg.state = OTG_STATE_B_PERIPHERAL;

            spin_unlock_irqrestore(&dev->lock, flags);

            msm_otg_set_power(&dev->otg, 0);

            msm_otg_start_peripheral(&dev->otg, 1);

        } else {

            msm_otg_set_power(&dev->otg, 0);第一次所走路線

            pr_debug("entering into lpm\n");

            msm_otg_put_suspend(dev);//進入runtime睡眠流程

            if (dev->pdata->ldo_set_voltage)

                dev->pdata->ldo_set_voltage(3075);

        }

        break;

    case OTG_STATE_B_PERIPHERAL:

        if (!test_bit(ID, &dev->inputs) ||

                test_bit(ID_A, &dev->inputs) ||

                test_bit(ID_B, &dev->inputs) ||

                !test_bit(B_SESS_VLD, &dev->inputs)) {

            pr_debug("!id  || id_a/b || !b_sess_vld\n");

            clear_bit(B_BUS_REQ, &dev->inputs);

            spin_lock_irqsave(&dev->lock, flags);

            dev->otg.state = OTG_STATE_B_IDLE;

            spin_unlock_irqrestore(&dev->lock, flags);

            msm_otg_start_peripheral(&dev->otg, 0);

            dev->b_last_se0_sess = jiffies;

            otg_reset(&dev->otg, 1);

            work = 1;

        }

        break;

    default:

        pr_err("invalid OTG state\n");

    }

    if (work)

        queue_work(dev->wq, &dev->sm_work);//第一次所走路線

    if (!work_pending(&dev->sm_work) && !hrtimer_active(&dev->timer) &&

            !work_pending(&dev->otg_resume_work))

        wake_unlock(&dev->wlock);//所用的work,timer的處理函數都執行完時,解鎖wakelock

}

1、沒有插入usb時,系統初始化第一次排程work後dev->otg.state為OTG_STATE_B_IDLE,再次排程work,進入low power模式,解鎖wakelock,睡眠。

2、插入usb時,系統從OTG_STATE_B_IDLE醒來,在otg irq中喚醒系統,并鎖定wakelock,然後繼續處理硬體中斷,開啟外圍裝置控制器。并設定dev->otg.state的狀态為OTG_STATE_B_PERIPHERAL;

3、當usb移除的時候,排程work,再次設定dev->otg.state為OTG_STATE_B_IDLE,并關閉裝置控制器。繼續排程work,進入low power模式,并解鎖wakelock,進入系統睡眠。

http://blog.chinaunix.net/uid-25909619-id-3323921.html