162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
462306a36Sopenharmony_ci * Author: Chao Xie <chao.xie@marvell.com>
562306a36Sopenharmony_ci *	   Neil Zhang <zhangwm@marvell.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/io.h>
1162306a36Sopenharmony_ci#include <linux/iopoll.h>
1262306a36Sopenharmony_ci#include <linux/uaccess.h>
1362306a36Sopenharmony_ci#include <linux/device.h>
1462306a36Sopenharmony_ci#include <linux/proc_fs.h>
1562306a36Sopenharmony_ci#include <linux/clk.h>
1662306a36Sopenharmony_ci#include <linux/workqueue.h>
1762306a36Sopenharmony_ci#include <linux/platform_device.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/usb.h>
2062306a36Sopenharmony_ci#include <linux/usb/ch9.h>
2162306a36Sopenharmony_ci#include <linux/usb/otg.h>
2262306a36Sopenharmony_ci#include <linux/usb/gadget.h>
2362306a36Sopenharmony_ci#include <linux/usb/hcd.h>
2462306a36Sopenharmony_ci#include <linux/platform_data/mv_usb.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "phy-mv-usb.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define	DRIVER_DESC	"Marvell USB OTG transceiver driver"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
3162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic const char driver_name[] = "mv-otg";
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic char *state_string[] = {
3662306a36Sopenharmony_ci	"undefined",
3762306a36Sopenharmony_ci	"b_idle",
3862306a36Sopenharmony_ci	"b_srp_init",
3962306a36Sopenharmony_ci	"b_peripheral",
4062306a36Sopenharmony_ci	"b_wait_acon",
4162306a36Sopenharmony_ci	"b_host",
4262306a36Sopenharmony_ci	"a_idle",
4362306a36Sopenharmony_ci	"a_wait_vrise",
4462306a36Sopenharmony_ci	"a_wait_bcon",
4562306a36Sopenharmony_ci	"a_host",
4662306a36Sopenharmony_ci	"a_suspend",
4762306a36Sopenharmony_ci	"a_peripheral",
4862306a36Sopenharmony_ci	"a_wait_vfall",
4962306a36Sopenharmony_ci	"a_vbus_err"
5062306a36Sopenharmony_ci};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic int mv_otg_set_vbus(struct usb_otg *otg, bool on)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	struct mv_otg *mvotg = container_of(otg->usb_phy, struct mv_otg, phy);
5562306a36Sopenharmony_ci	if (mvotg->pdata->set_vbus == NULL)
5662306a36Sopenharmony_ci		return -ENODEV;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	return mvotg->pdata->set_vbus(on);
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic int mv_otg_set_host(struct usb_otg *otg,
6262306a36Sopenharmony_ci			   struct usb_bus *host)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	otg->host = host;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return 0;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic int mv_otg_set_peripheral(struct usb_otg *otg,
7062306a36Sopenharmony_ci				 struct usb_gadget *gadget)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	otg->gadget = gadget;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	return 0;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic void mv_otg_run_state_machine(struct mv_otg *mvotg,
7862306a36Sopenharmony_ci				     unsigned long delay)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	dev_dbg(&mvotg->pdev->dev, "transceiver is updated\n");
8162306a36Sopenharmony_ci	if (!mvotg->qwork)
8262306a36Sopenharmony_ci		return;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	queue_delayed_work(mvotg->qwork, &mvotg->work, delay);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic void mv_otg_timer_await_bcon(struct timer_list *t)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct mv_otg *mvotg = from_timer(mvotg, t,
9062306a36Sopenharmony_ci					  otg_ctrl.timer[A_WAIT_BCON_TIMER]);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	mvotg->otg_ctrl.a_wait_bcon_timeout = 1;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	dev_info(&mvotg->pdev->dev, "B Device No Response!\n");
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (spin_trylock(&mvotg->wq_lock)) {
9762306a36Sopenharmony_ci		mv_otg_run_state_machine(mvotg, 0);
9862306a36Sopenharmony_ci		spin_unlock(&mvotg->wq_lock);
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic int mv_otg_cancel_timer(struct mv_otg *mvotg, unsigned int id)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct timer_list *timer;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (id >= OTG_TIMER_NUM)
10762306a36Sopenharmony_ci		return -EINVAL;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	timer = &mvotg->otg_ctrl.timer[id];
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (timer_pending(timer))
11262306a36Sopenharmony_ci		del_timer(timer);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	return 0;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic int mv_otg_set_timer(struct mv_otg *mvotg, unsigned int id,
11862306a36Sopenharmony_ci			    unsigned long interval)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct timer_list *timer;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (id >= OTG_TIMER_NUM)
12362306a36Sopenharmony_ci		return -EINVAL;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	timer = &mvotg->otg_ctrl.timer[id];
12662306a36Sopenharmony_ci	if (timer_pending(timer)) {
12762306a36Sopenharmony_ci		dev_err(&mvotg->pdev->dev, "Timer%d is already running\n", id);
12862306a36Sopenharmony_ci		return -EBUSY;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	timer->expires = jiffies + interval;
13262306a36Sopenharmony_ci	add_timer(timer);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	return 0;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic int mv_otg_reset(struct mv_otg *mvotg)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	u32 tmp;
14062306a36Sopenharmony_ci	int ret;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* Stop the controller */
14362306a36Sopenharmony_ci	tmp = readl(&mvotg->op_regs->usbcmd);
14462306a36Sopenharmony_ci	tmp &= ~USBCMD_RUN_STOP;
14562306a36Sopenharmony_ci	writel(tmp, &mvotg->op_regs->usbcmd);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	/* Reset the controller to get default values */
14862306a36Sopenharmony_ci	writel(USBCMD_CTRL_RESET, &mvotg->op_regs->usbcmd);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	ret = readl_poll_timeout_atomic(&mvotg->op_regs->usbcmd, tmp,
15162306a36Sopenharmony_ci				(tmp & USBCMD_CTRL_RESET), 10, 10000);
15262306a36Sopenharmony_ci	if (ret < 0) {
15362306a36Sopenharmony_ci		dev_err(&mvotg->pdev->dev,
15462306a36Sopenharmony_ci			"Wait for RESET completed TIMEOUT\n");
15562306a36Sopenharmony_ci		return ret;
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	writel(0x0, &mvotg->op_regs->usbintr);
15962306a36Sopenharmony_ci	tmp = readl(&mvotg->op_regs->usbsts);
16062306a36Sopenharmony_ci	writel(tmp, &mvotg->op_regs->usbsts);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	return 0;
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic void mv_otg_init_irq(struct mv_otg *mvotg)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	u32 otgsc;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	mvotg->irq_en = OTGSC_INTR_A_SESSION_VALID
17062306a36Sopenharmony_ci	    | OTGSC_INTR_A_VBUS_VALID;
17162306a36Sopenharmony_ci	mvotg->irq_status = OTGSC_INTSTS_A_SESSION_VALID
17262306a36Sopenharmony_ci	    | OTGSC_INTSTS_A_VBUS_VALID;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (mvotg->pdata->vbus == NULL) {
17562306a36Sopenharmony_ci		mvotg->irq_en |= OTGSC_INTR_B_SESSION_VALID
17662306a36Sopenharmony_ci		    | OTGSC_INTR_B_SESSION_END;
17762306a36Sopenharmony_ci		mvotg->irq_status |= OTGSC_INTSTS_B_SESSION_VALID
17862306a36Sopenharmony_ci		    | OTGSC_INTSTS_B_SESSION_END;
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	if (mvotg->pdata->id == NULL) {
18262306a36Sopenharmony_ci		mvotg->irq_en |= OTGSC_INTR_USB_ID;
18362306a36Sopenharmony_ci		mvotg->irq_status |= OTGSC_INTSTS_USB_ID;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	otgsc = readl(&mvotg->op_regs->otgsc);
18762306a36Sopenharmony_ci	otgsc |= mvotg->irq_en;
18862306a36Sopenharmony_ci	writel(otgsc, &mvotg->op_regs->otgsc);
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic void mv_otg_start_host(struct mv_otg *mvotg, int on)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci#ifdef CONFIG_USB
19462306a36Sopenharmony_ci	struct usb_otg *otg = mvotg->phy.otg;
19562306a36Sopenharmony_ci	struct usb_hcd *hcd;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	if (!otg->host)
19862306a36Sopenharmony_ci		return;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	dev_info(&mvotg->pdev->dev, "%s host\n", on ? "start" : "stop");
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	hcd = bus_to_hcd(otg->host);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	if (on) {
20562306a36Sopenharmony_ci		usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
20662306a36Sopenharmony_ci		device_wakeup_enable(hcd->self.controller);
20762306a36Sopenharmony_ci	} else {
20862306a36Sopenharmony_ci		usb_remove_hcd(hcd);
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci#endif /* CONFIG_USB */
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic void mv_otg_start_periphrals(struct mv_otg *mvotg, int on)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct usb_otg *otg = mvotg->phy.otg;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	if (!otg->gadget)
21862306a36Sopenharmony_ci		return;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	dev_info(mvotg->phy.dev, "gadget %s\n", on ? "on" : "off");
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	if (on)
22362306a36Sopenharmony_ci		usb_gadget_vbus_connect(otg->gadget);
22462306a36Sopenharmony_ci	else
22562306a36Sopenharmony_ci		usb_gadget_vbus_disconnect(otg->gadget);
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic void otg_clock_enable(struct mv_otg *mvotg)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	clk_prepare_enable(mvotg->clk);
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic void otg_clock_disable(struct mv_otg *mvotg)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	clk_disable_unprepare(mvotg->clk);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic int mv_otg_enable_internal(struct mv_otg *mvotg)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	int retval = 0;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (mvotg->active)
24362306a36Sopenharmony_ci		return 0;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	dev_dbg(&mvotg->pdev->dev, "otg enabled\n");
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	otg_clock_enable(mvotg);
24862306a36Sopenharmony_ci	if (mvotg->pdata->phy_init) {
24962306a36Sopenharmony_ci		retval = mvotg->pdata->phy_init(mvotg->phy_regs);
25062306a36Sopenharmony_ci		if (retval) {
25162306a36Sopenharmony_ci			dev_err(&mvotg->pdev->dev,
25262306a36Sopenharmony_ci				"init phy error %d\n", retval);
25362306a36Sopenharmony_ci			otg_clock_disable(mvotg);
25462306a36Sopenharmony_ci			return retval;
25562306a36Sopenharmony_ci		}
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci	mvotg->active = 1;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	return 0;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic int mv_otg_enable(struct mv_otg *mvotg)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	if (mvotg->clock_gating)
26662306a36Sopenharmony_ci		return mv_otg_enable_internal(mvotg);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return 0;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic void mv_otg_disable_internal(struct mv_otg *mvotg)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	if (mvotg->active) {
27462306a36Sopenharmony_ci		dev_dbg(&mvotg->pdev->dev, "otg disabled\n");
27562306a36Sopenharmony_ci		if (mvotg->pdata->phy_deinit)
27662306a36Sopenharmony_ci			mvotg->pdata->phy_deinit(mvotg->phy_regs);
27762306a36Sopenharmony_ci		otg_clock_disable(mvotg);
27862306a36Sopenharmony_ci		mvotg->active = 0;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic void mv_otg_disable(struct mv_otg *mvotg)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	if (mvotg->clock_gating)
28562306a36Sopenharmony_ci		mv_otg_disable_internal(mvotg);
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic void mv_otg_update_inputs(struct mv_otg *mvotg)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl;
29162306a36Sopenharmony_ci	u32 otgsc;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	otgsc = readl(&mvotg->op_regs->otgsc);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (mvotg->pdata->vbus) {
29662306a36Sopenharmony_ci		if (mvotg->pdata->vbus->poll() == VBUS_HIGH) {
29762306a36Sopenharmony_ci			otg_ctrl->b_sess_vld = 1;
29862306a36Sopenharmony_ci			otg_ctrl->b_sess_end = 0;
29962306a36Sopenharmony_ci		} else {
30062306a36Sopenharmony_ci			otg_ctrl->b_sess_vld = 0;
30162306a36Sopenharmony_ci			otg_ctrl->b_sess_end = 1;
30262306a36Sopenharmony_ci		}
30362306a36Sopenharmony_ci	} else {
30462306a36Sopenharmony_ci		otg_ctrl->b_sess_vld = !!(otgsc & OTGSC_STS_B_SESSION_VALID);
30562306a36Sopenharmony_ci		otg_ctrl->b_sess_end = !!(otgsc & OTGSC_STS_B_SESSION_END);
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	if (mvotg->pdata->id)
30962306a36Sopenharmony_ci		otg_ctrl->id = !!mvotg->pdata->id->poll();
31062306a36Sopenharmony_ci	else
31162306a36Sopenharmony_ci		otg_ctrl->id = !!(otgsc & OTGSC_STS_USB_ID);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (mvotg->pdata->otg_force_a_bus_req && !otg_ctrl->id)
31462306a36Sopenharmony_ci		otg_ctrl->a_bus_req = 1;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	otg_ctrl->a_sess_vld = !!(otgsc & OTGSC_STS_A_SESSION_VALID);
31762306a36Sopenharmony_ci	otg_ctrl->a_vbus_vld = !!(otgsc & OTGSC_STS_A_VBUS_VALID);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	dev_dbg(&mvotg->pdev->dev, "%s: ", __func__);
32062306a36Sopenharmony_ci	dev_dbg(&mvotg->pdev->dev, "id %d\n", otg_ctrl->id);
32162306a36Sopenharmony_ci	dev_dbg(&mvotg->pdev->dev, "b_sess_vld %d\n", otg_ctrl->b_sess_vld);
32262306a36Sopenharmony_ci	dev_dbg(&mvotg->pdev->dev, "b_sess_end %d\n", otg_ctrl->b_sess_end);
32362306a36Sopenharmony_ci	dev_dbg(&mvotg->pdev->dev, "a_vbus_vld %d\n", otg_ctrl->a_vbus_vld);
32462306a36Sopenharmony_ci	dev_dbg(&mvotg->pdev->dev, "a_sess_vld %d\n", otg_ctrl->a_sess_vld);
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic void mv_otg_update_state(struct mv_otg *mvotg)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl;
33062306a36Sopenharmony_ci	int old_state = mvotg->phy.otg->state;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	switch (old_state) {
33362306a36Sopenharmony_ci	case OTG_STATE_UNDEFINED:
33462306a36Sopenharmony_ci		mvotg->phy.otg->state = OTG_STATE_B_IDLE;
33562306a36Sopenharmony_ci		fallthrough;
33662306a36Sopenharmony_ci	case OTG_STATE_B_IDLE:
33762306a36Sopenharmony_ci		if (otg_ctrl->id == 0)
33862306a36Sopenharmony_ci			mvotg->phy.otg->state = OTG_STATE_A_IDLE;
33962306a36Sopenharmony_ci		else if (otg_ctrl->b_sess_vld)
34062306a36Sopenharmony_ci			mvotg->phy.otg->state = OTG_STATE_B_PERIPHERAL;
34162306a36Sopenharmony_ci		break;
34262306a36Sopenharmony_ci	case OTG_STATE_B_PERIPHERAL:
34362306a36Sopenharmony_ci		if (!otg_ctrl->b_sess_vld || otg_ctrl->id == 0)
34462306a36Sopenharmony_ci			mvotg->phy.otg->state = OTG_STATE_B_IDLE;
34562306a36Sopenharmony_ci		break;
34662306a36Sopenharmony_ci	case OTG_STATE_A_IDLE:
34762306a36Sopenharmony_ci		if (otg_ctrl->id)
34862306a36Sopenharmony_ci			mvotg->phy.otg->state = OTG_STATE_B_IDLE;
34962306a36Sopenharmony_ci		else if (!(otg_ctrl->a_bus_drop) &&
35062306a36Sopenharmony_ci			 (otg_ctrl->a_bus_req || otg_ctrl->a_srp_det))
35162306a36Sopenharmony_ci			mvotg->phy.otg->state = OTG_STATE_A_WAIT_VRISE;
35262306a36Sopenharmony_ci		break;
35362306a36Sopenharmony_ci	case OTG_STATE_A_WAIT_VRISE:
35462306a36Sopenharmony_ci		if (otg_ctrl->a_vbus_vld)
35562306a36Sopenharmony_ci			mvotg->phy.otg->state = OTG_STATE_A_WAIT_BCON;
35662306a36Sopenharmony_ci		break;
35762306a36Sopenharmony_ci	case OTG_STATE_A_WAIT_BCON:
35862306a36Sopenharmony_ci		if (otg_ctrl->id || otg_ctrl->a_bus_drop
35962306a36Sopenharmony_ci		    || otg_ctrl->a_wait_bcon_timeout) {
36062306a36Sopenharmony_ci			mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
36162306a36Sopenharmony_ci			mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
36262306a36Sopenharmony_ci			mvotg->phy.otg->state = OTG_STATE_A_WAIT_VFALL;
36362306a36Sopenharmony_ci			otg_ctrl->a_bus_req = 0;
36462306a36Sopenharmony_ci		} else if (!otg_ctrl->a_vbus_vld) {
36562306a36Sopenharmony_ci			mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
36662306a36Sopenharmony_ci			mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
36762306a36Sopenharmony_ci			mvotg->phy.otg->state = OTG_STATE_A_VBUS_ERR;
36862306a36Sopenharmony_ci		} else if (otg_ctrl->b_conn) {
36962306a36Sopenharmony_ci			mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
37062306a36Sopenharmony_ci			mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
37162306a36Sopenharmony_ci			mvotg->phy.otg->state = OTG_STATE_A_HOST;
37262306a36Sopenharmony_ci		}
37362306a36Sopenharmony_ci		break;
37462306a36Sopenharmony_ci	case OTG_STATE_A_HOST:
37562306a36Sopenharmony_ci		if (otg_ctrl->id || !otg_ctrl->b_conn
37662306a36Sopenharmony_ci		    || otg_ctrl->a_bus_drop)
37762306a36Sopenharmony_ci			mvotg->phy.otg->state = OTG_STATE_A_WAIT_BCON;
37862306a36Sopenharmony_ci		else if (!otg_ctrl->a_vbus_vld)
37962306a36Sopenharmony_ci			mvotg->phy.otg->state = OTG_STATE_A_VBUS_ERR;
38062306a36Sopenharmony_ci		break;
38162306a36Sopenharmony_ci	case OTG_STATE_A_WAIT_VFALL:
38262306a36Sopenharmony_ci		if (otg_ctrl->id
38362306a36Sopenharmony_ci		    || (!otg_ctrl->b_conn && otg_ctrl->a_sess_vld)
38462306a36Sopenharmony_ci		    || otg_ctrl->a_bus_req)
38562306a36Sopenharmony_ci			mvotg->phy.otg->state = OTG_STATE_A_IDLE;
38662306a36Sopenharmony_ci		break;
38762306a36Sopenharmony_ci	case OTG_STATE_A_VBUS_ERR:
38862306a36Sopenharmony_ci		if (otg_ctrl->id || otg_ctrl->a_clr_err
38962306a36Sopenharmony_ci		    || otg_ctrl->a_bus_drop) {
39062306a36Sopenharmony_ci			otg_ctrl->a_clr_err = 0;
39162306a36Sopenharmony_ci			mvotg->phy.otg->state = OTG_STATE_A_WAIT_VFALL;
39262306a36Sopenharmony_ci		}
39362306a36Sopenharmony_ci		break;
39462306a36Sopenharmony_ci	default:
39562306a36Sopenharmony_ci		break;
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic void mv_otg_work(struct work_struct *work)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct mv_otg *mvotg;
40262306a36Sopenharmony_ci	struct usb_otg *otg;
40362306a36Sopenharmony_ci	int old_state;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	mvotg = container_of(to_delayed_work(work), struct mv_otg, work);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cirun:
40862306a36Sopenharmony_ci	/* work queue is single thread, or we need spin_lock to protect */
40962306a36Sopenharmony_ci	otg = mvotg->phy.otg;
41062306a36Sopenharmony_ci	old_state = otg->state;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	if (!mvotg->active)
41362306a36Sopenharmony_ci		return;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	mv_otg_update_inputs(mvotg);
41662306a36Sopenharmony_ci	mv_otg_update_state(mvotg);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	if (old_state != mvotg->phy.otg->state) {
41962306a36Sopenharmony_ci		dev_info(&mvotg->pdev->dev, "change from state %s to %s\n",
42062306a36Sopenharmony_ci			 state_string[old_state],
42162306a36Sopenharmony_ci			 state_string[mvotg->phy.otg->state]);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci		switch (mvotg->phy.otg->state) {
42462306a36Sopenharmony_ci		case OTG_STATE_B_IDLE:
42562306a36Sopenharmony_ci			otg->default_a = 0;
42662306a36Sopenharmony_ci			if (old_state == OTG_STATE_B_PERIPHERAL)
42762306a36Sopenharmony_ci				mv_otg_start_periphrals(mvotg, 0);
42862306a36Sopenharmony_ci			mv_otg_reset(mvotg);
42962306a36Sopenharmony_ci			mv_otg_disable(mvotg);
43062306a36Sopenharmony_ci			usb_phy_set_event(&mvotg->phy, USB_EVENT_NONE);
43162306a36Sopenharmony_ci			break;
43262306a36Sopenharmony_ci		case OTG_STATE_B_PERIPHERAL:
43362306a36Sopenharmony_ci			mv_otg_enable(mvotg);
43462306a36Sopenharmony_ci			mv_otg_start_periphrals(mvotg, 1);
43562306a36Sopenharmony_ci			usb_phy_set_event(&mvotg->phy, USB_EVENT_ENUMERATED);
43662306a36Sopenharmony_ci			break;
43762306a36Sopenharmony_ci		case OTG_STATE_A_IDLE:
43862306a36Sopenharmony_ci			otg->default_a = 1;
43962306a36Sopenharmony_ci			mv_otg_enable(mvotg);
44062306a36Sopenharmony_ci			if (old_state == OTG_STATE_A_WAIT_VFALL)
44162306a36Sopenharmony_ci				mv_otg_start_host(mvotg, 0);
44262306a36Sopenharmony_ci			mv_otg_reset(mvotg);
44362306a36Sopenharmony_ci			break;
44462306a36Sopenharmony_ci		case OTG_STATE_A_WAIT_VRISE:
44562306a36Sopenharmony_ci			mv_otg_set_vbus(otg, 1);
44662306a36Sopenharmony_ci			break;
44762306a36Sopenharmony_ci		case OTG_STATE_A_WAIT_BCON:
44862306a36Sopenharmony_ci			if (old_state != OTG_STATE_A_HOST)
44962306a36Sopenharmony_ci				mv_otg_start_host(mvotg, 1);
45062306a36Sopenharmony_ci			mv_otg_set_timer(mvotg, A_WAIT_BCON_TIMER,
45162306a36Sopenharmony_ci					 T_A_WAIT_BCON);
45262306a36Sopenharmony_ci			/*
45362306a36Sopenharmony_ci			 * Now, we directly enter A_HOST. So set b_conn = 1
45462306a36Sopenharmony_ci			 * here. In fact, it need host driver to notify us.
45562306a36Sopenharmony_ci			 */
45662306a36Sopenharmony_ci			mvotg->otg_ctrl.b_conn = 1;
45762306a36Sopenharmony_ci			break;
45862306a36Sopenharmony_ci		case OTG_STATE_A_HOST:
45962306a36Sopenharmony_ci			break;
46062306a36Sopenharmony_ci		case OTG_STATE_A_WAIT_VFALL:
46162306a36Sopenharmony_ci			/*
46262306a36Sopenharmony_ci			 * Now, we has exited A_HOST. So set b_conn = 0
46362306a36Sopenharmony_ci			 * here. In fact, it need host driver to notify us.
46462306a36Sopenharmony_ci			 */
46562306a36Sopenharmony_ci			mvotg->otg_ctrl.b_conn = 0;
46662306a36Sopenharmony_ci			mv_otg_set_vbus(otg, 0);
46762306a36Sopenharmony_ci			break;
46862306a36Sopenharmony_ci		case OTG_STATE_A_VBUS_ERR:
46962306a36Sopenharmony_ci			break;
47062306a36Sopenharmony_ci		default:
47162306a36Sopenharmony_ci			break;
47262306a36Sopenharmony_ci		}
47362306a36Sopenharmony_ci		goto run;
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic irqreturn_t mv_otg_irq(int irq, void *dev)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	struct mv_otg *mvotg = dev;
48062306a36Sopenharmony_ci	u32 otgsc;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	otgsc = readl(&mvotg->op_regs->otgsc);
48362306a36Sopenharmony_ci	writel(otgsc, &mvotg->op_regs->otgsc);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	/*
48662306a36Sopenharmony_ci	 * if we have vbus, then the vbus detection for B-device
48762306a36Sopenharmony_ci	 * will be done by mv_otg_inputs_irq().
48862306a36Sopenharmony_ci	 */
48962306a36Sopenharmony_ci	if (mvotg->pdata->vbus)
49062306a36Sopenharmony_ci		if ((otgsc & OTGSC_STS_USB_ID) &&
49162306a36Sopenharmony_ci		    !(otgsc & OTGSC_INTSTS_USB_ID))
49262306a36Sopenharmony_ci			return IRQ_NONE;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	if ((otgsc & mvotg->irq_status) == 0)
49562306a36Sopenharmony_ci		return IRQ_NONE;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	mv_otg_run_state_machine(mvotg, 0);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	return IRQ_HANDLED;
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic irqreturn_t mv_otg_inputs_irq(int irq, void *dev)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	struct mv_otg *mvotg = dev;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	/* The clock may disabled at this time */
50762306a36Sopenharmony_ci	if (!mvotg->active) {
50862306a36Sopenharmony_ci		mv_otg_enable(mvotg);
50962306a36Sopenharmony_ci		mv_otg_init_irq(mvotg);
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	mv_otg_run_state_machine(mvotg, 0);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	return IRQ_HANDLED;
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_cistatic ssize_t
51862306a36Sopenharmony_cia_bus_req_show(struct device *dev, struct device_attribute *attr, char *buf)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	struct mv_otg *mvotg = dev_get_drvdata(dev);
52162306a36Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%d\n",
52262306a36Sopenharmony_ci			 mvotg->otg_ctrl.a_bus_req);
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_cistatic ssize_t
52662306a36Sopenharmony_cia_bus_req_store(struct device *dev, struct device_attribute *attr,
52762306a36Sopenharmony_ci	      const char *buf, size_t count)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	struct mv_otg *mvotg = dev_get_drvdata(dev);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	if (count > 2)
53262306a36Sopenharmony_ci		return -1;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	/* We will use this interface to change to A device */
53562306a36Sopenharmony_ci	if (mvotg->phy.otg->state != OTG_STATE_B_IDLE
53662306a36Sopenharmony_ci	    && mvotg->phy.otg->state != OTG_STATE_A_IDLE)
53762306a36Sopenharmony_ci		return -1;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	/* The clock may disabled and we need to set irq for ID detected */
54062306a36Sopenharmony_ci	mv_otg_enable(mvotg);
54162306a36Sopenharmony_ci	mv_otg_init_irq(mvotg);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (buf[0] == '1') {
54462306a36Sopenharmony_ci		mvotg->otg_ctrl.a_bus_req = 1;
54562306a36Sopenharmony_ci		mvotg->otg_ctrl.a_bus_drop = 0;
54662306a36Sopenharmony_ci		dev_dbg(&mvotg->pdev->dev,
54762306a36Sopenharmony_ci			"User request: a_bus_req = 1\n");
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci		if (spin_trylock(&mvotg->wq_lock)) {
55062306a36Sopenharmony_ci			mv_otg_run_state_machine(mvotg, 0);
55162306a36Sopenharmony_ci			spin_unlock(&mvotg->wq_lock);
55262306a36Sopenharmony_ci		}
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	return count;
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(a_bus_req);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistatic ssize_t
56162306a36Sopenharmony_cia_clr_err_store(struct device *dev, struct device_attribute *attr,
56262306a36Sopenharmony_ci	      const char *buf, size_t count)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	struct mv_otg *mvotg = dev_get_drvdata(dev);
56562306a36Sopenharmony_ci	if (!mvotg->phy.otg->default_a)
56662306a36Sopenharmony_ci		return -1;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	if (count > 2)
56962306a36Sopenharmony_ci		return -1;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	if (buf[0] == '1') {
57262306a36Sopenharmony_ci		mvotg->otg_ctrl.a_clr_err = 1;
57362306a36Sopenharmony_ci		dev_dbg(&mvotg->pdev->dev,
57462306a36Sopenharmony_ci			"User request: a_clr_err = 1\n");
57562306a36Sopenharmony_ci	}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	if (spin_trylock(&mvotg->wq_lock)) {
57862306a36Sopenharmony_ci		mv_otg_run_state_machine(mvotg, 0);
57962306a36Sopenharmony_ci		spin_unlock(&mvotg->wq_lock);
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	return count;
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic DEVICE_ATTR_WO(a_clr_err);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_cistatic ssize_t
58862306a36Sopenharmony_cia_bus_drop_show(struct device *dev, struct device_attribute *attr,
58962306a36Sopenharmony_ci	       char *buf)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	struct mv_otg *mvotg = dev_get_drvdata(dev);
59262306a36Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%d\n",
59362306a36Sopenharmony_ci			 mvotg->otg_ctrl.a_bus_drop);
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic ssize_t
59762306a36Sopenharmony_cia_bus_drop_store(struct device *dev, struct device_attribute *attr,
59862306a36Sopenharmony_ci	       const char *buf, size_t count)
59962306a36Sopenharmony_ci{
60062306a36Sopenharmony_ci	struct mv_otg *mvotg = dev_get_drvdata(dev);
60162306a36Sopenharmony_ci	if (!mvotg->phy.otg->default_a)
60262306a36Sopenharmony_ci		return -1;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	if (count > 2)
60562306a36Sopenharmony_ci		return -1;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	if (buf[0] == '0') {
60862306a36Sopenharmony_ci		mvotg->otg_ctrl.a_bus_drop = 0;
60962306a36Sopenharmony_ci		dev_dbg(&mvotg->pdev->dev,
61062306a36Sopenharmony_ci			"User request: a_bus_drop = 0\n");
61162306a36Sopenharmony_ci	} else if (buf[0] == '1') {
61262306a36Sopenharmony_ci		mvotg->otg_ctrl.a_bus_drop = 1;
61362306a36Sopenharmony_ci		mvotg->otg_ctrl.a_bus_req = 0;
61462306a36Sopenharmony_ci		dev_dbg(&mvotg->pdev->dev,
61562306a36Sopenharmony_ci			"User request: a_bus_drop = 1\n");
61662306a36Sopenharmony_ci		dev_dbg(&mvotg->pdev->dev,
61762306a36Sopenharmony_ci			"User request: and a_bus_req = 0\n");
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	if (spin_trylock(&mvotg->wq_lock)) {
62162306a36Sopenharmony_ci		mv_otg_run_state_machine(mvotg, 0);
62262306a36Sopenharmony_ci		spin_unlock(&mvotg->wq_lock);
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	return count;
62662306a36Sopenharmony_ci}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(a_bus_drop);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_cistatic struct attribute *inputs_attrs[] = {
63162306a36Sopenharmony_ci	&dev_attr_a_bus_req.attr,
63262306a36Sopenharmony_ci	&dev_attr_a_clr_err.attr,
63362306a36Sopenharmony_ci	&dev_attr_a_bus_drop.attr,
63462306a36Sopenharmony_ci	NULL,
63562306a36Sopenharmony_ci};
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_cistatic const struct attribute_group inputs_attr_group = {
63862306a36Sopenharmony_ci	.name = "inputs",
63962306a36Sopenharmony_ci	.attrs = inputs_attrs,
64062306a36Sopenharmony_ci};
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cistatic const struct attribute_group *mv_otg_groups[] = {
64362306a36Sopenharmony_ci	&inputs_attr_group,
64462306a36Sopenharmony_ci	NULL,
64562306a36Sopenharmony_ci};
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic void mv_otg_remove(struct platform_device *pdev)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	struct mv_otg *mvotg = platform_get_drvdata(pdev);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	if (mvotg->qwork)
65262306a36Sopenharmony_ci		destroy_workqueue(mvotg->qwork);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	mv_otg_disable(mvotg);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	usb_remove_phy(&mvotg->phy);
65762306a36Sopenharmony_ci}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_cistatic int mv_otg_probe(struct platform_device *pdev)
66062306a36Sopenharmony_ci{
66162306a36Sopenharmony_ci	struct mv_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
66262306a36Sopenharmony_ci	struct mv_otg *mvotg;
66362306a36Sopenharmony_ci	struct usb_otg *otg;
66462306a36Sopenharmony_ci	struct resource *r;
66562306a36Sopenharmony_ci	int retval = 0, i;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	if (pdata == NULL) {
66862306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to get platform data\n");
66962306a36Sopenharmony_ci		return -ENODEV;
67062306a36Sopenharmony_ci	}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	mvotg = devm_kzalloc(&pdev->dev, sizeof(*mvotg), GFP_KERNEL);
67362306a36Sopenharmony_ci	if (!mvotg)
67462306a36Sopenharmony_ci		return -ENOMEM;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
67762306a36Sopenharmony_ci	if (!otg)
67862306a36Sopenharmony_ci		return -ENOMEM;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	platform_set_drvdata(pdev, mvotg);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	mvotg->pdev = pdev;
68362306a36Sopenharmony_ci	mvotg->pdata = pdata;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	mvotg->clk = devm_clk_get(&pdev->dev, NULL);
68662306a36Sopenharmony_ci	if (IS_ERR(mvotg->clk))
68762306a36Sopenharmony_ci		return PTR_ERR(mvotg->clk);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	mvotg->qwork = create_singlethread_workqueue("mv_otg_queue");
69062306a36Sopenharmony_ci	if (!mvotg->qwork) {
69162306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "cannot create workqueue for OTG\n");
69262306a36Sopenharmony_ci		return -ENOMEM;
69362306a36Sopenharmony_ci	}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	INIT_DELAYED_WORK(&mvotg->work, mv_otg_work);
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	/* OTG common part */
69862306a36Sopenharmony_ci	mvotg->pdev = pdev;
69962306a36Sopenharmony_ci	mvotg->phy.dev = &pdev->dev;
70062306a36Sopenharmony_ci	mvotg->phy.otg = otg;
70162306a36Sopenharmony_ci	mvotg->phy.label = driver_name;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	otg->state = OTG_STATE_UNDEFINED;
70462306a36Sopenharmony_ci	otg->usb_phy = &mvotg->phy;
70562306a36Sopenharmony_ci	otg->set_host = mv_otg_set_host;
70662306a36Sopenharmony_ci	otg->set_peripheral = mv_otg_set_peripheral;
70762306a36Sopenharmony_ci	otg->set_vbus = mv_otg_set_vbus;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	for (i = 0; i < OTG_TIMER_NUM; i++)
71062306a36Sopenharmony_ci		timer_setup(&mvotg->otg_ctrl.timer[i],
71162306a36Sopenharmony_ci			    mv_otg_timer_await_bcon, 0);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	r = platform_get_resource_byname(mvotg->pdev,
71462306a36Sopenharmony_ci					 IORESOURCE_MEM, "phyregs");
71562306a36Sopenharmony_ci	if (r == NULL) {
71662306a36Sopenharmony_ci		dev_err(&pdev->dev, "no phy I/O memory resource defined\n");
71762306a36Sopenharmony_ci		retval = -ENODEV;
71862306a36Sopenharmony_ci		goto err_destroy_workqueue;
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	mvotg->phy_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r));
72262306a36Sopenharmony_ci	if (mvotg->phy_regs == NULL) {
72362306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to map phy I/O memory\n");
72462306a36Sopenharmony_ci		retval = -EFAULT;
72562306a36Sopenharmony_ci		goto err_destroy_workqueue;
72662306a36Sopenharmony_ci	}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	r = platform_get_resource_byname(mvotg->pdev,
72962306a36Sopenharmony_ci					 IORESOURCE_MEM, "capregs");
73062306a36Sopenharmony_ci	if (r == NULL) {
73162306a36Sopenharmony_ci		dev_err(&pdev->dev, "no I/O memory resource defined\n");
73262306a36Sopenharmony_ci		retval = -ENODEV;
73362306a36Sopenharmony_ci		goto err_destroy_workqueue;
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	mvotg->cap_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r));
73762306a36Sopenharmony_ci	if (mvotg->cap_regs == NULL) {
73862306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to map I/O memory\n");
73962306a36Sopenharmony_ci		retval = -EFAULT;
74062306a36Sopenharmony_ci		goto err_destroy_workqueue;
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	/* we will acces controller register, so enable the udc controller */
74462306a36Sopenharmony_ci	retval = mv_otg_enable_internal(mvotg);
74562306a36Sopenharmony_ci	if (retval) {
74662306a36Sopenharmony_ci		dev_err(&pdev->dev, "mv otg enable error %d\n", retval);
74762306a36Sopenharmony_ci		goto err_destroy_workqueue;
74862306a36Sopenharmony_ci	}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	mvotg->op_regs =
75162306a36Sopenharmony_ci		(struct mv_otg_regs __iomem *) ((unsigned long) mvotg->cap_regs
75262306a36Sopenharmony_ci			+ (readl(mvotg->cap_regs) & CAPLENGTH_MASK));
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	if (pdata->id) {
75562306a36Sopenharmony_ci		retval = devm_request_threaded_irq(&pdev->dev, pdata->id->irq,
75662306a36Sopenharmony_ci						NULL, mv_otg_inputs_irq,
75762306a36Sopenharmony_ci						IRQF_ONESHOT, "id", mvotg);
75862306a36Sopenharmony_ci		if (retval) {
75962306a36Sopenharmony_ci			dev_info(&pdev->dev,
76062306a36Sopenharmony_ci				 "Failed to request irq for ID\n");
76162306a36Sopenharmony_ci			pdata->id = NULL;
76262306a36Sopenharmony_ci		}
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	if (pdata->vbus) {
76662306a36Sopenharmony_ci		mvotg->clock_gating = 1;
76762306a36Sopenharmony_ci		retval = devm_request_threaded_irq(&pdev->dev, pdata->vbus->irq,
76862306a36Sopenharmony_ci						NULL, mv_otg_inputs_irq,
76962306a36Sopenharmony_ci						IRQF_ONESHOT, "vbus", mvotg);
77062306a36Sopenharmony_ci		if (retval) {
77162306a36Sopenharmony_ci			dev_info(&pdev->dev,
77262306a36Sopenharmony_ci				 "Failed to request irq for VBUS, "
77362306a36Sopenharmony_ci				 "disable clock gating\n");
77462306a36Sopenharmony_ci			mvotg->clock_gating = 0;
77562306a36Sopenharmony_ci			pdata->vbus = NULL;
77662306a36Sopenharmony_ci		}
77762306a36Sopenharmony_ci	}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	if (pdata->disable_otg_clock_gating)
78062306a36Sopenharmony_ci		mvotg->clock_gating = 0;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	mv_otg_reset(mvotg);
78362306a36Sopenharmony_ci	mv_otg_init_irq(mvotg);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	r = platform_get_resource(mvotg->pdev, IORESOURCE_IRQ, 0);
78662306a36Sopenharmony_ci	if (r == NULL) {
78762306a36Sopenharmony_ci		dev_err(&pdev->dev, "no IRQ resource defined\n");
78862306a36Sopenharmony_ci		retval = -ENODEV;
78962306a36Sopenharmony_ci		goto err_disable_clk;
79062306a36Sopenharmony_ci	}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	mvotg->irq = r->start;
79362306a36Sopenharmony_ci	if (devm_request_irq(&pdev->dev, mvotg->irq, mv_otg_irq, IRQF_SHARED,
79462306a36Sopenharmony_ci			driver_name, mvotg)) {
79562306a36Sopenharmony_ci		dev_err(&pdev->dev, "Request irq %d for OTG failed\n",
79662306a36Sopenharmony_ci			mvotg->irq);
79762306a36Sopenharmony_ci		mvotg->irq = 0;
79862306a36Sopenharmony_ci		retval = -ENODEV;
79962306a36Sopenharmony_ci		goto err_disable_clk;
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	retval = usb_add_phy(&mvotg->phy, USB_PHY_TYPE_USB2);
80362306a36Sopenharmony_ci	if (retval < 0) {
80462306a36Sopenharmony_ci		dev_err(&pdev->dev, "can't register transceiver, %d\n",
80562306a36Sopenharmony_ci			retval);
80662306a36Sopenharmony_ci		goto err_disable_clk;
80762306a36Sopenharmony_ci	}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	spin_lock_init(&mvotg->wq_lock);
81062306a36Sopenharmony_ci	if (spin_trylock(&mvotg->wq_lock)) {
81162306a36Sopenharmony_ci		mv_otg_run_state_machine(mvotg, 2 * HZ);
81262306a36Sopenharmony_ci		spin_unlock(&mvotg->wq_lock);
81362306a36Sopenharmony_ci	}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	dev_info(&pdev->dev,
81662306a36Sopenharmony_ci		 "successful probe OTG device %s clock gating.\n",
81762306a36Sopenharmony_ci		 mvotg->clock_gating ? "with" : "without");
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	return 0;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_cierr_disable_clk:
82262306a36Sopenharmony_ci	mv_otg_disable_internal(mvotg);
82362306a36Sopenharmony_cierr_destroy_workqueue:
82462306a36Sopenharmony_ci	destroy_workqueue(mvotg->qwork);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	return retval;
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci#ifdef CONFIG_PM
83062306a36Sopenharmony_cistatic int mv_otg_suspend(struct platform_device *pdev, pm_message_t state)
83162306a36Sopenharmony_ci{
83262306a36Sopenharmony_ci	struct mv_otg *mvotg = platform_get_drvdata(pdev);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	if (mvotg->phy.otg->state != OTG_STATE_B_IDLE) {
83562306a36Sopenharmony_ci		dev_info(&pdev->dev,
83662306a36Sopenharmony_ci			 "OTG state is not B_IDLE, it is %d!\n",
83762306a36Sopenharmony_ci			 mvotg->phy.otg->state);
83862306a36Sopenharmony_ci		return -EAGAIN;
83962306a36Sopenharmony_ci	}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	if (!mvotg->clock_gating)
84262306a36Sopenharmony_ci		mv_otg_disable_internal(mvotg);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	return 0;
84562306a36Sopenharmony_ci}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_cistatic int mv_otg_resume(struct platform_device *pdev)
84862306a36Sopenharmony_ci{
84962306a36Sopenharmony_ci	struct mv_otg *mvotg = platform_get_drvdata(pdev);
85062306a36Sopenharmony_ci	u32 otgsc;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	if (!mvotg->clock_gating) {
85362306a36Sopenharmony_ci		mv_otg_enable_internal(mvotg);
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci		otgsc = readl(&mvotg->op_regs->otgsc);
85662306a36Sopenharmony_ci		otgsc |= mvotg->irq_en;
85762306a36Sopenharmony_ci		writel(otgsc, &mvotg->op_regs->otgsc);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci		if (spin_trylock(&mvotg->wq_lock)) {
86062306a36Sopenharmony_ci			mv_otg_run_state_machine(mvotg, 0);
86162306a36Sopenharmony_ci			spin_unlock(&mvotg->wq_lock);
86262306a36Sopenharmony_ci		}
86362306a36Sopenharmony_ci	}
86462306a36Sopenharmony_ci	return 0;
86562306a36Sopenharmony_ci}
86662306a36Sopenharmony_ci#endif
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_cistatic struct platform_driver mv_otg_driver = {
86962306a36Sopenharmony_ci	.probe = mv_otg_probe,
87062306a36Sopenharmony_ci	.remove_new = mv_otg_remove,
87162306a36Sopenharmony_ci	.driver = {
87262306a36Sopenharmony_ci		   .name = driver_name,
87362306a36Sopenharmony_ci		   .dev_groups = mv_otg_groups,
87462306a36Sopenharmony_ci		   },
87562306a36Sopenharmony_ci#ifdef CONFIG_PM
87662306a36Sopenharmony_ci	.suspend = mv_otg_suspend,
87762306a36Sopenharmony_ci	.resume = mv_otg_resume,
87862306a36Sopenharmony_ci#endif
87962306a36Sopenharmony_ci};
88062306a36Sopenharmony_cimodule_platform_driver(mv_otg_driver);
881