18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2007,2008 Freescale semiconductor, Inc.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Li Yang <LeoLi@freescale.com>
68c2ecf20Sopenharmony_ci *         Jerry Huang <Chang-Ming.Huang@freescale.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Initialization based on code from Shlomi Gridish.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/delay.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
168c2ecf20Sopenharmony_ci#include <linux/errno.h>
178c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
188c2ecf20Sopenharmony_ci#include <linux/io.h>
198c2ecf20Sopenharmony_ci#include <linux/timer.h>
208c2ecf20Sopenharmony_ci#include <linux/usb.h>
218c2ecf20Sopenharmony_ci#include <linux/device.h>
228c2ecf20Sopenharmony_ci#include <linux/usb/ch9.h>
238c2ecf20Sopenharmony_ci#include <linux/usb/gadget.h>
248c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
258c2ecf20Sopenharmony_ci#include <linux/time.h>
268c2ecf20Sopenharmony_ci#include <linux/fsl_devices.h>
278c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
288c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include "phy-fsl-usb.h"
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#ifdef VERBOSE
358c2ecf20Sopenharmony_ci#define VDBG(fmt, args...) pr_debug("[%s]  " fmt, \
368c2ecf20Sopenharmony_ci				 __func__, ## args)
378c2ecf20Sopenharmony_ci#else
388c2ecf20Sopenharmony_ci#define VDBG(stuff...)	do {} while (0)
398c2ecf20Sopenharmony_ci#endif
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define DRIVER_VERSION "Rev. 1.55"
428c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Jerry Huang/Li Yang"
438c2ecf20Sopenharmony_ci#define DRIVER_DESC "Freescale USB OTG Transceiver Driver"
448c2ecf20Sopenharmony_ci#define DRIVER_INFO DRIVER_DESC " " DRIVER_VERSION
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic const char driver_name[] = "fsl-usb2-otg";
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ciconst pm_message_t otg_suspend_state = {
498c2ecf20Sopenharmony_ci	.event = 1,
508c2ecf20Sopenharmony_ci};
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define HA_DATA_PULSE
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic struct usb_dr_mmap *usb_dr_regs;
558c2ecf20Sopenharmony_cistatic struct fsl_otg *fsl_otg_dev;
568c2ecf20Sopenharmony_cistatic int srp_wait_done;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/* FSM timers */
598c2ecf20Sopenharmony_cistruct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, *a_aidl_bdis_tmr,
608c2ecf20Sopenharmony_ci	*b_ase0_brst_tmr, *b_se0_srp_tmr;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* Driver specific timers */
638c2ecf20Sopenharmony_cistruct fsl_otg_timer *b_data_pulse_tmr, *b_vbus_pulse_tmr, *b_srp_fail_tmr,
648c2ecf20Sopenharmony_ci	*b_srp_wait_tmr, *a_wait_enum_tmr;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic struct list_head active_timers;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic const struct fsl_otg_config fsl_otg_initdata = {
698c2ecf20Sopenharmony_ci	.otg_port = 1,
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC32
738c2ecf20Sopenharmony_cistatic u32 _fsl_readl_be(const unsigned __iomem *p)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	return in_be32(p);
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic u32 _fsl_readl_le(const unsigned __iomem *p)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	return in_le32(p);
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic void _fsl_writel_be(u32 v, unsigned __iomem *p)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	out_be32(p, v);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic void _fsl_writel_le(u32 v, unsigned __iomem *p)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	out_le32(p, v);
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic u32 (*_fsl_readl)(const unsigned __iomem *p);
948c2ecf20Sopenharmony_cistatic void (*_fsl_writel)(u32 v, unsigned __iomem *p);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci#define fsl_readl(p)		(*_fsl_readl)((p))
978c2ecf20Sopenharmony_ci#define fsl_writel(v, p)	(*_fsl_writel)((v), (p))
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci#else
1008c2ecf20Sopenharmony_ci#define fsl_readl(addr)		readl(addr)
1018c2ecf20Sopenharmony_ci#define fsl_writel(val, addr)	writel(val, addr)
1028c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC32 */
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ciint write_ulpi(u8 addr, u8 data)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	u32 temp;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	temp = 0x60000000 | (addr << 16) | data;
1098c2ecf20Sopenharmony_ci	fsl_writel(temp, &usb_dr_regs->ulpiview);
1108c2ecf20Sopenharmony_ci	return 0;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/* -------------------------------------------------------------*/
1148c2ecf20Sopenharmony_ci/* Operations that will be called from OTG Finite State Machine */
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/* Charge vbus for vbus pulsing in SRP */
1178c2ecf20Sopenharmony_civoid fsl_otg_chrg_vbus(struct otg_fsm *fsm, int on)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	u32 tmp;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (on)
1248c2ecf20Sopenharmony_ci		/* stop discharging, start charging */
1258c2ecf20Sopenharmony_ci		tmp = (tmp & ~OTGSC_CTRL_VBUS_DISCHARGE) |
1268c2ecf20Sopenharmony_ci			OTGSC_CTRL_VBUS_CHARGE;
1278c2ecf20Sopenharmony_ci	else
1288c2ecf20Sopenharmony_ci		/* stop charging */
1298c2ecf20Sopenharmony_ci		tmp &= ~OTGSC_CTRL_VBUS_CHARGE;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	fsl_writel(tmp, &usb_dr_regs->otgsc);
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci/* Discharge vbus through a resistor to ground */
1358c2ecf20Sopenharmony_civoid fsl_otg_dischrg_vbus(int on)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	u32 tmp;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (on)
1428c2ecf20Sopenharmony_ci		/* stop charging, start discharging */
1438c2ecf20Sopenharmony_ci		tmp = (tmp & ~OTGSC_CTRL_VBUS_CHARGE) |
1448c2ecf20Sopenharmony_ci			OTGSC_CTRL_VBUS_DISCHARGE;
1458c2ecf20Sopenharmony_ci	else
1468c2ecf20Sopenharmony_ci		/* stop discharging */
1478c2ecf20Sopenharmony_ci		tmp &= ~OTGSC_CTRL_VBUS_DISCHARGE;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	fsl_writel(tmp, &usb_dr_regs->otgsc);
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/* A-device driver vbus, controlled through PP bit in PORTSC */
1538c2ecf20Sopenharmony_civoid fsl_otg_drv_vbus(struct otg_fsm *fsm, int on)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	u32 tmp;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if (on) {
1588c2ecf20Sopenharmony_ci		tmp = fsl_readl(&usb_dr_regs->portsc) & ~PORTSC_W1C_BITS;
1598c2ecf20Sopenharmony_ci		fsl_writel(tmp | PORTSC_PORT_POWER, &usb_dr_regs->portsc);
1608c2ecf20Sopenharmony_ci	} else {
1618c2ecf20Sopenharmony_ci		tmp = fsl_readl(&usb_dr_regs->portsc) &
1628c2ecf20Sopenharmony_ci		      ~PORTSC_W1C_BITS & ~PORTSC_PORT_POWER;
1638c2ecf20Sopenharmony_ci		fsl_writel(tmp, &usb_dr_regs->portsc);
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci/*
1688c2ecf20Sopenharmony_ci * Pull-up D+, signalling connect by periperal. Also used in
1698c2ecf20Sopenharmony_ci * data-line pulsing in SRP
1708c2ecf20Sopenharmony_ci */
1718c2ecf20Sopenharmony_civoid fsl_otg_loc_conn(struct otg_fsm *fsm, int on)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	u32 tmp;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (on)
1788c2ecf20Sopenharmony_ci		tmp |= OTGSC_CTRL_DATA_PULSING;
1798c2ecf20Sopenharmony_ci	else
1808c2ecf20Sopenharmony_ci		tmp &= ~OTGSC_CTRL_DATA_PULSING;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	fsl_writel(tmp, &usb_dr_regs->otgsc);
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci/*
1868c2ecf20Sopenharmony_ci * Generate SOF by host.  This is controlled through suspend/resume the
1878c2ecf20Sopenharmony_ci * port.  In host mode, controller will automatically send SOF.
1888c2ecf20Sopenharmony_ci * Suspend will block the data on the port.
1898c2ecf20Sopenharmony_ci */
1908c2ecf20Sopenharmony_civoid fsl_otg_loc_sof(struct otg_fsm *fsm, int on)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	u32 tmp;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	tmp = fsl_readl(&fsl_otg_dev->dr_mem_map->portsc) & ~PORTSC_W1C_BITS;
1958c2ecf20Sopenharmony_ci	if (on)
1968c2ecf20Sopenharmony_ci		tmp |= PORTSC_PORT_FORCE_RESUME;
1978c2ecf20Sopenharmony_ci	else
1988c2ecf20Sopenharmony_ci		tmp |= PORTSC_PORT_SUSPEND;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	fsl_writel(tmp, &fsl_otg_dev->dr_mem_map->portsc);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci/* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */
2058c2ecf20Sopenharmony_civoid fsl_otg_start_pulse(struct otg_fsm *fsm)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	u32 tmp;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	srp_wait_done = 0;
2108c2ecf20Sopenharmony_ci#ifdef HA_DATA_PULSE
2118c2ecf20Sopenharmony_ci	tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK;
2128c2ecf20Sopenharmony_ci	tmp |= OTGSC_HA_DATA_PULSE;
2138c2ecf20Sopenharmony_ci	fsl_writel(tmp, &usb_dr_regs->otgsc);
2148c2ecf20Sopenharmony_ci#else
2158c2ecf20Sopenharmony_ci	fsl_otg_loc_conn(1);
2168c2ecf20Sopenharmony_ci#endif
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	fsl_otg_add_timer(fsm, b_data_pulse_tmr);
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_civoid b_data_pulse_end(unsigned long foo)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci#ifdef HA_DATA_PULSE
2248c2ecf20Sopenharmony_ci#else
2258c2ecf20Sopenharmony_ci	fsl_otg_loc_conn(0);
2268c2ecf20Sopenharmony_ci#endif
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	/* Do VBUS pulse after data pulse */
2298c2ecf20Sopenharmony_ci	fsl_otg_pulse_vbus();
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_civoid fsl_otg_pulse_vbus(void)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	srp_wait_done = 0;
2358c2ecf20Sopenharmony_ci	fsl_otg_chrg_vbus(&fsl_otg_dev->fsm, 1);
2368c2ecf20Sopenharmony_ci	/* start the timer to end vbus charge */
2378c2ecf20Sopenharmony_ci	fsl_otg_add_timer(&fsl_otg_dev->fsm, b_vbus_pulse_tmr);
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_civoid b_vbus_pulse_end(unsigned long foo)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	fsl_otg_chrg_vbus(&fsl_otg_dev->fsm, 0);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	/*
2458c2ecf20Sopenharmony_ci	 * As USB3300 using the same a_sess_vld and b_sess_vld voltage
2468c2ecf20Sopenharmony_ci	 * we need to discharge the bus for a while to distinguish
2478c2ecf20Sopenharmony_ci	 * residual voltage of vbus pulsing and A device pull up
2488c2ecf20Sopenharmony_ci	 */
2498c2ecf20Sopenharmony_ci	fsl_otg_dischrg_vbus(1);
2508c2ecf20Sopenharmony_ci	fsl_otg_add_timer(&fsl_otg_dev->fsm, b_srp_wait_tmr);
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_civoid b_srp_end(unsigned long foo)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	fsl_otg_dischrg_vbus(0);
2568c2ecf20Sopenharmony_ci	srp_wait_done = 1;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	if ((fsl_otg_dev->phy.otg->state == OTG_STATE_B_SRP_INIT) &&
2598c2ecf20Sopenharmony_ci	    fsl_otg_dev->fsm.b_sess_vld)
2608c2ecf20Sopenharmony_ci		fsl_otg_dev->fsm.b_srp_done = 1;
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci/*
2648c2ecf20Sopenharmony_ci * Workaround for a_host suspending too fast.  When a_bus_req=0,
2658c2ecf20Sopenharmony_ci * a_host will start by SRP.  It needs to set b_hnp_enable before
2668c2ecf20Sopenharmony_ci * actually suspending to start HNP
2678c2ecf20Sopenharmony_ci */
2688c2ecf20Sopenharmony_civoid a_wait_enum(unsigned long foo)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	VDBG("a_wait_enum timeout\n");
2718c2ecf20Sopenharmony_ci	if (!fsl_otg_dev->phy.otg->host->b_hnp_enable)
2728c2ecf20Sopenharmony_ci		fsl_otg_add_timer(&fsl_otg_dev->fsm, a_wait_enum_tmr);
2738c2ecf20Sopenharmony_ci	else
2748c2ecf20Sopenharmony_ci		otg_statemachine(&fsl_otg_dev->fsm);
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci/* The timeout callback function to set time out bit */
2788c2ecf20Sopenharmony_civoid set_tmout(unsigned long indicator)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	*(int *)indicator = 1;
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci/* Initialize timers */
2848c2ecf20Sopenharmony_ciint fsl_otg_init_timers(struct otg_fsm *fsm)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	/* FSM used timers */
2878c2ecf20Sopenharmony_ci	a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE,
2888c2ecf20Sopenharmony_ci				(unsigned long)&fsm->a_wait_vrise_tmout);
2898c2ecf20Sopenharmony_ci	if (!a_wait_vrise_tmr)
2908c2ecf20Sopenharmony_ci		return -ENOMEM;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON,
2938c2ecf20Sopenharmony_ci				(unsigned long)&fsm->a_wait_bcon_tmout);
2948c2ecf20Sopenharmony_ci	if (!a_wait_bcon_tmr)
2958c2ecf20Sopenharmony_ci		return -ENOMEM;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS,
2988c2ecf20Sopenharmony_ci				(unsigned long)&fsm->a_aidl_bdis_tmout);
2998c2ecf20Sopenharmony_ci	if (!a_aidl_bdis_tmr)
3008c2ecf20Sopenharmony_ci		return -ENOMEM;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST,
3038c2ecf20Sopenharmony_ci				(unsigned long)&fsm->b_ase0_brst_tmout);
3048c2ecf20Sopenharmony_ci	if (!b_ase0_brst_tmr)
3058c2ecf20Sopenharmony_ci		return -ENOMEM;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP,
3088c2ecf20Sopenharmony_ci				(unsigned long)&fsm->b_se0_srp);
3098c2ecf20Sopenharmony_ci	if (!b_se0_srp_tmr)
3108c2ecf20Sopenharmony_ci		return -ENOMEM;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	b_srp_fail_tmr = otg_timer_initializer(&set_tmout, TB_SRP_FAIL,
3138c2ecf20Sopenharmony_ci				(unsigned long)&fsm->b_srp_done);
3148c2ecf20Sopenharmony_ci	if (!b_srp_fail_tmr)
3158c2ecf20Sopenharmony_ci		return -ENOMEM;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	a_wait_enum_tmr = otg_timer_initializer(&a_wait_enum, 10,
3188c2ecf20Sopenharmony_ci				(unsigned long)&fsm);
3198c2ecf20Sopenharmony_ci	if (!a_wait_enum_tmr)
3208c2ecf20Sopenharmony_ci		return -ENOMEM;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	/* device driver used timers */
3238c2ecf20Sopenharmony_ci	b_srp_wait_tmr = otg_timer_initializer(&b_srp_end, TB_SRP_WAIT, 0);
3248c2ecf20Sopenharmony_ci	if (!b_srp_wait_tmr)
3258c2ecf20Sopenharmony_ci		return -ENOMEM;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	b_data_pulse_tmr = otg_timer_initializer(&b_data_pulse_end,
3288c2ecf20Sopenharmony_ci				TB_DATA_PLS, 0);
3298c2ecf20Sopenharmony_ci	if (!b_data_pulse_tmr)
3308c2ecf20Sopenharmony_ci		return -ENOMEM;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	b_vbus_pulse_tmr = otg_timer_initializer(&b_vbus_pulse_end,
3338c2ecf20Sopenharmony_ci				TB_VBUS_PLS, 0);
3348c2ecf20Sopenharmony_ci	if (!b_vbus_pulse_tmr)
3358c2ecf20Sopenharmony_ci		return -ENOMEM;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	return 0;
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci/* Uninitialize timers */
3418c2ecf20Sopenharmony_civoid fsl_otg_uninit_timers(void)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	/* FSM used timers */
3448c2ecf20Sopenharmony_ci	kfree(a_wait_vrise_tmr);
3458c2ecf20Sopenharmony_ci	kfree(a_wait_bcon_tmr);
3468c2ecf20Sopenharmony_ci	kfree(a_aidl_bdis_tmr);
3478c2ecf20Sopenharmony_ci	kfree(b_ase0_brst_tmr);
3488c2ecf20Sopenharmony_ci	kfree(b_se0_srp_tmr);
3498c2ecf20Sopenharmony_ci	kfree(b_srp_fail_tmr);
3508c2ecf20Sopenharmony_ci	kfree(a_wait_enum_tmr);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	/* device driver used timers */
3538c2ecf20Sopenharmony_ci	kfree(b_srp_wait_tmr);
3548c2ecf20Sopenharmony_ci	kfree(b_data_pulse_tmr);
3558c2ecf20Sopenharmony_ci	kfree(b_vbus_pulse_tmr);
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic struct fsl_otg_timer *fsl_otg_get_timer(enum otg_fsm_timer t)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	struct fsl_otg_timer *timer;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	/* REVISIT: use array of pointers to timers instead */
3638c2ecf20Sopenharmony_ci	switch (t) {
3648c2ecf20Sopenharmony_ci	case A_WAIT_VRISE:
3658c2ecf20Sopenharmony_ci		timer = a_wait_vrise_tmr;
3668c2ecf20Sopenharmony_ci		break;
3678c2ecf20Sopenharmony_ci	case A_WAIT_BCON:
3688c2ecf20Sopenharmony_ci		timer = a_wait_vrise_tmr;
3698c2ecf20Sopenharmony_ci		break;
3708c2ecf20Sopenharmony_ci	case A_AIDL_BDIS:
3718c2ecf20Sopenharmony_ci		timer = a_wait_vrise_tmr;
3728c2ecf20Sopenharmony_ci		break;
3738c2ecf20Sopenharmony_ci	case B_ASE0_BRST:
3748c2ecf20Sopenharmony_ci		timer = a_wait_vrise_tmr;
3758c2ecf20Sopenharmony_ci		break;
3768c2ecf20Sopenharmony_ci	case B_SE0_SRP:
3778c2ecf20Sopenharmony_ci		timer = a_wait_vrise_tmr;
3788c2ecf20Sopenharmony_ci		break;
3798c2ecf20Sopenharmony_ci	case B_SRP_FAIL:
3808c2ecf20Sopenharmony_ci		timer = a_wait_vrise_tmr;
3818c2ecf20Sopenharmony_ci		break;
3828c2ecf20Sopenharmony_ci	case A_WAIT_ENUM:
3838c2ecf20Sopenharmony_ci		timer = a_wait_vrise_tmr;
3848c2ecf20Sopenharmony_ci		break;
3858c2ecf20Sopenharmony_ci	default:
3868c2ecf20Sopenharmony_ci		timer = NULL;
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	return timer;
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci/* Add timer to timer list */
3938c2ecf20Sopenharmony_civoid fsl_otg_add_timer(struct otg_fsm *fsm, void *gtimer)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	struct fsl_otg_timer *timer = gtimer;
3968c2ecf20Sopenharmony_ci	struct fsl_otg_timer *tmp_timer;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	/*
3998c2ecf20Sopenharmony_ci	 * Check if the timer is already in the active list,
4008c2ecf20Sopenharmony_ci	 * if so update timer count
4018c2ecf20Sopenharmony_ci	 */
4028c2ecf20Sopenharmony_ci	list_for_each_entry(tmp_timer, &active_timers, list)
4038c2ecf20Sopenharmony_ci	    if (tmp_timer == timer) {
4048c2ecf20Sopenharmony_ci		timer->count = timer->expires;
4058c2ecf20Sopenharmony_ci		return;
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci	timer->count = timer->expires;
4088c2ecf20Sopenharmony_ci	list_add_tail(&timer->list, &active_timers);
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic void fsl_otg_fsm_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	struct fsl_otg_timer *timer;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	timer = fsl_otg_get_timer(t);
4168c2ecf20Sopenharmony_ci	if (!timer)
4178c2ecf20Sopenharmony_ci		return;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	fsl_otg_add_timer(fsm, timer);
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci/* Remove timer from the timer list; clear timeout status */
4238c2ecf20Sopenharmony_civoid fsl_otg_del_timer(struct otg_fsm *fsm, void *gtimer)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	struct fsl_otg_timer *timer = gtimer;
4268c2ecf20Sopenharmony_ci	struct fsl_otg_timer *tmp_timer, *del_tmp;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list)
4298c2ecf20Sopenharmony_ci		if (tmp_timer == timer)
4308c2ecf20Sopenharmony_ci			list_del(&timer->list);
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cistatic void fsl_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
4348c2ecf20Sopenharmony_ci{
4358c2ecf20Sopenharmony_ci	struct fsl_otg_timer *timer;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	timer = fsl_otg_get_timer(t);
4388c2ecf20Sopenharmony_ci	if (!timer)
4398c2ecf20Sopenharmony_ci		return;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	fsl_otg_del_timer(fsm, timer);
4428c2ecf20Sopenharmony_ci}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci/* Reset controller, not reset the bus */
4458c2ecf20Sopenharmony_civoid otg_reset_controller(void)
4468c2ecf20Sopenharmony_ci{
4478c2ecf20Sopenharmony_ci	u32 command;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	command = fsl_readl(&usb_dr_regs->usbcmd);
4508c2ecf20Sopenharmony_ci	command |= (1 << 1);
4518c2ecf20Sopenharmony_ci	fsl_writel(command, &usb_dr_regs->usbcmd);
4528c2ecf20Sopenharmony_ci	while (fsl_readl(&usb_dr_regs->usbcmd) & (1 << 1))
4538c2ecf20Sopenharmony_ci		;
4548c2ecf20Sopenharmony_ci}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci/* Call suspend/resume routines in host driver */
4578c2ecf20Sopenharmony_ciint fsl_otg_start_host(struct otg_fsm *fsm, int on)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	struct usb_otg *otg = fsm->otg;
4608c2ecf20Sopenharmony_ci	struct device *dev;
4618c2ecf20Sopenharmony_ci	struct fsl_otg *otg_dev =
4628c2ecf20Sopenharmony_ci		container_of(otg->usb_phy, struct fsl_otg, phy);
4638c2ecf20Sopenharmony_ci	u32 retval = 0;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	if (!otg->host)
4668c2ecf20Sopenharmony_ci		return -ENODEV;
4678c2ecf20Sopenharmony_ci	dev = otg->host->controller;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	/*
4708c2ecf20Sopenharmony_ci	 * Update a_vbus_vld state as a_vbus_vld int is disabled
4718c2ecf20Sopenharmony_ci	 * in device mode
4728c2ecf20Sopenharmony_ci	 */
4738c2ecf20Sopenharmony_ci	fsm->a_vbus_vld =
4748c2ecf20Sopenharmony_ci		!!(fsl_readl(&usb_dr_regs->otgsc) & OTGSC_STS_A_VBUS_VALID);
4758c2ecf20Sopenharmony_ci	if (on) {
4768c2ecf20Sopenharmony_ci		/* start fsl usb host controller */
4778c2ecf20Sopenharmony_ci		if (otg_dev->host_working)
4788c2ecf20Sopenharmony_ci			goto end;
4798c2ecf20Sopenharmony_ci		else {
4808c2ecf20Sopenharmony_ci			otg_reset_controller();
4818c2ecf20Sopenharmony_ci			VDBG("host on......\n");
4828c2ecf20Sopenharmony_ci			if (dev->driver->pm && dev->driver->pm->resume) {
4838c2ecf20Sopenharmony_ci				retval = dev->driver->pm->resume(dev);
4848c2ecf20Sopenharmony_ci				if (fsm->id) {
4858c2ecf20Sopenharmony_ci					/* default-b */
4868c2ecf20Sopenharmony_ci					fsl_otg_drv_vbus(fsm, 1);
4878c2ecf20Sopenharmony_ci					/*
4888c2ecf20Sopenharmony_ci					 * Workaround: b_host can't driver
4898c2ecf20Sopenharmony_ci					 * vbus, but PP in PORTSC needs to
4908c2ecf20Sopenharmony_ci					 * be 1 for host to work.
4918c2ecf20Sopenharmony_ci					 * So we set drv_vbus bit in
4928c2ecf20Sopenharmony_ci					 * transceiver to 0 thru ULPI.
4938c2ecf20Sopenharmony_ci					 */
4948c2ecf20Sopenharmony_ci					write_ulpi(0x0c, 0x20);
4958c2ecf20Sopenharmony_ci				}
4968c2ecf20Sopenharmony_ci			}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci			otg_dev->host_working = 1;
4998c2ecf20Sopenharmony_ci		}
5008c2ecf20Sopenharmony_ci	} else {
5018c2ecf20Sopenharmony_ci		/* stop fsl usb host controller */
5028c2ecf20Sopenharmony_ci		if (!otg_dev->host_working)
5038c2ecf20Sopenharmony_ci			goto end;
5048c2ecf20Sopenharmony_ci		else {
5058c2ecf20Sopenharmony_ci			VDBG("host off......\n");
5068c2ecf20Sopenharmony_ci			if (dev && dev->driver) {
5078c2ecf20Sopenharmony_ci				if (dev->driver->pm && dev->driver->pm->suspend)
5088c2ecf20Sopenharmony_ci					retval = dev->driver->pm->suspend(dev);
5098c2ecf20Sopenharmony_ci				if (fsm->id)
5108c2ecf20Sopenharmony_ci					/* default-b */
5118c2ecf20Sopenharmony_ci					fsl_otg_drv_vbus(fsm, 0);
5128c2ecf20Sopenharmony_ci			}
5138c2ecf20Sopenharmony_ci			otg_dev->host_working = 0;
5148c2ecf20Sopenharmony_ci		}
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ciend:
5178c2ecf20Sopenharmony_ci	return retval;
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci/*
5218c2ecf20Sopenharmony_ci * Call suspend and resume function in udc driver
5228c2ecf20Sopenharmony_ci * to stop and start udc driver.
5238c2ecf20Sopenharmony_ci */
5248c2ecf20Sopenharmony_ciint fsl_otg_start_gadget(struct otg_fsm *fsm, int on)
5258c2ecf20Sopenharmony_ci{
5268c2ecf20Sopenharmony_ci	struct usb_otg *otg = fsm->otg;
5278c2ecf20Sopenharmony_ci	struct device *dev;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	if (!otg->gadget || !otg->gadget->dev.parent)
5308c2ecf20Sopenharmony_ci		return -ENODEV;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	VDBG("gadget %s\n", on ? "on" : "off");
5338c2ecf20Sopenharmony_ci	dev = otg->gadget->dev.parent;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	if (on) {
5368c2ecf20Sopenharmony_ci		if (dev->driver->resume)
5378c2ecf20Sopenharmony_ci			dev->driver->resume(dev);
5388c2ecf20Sopenharmony_ci	} else {
5398c2ecf20Sopenharmony_ci		if (dev->driver->suspend)
5408c2ecf20Sopenharmony_ci			dev->driver->suspend(dev, otg_suspend_state);
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	return 0;
5448c2ecf20Sopenharmony_ci}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci/*
5478c2ecf20Sopenharmony_ci * Called by initialization code of host driver.  Register host controller
5488c2ecf20Sopenharmony_ci * to the OTG.  Suspend host for OTG role detection.
5498c2ecf20Sopenharmony_ci */
5508c2ecf20Sopenharmony_cistatic int fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	struct fsl_otg *otg_dev;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	if (!otg)
5558c2ecf20Sopenharmony_ci		return -ENODEV;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	otg_dev = container_of(otg->usb_phy, struct fsl_otg, phy);
5588c2ecf20Sopenharmony_ci	if (otg_dev != fsl_otg_dev)
5598c2ecf20Sopenharmony_ci		return -ENODEV;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	otg->host = host;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	otg_dev->fsm.a_bus_drop = 0;
5648c2ecf20Sopenharmony_ci	otg_dev->fsm.a_bus_req = 1;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	if (host) {
5678c2ecf20Sopenharmony_ci		VDBG("host off......\n");
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci		otg->host->otg_port = fsl_otg_initdata.otg_port;
5708c2ecf20Sopenharmony_ci		otg->host->is_b_host = otg_dev->fsm.id;
5718c2ecf20Sopenharmony_ci		/*
5728c2ecf20Sopenharmony_ci		 * must leave time for hub_wq to finish its thing
5738c2ecf20Sopenharmony_ci		 * before yanking the host driver out from under it,
5748c2ecf20Sopenharmony_ci		 * so suspend the host after a short delay.
5758c2ecf20Sopenharmony_ci		 */
5768c2ecf20Sopenharmony_ci		otg_dev->host_working = 1;
5778c2ecf20Sopenharmony_ci		schedule_delayed_work(&otg_dev->otg_event, 100);
5788c2ecf20Sopenharmony_ci		return 0;
5798c2ecf20Sopenharmony_ci	} else {
5808c2ecf20Sopenharmony_ci		/* host driver going away */
5818c2ecf20Sopenharmony_ci		if (!(fsl_readl(&otg_dev->dr_mem_map->otgsc) &
5828c2ecf20Sopenharmony_ci		      OTGSC_STS_USB_ID)) {
5838c2ecf20Sopenharmony_ci			/* Mini-A cable connected */
5848c2ecf20Sopenharmony_ci			struct otg_fsm *fsm = &otg_dev->fsm;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci			otg->state = OTG_STATE_UNDEFINED;
5878c2ecf20Sopenharmony_ci			fsm->protocol = PROTO_UNDEF;
5888c2ecf20Sopenharmony_ci		}
5898c2ecf20Sopenharmony_ci	}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	otg_dev->host_working = 0;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	otg_statemachine(&otg_dev->fsm);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	return 0;
5968c2ecf20Sopenharmony_ci}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci/* Called by initialization code of udc.  Register udc to OTG. */
5998c2ecf20Sopenharmony_cistatic int fsl_otg_set_peripheral(struct usb_otg *otg,
6008c2ecf20Sopenharmony_ci					struct usb_gadget *gadget)
6018c2ecf20Sopenharmony_ci{
6028c2ecf20Sopenharmony_ci	struct fsl_otg *otg_dev;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	if (!otg)
6058c2ecf20Sopenharmony_ci		return -ENODEV;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	otg_dev = container_of(otg->usb_phy, struct fsl_otg, phy);
6088c2ecf20Sopenharmony_ci	VDBG("otg_dev 0x%x\n", (int)otg_dev);
6098c2ecf20Sopenharmony_ci	VDBG("fsl_otg_dev 0x%x\n", (int)fsl_otg_dev);
6108c2ecf20Sopenharmony_ci	if (otg_dev != fsl_otg_dev)
6118c2ecf20Sopenharmony_ci		return -ENODEV;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	if (!gadget) {
6148c2ecf20Sopenharmony_ci		if (!otg->default_a)
6158c2ecf20Sopenharmony_ci			otg->gadget->ops->vbus_draw(otg->gadget, 0);
6168c2ecf20Sopenharmony_ci		usb_gadget_vbus_disconnect(otg->gadget);
6178c2ecf20Sopenharmony_ci		otg->gadget = 0;
6188c2ecf20Sopenharmony_ci		otg_dev->fsm.b_bus_req = 0;
6198c2ecf20Sopenharmony_ci		otg_statemachine(&otg_dev->fsm);
6208c2ecf20Sopenharmony_ci		return 0;
6218c2ecf20Sopenharmony_ci	}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	otg->gadget = gadget;
6248c2ecf20Sopenharmony_ci	otg->gadget->is_a_peripheral = !otg_dev->fsm.id;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	otg_dev->fsm.b_bus_req = 1;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	/* start the gadget right away if the ID pin says Mini-B */
6298c2ecf20Sopenharmony_ci	pr_debug("ID pin=%d\n", otg_dev->fsm.id);
6308c2ecf20Sopenharmony_ci	if (otg_dev->fsm.id == 1) {
6318c2ecf20Sopenharmony_ci		fsl_otg_start_host(&otg_dev->fsm, 0);
6328c2ecf20Sopenharmony_ci		otg_drv_vbus(&otg_dev->fsm, 0);
6338c2ecf20Sopenharmony_ci		fsl_otg_start_gadget(&otg_dev->fsm, 1);
6348c2ecf20Sopenharmony_ci	}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	return 0;
6378c2ecf20Sopenharmony_ci}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci/*
6408c2ecf20Sopenharmony_ci * Delayed pin detect interrupt processing.
6418c2ecf20Sopenharmony_ci *
6428c2ecf20Sopenharmony_ci * When the Mini-A cable is disconnected from the board,
6438c2ecf20Sopenharmony_ci * the pin-detect interrupt happens before the disconnect
6448c2ecf20Sopenharmony_ci * interrupts for the connected device(s).  In order to
6458c2ecf20Sopenharmony_ci * process the disconnect interrupt(s) prior to switching
6468c2ecf20Sopenharmony_ci * roles, the pin-detect interrupts are delayed, and handled
6478c2ecf20Sopenharmony_ci * by this routine.
6488c2ecf20Sopenharmony_ci */
6498c2ecf20Sopenharmony_cistatic void fsl_otg_event(struct work_struct *work)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	struct fsl_otg *og = container_of(work, struct fsl_otg, otg_event.work);
6528c2ecf20Sopenharmony_ci	struct otg_fsm *fsm = &og->fsm;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	if (fsm->id) {		/* switch to gadget */
6558c2ecf20Sopenharmony_ci		fsl_otg_start_host(fsm, 0);
6568c2ecf20Sopenharmony_ci		otg_drv_vbus(fsm, 0);
6578c2ecf20Sopenharmony_ci		fsl_otg_start_gadget(fsm, 1);
6588c2ecf20Sopenharmony_ci	}
6598c2ecf20Sopenharmony_ci}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci/* B-device start SRP */
6628c2ecf20Sopenharmony_cistatic int fsl_otg_start_srp(struct usb_otg *otg)
6638c2ecf20Sopenharmony_ci{
6648c2ecf20Sopenharmony_ci	struct fsl_otg *otg_dev;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	if (!otg || otg->state != OTG_STATE_B_IDLE)
6678c2ecf20Sopenharmony_ci		return -ENODEV;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	otg_dev = container_of(otg->usb_phy, struct fsl_otg, phy);
6708c2ecf20Sopenharmony_ci	if (otg_dev != fsl_otg_dev)
6718c2ecf20Sopenharmony_ci		return -ENODEV;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	otg_dev->fsm.b_bus_req = 1;
6748c2ecf20Sopenharmony_ci	otg_statemachine(&otg_dev->fsm);
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	return 0;
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci/* A_host suspend will call this function to start hnp */
6808c2ecf20Sopenharmony_cistatic int fsl_otg_start_hnp(struct usb_otg *otg)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	struct fsl_otg *otg_dev;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	if (!otg)
6858c2ecf20Sopenharmony_ci		return -ENODEV;
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	otg_dev = container_of(otg->usb_phy, struct fsl_otg, phy);
6888c2ecf20Sopenharmony_ci	if (otg_dev != fsl_otg_dev)
6898c2ecf20Sopenharmony_ci		return -ENODEV;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	pr_debug("start_hnp...\n");
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	/* clear a_bus_req to enter a_suspend state */
6948c2ecf20Sopenharmony_ci	otg_dev->fsm.a_bus_req = 0;
6958c2ecf20Sopenharmony_ci	otg_statemachine(&otg_dev->fsm);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	return 0;
6988c2ecf20Sopenharmony_ci}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci/*
7018c2ecf20Sopenharmony_ci * Interrupt handler.  OTG/host/peripheral share the same int line.
7028c2ecf20Sopenharmony_ci * OTG driver clears OTGSC interrupts and leaves USB interrupts
7038c2ecf20Sopenharmony_ci * intact.  It needs to have knowledge of some USB interrupts
7048c2ecf20Sopenharmony_ci * such as port change.
7058c2ecf20Sopenharmony_ci */
7068c2ecf20Sopenharmony_ciirqreturn_t fsl_otg_isr(int irq, void *dev_id)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm;
7098c2ecf20Sopenharmony_ci	struct usb_otg *otg = ((struct fsl_otg *)dev_id)->phy.otg;
7108c2ecf20Sopenharmony_ci	u32 otg_int_src, otg_sc;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	otg_sc = fsl_readl(&usb_dr_regs->otgsc);
7138c2ecf20Sopenharmony_ci	otg_int_src = otg_sc & OTGSC_INTSTS_MASK & (otg_sc >> 8);
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	/* Only clear otg interrupts */
7168c2ecf20Sopenharmony_ci	fsl_writel(otg_sc, &usb_dr_regs->otgsc);
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	/*FIXME: ID change not generate when init to 0 */
7198c2ecf20Sopenharmony_ci	fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0;
7208c2ecf20Sopenharmony_ci	otg->default_a = (fsm->id == 0);
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	/* process OTG interrupts */
7238c2ecf20Sopenharmony_ci	if (otg_int_src) {
7248c2ecf20Sopenharmony_ci		if (otg_int_src & OTGSC_INTSTS_USB_ID) {
7258c2ecf20Sopenharmony_ci			fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0;
7268c2ecf20Sopenharmony_ci			otg->default_a = (fsm->id == 0);
7278c2ecf20Sopenharmony_ci			/* clear conn information */
7288c2ecf20Sopenharmony_ci			if (fsm->id)
7298c2ecf20Sopenharmony_ci				fsm->b_conn = 0;
7308c2ecf20Sopenharmony_ci			else
7318c2ecf20Sopenharmony_ci				fsm->a_conn = 0;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci			if (otg->host)
7348c2ecf20Sopenharmony_ci				otg->host->is_b_host = fsm->id;
7358c2ecf20Sopenharmony_ci			if (otg->gadget)
7368c2ecf20Sopenharmony_ci				otg->gadget->is_a_peripheral = !fsm->id;
7378c2ecf20Sopenharmony_ci			VDBG("ID int (ID is %d)\n", fsm->id);
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci			if (fsm->id) {	/* switch to gadget */
7408c2ecf20Sopenharmony_ci				schedule_delayed_work(
7418c2ecf20Sopenharmony_ci					&((struct fsl_otg *)dev_id)->otg_event,
7428c2ecf20Sopenharmony_ci					100);
7438c2ecf20Sopenharmony_ci			} else {	/* switch to host */
7448c2ecf20Sopenharmony_ci				cancel_delayed_work(&
7458c2ecf20Sopenharmony_ci						    ((struct fsl_otg *)dev_id)->
7468c2ecf20Sopenharmony_ci						    otg_event);
7478c2ecf20Sopenharmony_ci				fsl_otg_start_gadget(fsm, 0);
7488c2ecf20Sopenharmony_ci				otg_drv_vbus(fsm, 1);
7498c2ecf20Sopenharmony_ci				fsl_otg_start_host(fsm, 1);
7508c2ecf20Sopenharmony_ci			}
7518c2ecf20Sopenharmony_ci			return IRQ_HANDLED;
7528c2ecf20Sopenharmony_ci		}
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci	return IRQ_NONE;
7558c2ecf20Sopenharmony_ci}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_cistatic struct otg_fsm_ops fsl_otg_ops = {
7588c2ecf20Sopenharmony_ci	.chrg_vbus = fsl_otg_chrg_vbus,
7598c2ecf20Sopenharmony_ci	.drv_vbus = fsl_otg_drv_vbus,
7608c2ecf20Sopenharmony_ci	.loc_conn = fsl_otg_loc_conn,
7618c2ecf20Sopenharmony_ci	.loc_sof = fsl_otg_loc_sof,
7628c2ecf20Sopenharmony_ci	.start_pulse = fsl_otg_start_pulse,
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	.add_timer = fsl_otg_fsm_add_timer,
7658c2ecf20Sopenharmony_ci	.del_timer = fsl_otg_fsm_del_timer,
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	.start_host = fsl_otg_start_host,
7688c2ecf20Sopenharmony_ci	.start_gadget = fsl_otg_start_gadget,
7698c2ecf20Sopenharmony_ci};
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci/* Initialize the global variable fsl_otg_dev and request IRQ for OTG */
7728c2ecf20Sopenharmony_cistatic int fsl_otg_conf(struct platform_device *pdev)
7738c2ecf20Sopenharmony_ci{
7748c2ecf20Sopenharmony_ci	struct fsl_otg *fsl_otg_tc;
7758c2ecf20Sopenharmony_ci	int status;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	if (fsl_otg_dev)
7788c2ecf20Sopenharmony_ci		return 0;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	/* allocate space to fsl otg device */
7818c2ecf20Sopenharmony_ci	fsl_otg_tc = kzalloc(sizeof(struct fsl_otg), GFP_KERNEL);
7828c2ecf20Sopenharmony_ci	if (!fsl_otg_tc)
7838c2ecf20Sopenharmony_ci		return -ENOMEM;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	fsl_otg_tc->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
7868c2ecf20Sopenharmony_ci	if (!fsl_otg_tc->phy.otg) {
7878c2ecf20Sopenharmony_ci		kfree(fsl_otg_tc);
7888c2ecf20Sopenharmony_ci		return -ENOMEM;
7898c2ecf20Sopenharmony_ci	}
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&fsl_otg_tc->otg_event, fsl_otg_event);
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&active_timers);
7948c2ecf20Sopenharmony_ci	status = fsl_otg_init_timers(&fsl_otg_tc->fsm);
7958c2ecf20Sopenharmony_ci	if (status) {
7968c2ecf20Sopenharmony_ci		pr_info("Couldn't init OTG timers\n");
7978c2ecf20Sopenharmony_ci		goto err;
7988c2ecf20Sopenharmony_ci	}
7998c2ecf20Sopenharmony_ci	mutex_init(&fsl_otg_tc->fsm.lock);
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	/* Set OTG state machine operations */
8028c2ecf20Sopenharmony_ci	fsl_otg_tc->fsm.ops = &fsl_otg_ops;
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	/* initialize the otg structure */
8058c2ecf20Sopenharmony_ci	fsl_otg_tc->phy.label = DRIVER_DESC;
8068c2ecf20Sopenharmony_ci	fsl_otg_tc->phy.dev = &pdev->dev;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	fsl_otg_tc->phy.otg->usb_phy = &fsl_otg_tc->phy;
8098c2ecf20Sopenharmony_ci	fsl_otg_tc->phy.otg->set_host = fsl_otg_set_host;
8108c2ecf20Sopenharmony_ci	fsl_otg_tc->phy.otg->set_peripheral = fsl_otg_set_peripheral;
8118c2ecf20Sopenharmony_ci	fsl_otg_tc->phy.otg->start_hnp = fsl_otg_start_hnp;
8128c2ecf20Sopenharmony_ci	fsl_otg_tc->phy.otg->start_srp = fsl_otg_start_srp;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	fsl_otg_dev = fsl_otg_tc;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	/* Store the otg transceiver */
8178c2ecf20Sopenharmony_ci	status = usb_add_phy(&fsl_otg_tc->phy, USB_PHY_TYPE_USB2);
8188c2ecf20Sopenharmony_ci	if (status) {
8198c2ecf20Sopenharmony_ci		pr_warn(FSL_OTG_NAME ": unable to register OTG transceiver.\n");
8208c2ecf20Sopenharmony_ci		goto err;
8218c2ecf20Sopenharmony_ci	}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	return 0;
8248c2ecf20Sopenharmony_cierr:
8258c2ecf20Sopenharmony_ci	fsl_otg_uninit_timers();
8268c2ecf20Sopenharmony_ci	kfree(fsl_otg_tc->phy.otg);
8278c2ecf20Sopenharmony_ci	kfree(fsl_otg_tc);
8288c2ecf20Sopenharmony_ci	return status;
8298c2ecf20Sopenharmony_ci}
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci/* OTG Initialization */
8328c2ecf20Sopenharmony_ciint usb_otg_start(struct platform_device *pdev)
8338c2ecf20Sopenharmony_ci{
8348c2ecf20Sopenharmony_ci	struct fsl_otg *p_otg;
8358c2ecf20Sopenharmony_ci	struct usb_phy *otg_trans = usb_get_phy(USB_PHY_TYPE_USB2);
8368c2ecf20Sopenharmony_ci	struct otg_fsm *fsm;
8378c2ecf20Sopenharmony_ci	int status;
8388c2ecf20Sopenharmony_ci	struct resource *res;
8398c2ecf20Sopenharmony_ci	u32 temp;
8408c2ecf20Sopenharmony_ci	struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	p_otg = container_of(otg_trans, struct fsl_otg, phy);
8438c2ecf20Sopenharmony_ci	fsm = &p_otg->fsm;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	/* Initialize the state machine structure with default values */
8468c2ecf20Sopenharmony_ci	SET_OTG_STATE(otg_trans, OTG_STATE_UNDEFINED);
8478c2ecf20Sopenharmony_ci	fsm->otg = p_otg->phy.otg;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	/* We don't require predefined MEM/IRQ resource index */
8508c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
8518c2ecf20Sopenharmony_ci	if (!res)
8528c2ecf20Sopenharmony_ci		return -ENXIO;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	/* We don't request_mem_region here to enable resource sharing
8558c2ecf20Sopenharmony_ci	 * with host/device */
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	usb_dr_regs = ioremap(res->start, sizeof(struct usb_dr_mmap));
8588c2ecf20Sopenharmony_ci	p_otg->dr_mem_map = (struct usb_dr_mmap *)usb_dr_regs;
8598c2ecf20Sopenharmony_ci	pdata->regs = (void *)usb_dr_regs;
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	if (pdata->init && pdata->init(pdev) != 0)
8628c2ecf20Sopenharmony_ci		return -EINVAL;
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC32
8658c2ecf20Sopenharmony_ci	if (pdata->big_endian_mmio) {
8668c2ecf20Sopenharmony_ci		_fsl_readl = _fsl_readl_be;
8678c2ecf20Sopenharmony_ci		_fsl_writel = _fsl_writel_be;
8688c2ecf20Sopenharmony_ci	} else {
8698c2ecf20Sopenharmony_ci		_fsl_readl = _fsl_readl_le;
8708c2ecf20Sopenharmony_ci		_fsl_writel = _fsl_writel_le;
8718c2ecf20Sopenharmony_ci	}
8728c2ecf20Sopenharmony_ci#endif
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	/* request irq */
8758c2ecf20Sopenharmony_ci	p_otg->irq = platform_get_irq(pdev, 0);
8768c2ecf20Sopenharmony_ci	if (p_otg->irq < 0)
8778c2ecf20Sopenharmony_ci		return p_otg->irq;
8788c2ecf20Sopenharmony_ci	status = request_irq(p_otg->irq, fsl_otg_isr,
8798c2ecf20Sopenharmony_ci				IRQF_SHARED, driver_name, p_otg);
8808c2ecf20Sopenharmony_ci	if (status) {
8818c2ecf20Sopenharmony_ci		dev_dbg(p_otg->phy.dev, "can't get IRQ %d, error %d\n",
8828c2ecf20Sopenharmony_ci			p_otg->irq, status);
8838c2ecf20Sopenharmony_ci		iounmap(p_otg->dr_mem_map);
8848c2ecf20Sopenharmony_ci		kfree(p_otg->phy.otg);
8858c2ecf20Sopenharmony_ci		kfree(p_otg);
8868c2ecf20Sopenharmony_ci		return status;
8878c2ecf20Sopenharmony_ci	}
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	/* stop the controller */
8908c2ecf20Sopenharmony_ci	temp = fsl_readl(&p_otg->dr_mem_map->usbcmd);
8918c2ecf20Sopenharmony_ci	temp &= ~USB_CMD_RUN_STOP;
8928c2ecf20Sopenharmony_ci	fsl_writel(temp, &p_otg->dr_mem_map->usbcmd);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	/* reset the controller */
8958c2ecf20Sopenharmony_ci	temp = fsl_readl(&p_otg->dr_mem_map->usbcmd);
8968c2ecf20Sopenharmony_ci	temp |= USB_CMD_CTRL_RESET;
8978c2ecf20Sopenharmony_ci	fsl_writel(temp, &p_otg->dr_mem_map->usbcmd);
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	/* wait reset completed */
9008c2ecf20Sopenharmony_ci	while (fsl_readl(&p_otg->dr_mem_map->usbcmd) & USB_CMD_CTRL_RESET)
9018c2ecf20Sopenharmony_ci		;
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	/* configure the VBUSHS as IDLE(both host and device) */
9048c2ecf20Sopenharmony_ci	temp = USB_MODE_STREAM_DISABLE | (pdata->es ? USB_MODE_ES : 0);
9058c2ecf20Sopenharmony_ci	fsl_writel(temp, &p_otg->dr_mem_map->usbmode);
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	/* configure PHY interface */
9088c2ecf20Sopenharmony_ci	temp = fsl_readl(&p_otg->dr_mem_map->portsc);
9098c2ecf20Sopenharmony_ci	temp &= ~(PORTSC_PHY_TYPE_SEL | PORTSC_PTW);
9108c2ecf20Sopenharmony_ci	switch (pdata->phy_mode) {
9118c2ecf20Sopenharmony_ci	case FSL_USB2_PHY_ULPI:
9128c2ecf20Sopenharmony_ci		temp |= PORTSC_PTS_ULPI;
9138c2ecf20Sopenharmony_ci		break;
9148c2ecf20Sopenharmony_ci	case FSL_USB2_PHY_UTMI_WIDE:
9158c2ecf20Sopenharmony_ci		temp |= PORTSC_PTW_16BIT;
9168c2ecf20Sopenharmony_ci		fallthrough;
9178c2ecf20Sopenharmony_ci	case FSL_USB2_PHY_UTMI:
9188c2ecf20Sopenharmony_ci		temp |= PORTSC_PTS_UTMI;
9198c2ecf20Sopenharmony_ci		fallthrough;
9208c2ecf20Sopenharmony_ci	default:
9218c2ecf20Sopenharmony_ci		break;
9228c2ecf20Sopenharmony_ci	}
9238c2ecf20Sopenharmony_ci	fsl_writel(temp, &p_otg->dr_mem_map->portsc);
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	if (pdata->have_sysif_regs) {
9268c2ecf20Sopenharmony_ci		/* configure control enable IO output, big endian register */
9278c2ecf20Sopenharmony_ci		temp = __raw_readl(&p_otg->dr_mem_map->control);
9288c2ecf20Sopenharmony_ci		temp |= USB_CTRL_IOENB;
9298c2ecf20Sopenharmony_ci		__raw_writel(temp, &p_otg->dr_mem_map->control);
9308c2ecf20Sopenharmony_ci	}
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	/* disable all interrupt and clear all OTGSC status */
9338c2ecf20Sopenharmony_ci	temp = fsl_readl(&p_otg->dr_mem_map->otgsc);
9348c2ecf20Sopenharmony_ci	temp &= ~OTGSC_INTERRUPT_ENABLE_BITS_MASK;
9358c2ecf20Sopenharmony_ci	temp |= OTGSC_INTERRUPT_STATUS_BITS_MASK | OTGSC_CTRL_VBUS_DISCHARGE;
9368c2ecf20Sopenharmony_ci	fsl_writel(temp, &p_otg->dr_mem_map->otgsc);
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	/*
9398c2ecf20Sopenharmony_ci	 * The identification (id) input is FALSE when a Mini-A plug is inserted
9408c2ecf20Sopenharmony_ci	 * in the devices Mini-AB receptacle. Otherwise, this input is TRUE.
9418c2ecf20Sopenharmony_ci	 * Also: record initial state of ID pin
9428c2ecf20Sopenharmony_ci	 */
9438c2ecf20Sopenharmony_ci	if (fsl_readl(&p_otg->dr_mem_map->otgsc) & OTGSC_STS_USB_ID) {
9448c2ecf20Sopenharmony_ci		p_otg->phy.otg->state = OTG_STATE_UNDEFINED;
9458c2ecf20Sopenharmony_ci		p_otg->fsm.id = 1;
9468c2ecf20Sopenharmony_ci	} else {
9478c2ecf20Sopenharmony_ci		p_otg->phy.otg->state = OTG_STATE_A_IDLE;
9488c2ecf20Sopenharmony_ci		p_otg->fsm.id = 0;
9498c2ecf20Sopenharmony_ci	}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	pr_debug("initial ID pin=%d\n", p_otg->fsm.id);
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	/* enable OTG ID pin interrupt */
9548c2ecf20Sopenharmony_ci	temp = fsl_readl(&p_otg->dr_mem_map->otgsc);
9558c2ecf20Sopenharmony_ci	temp |= OTGSC_INTR_USB_ID_EN;
9568c2ecf20Sopenharmony_ci	temp &= ~(OTGSC_CTRL_VBUS_DISCHARGE | OTGSC_INTR_1MS_TIMER_EN);
9578c2ecf20Sopenharmony_ci	fsl_writel(temp, &p_otg->dr_mem_map->otgsc);
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	return 0;
9608c2ecf20Sopenharmony_ci}
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_cistatic int fsl_otg_probe(struct platform_device *pdev)
9638c2ecf20Sopenharmony_ci{
9648c2ecf20Sopenharmony_ci	int ret;
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	if (!dev_get_platdata(&pdev->dev))
9678c2ecf20Sopenharmony_ci		return -ENODEV;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	/* configure the OTG */
9708c2ecf20Sopenharmony_ci	ret = fsl_otg_conf(pdev);
9718c2ecf20Sopenharmony_ci	if (ret) {
9728c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Couldn't configure OTG module\n");
9738c2ecf20Sopenharmony_ci		return ret;
9748c2ecf20Sopenharmony_ci	}
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	/* start OTG */
9778c2ecf20Sopenharmony_ci	ret = usb_otg_start(pdev);
9788c2ecf20Sopenharmony_ci	if (ret) {
9798c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Can't init FSL OTG device\n");
9808c2ecf20Sopenharmony_ci		return ret;
9818c2ecf20Sopenharmony_ci	}
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	return ret;
9848c2ecf20Sopenharmony_ci}
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_cistatic int fsl_otg_remove(struct platform_device *pdev)
9878c2ecf20Sopenharmony_ci{
9888c2ecf20Sopenharmony_ci	struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	usb_remove_phy(&fsl_otg_dev->phy);
9918c2ecf20Sopenharmony_ci	free_irq(fsl_otg_dev->irq, fsl_otg_dev);
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	iounmap((void *)usb_dr_regs);
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	fsl_otg_uninit_timers();
9968c2ecf20Sopenharmony_ci	kfree(fsl_otg_dev->phy.otg);
9978c2ecf20Sopenharmony_ci	kfree(fsl_otg_dev);
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	if (pdata->exit)
10008c2ecf20Sopenharmony_ci		pdata->exit(pdev);
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	return 0;
10038c2ecf20Sopenharmony_ci}
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_cistruct platform_driver fsl_otg_driver = {
10068c2ecf20Sopenharmony_ci	.probe = fsl_otg_probe,
10078c2ecf20Sopenharmony_ci	.remove = fsl_otg_remove,
10088c2ecf20Sopenharmony_ci	.driver = {
10098c2ecf20Sopenharmony_ci		.name = driver_name,
10108c2ecf20Sopenharmony_ci		.owner = THIS_MODULE,
10118c2ecf20Sopenharmony_ci	},
10128c2ecf20Sopenharmony_ci};
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_cimodule_platform_driver(fsl_otg_driver);
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_INFO);
10178c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
10188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1019