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