162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2007,2008 Freescale semiconductor, Inc. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Li Yang <LeoLi@freescale.com> 662306a36Sopenharmony_ci * Jerry Huang <Chang-Ming.Huang@freescale.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Initialization based on code from Shlomi Gridish. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/proc_fs.h> 1662306a36Sopenharmony_ci#include <linux/errno.h> 1762306a36Sopenharmony_ci#include <linux/interrupt.h> 1862306a36Sopenharmony_ci#include <linux/io.h> 1962306a36Sopenharmony_ci#include <linux/timer.h> 2062306a36Sopenharmony_ci#include <linux/usb.h> 2162306a36Sopenharmony_ci#include <linux/device.h> 2262306a36Sopenharmony_ci#include <linux/usb/ch9.h> 2362306a36Sopenharmony_ci#include <linux/usb/gadget.h> 2462306a36Sopenharmony_ci#include <linux/workqueue.h> 2562306a36Sopenharmony_ci#include <linux/time.h> 2662306a36Sopenharmony_ci#include <linux/fsl_devices.h> 2762306a36Sopenharmony_ci#include <linux/platform_device.h> 2862306a36Sopenharmony_ci#include <linux/uaccess.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <asm/unaligned.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include "phy-fsl-usb.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#ifdef VERBOSE 3562306a36Sopenharmony_ci#define VDBG(fmt, args...) pr_debug("[%s] " fmt, \ 3662306a36Sopenharmony_ci __func__, ## args) 3762306a36Sopenharmony_ci#else 3862306a36Sopenharmony_ci#define VDBG(stuff...) do {} while (0) 3962306a36Sopenharmony_ci#endif 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define DRIVER_VERSION "Rev. 1.55" 4262306a36Sopenharmony_ci#define DRIVER_AUTHOR "Jerry Huang/Li Yang" 4362306a36Sopenharmony_ci#define DRIVER_DESC "Freescale USB OTG Transceiver Driver" 4462306a36Sopenharmony_ci#define DRIVER_INFO DRIVER_DESC " " DRIVER_VERSION 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic const char driver_name[] = "fsl-usb2-otg"; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ciconst pm_message_t otg_suspend_state = { 4962306a36Sopenharmony_ci .event = 1, 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define HA_DATA_PULSE 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic struct usb_dr_mmap *usb_dr_regs; 5562306a36Sopenharmony_cistatic struct fsl_otg *fsl_otg_dev; 5662306a36Sopenharmony_cistatic int srp_wait_done; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* FSM timers */ 5962306a36Sopenharmony_cistruct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, *a_aidl_bdis_tmr, 6062306a36Sopenharmony_ci *b_ase0_brst_tmr, *b_se0_srp_tmr; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* Driver specific timers */ 6362306a36Sopenharmony_cistruct fsl_otg_timer *b_data_pulse_tmr, *b_vbus_pulse_tmr, *b_srp_fail_tmr, 6462306a36Sopenharmony_ci *b_srp_wait_tmr, *a_wait_enum_tmr; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic struct list_head active_timers; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic const struct fsl_otg_config fsl_otg_initdata = { 6962306a36Sopenharmony_ci .otg_port = 1, 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#ifdef CONFIG_PPC32 7362306a36Sopenharmony_cistatic u32 _fsl_readl_be(const unsigned __iomem *p) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci return in_be32(p); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic u32 _fsl_readl_le(const unsigned __iomem *p) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci return in_le32(p); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void _fsl_writel_be(u32 v, unsigned __iomem *p) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci out_be32(p, v); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void _fsl_writel_le(u32 v, unsigned __iomem *p) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci out_le32(p, v); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic u32 (*_fsl_readl)(const unsigned __iomem *p); 9462306a36Sopenharmony_cistatic void (*_fsl_writel)(u32 v, unsigned __iomem *p); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define fsl_readl(p) (*_fsl_readl)((p)) 9762306a36Sopenharmony_ci#define fsl_writel(v, p) (*_fsl_writel)((v), (p)) 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#else 10062306a36Sopenharmony_ci#define fsl_readl(addr) readl(addr) 10162306a36Sopenharmony_ci#define fsl_writel(val, addr) writel(val, addr) 10262306a36Sopenharmony_ci#endif /* CONFIG_PPC32 */ 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ciint write_ulpi(u8 addr, u8 data) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci u32 temp; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci temp = 0x60000000 | (addr << 16) | data; 10962306a36Sopenharmony_ci fsl_writel(temp, &usb_dr_regs->ulpiview); 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* -------------------------------------------------------------*/ 11462306a36Sopenharmony_ci/* Operations that will be called from OTG Finite State Machine */ 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* Charge vbus for vbus pulsing in SRP */ 11762306a36Sopenharmony_civoid fsl_otg_chrg_vbus(struct otg_fsm *fsm, int on) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci u32 tmp; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (on) 12462306a36Sopenharmony_ci /* stop discharging, start charging */ 12562306a36Sopenharmony_ci tmp = (tmp & ~OTGSC_CTRL_VBUS_DISCHARGE) | 12662306a36Sopenharmony_ci OTGSC_CTRL_VBUS_CHARGE; 12762306a36Sopenharmony_ci else 12862306a36Sopenharmony_ci /* stop charging */ 12962306a36Sopenharmony_ci tmp &= ~OTGSC_CTRL_VBUS_CHARGE; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci fsl_writel(tmp, &usb_dr_regs->otgsc); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* Discharge vbus through a resistor to ground */ 13562306a36Sopenharmony_civoid fsl_otg_dischrg_vbus(int on) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci u32 tmp; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (on) 14262306a36Sopenharmony_ci /* stop charging, start discharging */ 14362306a36Sopenharmony_ci tmp = (tmp & ~OTGSC_CTRL_VBUS_CHARGE) | 14462306a36Sopenharmony_ci OTGSC_CTRL_VBUS_DISCHARGE; 14562306a36Sopenharmony_ci else 14662306a36Sopenharmony_ci /* stop discharging */ 14762306a36Sopenharmony_ci tmp &= ~OTGSC_CTRL_VBUS_DISCHARGE; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci fsl_writel(tmp, &usb_dr_regs->otgsc); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci/* A-device driver vbus, controlled through PP bit in PORTSC */ 15362306a36Sopenharmony_civoid fsl_otg_drv_vbus(struct otg_fsm *fsm, int on) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci u32 tmp; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (on) { 15862306a36Sopenharmony_ci tmp = fsl_readl(&usb_dr_regs->portsc) & ~PORTSC_W1C_BITS; 15962306a36Sopenharmony_ci fsl_writel(tmp | PORTSC_PORT_POWER, &usb_dr_regs->portsc); 16062306a36Sopenharmony_ci } else { 16162306a36Sopenharmony_ci tmp = fsl_readl(&usb_dr_regs->portsc) & 16262306a36Sopenharmony_ci ~PORTSC_W1C_BITS & ~PORTSC_PORT_POWER; 16362306a36Sopenharmony_ci fsl_writel(tmp, &usb_dr_regs->portsc); 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci/* 16862306a36Sopenharmony_ci * Pull-up D+, signalling connect by periperal. Also used in 16962306a36Sopenharmony_ci * data-line pulsing in SRP 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_civoid fsl_otg_loc_conn(struct otg_fsm *fsm, int on) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci u32 tmp; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (on) 17862306a36Sopenharmony_ci tmp |= OTGSC_CTRL_DATA_PULSING; 17962306a36Sopenharmony_ci else 18062306a36Sopenharmony_ci tmp &= ~OTGSC_CTRL_DATA_PULSING; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci fsl_writel(tmp, &usb_dr_regs->otgsc); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* 18662306a36Sopenharmony_ci * Generate SOF by host. This is controlled through suspend/resume the 18762306a36Sopenharmony_ci * port. In host mode, controller will automatically send SOF. 18862306a36Sopenharmony_ci * Suspend will block the data on the port. 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_civoid fsl_otg_loc_sof(struct otg_fsm *fsm, int on) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci u32 tmp; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci tmp = fsl_readl(&fsl_otg_dev->dr_mem_map->portsc) & ~PORTSC_W1C_BITS; 19562306a36Sopenharmony_ci if (on) 19662306a36Sopenharmony_ci tmp |= PORTSC_PORT_FORCE_RESUME; 19762306a36Sopenharmony_ci else 19862306a36Sopenharmony_ci tmp |= PORTSC_PORT_SUSPEND; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci fsl_writel(tmp, &fsl_otg_dev->dr_mem_map->portsc); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */ 20562306a36Sopenharmony_civoid fsl_otg_start_pulse(struct otg_fsm *fsm) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci u32 tmp; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci srp_wait_done = 0; 21062306a36Sopenharmony_ci#ifdef HA_DATA_PULSE 21162306a36Sopenharmony_ci tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; 21262306a36Sopenharmony_ci tmp |= OTGSC_HA_DATA_PULSE; 21362306a36Sopenharmony_ci fsl_writel(tmp, &usb_dr_regs->otgsc); 21462306a36Sopenharmony_ci#else 21562306a36Sopenharmony_ci fsl_otg_loc_conn(1); 21662306a36Sopenharmony_ci#endif 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci fsl_otg_add_timer(fsm, b_data_pulse_tmr); 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_civoid b_data_pulse_end(unsigned long foo) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci#ifdef HA_DATA_PULSE 22462306a36Sopenharmony_ci#else 22562306a36Sopenharmony_ci fsl_otg_loc_conn(0); 22662306a36Sopenharmony_ci#endif 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* Do VBUS pulse after data pulse */ 22962306a36Sopenharmony_ci fsl_otg_pulse_vbus(); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_civoid fsl_otg_pulse_vbus(void) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci srp_wait_done = 0; 23562306a36Sopenharmony_ci fsl_otg_chrg_vbus(&fsl_otg_dev->fsm, 1); 23662306a36Sopenharmony_ci /* start the timer to end vbus charge */ 23762306a36Sopenharmony_ci fsl_otg_add_timer(&fsl_otg_dev->fsm, b_vbus_pulse_tmr); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_civoid b_vbus_pulse_end(unsigned long foo) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci fsl_otg_chrg_vbus(&fsl_otg_dev->fsm, 0); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* 24562306a36Sopenharmony_ci * As USB3300 using the same a_sess_vld and b_sess_vld voltage 24662306a36Sopenharmony_ci * we need to discharge the bus for a while to distinguish 24762306a36Sopenharmony_ci * residual voltage of vbus pulsing and A device pull up 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_ci fsl_otg_dischrg_vbus(1); 25062306a36Sopenharmony_ci fsl_otg_add_timer(&fsl_otg_dev->fsm, b_srp_wait_tmr); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_civoid b_srp_end(unsigned long foo) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci fsl_otg_dischrg_vbus(0); 25662306a36Sopenharmony_ci srp_wait_done = 1; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if ((fsl_otg_dev->phy.otg->state == OTG_STATE_B_SRP_INIT) && 25962306a36Sopenharmony_ci fsl_otg_dev->fsm.b_sess_vld) 26062306a36Sopenharmony_ci fsl_otg_dev->fsm.b_srp_done = 1; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci/* 26462306a36Sopenharmony_ci * Workaround for a_host suspending too fast. When a_bus_req=0, 26562306a36Sopenharmony_ci * a_host will start by SRP. It needs to set b_hnp_enable before 26662306a36Sopenharmony_ci * actually suspending to start HNP 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_civoid a_wait_enum(unsigned long foo) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci VDBG("a_wait_enum timeout\n"); 27162306a36Sopenharmony_ci if (!fsl_otg_dev->phy.otg->host->b_hnp_enable) 27262306a36Sopenharmony_ci fsl_otg_add_timer(&fsl_otg_dev->fsm, a_wait_enum_tmr); 27362306a36Sopenharmony_ci else 27462306a36Sopenharmony_ci otg_statemachine(&fsl_otg_dev->fsm); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/* The timeout callback function to set time out bit */ 27862306a36Sopenharmony_civoid set_tmout(unsigned long indicator) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci *(int *)indicator = 1; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci/* Initialize timers */ 28462306a36Sopenharmony_ciint fsl_otg_init_timers(struct otg_fsm *fsm) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci /* FSM used timers */ 28762306a36Sopenharmony_ci a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE, 28862306a36Sopenharmony_ci (unsigned long)&fsm->a_wait_vrise_tmout); 28962306a36Sopenharmony_ci if (!a_wait_vrise_tmr) 29062306a36Sopenharmony_ci return -ENOMEM; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON, 29362306a36Sopenharmony_ci (unsigned long)&fsm->a_wait_bcon_tmout); 29462306a36Sopenharmony_ci if (!a_wait_bcon_tmr) 29562306a36Sopenharmony_ci return -ENOMEM; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS, 29862306a36Sopenharmony_ci (unsigned long)&fsm->a_aidl_bdis_tmout); 29962306a36Sopenharmony_ci if (!a_aidl_bdis_tmr) 30062306a36Sopenharmony_ci return -ENOMEM; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST, 30362306a36Sopenharmony_ci (unsigned long)&fsm->b_ase0_brst_tmout); 30462306a36Sopenharmony_ci if (!b_ase0_brst_tmr) 30562306a36Sopenharmony_ci return -ENOMEM; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP, 30862306a36Sopenharmony_ci (unsigned long)&fsm->b_se0_srp); 30962306a36Sopenharmony_ci if (!b_se0_srp_tmr) 31062306a36Sopenharmony_ci return -ENOMEM; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci b_srp_fail_tmr = otg_timer_initializer(&set_tmout, TB_SRP_FAIL, 31362306a36Sopenharmony_ci (unsigned long)&fsm->b_srp_done); 31462306a36Sopenharmony_ci if (!b_srp_fail_tmr) 31562306a36Sopenharmony_ci return -ENOMEM; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci a_wait_enum_tmr = otg_timer_initializer(&a_wait_enum, 10, 31862306a36Sopenharmony_ci (unsigned long)&fsm); 31962306a36Sopenharmony_ci if (!a_wait_enum_tmr) 32062306a36Sopenharmony_ci return -ENOMEM; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* device driver used timers */ 32362306a36Sopenharmony_ci b_srp_wait_tmr = otg_timer_initializer(&b_srp_end, TB_SRP_WAIT, 0); 32462306a36Sopenharmony_ci if (!b_srp_wait_tmr) 32562306a36Sopenharmony_ci return -ENOMEM; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci b_data_pulse_tmr = otg_timer_initializer(&b_data_pulse_end, 32862306a36Sopenharmony_ci TB_DATA_PLS, 0); 32962306a36Sopenharmony_ci if (!b_data_pulse_tmr) 33062306a36Sopenharmony_ci return -ENOMEM; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci b_vbus_pulse_tmr = otg_timer_initializer(&b_vbus_pulse_end, 33362306a36Sopenharmony_ci TB_VBUS_PLS, 0); 33462306a36Sopenharmony_ci if (!b_vbus_pulse_tmr) 33562306a36Sopenharmony_ci return -ENOMEM; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci return 0; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci/* Uninitialize timers */ 34162306a36Sopenharmony_civoid fsl_otg_uninit_timers(void) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci /* FSM used timers */ 34462306a36Sopenharmony_ci kfree(a_wait_vrise_tmr); 34562306a36Sopenharmony_ci kfree(a_wait_bcon_tmr); 34662306a36Sopenharmony_ci kfree(a_aidl_bdis_tmr); 34762306a36Sopenharmony_ci kfree(b_ase0_brst_tmr); 34862306a36Sopenharmony_ci kfree(b_se0_srp_tmr); 34962306a36Sopenharmony_ci kfree(b_srp_fail_tmr); 35062306a36Sopenharmony_ci kfree(a_wait_enum_tmr); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci /* device driver used timers */ 35362306a36Sopenharmony_ci kfree(b_srp_wait_tmr); 35462306a36Sopenharmony_ci kfree(b_data_pulse_tmr); 35562306a36Sopenharmony_ci kfree(b_vbus_pulse_tmr); 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic struct fsl_otg_timer *fsl_otg_get_timer(enum otg_fsm_timer t) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct fsl_otg_timer *timer; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* REVISIT: use array of pointers to timers instead */ 36362306a36Sopenharmony_ci switch (t) { 36462306a36Sopenharmony_ci case A_WAIT_VRISE: 36562306a36Sopenharmony_ci timer = a_wait_vrise_tmr; 36662306a36Sopenharmony_ci break; 36762306a36Sopenharmony_ci case A_WAIT_BCON: 36862306a36Sopenharmony_ci timer = a_wait_vrise_tmr; 36962306a36Sopenharmony_ci break; 37062306a36Sopenharmony_ci case A_AIDL_BDIS: 37162306a36Sopenharmony_ci timer = a_wait_vrise_tmr; 37262306a36Sopenharmony_ci break; 37362306a36Sopenharmony_ci case B_ASE0_BRST: 37462306a36Sopenharmony_ci timer = a_wait_vrise_tmr; 37562306a36Sopenharmony_ci break; 37662306a36Sopenharmony_ci case B_SE0_SRP: 37762306a36Sopenharmony_ci timer = a_wait_vrise_tmr; 37862306a36Sopenharmony_ci break; 37962306a36Sopenharmony_ci case B_SRP_FAIL: 38062306a36Sopenharmony_ci timer = a_wait_vrise_tmr; 38162306a36Sopenharmony_ci break; 38262306a36Sopenharmony_ci case A_WAIT_ENUM: 38362306a36Sopenharmony_ci timer = a_wait_vrise_tmr; 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci default: 38662306a36Sopenharmony_ci timer = NULL; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return timer; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/* Add timer to timer list */ 39362306a36Sopenharmony_civoid fsl_otg_add_timer(struct otg_fsm *fsm, void *gtimer) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct fsl_otg_timer *timer = gtimer; 39662306a36Sopenharmony_ci struct fsl_otg_timer *tmp_timer; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* 39962306a36Sopenharmony_ci * Check if the timer is already in the active list, 40062306a36Sopenharmony_ci * if so update timer count 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_ci list_for_each_entry(tmp_timer, &active_timers, list) 40362306a36Sopenharmony_ci if (tmp_timer == timer) { 40462306a36Sopenharmony_ci timer->count = timer->expires; 40562306a36Sopenharmony_ci return; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci timer->count = timer->expires; 40862306a36Sopenharmony_ci list_add_tail(&timer->list, &active_timers); 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic void fsl_otg_fsm_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer t) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci struct fsl_otg_timer *timer; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci timer = fsl_otg_get_timer(t); 41662306a36Sopenharmony_ci if (!timer) 41762306a36Sopenharmony_ci return; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci fsl_otg_add_timer(fsm, timer); 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci/* Remove timer from the timer list; clear timeout status */ 42362306a36Sopenharmony_civoid fsl_otg_del_timer(struct otg_fsm *fsm, void *gtimer) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct fsl_otg_timer *timer = gtimer; 42662306a36Sopenharmony_ci struct fsl_otg_timer *tmp_timer, *del_tmp; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) 42962306a36Sopenharmony_ci if (tmp_timer == timer) 43062306a36Sopenharmony_ci list_del(&timer->list); 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic void fsl_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci struct fsl_otg_timer *timer; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci timer = fsl_otg_get_timer(t); 43862306a36Sopenharmony_ci if (!timer) 43962306a36Sopenharmony_ci return; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci fsl_otg_del_timer(fsm, timer); 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci/* Reset controller, not reset the bus */ 44562306a36Sopenharmony_civoid otg_reset_controller(void) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci u32 command; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci command = fsl_readl(&usb_dr_regs->usbcmd); 45062306a36Sopenharmony_ci command |= (1 << 1); 45162306a36Sopenharmony_ci fsl_writel(command, &usb_dr_regs->usbcmd); 45262306a36Sopenharmony_ci while (fsl_readl(&usb_dr_regs->usbcmd) & (1 << 1)) 45362306a36Sopenharmony_ci ; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci/* Call suspend/resume routines in host driver */ 45762306a36Sopenharmony_ciint fsl_otg_start_host(struct otg_fsm *fsm, int on) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci struct usb_otg *otg = fsm->otg; 46062306a36Sopenharmony_ci struct device *dev; 46162306a36Sopenharmony_ci struct fsl_otg *otg_dev = 46262306a36Sopenharmony_ci container_of(otg->usb_phy, struct fsl_otg, phy); 46362306a36Sopenharmony_ci u32 retval = 0; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (!otg->host) 46662306a36Sopenharmony_ci return -ENODEV; 46762306a36Sopenharmony_ci dev = otg->host->controller; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* 47062306a36Sopenharmony_ci * Update a_vbus_vld state as a_vbus_vld int is disabled 47162306a36Sopenharmony_ci * in device mode 47262306a36Sopenharmony_ci */ 47362306a36Sopenharmony_ci fsm->a_vbus_vld = 47462306a36Sopenharmony_ci !!(fsl_readl(&usb_dr_regs->otgsc) & OTGSC_STS_A_VBUS_VALID); 47562306a36Sopenharmony_ci if (on) { 47662306a36Sopenharmony_ci /* start fsl usb host controller */ 47762306a36Sopenharmony_ci if (otg_dev->host_working) 47862306a36Sopenharmony_ci goto end; 47962306a36Sopenharmony_ci else { 48062306a36Sopenharmony_ci otg_reset_controller(); 48162306a36Sopenharmony_ci VDBG("host on......\n"); 48262306a36Sopenharmony_ci if (dev->driver->pm && dev->driver->pm->resume) { 48362306a36Sopenharmony_ci retval = dev->driver->pm->resume(dev); 48462306a36Sopenharmony_ci if (fsm->id) { 48562306a36Sopenharmony_ci /* default-b */ 48662306a36Sopenharmony_ci fsl_otg_drv_vbus(fsm, 1); 48762306a36Sopenharmony_ci /* 48862306a36Sopenharmony_ci * Workaround: b_host can't driver 48962306a36Sopenharmony_ci * vbus, but PP in PORTSC needs to 49062306a36Sopenharmony_ci * be 1 for host to work. 49162306a36Sopenharmony_ci * So we set drv_vbus bit in 49262306a36Sopenharmony_ci * transceiver to 0 thru ULPI. 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_ci write_ulpi(0x0c, 0x20); 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci otg_dev->host_working = 1; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci } else { 50162306a36Sopenharmony_ci /* stop fsl usb host controller */ 50262306a36Sopenharmony_ci if (!otg_dev->host_working) 50362306a36Sopenharmony_ci goto end; 50462306a36Sopenharmony_ci else { 50562306a36Sopenharmony_ci VDBG("host off......\n"); 50662306a36Sopenharmony_ci if (dev && dev->driver) { 50762306a36Sopenharmony_ci if (dev->driver->pm && dev->driver->pm->suspend) 50862306a36Sopenharmony_ci retval = dev->driver->pm->suspend(dev); 50962306a36Sopenharmony_ci if (fsm->id) 51062306a36Sopenharmony_ci /* default-b */ 51162306a36Sopenharmony_ci fsl_otg_drv_vbus(fsm, 0); 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci otg_dev->host_working = 0; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ciend: 51762306a36Sopenharmony_ci return retval; 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci/* 52162306a36Sopenharmony_ci * Call suspend and resume function in udc driver 52262306a36Sopenharmony_ci * to stop and start udc driver. 52362306a36Sopenharmony_ci */ 52462306a36Sopenharmony_ciint fsl_otg_start_gadget(struct otg_fsm *fsm, int on) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci struct usb_otg *otg = fsm->otg; 52762306a36Sopenharmony_ci struct device *dev; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (!otg->gadget || !otg->gadget->dev.parent) 53062306a36Sopenharmony_ci return -ENODEV; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci VDBG("gadget %s\n", on ? "on" : "off"); 53362306a36Sopenharmony_ci dev = otg->gadget->dev.parent; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (on) { 53662306a36Sopenharmony_ci if (dev->driver->resume) 53762306a36Sopenharmony_ci dev->driver->resume(dev); 53862306a36Sopenharmony_ci } else { 53962306a36Sopenharmony_ci if (dev->driver->suspend) 54062306a36Sopenharmony_ci dev->driver->suspend(dev, otg_suspend_state); 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci return 0; 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci/* 54762306a36Sopenharmony_ci * Called by initialization code of host driver. Register host controller 54862306a36Sopenharmony_ci * to the OTG. Suspend host for OTG role detection. 54962306a36Sopenharmony_ci */ 55062306a36Sopenharmony_cistatic int fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct fsl_otg *otg_dev; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (!otg) 55562306a36Sopenharmony_ci return -ENODEV; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci otg_dev = container_of(otg->usb_phy, struct fsl_otg, phy); 55862306a36Sopenharmony_ci if (otg_dev != fsl_otg_dev) 55962306a36Sopenharmony_ci return -ENODEV; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci otg->host = host; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci otg_dev->fsm.a_bus_drop = 0; 56462306a36Sopenharmony_ci otg_dev->fsm.a_bus_req = 1; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (host) { 56762306a36Sopenharmony_ci VDBG("host off......\n"); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci otg->host->otg_port = fsl_otg_initdata.otg_port; 57062306a36Sopenharmony_ci otg->host->is_b_host = otg_dev->fsm.id; 57162306a36Sopenharmony_ci /* 57262306a36Sopenharmony_ci * must leave time for hub_wq to finish its thing 57362306a36Sopenharmony_ci * before yanking the host driver out from under it, 57462306a36Sopenharmony_ci * so suspend the host after a short delay. 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_ci otg_dev->host_working = 1; 57762306a36Sopenharmony_ci schedule_delayed_work(&otg_dev->otg_event, 100); 57862306a36Sopenharmony_ci return 0; 57962306a36Sopenharmony_ci } else { 58062306a36Sopenharmony_ci /* host driver going away */ 58162306a36Sopenharmony_ci if (!(fsl_readl(&otg_dev->dr_mem_map->otgsc) & 58262306a36Sopenharmony_ci OTGSC_STS_USB_ID)) { 58362306a36Sopenharmony_ci /* Mini-A cable connected */ 58462306a36Sopenharmony_ci struct otg_fsm *fsm = &otg_dev->fsm; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci otg->state = OTG_STATE_UNDEFINED; 58762306a36Sopenharmony_ci fsm->protocol = PROTO_UNDEF; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci otg_dev->host_working = 0; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci otg_statemachine(&otg_dev->fsm); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci return 0; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci/* Called by initialization code of udc. Register udc to OTG. */ 59962306a36Sopenharmony_cistatic int fsl_otg_set_peripheral(struct usb_otg *otg, 60062306a36Sopenharmony_ci struct usb_gadget *gadget) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci struct fsl_otg *otg_dev; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (!otg) 60562306a36Sopenharmony_ci return -ENODEV; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci otg_dev = container_of(otg->usb_phy, struct fsl_otg, phy); 60862306a36Sopenharmony_ci VDBG("otg_dev 0x%x\n", (int)otg_dev); 60962306a36Sopenharmony_ci VDBG("fsl_otg_dev 0x%x\n", (int)fsl_otg_dev); 61062306a36Sopenharmony_ci if (otg_dev != fsl_otg_dev) 61162306a36Sopenharmony_ci return -ENODEV; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (!gadget) { 61462306a36Sopenharmony_ci if (!otg->default_a) 61562306a36Sopenharmony_ci otg->gadget->ops->vbus_draw(otg->gadget, 0); 61662306a36Sopenharmony_ci usb_gadget_vbus_disconnect(otg->gadget); 61762306a36Sopenharmony_ci otg->gadget = 0; 61862306a36Sopenharmony_ci otg_dev->fsm.b_bus_req = 0; 61962306a36Sopenharmony_ci otg_statemachine(&otg_dev->fsm); 62062306a36Sopenharmony_ci return 0; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci otg->gadget = gadget; 62462306a36Sopenharmony_ci otg->gadget->is_a_peripheral = !otg_dev->fsm.id; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci otg_dev->fsm.b_bus_req = 1; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci /* start the gadget right away if the ID pin says Mini-B */ 62962306a36Sopenharmony_ci pr_debug("ID pin=%d\n", otg_dev->fsm.id); 63062306a36Sopenharmony_ci if (otg_dev->fsm.id == 1) { 63162306a36Sopenharmony_ci fsl_otg_start_host(&otg_dev->fsm, 0); 63262306a36Sopenharmony_ci otg_drv_vbus(&otg_dev->fsm, 0); 63362306a36Sopenharmony_ci fsl_otg_start_gadget(&otg_dev->fsm, 1); 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci return 0; 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci/* 64062306a36Sopenharmony_ci * Delayed pin detect interrupt processing. 64162306a36Sopenharmony_ci * 64262306a36Sopenharmony_ci * When the Mini-A cable is disconnected from the board, 64362306a36Sopenharmony_ci * the pin-detect interrupt happens before the disconnect 64462306a36Sopenharmony_ci * interrupts for the connected device(s). In order to 64562306a36Sopenharmony_ci * process the disconnect interrupt(s) prior to switching 64662306a36Sopenharmony_ci * roles, the pin-detect interrupts are delayed, and handled 64762306a36Sopenharmony_ci * by this routine. 64862306a36Sopenharmony_ci */ 64962306a36Sopenharmony_cistatic void fsl_otg_event(struct work_struct *work) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci struct fsl_otg *og = container_of(work, struct fsl_otg, otg_event.work); 65262306a36Sopenharmony_ci struct otg_fsm *fsm = &og->fsm; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (fsm->id) { /* switch to gadget */ 65562306a36Sopenharmony_ci fsl_otg_start_host(fsm, 0); 65662306a36Sopenharmony_ci otg_drv_vbus(fsm, 0); 65762306a36Sopenharmony_ci fsl_otg_start_gadget(fsm, 1); 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci/* B-device start SRP */ 66262306a36Sopenharmony_cistatic int fsl_otg_start_srp(struct usb_otg *otg) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci struct fsl_otg *otg_dev; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (!otg || otg->state != OTG_STATE_B_IDLE) 66762306a36Sopenharmony_ci return -ENODEV; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci otg_dev = container_of(otg->usb_phy, struct fsl_otg, phy); 67062306a36Sopenharmony_ci if (otg_dev != fsl_otg_dev) 67162306a36Sopenharmony_ci return -ENODEV; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci otg_dev->fsm.b_bus_req = 1; 67462306a36Sopenharmony_ci otg_statemachine(&otg_dev->fsm); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci return 0; 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci/* A_host suspend will call this function to start hnp */ 68062306a36Sopenharmony_cistatic int fsl_otg_start_hnp(struct usb_otg *otg) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci struct fsl_otg *otg_dev; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if (!otg) 68562306a36Sopenharmony_ci return -ENODEV; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci otg_dev = container_of(otg->usb_phy, struct fsl_otg, phy); 68862306a36Sopenharmony_ci if (otg_dev != fsl_otg_dev) 68962306a36Sopenharmony_ci return -ENODEV; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci pr_debug("start_hnp...\n"); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* clear a_bus_req to enter a_suspend state */ 69462306a36Sopenharmony_ci otg_dev->fsm.a_bus_req = 0; 69562306a36Sopenharmony_ci otg_statemachine(&otg_dev->fsm); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci return 0; 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci/* 70162306a36Sopenharmony_ci * Interrupt handler. OTG/host/peripheral share the same int line. 70262306a36Sopenharmony_ci * OTG driver clears OTGSC interrupts and leaves USB interrupts 70362306a36Sopenharmony_ci * intact. It needs to have knowledge of some USB interrupts 70462306a36Sopenharmony_ci * such as port change. 70562306a36Sopenharmony_ci */ 70662306a36Sopenharmony_ciirqreturn_t fsl_otg_isr(int irq, void *dev_id) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm; 70962306a36Sopenharmony_ci struct usb_otg *otg = ((struct fsl_otg *)dev_id)->phy.otg; 71062306a36Sopenharmony_ci u32 otg_int_src, otg_sc; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci otg_sc = fsl_readl(&usb_dr_regs->otgsc); 71362306a36Sopenharmony_ci otg_int_src = otg_sc & OTGSC_INTSTS_MASK & (otg_sc >> 8); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci /* Only clear otg interrupts */ 71662306a36Sopenharmony_ci fsl_writel(otg_sc, &usb_dr_regs->otgsc); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci /*FIXME: ID change not generate when init to 0 */ 71962306a36Sopenharmony_ci fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; 72062306a36Sopenharmony_ci otg->default_a = (fsm->id == 0); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* process OTG interrupts */ 72362306a36Sopenharmony_ci if (otg_int_src) { 72462306a36Sopenharmony_ci if (otg_int_src & OTGSC_INTSTS_USB_ID) { 72562306a36Sopenharmony_ci fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; 72662306a36Sopenharmony_ci otg->default_a = (fsm->id == 0); 72762306a36Sopenharmony_ci /* clear conn information */ 72862306a36Sopenharmony_ci if (fsm->id) 72962306a36Sopenharmony_ci fsm->b_conn = 0; 73062306a36Sopenharmony_ci else 73162306a36Sopenharmony_ci fsm->a_conn = 0; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci if (otg->host) 73462306a36Sopenharmony_ci otg->host->is_b_host = fsm->id; 73562306a36Sopenharmony_ci if (otg->gadget) 73662306a36Sopenharmony_ci otg->gadget->is_a_peripheral = !fsm->id; 73762306a36Sopenharmony_ci VDBG("ID int (ID is %d)\n", fsm->id); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (fsm->id) { /* switch to gadget */ 74062306a36Sopenharmony_ci schedule_delayed_work( 74162306a36Sopenharmony_ci &((struct fsl_otg *)dev_id)->otg_event, 74262306a36Sopenharmony_ci 100); 74362306a36Sopenharmony_ci } else { /* switch to host */ 74462306a36Sopenharmony_ci cancel_delayed_work(& 74562306a36Sopenharmony_ci ((struct fsl_otg *)dev_id)-> 74662306a36Sopenharmony_ci otg_event); 74762306a36Sopenharmony_ci fsl_otg_start_gadget(fsm, 0); 74862306a36Sopenharmony_ci otg_drv_vbus(fsm, 1); 74962306a36Sopenharmony_ci fsl_otg_start_host(fsm, 1); 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci return IRQ_HANDLED; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci return IRQ_NONE; 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_cistatic struct otg_fsm_ops fsl_otg_ops = { 75862306a36Sopenharmony_ci .chrg_vbus = fsl_otg_chrg_vbus, 75962306a36Sopenharmony_ci .drv_vbus = fsl_otg_drv_vbus, 76062306a36Sopenharmony_ci .loc_conn = fsl_otg_loc_conn, 76162306a36Sopenharmony_ci .loc_sof = fsl_otg_loc_sof, 76262306a36Sopenharmony_ci .start_pulse = fsl_otg_start_pulse, 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci .add_timer = fsl_otg_fsm_add_timer, 76562306a36Sopenharmony_ci .del_timer = fsl_otg_fsm_del_timer, 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci .start_host = fsl_otg_start_host, 76862306a36Sopenharmony_ci .start_gadget = fsl_otg_start_gadget, 76962306a36Sopenharmony_ci}; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci/* Initialize the global variable fsl_otg_dev and request IRQ for OTG */ 77262306a36Sopenharmony_cistatic int fsl_otg_conf(struct platform_device *pdev) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci struct fsl_otg *fsl_otg_tc; 77562306a36Sopenharmony_ci int status; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (fsl_otg_dev) 77862306a36Sopenharmony_ci return 0; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci /* allocate space to fsl otg device */ 78162306a36Sopenharmony_ci fsl_otg_tc = kzalloc(sizeof(struct fsl_otg), GFP_KERNEL); 78262306a36Sopenharmony_ci if (!fsl_otg_tc) 78362306a36Sopenharmony_ci return -ENOMEM; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci fsl_otg_tc->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL); 78662306a36Sopenharmony_ci if (!fsl_otg_tc->phy.otg) { 78762306a36Sopenharmony_ci kfree(fsl_otg_tc); 78862306a36Sopenharmony_ci return -ENOMEM; 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci INIT_DELAYED_WORK(&fsl_otg_tc->otg_event, fsl_otg_event); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci INIT_LIST_HEAD(&active_timers); 79462306a36Sopenharmony_ci status = fsl_otg_init_timers(&fsl_otg_tc->fsm); 79562306a36Sopenharmony_ci if (status) { 79662306a36Sopenharmony_ci pr_info("Couldn't init OTG timers\n"); 79762306a36Sopenharmony_ci goto err; 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci mutex_init(&fsl_otg_tc->fsm.lock); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* Set OTG state machine operations */ 80262306a36Sopenharmony_ci fsl_otg_tc->fsm.ops = &fsl_otg_ops; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci /* initialize the otg structure */ 80562306a36Sopenharmony_ci fsl_otg_tc->phy.label = DRIVER_DESC; 80662306a36Sopenharmony_ci fsl_otg_tc->phy.dev = &pdev->dev; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci fsl_otg_tc->phy.otg->usb_phy = &fsl_otg_tc->phy; 80962306a36Sopenharmony_ci fsl_otg_tc->phy.otg->set_host = fsl_otg_set_host; 81062306a36Sopenharmony_ci fsl_otg_tc->phy.otg->set_peripheral = fsl_otg_set_peripheral; 81162306a36Sopenharmony_ci fsl_otg_tc->phy.otg->start_hnp = fsl_otg_start_hnp; 81262306a36Sopenharmony_ci fsl_otg_tc->phy.otg->start_srp = fsl_otg_start_srp; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci fsl_otg_dev = fsl_otg_tc; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci /* Store the otg transceiver */ 81762306a36Sopenharmony_ci status = usb_add_phy(&fsl_otg_tc->phy, USB_PHY_TYPE_USB2); 81862306a36Sopenharmony_ci if (status) { 81962306a36Sopenharmony_ci pr_warn(FSL_OTG_NAME ": unable to register OTG transceiver.\n"); 82062306a36Sopenharmony_ci goto err; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci return 0; 82462306a36Sopenharmony_cierr: 82562306a36Sopenharmony_ci fsl_otg_uninit_timers(); 82662306a36Sopenharmony_ci kfree(fsl_otg_tc->phy.otg); 82762306a36Sopenharmony_ci kfree(fsl_otg_tc); 82862306a36Sopenharmony_ci return status; 82962306a36Sopenharmony_ci} 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci/* OTG Initialization */ 83262306a36Sopenharmony_ciint usb_otg_start(struct platform_device *pdev) 83362306a36Sopenharmony_ci{ 83462306a36Sopenharmony_ci struct fsl_otg *p_otg; 83562306a36Sopenharmony_ci struct usb_phy *otg_trans = usb_get_phy(USB_PHY_TYPE_USB2); 83662306a36Sopenharmony_ci struct otg_fsm *fsm; 83762306a36Sopenharmony_ci int status; 83862306a36Sopenharmony_ci struct resource *res; 83962306a36Sopenharmony_ci u32 temp; 84062306a36Sopenharmony_ci struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci p_otg = container_of(otg_trans, struct fsl_otg, phy); 84362306a36Sopenharmony_ci fsm = &p_otg->fsm; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci /* Initialize the state machine structure with default values */ 84662306a36Sopenharmony_ci SET_OTG_STATE(otg_trans, OTG_STATE_UNDEFINED); 84762306a36Sopenharmony_ci fsm->otg = p_otg->phy.otg; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci /* We don't require predefined MEM/IRQ resource index */ 85062306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 85162306a36Sopenharmony_ci if (!res) 85262306a36Sopenharmony_ci return -ENXIO; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci /* We don't request_mem_region here to enable resource sharing 85562306a36Sopenharmony_ci * with host/device */ 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci usb_dr_regs = ioremap(res->start, sizeof(struct usb_dr_mmap)); 85862306a36Sopenharmony_ci p_otg->dr_mem_map = (struct usb_dr_mmap *)usb_dr_regs; 85962306a36Sopenharmony_ci pdata->regs = (void *)usb_dr_regs; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci if (pdata->init && pdata->init(pdev) != 0) 86262306a36Sopenharmony_ci return -EINVAL; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci#ifdef CONFIG_PPC32 86562306a36Sopenharmony_ci if (pdata->big_endian_mmio) { 86662306a36Sopenharmony_ci _fsl_readl = _fsl_readl_be; 86762306a36Sopenharmony_ci _fsl_writel = _fsl_writel_be; 86862306a36Sopenharmony_ci } else { 86962306a36Sopenharmony_ci _fsl_readl = _fsl_readl_le; 87062306a36Sopenharmony_ci _fsl_writel = _fsl_writel_le; 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci#endif 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci /* request irq */ 87562306a36Sopenharmony_ci p_otg->irq = platform_get_irq(pdev, 0); 87662306a36Sopenharmony_ci if (p_otg->irq < 0) 87762306a36Sopenharmony_ci return p_otg->irq; 87862306a36Sopenharmony_ci status = request_irq(p_otg->irq, fsl_otg_isr, 87962306a36Sopenharmony_ci IRQF_SHARED, driver_name, p_otg); 88062306a36Sopenharmony_ci if (status) { 88162306a36Sopenharmony_ci dev_dbg(p_otg->phy.dev, "can't get IRQ %d, error %d\n", 88262306a36Sopenharmony_ci p_otg->irq, status); 88362306a36Sopenharmony_ci iounmap(p_otg->dr_mem_map); 88462306a36Sopenharmony_ci kfree(p_otg->phy.otg); 88562306a36Sopenharmony_ci kfree(p_otg); 88662306a36Sopenharmony_ci return status; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci /* stop the controller */ 89062306a36Sopenharmony_ci temp = fsl_readl(&p_otg->dr_mem_map->usbcmd); 89162306a36Sopenharmony_ci temp &= ~USB_CMD_RUN_STOP; 89262306a36Sopenharmony_ci fsl_writel(temp, &p_otg->dr_mem_map->usbcmd); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci /* reset the controller */ 89562306a36Sopenharmony_ci temp = fsl_readl(&p_otg->dr_mem_map->usbcmd); 89662306a36Sopenharmony_ci temp |= USB_CMD_CTRL_RESET; 89762306a36Sopenharmony_ci fsl_writel(temp, &p_otg->dr_mem_map->usbcmd); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci /* wait reset completed */ 90062306a36Sopenharmony_ci while (fsl_readl(&p_otg->dr_mem_map->usbcmd) & USB_CMD_CTRL_RESET) 90162306a36Sopenharmony_ci ; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci /* configure the VBUSHS as IDLE(both host and device) */ 90462306a36Sopenharmony_ci temp = USB_MODE_STREAM_DISABLE | (pdata->es ? USB_MODE_ES : 0); 90562306a36Sopenharmony_ci fsl_writel(temp, &p_otg->dr_mem_map->usbmode); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci /* configure PHY interface */ 90862306a36Sopenharmony_ci temp = fsl_readl(&p_otg->dr_mem_map->portsc); 90962306a36Sopenharmony_ci temp &= ~(PORTSC_PHY_TYPE_SEL | PORTSC_PTW); 91062306a36Sopenharmony_ci switch (pdata->phy_mode) { 91162306a36Sopenharmony_ci case FSL_USB2_PHY_ULPI: 91262306a36Sopenharmony_ci temp |= PORTSC_PTS_ULPI; 91362306a36Sopenharmony_ci break; 91462306a36Sopenharmony_ci case FSL_USB2_PHY_UTMI_WIDE: 91562306a36Sopenharmony_ci temp |= PORTSC_PTW_16BIT; 91662306a36Sopenharmony_ci fallthrough; 91762306a36Sopenharmony_ci case FSL_USB2_PHY_UTMI: 91862306a36Sopenharmony_ci temp |= PORTSC_PTS_UTMI; 91962306a36Sopenharmony_ci fallthrough; 92062306a36Sopenharmony_ci default: 92162306a36Sopenharmony_ci break; 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_ci fsl_writel(temp, &p_otg->dr_mem_map->portsc); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci if (pdata->have_sysif_regs) { 92662306a36Sopenharmony_ci /* configure control enable IO output, big endian register */ 92762306a36Sopenharmony_ci temp = __raw_readl(&p_otg->dr_mem_map->control); 92862306a36Sopenharmony_ci temp |= USB_CTRL_IOENB; 92962306a36Sopenharmony_ci __raw_writel(temp, &p_otg->dr_mem_map->control); 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci /* disable all interrupt and clear all OTGSC status */ 93362306a36Sopenharmony_ci temp = fsl_readl(&p_otg->dr_mem_map->otgsc); 93462306a36Sopenharmony_ci temp &= ~OTGSC_INTERRUPT_ENABLE_BITS_MASK; 93562306a36Sopenharmony_ci temp |= OTGSC_INTERRUPT_STATUS_BITS_MASK | OTGSC_CTRL_VBUS_DISCHARGE; 93662306a36Sopenharmony_ci fsl_writel(temp, &p_otg->dr_mem_map->otgsc); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci /* 93962306a36Sopenharmony_ci * The identification (id) input is FALSE when a Mini-A plug is inserted 94062306a36Sopenharmony_ci * in the devices Mini-AB receptacle. Otherwise, this input is TRUE. 94162306a36Sopenharmony_ci * Also: record initial state of ID pin 94262306a36Sopenharmony_ci */ 94362306a36Sopenharmony_ci if (fsl_readl(&p_otg->dr_mem_map->otgsc) & OTGSC_STS_USB_ID) { 94462306a36Sopenharmony_ci p_otg->phy.otg->state = OTG_STATE_UNDEFINED; 94562306a36Sopenharmony_ci p_otg->fsm.id = 1; 94662306a36Sopenharmony_ci } else { 94762306a36Sopenharmony_ci p_otg->phy.otg->state = OTG_STATE_A_IDLE; 94862306a36Sopenharmony_ci p_otg->fsm.id = 0; 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci pr_debug("initial ID pin=%d\n", p_otg->fsm.id); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci /* enable OTG ID pin interrupt */ 95462306a36Sopenharmony_ci temp = fsl_readl(&p_otg->dr_mem_map->otgsc); 95562306a36Sopenharmony_ci temp |= OTGSC_INTR_USB_ID_EN; 95662306a36Sopenharmony_ci temp &= ~(OTGSC_CTRL_VBUS_DISCHARGE | OTGSC_INTR_1MS_TIMER_EN); 95762306a36Sopenharmony_ci fsl_writel(temp, &p_otg->dr_mem_map->otgsc); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci return 0; 96062306a36Sopenharmony_ci} 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_cistatic int fsl_otg_probe(struct platform_device *pdev) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci int ret; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (!dev_get_platdata(&pdev->dev)) 96762306a36Sopenharmony_ci return -ENODEV; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci /* configure the OTG */ 97062306a36Sopenharmony_ci ret = fsl_otg_conf(pdev); 97162306a36Sopenharmony_ci if (ret) { 97262306a36Sopenharmony_ci dev_err(&pdev->dev, "Couldn't configure OTG module\n"); 97362306a36Sopenharmony_ci return ret; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* start OTG */ 97762306a36Sopenharmony_ci ret = usb_otg_start(pdev); 97862306a36Sopenharmony_ci if (ret) { 97962306a36Sopenharmony_ci dev_err(&pdev->dev, "Can't init FSL OTG device\n"); 98062306a36Sopenharmony_ci return ret; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci return ret; 98462306a36Sopenharmony_ci} 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_cistatic void fsl_otg_remove(struct platform_device *pdev) 98762306a36Sopenharmony_ci{ 98862306a36Sopenharmony_ci struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci usb_remove_phy(&fsl_otg_dev->phy); 99162306a36Sopenharmony_ci free_irq(fsl_otg_dev->irq, fsl_otg_dev); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci iounmap((void *)usb_dr_regs); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci fsl_otg_uninit_timers(); 99662306a36Sopenharmony_ci kfree(fsl_otg_dev->phy.otg); 99762306a36Sopenharmony_ci kfree(fsl_otg_dev); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci if (pdata->exit) 100062306a36Sopenharmony_ci pdata->exit(pdev); 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_cistruct platform_driver fsl_otg_driver = { 100462306a36Sopenharmony_ci .probe = fsl_otg_probe, 100562306a36Sopenharmony_ci .remove_new = fsl_otg_remove, 100662306a36Sopenharmony_ci .driver = { 100762306a36Sopenharmony_ci .name = driver_name, 100862306a36Sopenharmony_ci .owner = THIS_MODULE, 100962306a36Sopenharmony_ci }, 101062306a36Sopenharmony_ci}; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_cimodule_platform_driver(fsl_otg_driver); 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_INFO); 101562306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 101662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1017