blob: efccec9abc7df52f5e0df4118a8b921744c87e96 [file] [log] [blame]
/*
* Copyright (c) 2013 Qualcomm Atheros, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/usb_ch9.h>
#include <linux/usb_gadget.h>
#include <linux/usb.h>
#include <linux/i2c.h>
#include <linux/workqueue.h>
#include <asm/irq.h>
#include <asm/io.h>
#include "../core/hcd.h"
#include "../host/ehci.h"
#define ATH_USB_DEBUG
#include "../gadget/ath_usb_otg.h"
#include "../gadget/ath_usb_udc.h"
#define ATH_USB_HOST_REG_OFFSET (0x1A4)
#define ATH_USB_CAP_REG_OFFSET (0x100)
#define ATH_USB_DRIVER_NAME "ath_usb_otg"
#ifdef ATH_USB_DEBUG
static int ath_usb_debug_level = (
// ATH_USB_DEBUG_FUNCTION |
// ATH_USB_DEBUG_INTERRUPT |
// ATH_USB_DEBUG_ENDPOINT |
// ATH_USB_DEBUG_PORTSTATUS |
// ATH_USB_DEBUG_DEVICE |
// ATH_USB_DEBUG_MEMORY |
// ATH_USB_DEBUG_QUEUEHEAD |
// ATH_USB_DEBUG_DTD |
// ATH_USB_DEBUG_OTG |
0x0
);
#endif
static struct ath_usb_otg *ath_usb_otg = NULL;
static int ath_usb_start_hnp(struct otg_transceiver *dev);
static int ath_usb_start_srp(struct otg_transceiver *dev);
static int ath_usb_set_power(struct otg_transceiver *dev, unsigned mA);
static int ath_usb_host_suspend(struct ath_usb_otg *ath_usb);
static int ath_usb_host_resume(struct ath_usb_otg *ath_usb);
static void _usb_otg_state_machine (struct ath_usb_otg *ath_usb);
static int gadget_resume(struct ath_usb_otg *ath_usb);
static int gadget_suspend(struct ath_usb_otg *ath_usb);
void _usb_otg_process_b_idle (struct ath_usb_otg *ath_usb);
void _usb_otg_process_a_idle (struct ath_usb_otg *ath_usb);
void _usb_otg_process_a_wait_vfall(struct ath_usb_otg *ath_usb);
void _usb_otg_process_b_device_session_valid (struct ath_usb_otg *ath_usb);
void _usb_otg_process_a_wait_vrise(struct ath_usb_otg *ath_usb);
void _usb_otg_reset_state_machine (struct ath_usb_otg *ath_usb);
void *ath_usb_get_otg (void)
{
if (!ath_usb_otg) {
return (NULL);
}
return (ath_usb_otg);
}
static int enable_vbus_draw (struct ath_usb_otg *ath_usb, int value)
{
/* Nothing to do */
return (0);
}
static int power_down (struct ath_usb_otg *ath_usb)
{
return (0);
}
static void ath_usb_reset_otg_state (struct ath_usb_otg *ath_usb)
{
struct otg_transceiver *otg = &ath_usb->otg;
otg->state = OTG_STATE_A_IDLE;
otg->default_a = 0;
otg->host = NULL;
otg->gadget = NULL;
otg->port_status= 0;
otg->port_change= 0;
_usb_otg_reset_state_machine (ath_usb);
ath_usb->state.HOST_UP = 0;
}
/*FUNCTION*----------------------------------------------------------------
*
* Function Name : _usb_otg_reset_state_machine
* Returned Value : TRUE or FALSE
* Comments :
* Reset the state machine to its orginal state.
*END*--------------------------------------------------------------------*/
void _usb_otg_reset_state_machine (struct ath_usb_otg *ath_usb)
{
struct ath_usb_otg_vars *s_ptr;
s_ptr = &ath_usb->state;
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_reset_state_machine");
#endif
s_ptr->OTG_INT_STATUS = 0;
s_ptr->A_BUS_RESUME = 0;
s_ptr->A_BUS_SUSPEND = 0;
s_ptr->A_BUS_SUSPEND_REQ = 0;
s_ptr->A_CONN = 0;
s_ptr->B_BUS_RESUME = 0;
s_ptr->B_BUS_SUSPEND = 0;
s_ptr->B_CONN = 0;
s_ptr->A_SET_B_HNP_EN = 0;
s_ptr->B_SESS_REQ = 0;
s_ptr->B_SRP_DONE = 0;
s_ptr->B_HNP_ENABLE = 0;
s_ptr->A_CONNECTED = 0;
s_ptr->B_DISCONNECTED = 0;
s_ptr->CHECK_SESSION = TRUE;
s_ptr->A_SUSPEND_TIMER_ON = 0;
s_ptr->B_BUS_REQ = 0;
s_ptr->A_BUS_REQ = 1;
s_ptr->A_BUS_DROP = 0;
TB_SE0_SRP_TMR_OFF(s_ptr);
A_SRP_TMR_OFF(s_ptr);
TA_WAIT_VRISE_TMR_OFF(s_ptr);
TA_WAIT_BCON_TMR_OFF(s_ptr);
TA_AIDL_BDIS_TMR_OFF(s_ptr);
TA_BIDL_ADIS_TMR_OFF(s_ptr);
TB_DATA_PLS_TMR_OFF(s_ptr);
TB_SRP_INIT_TMR_OFF(s_ptr);
TB_SRP_FAIL_TMR_OFF(s_ptr);
TB_VBUS_PLS_TMR_OFF(s_ptr);
TB_ASE0_BRST_TMR_OFF(s_ptr);
TB_A_SUSPEND_TMR_OFF(s_ptr);
TB_VBUS_DSCHRG_TMR_OFF(s_ptr);
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_reset_state_machine, SUCCESSFUL");
#endif
}
/*FUNCTION*----------------------------------------------------------------
*
* Function Name : _usb_otg_process_exceptions
* Returned Value : TRUE or FALSE
* Comments :
* process the exception signals and switch state and return TRUE.
*END*--------------------------------------------------------------------*/
u8 _usb_otg_process_exceptions (struct ath_usb_otg *ath_usb)
{
struct ath_usb_otg_vars *s_ptr;
struct ath_usb_usb __iomem *usb_reg;
u8 previous_state;
u8 state;
u32 otg_int_status;
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_process_exceptions");
#endif
s_ptr = &ath_usb->state;
usb_reg = ath_usb->usb_reg;
state = ath_usb->otg.state;
otg_int_status = s_ptr->OTG_INT_STATUS;
/* Check exception for A device */
previous_state = state;
if ((state < OTG_STATE_B_IDLE) || (state > OTG_STATE_B_HOST)) {
/*
** Exceptions are as follows.
** 1. Transition to B_IDLE when id = TRUE.
** 2. Transition to A_WAIT_VFALL when V_BUS is voltage is fallen below
** 3. 4 VDC or not able to set the V_BUS voltage.
** 4. If a_wait_b_con_timout is set.
** 5. If a_aidl_bdis_tmout is set.
*/
/* if(m1 || m2 || m3 || m4 || m5) */
if ((ID_CHG(otg_int_status) && ID(usb_reg))||
((A_VBUS_CHG(otg_int_status) && A_VBUS_VLD_FALSE(usb_reg)) ) ||
(SESS_VLD_CHG(otg_int_status) && SESS_VLD_FALSE(usb_reg)) ||
(TA_WAIT_VRISE_TMR_EXPIRED(s_ptr)) ||
(TA_WAIT_BCON_TMR_EXPIRED(s_ptr)) ||
(TA_AIDL_BDIS_TMR_EXPIRED(s_ptr)))
{
#ifndef PERIPHERAL_ONLY
if ((state == OTG_STATE_A_IDLE) && (ID_CHG(otg_int_status) && ID(usb_reg)))
{
SET_STATE(OTG_STATE_B_IDLE, "F ");
s_ptr->B_BUS_REQ = FALSE;
_usb_otg_process_b_idle(ath_usb);
}
/* change made after I2C code merge the line containing && (state != A_WAIT_VRISE)
should work for I2C and non-I2C hardware.
*/
else if ((state != OTG_STATE_A_IDLE) && (state != OTG_STATE_A_WAIT_VFALL))
{
#if 0
if (TA_WAIT_VRISE_TMR_EXPIRED(s_ptr))
uartputs("\nTA_WAIT_VRISE_TMR_EXPIRED(s_ptr) == TRUE");
else
uartputs("\nTA_WAIT_VRISE_TMR_EXPIRED(s_ptr) == FALSE");
if (A_VBUS_CHG(otg_int_status))
uartputs("\nA_VBUS_CHG(otg_int_status) == TRUE");
else
uartputs("\nA_VBUS_CHG(otg_int_status) == FALSE");
if (A_VBUS_VLD_FALSE(usb_reg))
uartputs("\nA_VBUS_VLD_FALSE() == TRUE");
else
uartputs("\nA_VBUS_VLD_FALSE() == FALSE");
#endif
if ((TA_WAIT_VRISE_TMR_EXPIRED(s_ptr)) ||
(A_VBUS_CHG(otg_int_status) && A_VBUS_VLD_FALSE(usb_reg)))
{
// TODO ath_usb_service(ath_usb, USB_OTG_OVER_CURRENT);
}
#ifdef HNP_HARDWARE_ASSISTANCE
/*set the HABA bit off in OTGSC to disable hardware assitance */
AUTO_HNP_OFF(usb_reg);
#endif
SET_STATE(OTG_STATE_A_WAIT_VFALL, "s ");
if (TA_WAIT_BCON_TMR_EXPIRED(s_ptr))
{
TA_WAIT_BCON_TMR_OFF(s_ptr);
// TODO ath_usb_service(ath_usb, USB_OTG_NO_CONNECTION);
}
VBUS_CHG_OFF(usb_reg);
VBUS_OFF(usb_reg);
_usb_otg_process_a_wait_vfall(ath_usb);
}
#endif
}
} else {
/*
** Process B- device exceptions
*/
if ((ID_CHG(otg_int_status) && ID_FALSE(usb_reg)) ||
(SESS_VLD_CHG(otg_int_status) && SESS_VLD_FALSE(usb_reg) && ath_usb->otg.state != OTG_STATE_B_SRP_INIT))
{
VBUS_CHG_OFF(usb_reg);
VBUS_OFF(usb_reg);
s_ptr->B_BUS_REQ = FALSE;
if (ID(usb_reg) ) {
/************************************************************
SG 8/18/2003.
When software is about to move to B_IDLE state, it should
generate an HNP failure message if it is coming from
B_WAIT_ACON state. This is necessary to pass OPT on
test 5.9 because on some OTG PHY cards, VBUS can drop
faster (before B_ASE0_BRST_TMOUT timer expires) and
we can get a session valid interrupt. This interrupt
has caused the software to come here and it should
post the message to application saying that HNP has
failed.
************************************************************/
if ((state == OTG_STATE_B_WAIT_ACON) && s_ptr->B_HNP_ENABLE)
{
// TODO ath_usb_service(ath_usb, USB_OTG_HNP_FAILED);
}
/*if we are not running a suspend timer and waiting for HNP
to finish, we should move to B_IDLE state */
//if(!((state == B_PERIPHERAL) && s_ptr->A_SUSPEND_TIMER_ON))
{
/* proceed to B_IDLE state */
SET_STATE(OTG_STATE_B_IDLE, "G ");
PULL_UP_PULL_DOWN_IDLE(usb_reg);
_usb_otg_process_b_idle(ath_usb);
}
} else {
#ifndef PERIPHERAL_ONLY
SET_STATE(OTG_STATE_A_IDLE, "H ");
/* A unique case where we need to set this to TRUE */
s_ptr->A_BUS_REQ = TRUE;
_usb_otg_process_a_idle(ath_usb);
#endif
}
} /* endif */
}
if (ath_usb->otg.state != previous_state){
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_process_exceptions, SUCCESSFUL state changed");
#endif
return (TRUE);
} else {
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_process_exceptions, SUCCESSFUL no state change");
#endif
return (FALSE);
}
}
/*FUNCTION*----------------------------------------------------------------
*
* Function Name : _usb_otg_process_b_idle
* Returned Value : None
* Comments :
* Process B_IDLE state.
*
*END*--------------------------------------------------------------------*/
void _usb_otg_process_b_idle (struct ath_usb_otg *ath_usb)
{
struct ath_usb_otg_vars *s_ptr;
struct ath_usb_usb __iomem *usb_reg;
s32 srp_tmr;
u8 se0_srp;
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_process_b_idle");
#endif
s_ptr = &ath_usb->state;
usb_reg = ath_usb->usb_reg;
srp_tmr = s_ptr->TB_SE0_SRP_TMR;
se0_srp = s_ptr->B_SE0_SRP;
_usb_otg_reset_state_machine(ath_usb);
s_ptr->TB_SE0_SRP_TMR = srp_tmr;
s_ptr->B_SE0_SRP = se0_srp;
if (s_ptr->HOST_UP) {
s_ptr->HOST_UP = FALSE;
ath_usb_host_suspend(ath_usb);
PULL_UP_PULL_DOWN_IDLE(usb_reg);
}
// Alagu 04/11/06 OTG_DEVICE_INIT(ath_usb, usb_reg);
if (s_ptr->DEVICE_UP) {
s_ptr->DEVICE_UP = FALSE;
gadget_suspend(ath_usb);
PULL_UP_PULL_DOWN_IDLE(usb_reg);
}
/*
** When a B-device detects that the voltage on VBUS is greater
** than the B-Device Session Valid threshold (VSESS_VLD),
** then the B-device shall consider a session to be in progress.
** After the VBUS voltage crosses this threshold, the B-device
** shall assert either the D+ or D- data-line within 100 ms.
*/
if (SESS_VLD(usb_reg)) {
_usb_otg_process_b_device_session_valid(ath_usb);
} else if (s_ptr->B_BUS_REQ || s_ptr->B_SESS_REQ) {
/* ********* Starting SRP *****************************************
** When the B-device detects that VBUS has gone below its Session End
** threshold and detects that both D+ and D- have been low (SE0) for
** at least 2 ms (TB_SE0_SRP min), then any previous session on the
** A-device is over and a new session may start. The B-device may
** initiate the SRP any time the initial conditions of Section 5.3.2
** are met.
*/
/* activate the timer only if we want to do SRP */
if ((s_ptr->B_BUS_REQ || s_ptr->B_SESS_REQ) &&
(CHECK_TB_SE0_SRP_TMR_OFF(s_ptr)))
{
TB_SE0_SRP_TMR_ON(s_ptr, TB_SE0_SRP);
}
if ((s_ptr->B_BUS_REQ || s_ptr->B_SESS_REQ) && (B_SESS_END(usb_reg)) &&
(s_ptr->B_SE0_SRP))
{
TB_SE0_SRP_TMR_OFF(s_ptr);
s_ptr->B_SE0_SRP = FALSE;
/*
** initial conditions are met as described above and then turns
** on its data line pull-up resistor (either D+ or D-) for a
** period of 5 ms to 10 ms (TB_DATA_PLS). A dual-role B-device is
** only allowed to initiate SRP at full-speed, and thus shall
** only pull up D+. The duration of such a data line pulse is
** sufficient to allow the A-device to reject spurious voltage
** transients on the data lines.
*/
DP_HIGH_ON(usb_reg);
TB_SRP_FAIL_TMR_ON(s_ptr, TB_SRP_FAIL);
TB_SRP_INIT_TMR_ON(s_ptr, TB_SRP_INIT);
TB_DATA_PLS_TMR_ON(s_ptr, TB_DATA_PLS);
SET_STATE(OTG_STATE_B_SRP_INIT, "t ");
s_ptr->B_SRP_DONE = FALSE;
// TODO ath_usb_service(ath_usb, USB_OTG_SRP_ACTIVE);
} /* Endf */
}
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_process_b_idle, SUCCESSFUL");
#endif
}
#ifndef PERIPHERAL_ONLY
/*FUNCTION*----------------------------------------------------------------
*
* Function Name : _usb_otg_process_a_idle
* Returned Value : None
* Comments :
* Process B_IDLE state.
*
*END*--------------------------------------------------------------------*/
void _usb_otg_process_a_idle (struct ath_usb_otg *ath_usb)
{
struct ath_usb_otg_vars *s_ptr;
struct ath_usb_usb __iomem *usb_reg;
u8 a_srp_det;
s32 a_srp_tmr;
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_process_a_idle");
#endif
ath_usb_debug_otg("_usb_otg_process_a_idle\n");
s_ptr = &ath_usb->state;
usb_reg = ath_usb->usb_reg;
a_srp_det = s_ptr->A_SRP_DET;
a_srp_tmr = s_ptr->A_SRP_TMR;
_usb_otg_reset_state_machine(ath_usb);
s_ptr->A_SRP_DET = a_srp_det;
s_ptr->A_SRP_TMR = a_srp_tmr;
if (ID(usb_reg)) {
SET_STATE(OTG_STATE_B_IDLE, "J ");
_usb_otg_process_b_idle(ath_usb);
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_process_a_idle, SUCCESSFUL (state to B_IDLE)");
#endif
return;
}
if (s_ptr->DEVICE_UP) {
s_ptr->DEVICE_UP = FALSE;
gadget_suspend(ath_usb);
PULL_UP_PULL_DOWN_IDLE(usb_reg);
}
if (!s_ptr->HOST_UP) {
OTG_HOST_INIT(ath_usb, usb_reg);
}
if ((!s_ptr->A_BUS_REQ) && (s_ptr->A_SRP_TMR == -1) &&
(s_ptr->A_DATA_PULSE_DET))
{
A_SRP_TMR_ON(s_ptr, TB_DATA_PLS_MAX);
}
/* ************ SRP detection ****************************
** The A-device continuously monitors VBUS as long as power is
** available on the A-device. An A-device that is designed to
** detect the VBUS pulsing method will detect that VBUS has
** gone above the A-device Session Valid threshold (VA_SESS_VLD)
** and generate an indication that SRP has been detected.
*/
if ((!s_ptr->A_BUS_DROP) && (SESS_VLD(usb_reg) || s_ptr->A_SRP_DET ||
s_ptr->A_BUS_REQ))
{
s_ptr->A_SRP_DET = FALSE;
SET_STATE(OTG_STATE_A_WAIT_VRISE, "u ");
VBUS_ON(usb_reg);
TA_WAIT_VRISE_TMR_ON(s_ptr, TA_WAIT_VRISE);
_usb_otg_process_a_wait_vrise(ath_usb);
}
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_process_a_idle, SUCCESSFUL");
#endif
}
/*FUNCTION*----------------------------------------------------------------
*
* Function Name : _usb_otg_process_a_wait_vrise
* Returned Value : None
* Comments :
* Process A_WAIT_VRISE state.
*
*END*--------------------------------------------------------------------*/
void _usb_otg_process_a_wait_vrise(struct ath_usb_otg *ath_usb)
{
struct ath_usb_otg_vars *s_ptr;
struct ath_usb_usb __iomem *usb_reg;
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_process_a_wait_vrise");
#endif
ath_usb_debug_otg("_usb_otg_process_a_wait_vrise\n");
s_ptr = &ath_usb->state;
usb_reg = ath_usb->usb_reg;
if (A_VBUS_VLD(usb_reg)) {
TA_WAIT_VRISE_TMR_OFF(s_ptr);
/* switch to a_wait_bcon */
ath_usb->bVRISE_TIMEDOUT = 0;
SET_STATE(OTG_STATE_A_WAIT_BCON, "v ");
#ifndef TA_WAIT_BCON_TMR_FOR_EVER
TA_WAIT_BCON_TMR_ON(s_ptr, TA_WAIT_BCON);
#endif
} else {
if (TA_WAIT_VRISE_TMR_EXPIRED(s_ptr))
{
ath_usb->bVRISE_TIMEDOUT = 1;
SET_STATE(OTG_STATE_A_WAIT_BCON, "v2 ");
TA_WAIT_BCON_TMR_ON(s_ptr, TA_WAIT_BCON);
}
}
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_process_a_wait_vrise, SUCCESSFUL");
#endif
}
/*FUNCTION*----------------------------------------------------------------
*
* Function Name : _usb_otg_process_a_wait_vfall
* Returned Value : None
* Comments :
* Process A_WAIT_VFALL state.
*
*END*--------------------------------------------------------------------*/
void _usb_otg_process_a_wait_vfall(struct ath_usb_otg *ath_usb)
{
struct ath_usb_otg_vars *s_ptr;
struct ath_usb_usb __iomem *usb_reg;
u32 hw_signal;
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_process_a_wait_vfall");
#endif
ath_usb_debug_otg("_usb_otg_process_a_wait_vfall\n");
s_ptr = &ath_usb->state;
usb_reg = ath_usb->usb_reg;
hw_signal = s_ptr->OTG_INT_STATUS;
/* Turn this timer off if it is still on from A_peripheral state */
TA_BIDL_ADIS_TMR_OFF(s_ptr);
if (
(/*SESS_VLD_CHG(hw_signal) && */SESS_VLD_FALSE(usb_reg) /*&& (!s_ptr->B_CONN)*/) ||
ID(usb_reg) ||
s_ptr->A_BUS_REQ
)
{
SET_STATE(OTG_STATE_A_IDLE, "K ");
VBUS_CHG_OFF(usb_reg);
VBUS_OFF(usb_reg);
s_ptr->A_BUS_REQ = FALSE;
_usb_otg_process_a_idle(ath_usb);
}
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_process_a_wait_vfall, SUCCESSFUL");
#endif
}
#endif
/*FUNCTION*----------------------------------------------------------------
*
* Function Name : _usb_otg_process_b_device_session_valid
* Returned Value : None
* Comments :
* Process B device session valid.
*
*END*--------------------------------------------------------------------*/
void _usb_otg_process_b_device_session_valid (struct ath_usb_otg *ath_usb)
{
struct ath_usb_otg_vars *s_ptr;
struct ath_usb_usb __iomem *usb_reg;
s_ptr = &ath_usb->state;
usb_reg = ath_usb->usb_reg;
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_process_b_device_session_valid");
#endif
ath_usb_debug_otg("_usb_otg_process_b_device_session_valid\n");
if (ath_usb->otg.state == OTG_STATE_B_SRP_INIT) {
TB_SRP_FAIL_TMR_OFF(s_ptr);
TB_SRP_INIT_TMR_OFF(s_ptr);
TB_DATA_PLS_TMR_OFF(s_ptr);
TB_SE0_SRP_TMR_OFF(s_ptr);
TB_VBUS_DSCHRG_TMR_OFF(s_ptr);
}
DP_HIGH_OFF(usb_reg);
VBUS_CHG_OFF(usb_reg);
VBUS_DSCHG_OFF(usb_reg);
SET_STATE(OTG_STATE_B_PERIPHERAL, "w ");
OTG_DEVICE_INIT(ath_usb, usb_reg);
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_process_b_device_session_valid, SUCCESSFUL, state to B_PERIPHERAL");
#endif
}
/*FUNCTION*----------------------------------------------------------------
*
* Function Name : _usb_otg_process_b_srp_init
* Returned Value : None
* Comments :
* Process OTG_STATE_B_SRP_INIT state.
*
*END*--------------------------------------------------------------------*/
void _usb_otg_process_b_srp_init (struct ath_usb_otg *ath_usb)
{
struct ath_usb_otg_vars *s_ptr;
struct ath_usb_usb __iomem *usb_reg;
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_process_b_srp_init");
#endif
ath_usb_debug_otg("_usb_otg_process_b_srp_init\n");
s_ptr = &ath_usb->state;
usb_reg = ath_usb->usb_reg;
/*usb_otg_ptr->DO_SRP = FALSE;*/
/*
** When a B-device detects that the voltage on VBUS is greater than the
** B-Device Session Valid threshold (VSESS_VLD), then the B-device
** shall consider a session to be in progress. After the VBUS voltage
** crosses this threshold, the B-device shall assert either the D+ or
** D- data-line within 100 ms(TB_SVLD_BCON max).
*/
if ((SESS_VLD(usb_reg)) && (s_ptr->CHECK_SESSION)) {
_usb_otg_process_b_device_session_valid(ath_usb);
} else {
/* check_srp_activity */
if (TB_DATA_PLS_TMR_EXPIRED(s_ptr))
{
TB_DATA_PLS_TMR_OFF(s_ptr);
DP_HIGH_OFF(usb_reg);
/* An A-device is only required to respond to one of the two
** SRP signaling methods. A B-device shall use both methods
** when initiating SRP to insure that the A-device responds.
*/
s_ptr->CHECK_SESSION = FALSE;
VBUS_CHG_ON(usb_reg);
/* Turn on the timer so as to keep VBUS pusling ON for 8 ms */
TB_VBUS_PLS_TMR_ON(s_ptr, TB_VBUS_PLS);
}
if (TB_VBUS_PLS_TMR_EXPIRED(s_ptr)) {
/* Turn off the VBus pulsing timer */
TB_VBUS_PLS_TMR_OFF(s_ptr);
/* Stop VBus pulsing */
VBUS_CHG_OFF(usb_reg);
#if 0
s_ptr->CHECK_SESSION = TRUE;
/* If session is valid then proceed */
if (SESS_VLD(usb_reg)) {
_usb_otg_process_b_device_session_valid(ath_usb);
}
#endif
VBUS_DSCHG_ON(usb_reg);
TB_VBUS_DSCHRG_TMR_ON(s_ptr, TB_VBUS_DSCHRG);
}
if (TB_VBUS_DSCHRG_TMR_EXPIRED(s_ptr)) {
VBUS_DSCHG_OFF(usb_reg);
TB_VBUS_DSCHRG_TMR_OFF(s_ptr);
s_ptr->CHECK_SESSION = TRUE;
/* If session is valid then proceed */
if (SESS_VLD(usb_reg)) {
_usb_otg_process_b_device_session_valid(ath_usb);
}
}
if (TB_SRP_INIT_TMR_EXPIRED(s_ptr))
{
TB_SRP_INIT_TMR_OFF(s_ptr);
VBUS_CHG_OFF(usb_reg);
}
/*
** The error call back have the following requirement:
** After initiating SRP, the B-device is required to wait at least
** 5 seconds (TB_TB_SRP_FAIL min) for the A-device to respond,
** before informing the user that the communication attempt
** has failed.
**
*/
if (TB_SRP_FAIL_TMR_EXPIRED(s_ptr)) {
TB_SRP_FAIL_TMR_OFF(s_ptr);
s_ptr->B_SRP_DONE = FALSE;
s_ptr->B_BUS_REQ = FALSE; /*we should set this to false to avoid looping with B_IDLE */
SET_STATE(OTG_STATE_B_IDLE, "M ");
DP_HIGH_OFF(usb_reg);
VBUS_CHG_OFF(usb_reg);
// TODO ath_usb_service(ath_usb, USB_OTG_SRP_FAIL);
_usb_otg_process_b_idle(ath_usb);
}
}
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_process_b_srp_init, SUCCESSFUL");
#endif
}
/*FUNCTION*----------------------------------------------------------------
*
* Function Name : _usb_otg_process_timers
* Returned Value : TRUE/ FALSE
* Comments :
* If any timers expired, the state machine will be executed and returns
* TRUE, otherwise it will return FALSE.
*
*END*--------------------------------------------------------------------*/
u8 _usb_otg_process_timers (struct ath_usb_otg *ath_usb)
{ /* Body */
struct ath_usb_otg_vars *s_ptr;
struct ath_usb_usb __iomem *usb_reg;
u8 timer_expired = FALSE;
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_process_timers");
#endif
s_ptr = &ath_usb->state;
usb_reg = ath_usb->usb_reg;
/* Process all the A timers */
if (s_ptr->TA_WAIT_VRISE_TMR > 0) {
s_ptr->TA_WAIT_VRISE_TMR--;
if (TA_WAIT_VRISE_TMR_EXPIRED(s_ptr)) {
timer_expired = TRUE;
LOG_TIMER("\nTA_WAIT_VRISE_TMR_EXPIRED ");
}
}
if (s_ptr->TA_WAIT_BCON_TMR > 0) {
s_ptr->TA_WAIT_BCON_TMR--;
if (TA_WAIT_BCON_TMR_EXPIRED(s_ptr)) {
timer_expired = TRUE;
LOG_TIMER("\nTA_WAIT_BCON_TMR_EXPIRED ");
}
}
if (s_ptr->TA_AIDL_BDIS_TMR > 0) {
s_ptr->TA_AIDL_BDIS_TMR--;
if (TA_AIDL_BDIS_TMR_EXPIRED(s_ptr)) {
timer_expired = TRUE;
LOG_TIMER("\nTA_AIDL_BDIS_TMR_EXPIRED ");
}
}
if (s_ptr->TA_BIDL_ADIS_TMR > 0) {
s_ptr->TA_BIDL_ADIS_TMR--;
if (TA_BIDL_ADIS_TMR_EXPIRED(s_ptr)) {
LOG_TIMER("\nTA_BIDL_ADIS_TMR_EXPIRED ");
timer_expired = TRUE;
}
}
/*
** Process all the debouncing timers first
*/
if (s_ptr->TB_SE0_SRP_TMR > 0) {
if (SE0(usb_reg) &&
(STABLE_J_SE0_LINE_STATE(usb_reg)))
{
/* signal is constant for 1 ms, so decrement counter */
s_ptr->TB_SE0_SRP_TMR--;
} else {
/* random behaviour; Reset the counter */
TB_SE0_SRP_TMR_ON(s_ptr, TB_SE0_SRP);
}
if (TB_SE0_SRP_TMR_EXPIRED(s_ptr)) {
s_ptr->B_SE0_SRP = TRUE;
LOG_TIMER("\nTB_SE0_SRP_TMR_EXPIRED ");
timer_expired = TRUE;
}
}
/* process all the B timers */
if (s_ptr->TB_DATA_PLS_TMR > 0) {
s_ptr->TB_DATA_PLS_TMR--;
if (TB_DATA_PLS_TMR_EXPIRED(s_ptr)) {
LOG_TIMER("\nTB_DATA_PLS_TMR_EXPIRED ");
timer_expired = TRUE;
}
}
if (s_ptr->TB_VBUS_PLS_TMR > 0) {
/* VBus pulsing timer */
s_ptr->TB_VBUS_PLS_TMR--;
if (TB_VBUS_PLS_TMR_EXPIRED(s_ptr)) {
LOG_TIMER("\nTB_VBUS_PLS_TMR_EXPIRED ");
timer_expired = TRUE;
#if 0
s_ptr->CHECK_SESSION = TRUE;
#endif
}
}
if (s_ptr->TB_SRP_INIT_TMR > 0) {
s_ptr->TB_SRP_INIT_TMR--;
if (TB_SRP_INIT_TMR_EXPIRED(s_ptr)) {
LOG_TIMER("\nTB_SRP_INIT_TMR_EXPIRED ");
timer_expired = TRUE;
}
}
if (s_ptr->TB_VBUS_DSCHRG_TMR > 0) {
s_ptr->TB_VBUS_DSCHRG_TMR--;
if (TB_VBUS_DSCHRG_TMR_EXPIRED(s_ptr)) {
timer_expired = TRUE;
/* Test GR */
/*s_ptr->CHECK_SESSION = TRUE;*/
LOG_TIMER("\nTB_VBUS_DSCHRG_TMR_EXPIRED ");
}
}
if (s_ptr->TB_SRP_FAIL_TMR > 0) {
s_ptr->TB_SRP_FAIL_TMR--;
if (TB_SRP_FAIL_TMR_EXPIRED(s_ptr)) {
LOG_TIMER("\nTB_SRP_FAIL_TMR_EXPIRED ");
timer_expired = TRUE;
}
}
if (s_ptr->TB_ASE0_BRST_TMR > 0) {
s_ptr->TB_ASE0_BRST_TMR--;
if (TB_ASE0_BRST_TMR_EXPIRED(s_ptr)) {
LOG_TIMER("\nTB_ASE0_BRST_TMR_EXPIRED ");
timer_expired = TRUE;
}
}
if (s_ptr->TB_A_SUSPEND_TMR > 0) {
s_ptr->TB_A_SUSPEND_TMR--;
if (TB_A_SUSPEND_TMR_EXPIRED(s_ptr)) {
LOG_TIMER("\nTB_A_SUSPEND_TMR_EXPIRED ");
timer_expired = TRUE;
}
}
if (s_ptr->A_SRP_TMR > 0) {
s_ptr->A_SRP_TMR = -1;
s_ptr->A_DATA_PULSE_DET = FALSE;
timer_expired = TRUE;
s_ptr->A_SRP_DET = TRUE;
}
if (timer_expired) {
_usb_otg_state_machine(ath_usb);
}
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_process_timers, SUCCESSFUL");
#endif
return timer_expired;
} /* EndBody */
static void ath_usb_reset_controller (struct ath_usb_otg *ath_usb)
{
u32 otgsc;
struct ath_usb_usb __iomem *usb_reg;
usb_reg = ath_usb->usb_reg;
if (!usb_reg) {
ath_usb_error("reset controller otg not initialized\n");
return;
}
/* Clear OTG status/control interrupts */
otgsc = readl(&usb_reg->otgsc);
writel (otgsc, &usb_reg->otgsc);
/* Mask all OTG Interrupts */
writel (otgsc & ~ATH_USB_OTGSC_IMASK, &usb_reg->otgsc);
/* Clear all OTG Interrupts */
writel (otgsc | ATH_USB_OTGSC_SMASK, &usb_reg->otgsc);
/* Place controller in Idle mode for dual host/device operation */
writel (ATH_USB_USBMODE_SDIS | ATH_USB_USBMODE_CM_IDLE, &usb_reg->usbmode);
ath_usb_debug_dev("Placing OTG controller in idle mode\n");
}
static int ath_usb_otg_enable (struct ath_usb_otg *ath_usb)
{
struct otg_transceiver *otg = &ath_usb->otg;
struct ath_usb_usb __iomem *usb_reg = ath_usb->usb_reg;
/*
* The identification (id) input is FALSE when a Mini-A plug is inserted
* in the device's Mini-AB receptacle. Otherwise, this input is TRUE.
*/
if (ID_FALSE(usb_reg)) {
otg->state = OTG_STATE_A_IDLE;
} else {
otg->state = OTG_STATE_UNDEFINED;
}
ath_usb_debug_dev("Enabling OTG interrupts\n");
ath_usb->enabled_ints = (ATH_USB_OTGSC_DPIE | ATH_USB_OTGSC_BSEIE |
ATH_USB_OTGSC_BSVIE | ATH_USB_OTGSC_ASVIE | ATH_USB_OTGSC_AVVIE |
ATH_USB_OTGSC_IDIE | ATH_USB_OTGSC_IDPU);
writel (ath_usb->enabled_ints, &usb_reg->otgsc);
_usb_otg_state_machine(ath_usb);
return (0);
}
#undef NO_HOST_SUSPEND
static int ath_usb_host_suspend(struct ath_usb_otg *ath_usb)
{
printk ("host_suspend\n");
#ifdef NO_HOST_SUSPEND
return 0;
#else
struct usb_hcd *hcd = NULL;
writel (ATH_USB_USBMODE_SDIS | ATH_USB_USBMODE_CM_IDLE, &ath_usb->usb_reg->usbmode);
if (!ath_usb->otg.host)
return -ENODEV;
/* Currently ASSUMES only the OTG port matters;
* other ports could be active...
*/
hcd = container_of(ath_usb->otg.host, struct usb_hcd, self);
return hcd->driver->bus_suspend(hcd);
#endif
}
static int ath_usb_host_resume(struct ath_usb_otg *ath_usb)
{
printk ("host_resume\n");
#ifdef NO_HOST_SUSPEND
return 0;
#else
struct usb_hcd *hcd = NULL;
writel (ATH_USB_SET_HOST_MODE, &ath_usb->usb_reg->usbmode);
if (!ath_usb->otg.host)
return -ENODEV;
hcd = container_of(ath_usb->otg.host, struct usb_hcd, self);
return hcd->driver->bus_resume(hcd);
#endif
}
static int gadget_resume(struct ath_usb_otg *ath_usb)
{
if (!ath_usb->otg.gadget)
return -ENODEV;
ath_usb->otg.gadget->b_hnp_enable = 0;
ath_usb->otg.gadget->a_hnp_support = 0;
ath_usb->otg.gadget->a_alt_hnp_support = 0;
writel ((ATH_USB_SET_DEV_MODE | ATH_USB_USBMODE_SLOM), &ath_usb->usb_reg->usbmode);
return usb_gadget_vbus_connect(ath_usb->otg.gadget);
}
static int gadget_suspend(struct ath_usb_otg *ath_usb)
{
if (!ath_usb->otg.gadget)
return -ENODEV;
ath_usb->otg.gadget->b_hnp_enable = 0;
ath_usb->otg.gadget->a_hnp_support = 0;
ath_usb->otg.gadget->a_alt_hnp_support = 0;
writel (ATH_USB_USBMODE_SDIS | ATH_USB_USBMODE_CM_IDLE, &ath_usb->usb_reg->usbmode);
return usb_gadget_vbus_disconnect(ath_usb->otg.gadget);
}
irqreturn_t ath_usb_otg_irq (int irq, void *__ath_usb, struct pt_regs *pr)
{
struct ath_usb_otg *ath_usb = __ath_usb;
u32 otg_status, otgsc, timer_expired;
u32 port_control;
struct ath_usb_otg_vars *s_ptr;
struct ath_usb_usb __iomem *usb_reg;
struct otg_transceiver *otg;
s_ptr = &ath_usb->state;
usb_reg = ath_usb->usb_reg;
otg = &ath_usb->otg;
timer_expired = FALSE;
/* Get the status of enabled interrupts */
otg_status = readl(&usb_reg->otgsc) & (ath_usb->enabled_ints >> 8);
do {
/* Clear all enabled interrupts */
otgsc = readl (&usb_reg->otgsc);
writel ((otgsc | otg_status), &usb_reg->otgsc);
/* If we have OTG interrupt call the state machine */
if (otg_status) {
s_ptr->OTG_INT_STATUS = otg_status;
if ((otg_status & ATH_USB_OTGSC_DPIS) && (!s_ptr->A_DATA_PULSE_DET))
{
s_ptr->A_DATA_PULSE_DET = TRUE;
}
/*
* If any timers expired it will process the event and
* any other signals at that time. We don't need to call
* the state machine if this is the case.
*/
if (otg_status & ATH_USB_OTGSC_1MSIS) {
timer_expired = _usb_otg_process_timers(ath_usb);
otg_status &= ~ATH_USB_OTGSC_1MSIS;
}
if (otg_status && (!timer_expired)) {
_usb_otg_state_machine(ath_usb);
} else {
timer_expired = FALSE;
}
s_ptr->OTG_INT_STATUS = 0;
}
/* A_DEVICE */
if (s_ptr->HOST_UP) {
/* Check data line pulsing by B device */
port_control = readl(&ath_usb->usb_reg->portscx[0]);
if (s_ptr->A_SRP_TMR == -1) {
if ((otg->state == OTG_STATE_B_WAIT_ACON) &&
((port_control & PORT_CONNECT)&&
(port_control & PORT_CSC)))
{
s_ptr->A_CONNECTED = TRUE;
}
if (otg->host) {
struct usb_hcd *hcd = NULL;
hcd = container_of(otg->host, struct usb_hcd, self);
hcd->driver->irq(hcd,pr);
set_bit (HCD_FLAG_SAW_IRQ, &hcd->flags);
}
}
}
/* B_DEVICE */
if (s_ptr->DEVICE_UP){
if ((otg->state == OTG_STATE_B_IDLE) &&
(readl(&ath_usb->usb_reg->usbsts) & STS_RST))
{
;
}
if (otg->gadget && ath_usb->udc_isr) {
(ath_usb->udc_isr)(irq, ath_usb->udc, pr);
}
}
/* check the interrupt again */
/* Get the status of enabled interrupts */
otg_status = readl(&usb_reg->otgsc) & (ath_usb->enabled_ints >> 8);
} while(otg_status);
return (IRQ_HANDLED);
}
/* add or disable the host device+driver */
static int
ath_usb_set_host(struct otg_transceiver *otg, struct usb_bus *host)
{
struct ath_usb_otg *ath_usb = container_of(otg, struct ath_usb_otg, otg);
if (!otg || ath_usb != ath_usb_otg) {
return -ENODEV;
}
if (!host) {
power_down(ath_usb);
ath_usb->otg.host = 0;
return 0;
}
#ifdef CONFIG_USB_OTG
ath_usb->otg.host = host;
dev_dbg(ath_usb->dev, "registered host\n");
ath_usb_host_suspend(ath_usb);
#if 0
if (ath_usb->otg.gadget)
return ath_usb_otg_enable(ath_usb);
# else
return ath_usb_otg_enable(ath_usb);
#endif
return 0;
#else
dev_dbg(ath_usb->dev, "host sessions not allowed\n");
return -EINVAL;
#endif
}
static int
ath_usb_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *gadget)
{
struct ath_usb_otg *ath_usb = container_of(otg, struct ath_usb_otg, otg);
if (!otg || ath_usb != ath_usb_otg)
return -ENODEV;
if (!gadget) {
if (!ath_usb->otg.default_a)
enable_vbus_draw(ath_usb, 0);
#if 0
usb_gadget_vbus_disconnect(ath_usb->otg.gadget);
#else
gadget_suspend(ath_usb);
#endif
ath_usb->otg.gadget = 0;
power_down(ath_usb);
return 0;
}
#ifdef CONFIG_USB_OTG
ath_usb->otg.gadget = gadget;
dev_dbg(ath_usb->dev, "registered gadget\n");
/* gadget driver may be suspended until vbus_connect () */
#if 0
if (ath_usb->otg.host)
return ath_usb_otg_enable(ath_usb);
#else
return ath_usb_otg_enable(ath_usb);
#endif
return 0;
#elif !defined(CONFIG_USB_EHCI_HCD) && !defined(CONFIG_USB_EHCI_HCD_MODULE)
ath_usb->otg.gadget = gadget;
/* TODO power_up(isp); */
ath_usb->otg.state = OTG_STATE_B_IDLE;
/* TODO Enable SESS_VLD and VBUS_VLD interrupts */
dev_info(ath_usb->dev, "B-Peripheral sessions ok\n");
/* If this has a Mini-AB connector, this mode is highly
* nonstandard ... but can be handy for testing, so long
* as you don't plug a Mini-A cable into the jack.
*/
if (isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE) & INTR_VBUS_VLD)
b_peripheral(isp);
return 0;
#else
dev_dbg(ath_usb->dev, "peripheral sessions not allowed\n");
return -EINVAL;
#endif
}
static void
ath_usb_otg_work (void *data)
{
}
static int ath_usb_otg_setup(void)
{
struct ath_usb_otg *ath_usb;
if (ath_usb_otg) {
printk ("device already initialized\n");
return (-EBUSY);
}
ath_usb = kzalloc(sizeof(struct ath_usb_otg), GFP_KERNEL);
if (!ath_usb) {
return (-ENOMEM);
}
INIT_WORK(&ath_usb->work, ath_usb_otg_work, ath_usb);
/* Reset OTG State machine */
ath_usb_reset_otg_state (ath_usb);
ath_usb->otg.label = AR_DRIVER_NAME;
ath_usb->otg.set_host = ath_usb_set_host,
ath_usb->otg.set_peripheral = ath_usb_set_peripheral,
ath_usb->otg.set_power = ath_usb_set_power,
ath_usb->otg.start_srp = ath_usb_start_srp,
ath_usb->otg.start_hnp = ath_usb_start_hnp,
ath_usb_otg = ath_usb;
return 0;
}
static int ath_usb_otg_shutdown(void)
{
struct ath_usb_otg *ath_usb;
if (!ath_usb_otg) {
return 0;
}
ath_usb = ath_usb_otg;
flush_scheduled_work();
kfree(ath_usb_otg);
ath_usb_otg = 0;
return 0;
}
static int ath_usb_otg_remove(struct device *dev)
{
if (!ath_usb_otg) {
return 0;
}
free_irq(ath_usb_otg->irqnum, ath_usb_otg);
/*shekar - 6th Dec06*/
release_mem_region(ath_usb_otg->rsrc_start, ath_usb_otg->rsrc_len);
ath_usb_reset_controller (ath_usb_otg);
ath_usb_otg_shutdown();
return 0;
}
/*FUNCTION*----------------------------------------------------------------
*
* Function Name : _usb_otg_set_status
* Returned Value : USB_OK or error code
* Comments :
* Set the value of a specified OTG parameter.
*
*END*--------------------------------------------------------------------*/
u8 _usb_otg_set_status (struct ath_usb_otg *ath_usb, u8 component, u8 status)
{
u8 state_change;
struct ath_usb_otg_vars *s_ptr;
unsigned long arflags;
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_set_status");
#endif
s_ptr = &ath_usb->state;
if (component < USB_OTG_A_BUS_DROP) {
state_change = TRUE;
} else {
state_change = FALSE;
}
USB_lock();
switch (component) {
#ifndef PERIPHERAL_ONLY
case USB_OTG_A_SET_B_HNP_ENABLE:
s_ptr->A_SET_B_HNP_EN = status;
break;
case USB_OTG_A_BUS_DROP:
s_ptr->A_BUS_DROP = status;
break;
case USB_OTG_A_BUS_REQ:
s_ptr->A_BUS_REQ = status;
break;
case USB_OTG_A_BUS_SUSPEND_REQ:
s_ptr->A_BUS_SUSPEND_REQ = status;
break;
case USB_OTG_A_BUS_SUSPEND:
s_ptr->A_BUS_SUSPEND = status;
break;
case USB_OTG_A_CONN:
s_ptr->A_CONN = status;
break;
case USB_OTG_HOST_ACTIVE:
s_ptr->HOST_UP = status;
break;
case USB_OTG_A_VBUS_ON:
/*
** The A-device turn ON V_BUS:
** if the A-device application is not wanting to drop
** the bus (a_bus_drop = FALSE), and if the A-device Application
** is requesting the bus (a_bus_req = TRUE), or SRP is detected
** on the bus (a_srp_det = TRUE).
*/
s_ptr->A_BUS_DROP = FALSE;
s_ptr->A_BUS_REQ = TRUE;
break;
case USB_OTG_A_BUS_RESUME:
s_ptr->A_BUS_RESUME = status;
break;
#endif
case USB_OTG_B_HNP_ENABLE:
s_ptr->B_HNP_ENABLE = status;
break;
case USB_OTG_B_BUS_REQ:
s_ptr->B_BUS_REQ = status;
break;
case USB_OTG_B_CONN:
#if 0
uartputs("\nsetting USB_OTG_B_CONN=");
if (status)
uartputs("TRUE");
else
uartputs("FALSE");
#endif
printk ("B_CONN Status %d\n", status);
s_ptr->B_CONN = status;
break;
case USB_OTG_B_BUS_SUSPEND:
s_ptr->B_BUS_SUSPEND = status;
break;
case USB_OTG_B_BUS_RESUME:
s_ptr->B_BUS_RESUME = status;
break;
case USB_OTG_DEVICE_ACTIVE:
s_ptr->DEVICE_UP = status;
break;
case USB_OTG_B_SRP_REQ:
if (ath_usb->otg.state != OTG_STATE_B_IDLE) {
USB_unlock();
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_set_status, invalid state");
#endif
return -EINVAL;
}
/*
** set the user condition to start SRP. When we call the state
** machine SRP req will be activated if preconditions are met
*/
#ifdef PERIPHERAL_ONLY
s_ptr->B_SESS_REQ = TRUE;
#endif
s_ptr->B_BUS_REQ = TRUE;
break;
default:
USB_unlock();
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_set_status, bad status");
#endif
return -EINVAL;
} /* Endswitch */
if (state_change) {
_usb_otg_state_machine(ath_usb);
}
USB_unlock();
#ifdef _OTG_DEBUG_
DEBUG_LOG_TRACE("_usb_otg_set_status, SUCCESSFUL");
#endif
return (0);
}
u8 usb_otg_set_status (u8 component, u8 status)
{
struct ath_usb_otg *ath_usb = ath_usb_otg;
if (ath_usb_otg) {
return (_usb_otg_set_status (ath_usb, component, status));
}
return (0);
}
static void _usb_otg_state_machine (struct ath_usb_otg *ath_usb)
{
struct ath_usb_otg_vars *s_ptr;
struct ath_usb_usb __iomem *usb_reg;
u32 state;
u32 hw_signal;
static u32 recursive = 0xffffffff;
static u8 bTriedToRecurse = FALSE;
/*
* We prevent recursion to make sure that all state transition actions occur
* in the proper sequence,but we do call _usb_otg_state_machine again at the
* end of the function if recursion was attempted
*/
recursive++;
if (recursive) {
bTriedToRecurse = TRUE;
goto done;
}
s_ptr = &ath_usb->state;
usb_reg = ath_usb->usb_reg;
state = ath_usb->otg.state;
hw_signal = s_ptr->OTG_INT_STATUS;
#ifdef PERIPHERAL_ONLY
switch (state) {
case OTG_STATE_B_IDLE:
_usb_otg_process_b_idle(ath_usb);
break;
case OTG_STATE_B_SRP_INIT:
_usb_otg_process_b_srp_init(ath_usb);
break;
case OTG_STATE_B_PERIPHERAL:
if (SESS_VLD_FALSE(usb_reg)) {
SET_STATE(OTG_STATE_B_IDLE, "C ");
s_ptr->B_BUS_REQ = FALSE;
VBUS_CHG_OFF(usb_reg);
VBUS_DSCHG_ON(usb_reg);
}
break;
default:
break;
} /* Endswitch */
#else
/*
* process exception if not start state
*/
if (state) {
if (_usb_otg_process_exceptions(ath_usb) ){
if (state != ath_usb->otg.state) {
goto done; /* state is changed so return */
}
}
}
switch (state) {
case OTG_STATE_UNDEFINED:
if(ID(usb_reg)) {
SET_STATE(OTG_STATE_B_IDLE, "D ");
_usb_otg_process_b_idle(ath_usb);
break;
} else {
/* ID is false when A device is identified */
SET_STATE(OTG_STATE_A_IDLE, "E ");
}
/* Fall through the A_IDLE state */
case OTG_STATE_A_IDLE:
_usb_otg_process_a_idle(ath_usb);
break;
case OTG_STATE_A_WAIT_VRISE:
_usb_otg_process_a_wait_vrise(ath_usb);
break;
case OTG_STATE_A_WAIT_BCON:
/*
* After waiting long enough to insure that the D+ line cannot
* be high due to the residual effect of the A-device pull-up,
* (see Section 5.1.9), the A-device sees that the D+ line
* is high (and D- low) indicating that the B-device is
* signaling a connect and is ready to respond as a Peripheral.
* At this point, the A-device becomes Host and asserts bus
* reset to start using the bus.
*/
if (s_ptr->B_CONN) {
s_ptr->B_BUS_SUSPEND = FALSE;
SET_STATE(OTG_STATE_A_HOST, "d ");
/* Turn Off A_WAIT_BCON timer since we are exiting the state */
TA_WAIT_BCON_TMR_OFF(s_ptr);
/*
* If the B device is not supported by A device
* a _bus_req = FALSE will set by the application
* during initial communication with the device
*/
} else if (TA_WAIT_BCON_TMR_EXPIRED(s_ptr)) {
TA_WAIT_BCON_TMR_OFF(s_ptr);
SET_STATE(OTG_STATE_A_WAIT_VFALL, "e ");
_usb_otg_process_a_wait_vfall(ath_usb);
}
/* Turn off timer if it is still on from the A-peripheral state */
TA_BIDL_ADIS_TMR_OFF(s_ptr);
break;
case OTG_STATE_A_HOST:
/*
* If mini B plug is removed RESET (DETACH) interrupt will
* happen on A-Device.The DETACH will processed by the host
* ISR and set b_conn to FALSE
*/
if (!s_ptr->B_CONN) {
/* switch to a_wait_bcon */
SET_STATE(OTG_STATE_A_WAIT_BCON, "f ");
/* Turn on A_WAIT_BCON timer since we are in A_WAIT_BCON state */
TA_WAIT_BCON_TMR_ON(s_ptr, TA_WAIT_BCON);
break;
}
/************ Starting HNP **************************************
* When the A-device is in a_host state and has set the dual-role
* B-device's HNP enable bit (b_hnp_enable = TRUE ie, a_suspend_req
* = TRUE), A-device shall place the connection to B-device into
* Suspend when it is finished using the bus. We can go to the
* suspend state also if user want to power down.
*/
if (!s_ptr->A_BUS_REQ || s_ptr->A_BUS_SUSPEND_REQ) {
SET_STATE(OTG_STATE_A_SUSPEND, "g ");
s_ptr->A_BUS_REQ = FALSE;
s_ptr->A_SET_B_HNP_EN = TRUE;
TA_AIDL_BDIS_TMR_ON(s_ptr, TA_AIDL_BDIS);
#ifdef HNP_HARDWARE_ASSISTANCE
/***********************************************************
Set the HABA bit in OTGSC to enable hardware assitance
for HNP. Please see the latest hardware revision manual and
documentation of OTGSC register to develop and understanding
for the functionality of the bit being set by the code here.
***********************************************************/
AUTO_HNP_ON(usb_reg);
#endif
/* CALL the Host API function to suspend the bus */
// TODO _usb_host_bus_control(usb_host_state_struct_ptr, USB_SUSPEND_SOF);
// TODO suspend_done = TRUE;
}
break;
case OTG_STATE_A_SUSPEND:
printk ("OTG_STATE_A_SUSPEND\n");
/*
* b_conn = FALSE if a RESET interrupt happens and it will set by
* the application; a_set_b_hnp_en is false as default and set to
* if the request is not stalled
*/
if (!s_ptr->B_CONN && !s_ptr->A_SET_B_HNP_EN) {
TA_AIDL_BDIS_TMR_OFF(s_ptr);
s_ptr->B_BUS_RESUME = FALSE;
#ifdef HNP_HARDWARE_ASSISTANCE
/*set the HABA bit in OTGSC to enable hardware assitance */
AUTO_HNP_OFF(usb_reg);
#endif
/* switch to a_wait_bcon */
SET_STATE(OTG_STATE_A_WAIT_BCON, "h ");
TA_WAIT_BCON_TMR_ON(s_ptr, TA_WAIT_BCON);
break;
}
/*
* If the B-device disconnects after the bus has been suspended,
* then this is an indication that the B-device is attempting
* to become Host. When the A-device detects the disconnect
* from the B-device, it shall turn on its D+ pull-up resistor
* within 3 ms (TA_BDIS_ACON max) to acknowledge
* the request from the B-device.
*/
if ((!s_ptr->B_CONN || s_ptr->B_DISCONNECTED) &&
(s_ptr->A_SET_B_HNP_EN))
{
PULL_UP_PULL_DOWN_IDLE(usb_reg);
if (s_ptr->HOST_UP) {
s_ptr->HOST_UP = FALSE;
ath_usb_host_suspend(ath_usb);
}
OTG_DEVICE_INIT(ath_usb, usb_reg);
SET_STATE(OTG_STATE_A_PERIPHERAL, "j ");
TA_AIDL_BDIS_TMR_OFF(s_ptr);
s_ptr->A_SET_B_HNP_EN = FALSE ;
s_ptr->A_BUS_SUSPEND_REQ = FALSE;
s_ptr->B_DISCONNECTED = FALSE;
break;
}
if (s_ptr->A_BUS_REQ || s_ptr->B_BUS_RESUME) {
#ifdef HNP_HARDWARE_ASSISTANCE
/*set the HABA bit in OTGSC to enable hardware assitance */
AUTO_HNP_OFF(usb_reg);
#endif
SET_STATE(OTG_STATE_A_HOST, "k ");
s_ptr->B_BUS_RESUME = FALSE;
TA_AIDL_BDIS_TMR_OFF(s_ptr);
s_ptr->A_BUS_SUSPEND_REQ = FALSE;
s_ptr->A_BUS_REQ = TRUE;
}
break;
case OTG_STATE_A_PERIPHERAL:
/*
* A-device detects lack of bus activity for more than 3 ms
* (TA_BIDL_ADIS min) and turns off its D+ pull-up.Alternatively,
* if the A-device has no further need to communicate with the
* B-device, the A-device may turn off VBUS and end the session.
* Sleep INT will be processed by the device ISR b_bus_suspend
* variable will be set by the application
* The following will be done when this happens:
*
* - disconnect its pull up
* - allow time for the data line to discharge
* - check if the B-device has connected its pull up
*/
if (s_ptr->B_BUS_SUSPEND) {
if (s_ptr->DEVICE_UP) {
s_ptr->DEVICE_UP = FALSE;
gadget_suspend(ath_usb);
PULL_UP_PULL_DOWN_IDLE(usb_reg);
}
if (!s_ptr->HOST_UP) {
OTG_HOST_INIT(ath_usb, usb_reg);
}
/* switch to a_wait_bcon */
SET_STATE(OTG_STATE_A_WAIT_BCON, "m ");
#ifndef TA_WAIT_BCON_TMR_FOR_EVER
TA_WAIT_BCON_TMR_ON(s_ptr, TA_WAIT_BCON);
s_ptr->B_BUS_SUSPEND = FALSE;
#endif
}
break;
case OTG_STATE_A_WAIT_VFALL:
_usb_otg_process_a_wait_vfall(ath_usb);
break;
case OTG_STATE_B_IDLE:
_usb_otg_process_b_idle(ath_usb);
break;
case OTG_STATE_B_SRP_INIT:
_usb_otg_process_b_srp_init(ath_usb);
break;
case OTG_STATE_B_PERIPHERAL:
/* a_bus_suspend (SLEEP_INT) will be processed by the DEVICE ISR */
/*
* B-device detects that bus is idle for more than 3 ms
* (TB_AIDL_BDIS min) and begins HNP by turning off pull-up on D+.
* This allows the bus to discharge to the SE0 state.
* If the bus was operating in HS mode, the B-device will first
* enter the full-speed mode and turn on its D+ pull-up resistor
* for at least TB_FS_BDIS min before turning off its pull up
* to start the HNP sequence.
* The following will be done when this happens:
*
* disconnect its pull up
* allow time for the data line to discharge
* check if the A-device has connected its pull up
*/
if (s_ptr->B_HNP_ENABLE && s_ptr->B_BUS_REQ &&
s_ptr->A_BUS_SUSPEND && (!s_ptr->A_SUSPEND_TIMER_ON))
{
TB_A_SUSPEND_TMR_ON(s_ptr, TB_A_SUSPEND);
s_ptr->A_SUSPEND_TIMER_ON = TRUE;
/*ensure that A_BUS_RESUME is false so that we look for
a fresh resume on the bus */
s_ptr->A_BUS_RESUME = FALSE;
}
/*
* when in suspended state in B_PERIPHERAL and host drives a resume
* we should recover back to B_PERIPHERAL state
*/
if((s_ptr->A_BUS_RESUME) && s_ptr->A_SUSPEND_TIMER_ON) {
TB_A_SUSPEND_TMR_OFF(s_ptr);
AUTO_RESET_OFF(usb_reg);
s_ptr->A_BUS_SUSPEND = FALSE;
}
if (s_ptr->A_SUSPEND_TIMER_ON) {
/*****************************************************
To meet the 1ms reset requirement for OPT test 5.4
, latest hardware revisions provide the autoreset bit
When this bit is set, hardware will drive a reset on
the bus, the moment device connects. This will ensure
that reset is hardware driver in a timely manner,
rather than driver by software when a 'connect'
interrupt happens. The bit has been added to meet
the OTP timing requirement for customers have large
interrupt latencies in their design
SG 06/18/2004
*****************************************************/
AUTO_RESET_ON(usb_reg);
/* 7ms timer to let the bus discharge to SE0 state */
if (TB_A_SUSPEND_TMR_EXPIRED(s_ptr)) {
s_ptr->A_SUSPEND_TIMER_ON = FALSE;
TB_A_SUSPEND_TMR_OFF(s_ptr);
s_ptr->A_BUS_SUSPEND = FALSE;
if (s_ptr->DEVICE_UP) {
s_ptr->DEVICE_UP = FALSE;
gadget_suspend(ath_usb);
PULL_UP_PULL_DOWN_IDLE(usb_reg);
}
if (!s_ptr->HOST_UP) {
OTG_HOST_INIT(ath_usb, usb_reg);
/* No VBUS_ON */
}
TB_ASE0_BRST_TMR_ON(s_ptr, TB_ASE0_BRST);
SET_STATE(OTG_STATE_B_WAIT_ACON, "n ");
}
}
break;
case OTG_STATE_B_WAIT_ACON:
/*
* After waiting long enough to insure that the D+ line
* cannot be high due to the residual effect of the B-device
* pull-up,(see Section 5.1.9), the B-device sees that the
* D+ line is high and D- low, (i.e. J state). This indicates
* that the A-device has recognized the HNP request from the
* B-device. At this point, the B-device becomes Host and
* asserts bus reset to start using the bus. The B-device
* must assert the bus reset (SE0) within 1 ms (TB_ACON_BSE0 max)
* of the time that the A-device turns on its
* pull-up.
*/
/* ATTACH_INT will be processed by the host ISR */
if (s_ptr->A_CONN) {
s_ptr->A_CONNECTED = FALSE;
s_ptr->A_BUS_SUSPEND = FALSE;
s_ptr->B_HNP_ENABLE = FALSE;
TB_ASE0_BRST_TMR_OFF(s_ptr);
SET_STATE(OTG_STATE_B_HOST, "p ");
break;
}
/*
* While waiting in the b_wait_acon state, the B-device
* may detect a K state on the bus. This indicates that the
* A-device is signaling a resume condition and is retaining
* control of the bus. In this case, the B-device will return
* to the b_peripheral state.
*
* a_bus_resume will be set by the host ISR when K state is
* detected if (a_bus_resume || b_ase0_brst_tmr)
*/
if (s_ptr->A_BUS_RESUME || (TB_ASE0_BRST_TMR_EXPIRED(s_ptr) &&
(!s_ptr->A_CONNECTED)))
{
s_ptr->A_BUS_RESUME = FALSE;
if (s_ptr->HOST_UP) {
s_ptr->HOST_UP = FALSE;
ath_usb_host_suspend(ath_usb);
PULL_UP_PULL_DOWN_IDLE(usb_reg);
}
/***********************************************************
OPT test 5.8 and 5.9 require that we must display a message
when a HNP fails. This event should allow the application
to know that HNP failed with Host.
***********************************************************/
if((TB_ASE0_BRST_TMR_EXPIRED(s_ptr)) && s_ptr->B_HNP_ENABLE)
{
// TODO ath_usb_service(ath_usb, USB_OTG_HNP_FAILED);
}
TB_ASE0_BRST_TMR_OFF(s_ptr);
OTG_DEVICE_INIT(ath_usb, usb_reg);
SET_STATE(OTG_STATE_B_PERIPHERAL, "q ");
s_ptr->A_BUS_SUSPEND = FALSE;
s_ptr->B_HNP_ENABLE = FALSE;
break;
}
if (TB_ASE0_BRST_TMR_EXPIRED(s_ptr)) {
TB_ASE0_BRST_TMR_OFF(s_ptr);
}
break;
case OTG_STATE_B_HOST:
/*
* If the B-device at any time detects more than 3.125 ms of SE0
* (TB_ASE0_BRST min), then this is an indication that the
* A-device is remaining Host and is resetting the bus. In this
* case the B-device shall return to the b_peripheral state
* and start to process the bus reset before TB_ASE0_BRST max.
*/
if (!s_ptr->B_BUS_REQ || !s_ptr->A_CONN) {
PULL_UP_PULL_DOWN_IDLE(usb_reg);
if (s_ptr->HOST_UP) {
s_ptr->HOST_UP = FALSE;
ath_usb_host_suspend(ath_usb);
}
OTG_DEVICE_INIT(ath_usb, usb_reg);
SET_STATE(OTG_STATE_B_PERIPHERAL, "r ");
}
break;
default:
break;
}
#endif /* PERIPHERAL-ONLY */
if ((s_ptr->TA_WAIT_VRISE_TMR > 0) ||
(s_ptr->TA_WAIT_BCON_TMR > 0) ||
(s_ptr->TA_AIDL_BDIS_TMR > 0) ||
(s_ptr->TA_BIDL_ADIS_TMR > 0) ||
(s_ptr->TB_DATA_PLS_TMR > 0) ||
(s_ptr->TB_SRP_INIT_TMR > 0) ||
(s_ptr->TB_SRP_FAIL_TMR > 0) ||
(s_ptr->TB_VBUS_PLS_TMR > 0) ||
(s_ptr->TB_ASE0_BRST_TMR > 0) ||
(s_ptr->TB_SE0_SRP_TMR > 0) ||
(s_ptr->TB_VBUS_DSCHRG_TMR > 0) ||
(s_ptr->TB_A_SUSPEND_TMR > 0) ||
(s_ptr->A_SRP_TMR > 0))
{
START_TIMER(usb_reg);
} else {
STOP_TIMER(usb_reg);
}
done:
recursive--;
/*
* If we are at the top level, and we had tried to recurse, we clear the
* recurse flag and we call _usb_otg_state_machine again.
*/
if (recursive == 0xffffffff) {
if (bTriedToRecurse) {
bTriedToRecurse = FALSE;
_usb_otg_state_machine(ath_usb); /* fake recursion */
}
}
return;
}
static int
ath_usb_set_power(struct otg_transceiver *dev, unsigned mA)
{
if (!ath_usb_otg)
return -ENODEV;
if (dev->state == OTG_STATE_B_PERIPHERAL)
enable_vbus_draw(ath_usb_otg, mA);
return 0;
}
static int ath_usb_start_srp(struct otg_transceiver *dev)
{
return 0;
}
static int
ath_usb_start_hnp(struct otg_transceiver *dev)
{
#ifdef CONFIG_USB_OTG
struct ath_usb_otg *ath_usb = container_of(dev, struct ath_usb_otg, otg);
printk("Start HNP \n");
if (!dev || ath_usb != ath_usb_otg)
return -ENODEV;
if (ath_usb->otg.default_a && (ath_usb->otg.host == NULL
|| !ath_usb->otg.host->b_hnp_enable))
return -ENOTCONN;
if (!ath_usb->otg.default_a && (ath_usb->otg.gadget == NULL
|| !ath_usb->otg.gadget->b_hnp_enable))
return -ENOTCONN;
/*
* We want hardware to manage most HNP protocol timings.
* So do this part as early as possible...
*/
_usb_otg_set_status(ath_usb, USB_OTG_A_SET_B_HNP_ENABLE, TRUE);
_usb_otg_set_status(ath_usb, USB_OTG_A_BUS_SUSPEND_REQ, TRUE);
_usb_otg_set_status(ath_usb, USB_OTG_A_BUS_REQ, FALSE);
return 0;
#else
/* srp-only */
return -EINVAL;
#endif
}
/* Proc Interface to Start HNP */
static int ath_usb_rd_status(char *page,char **start,off_t off,
int count,int *eof,void *data)
{
struct ath_usb_otg *ath_usb = (struct ath_usb_otg *)data;
char *seq = page;
int length;
if(!ath_usb || !ath_usb->usb_reg){
printk("Returned with !ath_usb \n");
return -EINVAL;
}
__u32 otgsc =0;
otgsc = readl(&ath_usb->usb_reg->otgsc);
#if 0
seq += sprintf(seq,"Driver : %s \n",AR_DRIVER_NAME);
seq += sprintf(seq,"OTG Device Running at %s Mode \n\n",
(readl(&ath_usb->usb_reg->portscx[0]) & MASK_MODE) ? "Device":"Host");
#endif
seq += sprintf(seq,"OTG Status: \n");
seq += sprintf(seq,"OTG - %s\n",(otgsc & MASK_USBID) ? "B Device":"A Device");
seq += sprintf(seq,"OTG - %s\n",(otgsc & MASK_ASV) ? "A Session Valid":
((otgsc & MASK_BSV) ? "B Session Valid":"Invalid State"));
length = seq - page - off;
if(length < count){
*eof = 1;
if(length <= 0){
return 0;
}
}else{
length = count;
}
*start = page + off;
return length;
}
static int ath_usb_wr_startHNP(struct file *file,const char *buf,
unsigned long count,void *data)
{
char start;
struct ath_usb_otg *ath_usb = (struct ath_usb_otg *)data;
struct otg_transceiver *transceiver = &ath_usb->otg;
unsigned long flags;
if(!ath_usb){
return -EINVAL;
}
if(copy_from_user(&start,buf,count)){
return -EINVAL;
}
if(start == 1){
ath_usb_start_hnp(transceiver);
local_irq_save(flags);
transceiver->state = OTG_STATE_A_SUSPEND;
local_irq_restore(flags);
}
return count;
}
int ath_usb_create_proc(struct ath_usb_otg *otg)
{
struct proc_dir_entry *usbroot = NULL;
struct proc_dir_entry *rdStatus;
struct proc_dir_entry *start_hnp;
usbroot = proc_mkdir("otg",0);
if(!usbroot){
return -1;
}
usbroot->owner = THIS_MODULE;
/* Read OTG Status */
rdStatus = create_proc_entry("status",S_IFREG|S_IRUGO|S_IWUSR,usbroot);
if(!rdStatus){
return -1;
}
rdStatus->nlink =1;
rdStatus->read_proc = ath_usb_rd_status;
rdStatus->data = (void *)otg;
rdStatus->owner = THIS_MODULE;
/* Entry to Start HNP */
start_hnp = create_proc_entry("startHNP",S_IFREG|S_IRUGO|S_IWUSR,usbroot);
if(!start_hnp){
return -1;
}
start_hnp->nlink =1;
start_hnp->write_proc = ath_usb_wr_startHNP;
start_hnp->data = (void *)otg;
start_hnp->owner = THIS_MODULE;
return(0);
}
static int
ath_usb_otg_probe(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct ath_usb_otg *ath_usb;
int retval;
ath_usb_debug_dev("\nprobing ath_usb otg...\n");
retval = ath_usb_otg_setup();
if (retval < 0) {
return (retval);
}
if (pdev->resource[1].flags != IORESOURCE_IRQ) {
ath_usb_error ("resource[1] is not IORESOURCE_IRQ");
retval = -ENOMEM;
}
ath_usb = ath_usb_otg;
ath_usb->rsrc_start = pdev->resource[0].start;
ath_usb->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
if (!request_mem_region(ath_usb->rsrc_start, ath_usb->rsrc_len,
AR_DRIVER_NAME))
{
ath_usb_error("request_mem_region failed");
retval = -EBUSY;
goto err1;
}
ath_usb->reg_base = ioremap(ath_usb->rsrc_start, ath_usb->rsrc_len);
if (!ath_usb->reg_base) {
ath_usb_error("ioremap failed");
retval = -ENOMEM;
goto err2;
}
ath_usb_debug_dev("otg->reg_base is %p\n", ath_usb->reg_base);
#if 0
/* Device Initialization - start */
ath_usb_reg_rmw_clear(ATH_USB_RESET,ATH_USB_RESET_USB_HOST);
ath_usb_reg_rmw_set(ATH_USB_RESET,ATH_USB_RESET_USB_PHY); //PHY RESET
/* Clear Host Mode */
ath_usb_reg_rmw_clear(ATH_USB_CONFIG,(1 << 2));
ath_usb_debug_dev("Usb Config Reg %x\n",ath_usb_reg_rd(ATH_USB_CONFIG));
mdelay(10);
ath_usb_reg_rmw_clear(ATH_USB_RESET,ATH_USB_RESET_USB_PHY);//PHY CLEAR RESET
ath_usb_debug_dev("ATH_USB_RESET %x \n",ath_usb_reg_rd(ATH_USB_RESET));
mdelay(10);
#endif
ath_usb_reg_rmw_clear(ATH_USB_RESET,ATH_USB_RESET_USB_PHY); //CLEAR PHY RESET
ath_usb_reg_rmw_clear(ATH_USB_RESET,ATH_USB_RESET_USB_HOST); //CLEAR HOST RESET
ath_usb->cap_reg = ath_usb->reg_base + ATH_USB_CAP_REG_OFFSET;
ath_usb->usb_reg = ath_usb->reg_base + ATH_USB_CAP_REG_OFFSET +
HC_LENGTH(readl(&ath_usb->cap_reg->hc_capbase));
#if 0
/* Setting 16-bit mode */
ath_usb_reg_rmw_set(&ath_usb->usb_reg->portscx[0],(1 <<28));
ath_usb_debug_dev("PORT_STATUS[0] %p = %x\n", &ath_usb->usb_reg->portscx[0], readl(&ath_usb->usb_reg->portscx[0]));
mdelay(10);
#endif
ath_usb->irqnum = pdev->resource[1].start;
if ((retval = request_irq (ath_usb->irqnum, &ath_usb_otg_irq,
(SA_INTERRUPT | SA_SHIRQ), "ath_usb-irq", ath_usb) != 0))
{
dev_err(ath_usb->dev, "request interrupt %d failed\n", ath_usb->irqnum);
goto err3;
}
spin_lock_init(&ath_usb->arlock);
ath_usb->otg.dev = dev;
ath_usb->dev = dev;
ath_usb_reset_controller(ath_usb);
/*Create Proc File Sys */
if(ath_usb_create_proc(ath_usb_otg) < 0){
ath_usb_debug_dev("Failed to Create Procfs \n");
}
// TODO remove after OTG Testing
// ath_usb_otg_enable(ath_usb);
return (0);
err3:
iounmap(ath_usb->usb_reg);
err2:
release_mem_region(ath_usb->rsrc_start, ath_usb->rsrc_len);
err1:
ath_usb_otg_shutdown();
return retval;
}
static struct device_driver ath_usb_otg_driver = {
.name = "ath-ehci",
.bus = &platform_bus_type,
.probe = ath_usb_otg_probe,
.remove = ath_usb_otg_remove,
};
static int __init ath_usb_otg_init(void)
{
return (driver_register(&ath_usb_otg_driver));
}
module_init(ath_usb_otg_init);
static void __exit ath_usb_otg_exit(void)
{
driver_unregister(&ath_usb_otg_driver);
}
module_exit(ath_usb_otg_exit);
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(ath_usb_get_otg);
EXPORT_SYMBOL(usb_otg_set_status);