blob: 1024c17165914b59dc26691ce26162dd932f42ba [file] [log] [blame] [edit]
/*
* arch/arm/cpu/armv8/gxb/usb.c
*
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//#include <common.h>
//#include <asm/cache.h>
#include <asm/arch/usb.h>
#include <asm/arch/romboot.h>
#include <asm/arch/secure_apb.h>
#ifdef CONFIG_USB_DWC_OTG_HCD
static amlogic_usb_config_t * g_usb_cfg[BOARD_USB_MODE_MAX][USB_PHY_PORT_MAX];
static char * g_clock_src_name_m8[]={
"XTAL input",
};
extern void _udelay(unsigned long usec);
extern void _mdelay(unsigned long usec);
//static int reset_count = 0;
//int set_usb_phy_clk(struct lm_device * plmdev,int is_enable)
//{
static int set_usb_phy_clock(amlogic_usb_config_t * usb_cfg)
{
int port_idx;
usb_peri_reg_t * peri;
usb_config_data_t config;
usb_ctrl_data_t control;
int clk_sel,clk_div;
unsigned int port = usb_cfg->base_addr & USB_PHY_PORT_MSK;
int time_dly = 500; //usec
if (!usb_cfg)
return -1;
if (port == USB_PHY_PORT_A) {
port_idx = 0;
peri = (usb_peri_reg_t*)PREI_USB_PHY_REG_A;//CBUS_REG_ADDR(PREI_USB_PHY_REG_A);
}else if(port == USB_PHY_PORT_B){
port_idx = 1;
peri = (usb_peri_reg_t*)PREI_USB_PHY_REG_B;//CBUS_REG_ADDR(PREI_USB_PHY_REG_B);
}else{
printf("usb base address error: %x\n",usb_cfg->base_addr);
return -1;
}
writel((1 << 2),P_RESET1_REGISTER);
printf("USB (%d) peri reg base: %x\n",port_idx,(uint32_t)(unsigned long long)peri);
clk_sel = usb_cfg->clk_selecter;
clk_div = usb_cfg->pll_divider;
config.d32 = peri->config;
config.b.clk_32k_alt_sel= 1;
peri->config = config.d32;
printf("USB (%d) use clock source: %s, div: %d\n",port_idx,g_clock_src_name_m8[clk_sel], clk_div);
control.d32 = peri->ctrl;
control.b.fsel = 5; /* PHY default is 24M (5), change to 12M (2) */
control.b.por = 1; /* power off default*/
peri->ctrl = control.d32;
_udelay(time_dly);
return 0;
}
//call after set clock
void set_usb_phy_power(amlogic_usb_config_t * usb_cfg,int is_on)
{
unsigned long delay = 1000;
int port_idx = 100;
unsigned int port = usb_cfg->base_addr & USB_PHY_PORT_MSK;
usb_peri_reg_t *peri_a,*peri_b,*peri_c,*peri_d,*peri;
peri = NULL;
usb_ctrl_data_t control;
usb_adp_bc_data_t adp_bc;
peri_a = (usb_peri_reg_t*)PREI_USB_PHY_REG_A;//CBUS_REG_ADDR(PREI_USB_PHY_REG_A);
peri_b = (usb_peri_reg_t*)PREI_USB_PHY_REG_B;//CBUS_REG_ADDR(PREI_USB_PHY_REG_B);
// peri_c = (usb_peri_reg_t*)CBUS_REG_ADDR(PREI_USB_PHY_REG_C);
// peri_d = (usb_peri_reg_t*)CBUS_REG_ADDR(PREI_USB_PHY_REG_D);
peri_c = NULL;
peri_d = NULL;
if (port == USB_PHY_PORT_A) {
peri = peri_a;
port_idx = 0;
}else if(port == USB_PHY_PORT_B){
peri = peri_b;
port_idx = 1;
}
else if(port == USB_PHY_PORT_C){
peri = peri_c;
port_idx = 2;
}else if(port == USB_PHY_PORT_D){
peri = peri_d;
port_idx = 3;
}
if (is_on) {
control.d32 = peri->ctrl;
control.b.por = 0;
peri->ctrl = control.d32;
_udelay(delay);
/* read back clock detected flag*/
control.d32 = peri->ctrl;
if (!control.b.clk_detected) {
printf("USB (%d) PHY Clock not detected!\n",port_idx);
}
if (port == USB_PHY_PORT_B) {
adp_bc.d32 = peri->adp_bc;
adp_bc.b.aca_enable = 1;
peri->adp_bc = adp_bc.d32;
_udelay(50);
adp_bc.d32 = peri->adp_bc;
if (adp_bc.b.aca_pin_float) {
printf("USB-B ID detect failed!\n");
printf("Please use the chip after version RevA1!\n");
return;
}
}
}else{
control.d32 = peri->ctrl;
control.b.por = 1;
peri->ctrl = control.d32;
}
_udelay(delay);
}
const char * bc_name[]={
"UNKNOWN",
"SDP (PC)",
"DCP (Charger)",
"CDP (PC with Charger)",
};
#define T_DCD_TIMEOUT 10
#define T_VDPSRC_ON 40
#define T_VDMSRC_EN (20 + 5)
#define T_VDMSRC_DIS (20 + 5)
#define T_VDMSRC_ON 40
static void usb_bc_detect(amlogic_usb_config_t * usb_cfg)
{
int port_idx,timeout_det;
unsigned int port = usb_cfg->base_addr & USB_PHY_PORT_MSK;
usb_peri_reg_t *peri,*peri_a,*peri_b,*peri_c,*peri_d;
peri = NULL;
port_idx = 100;
usb_adp_bc_data_t adp_bc;
int bc_mode = BC_MODE_UNKNOWN;
peri_a = (usb_peri_reg_t*)PREI_USB_PHY_REG_A;//CBUS_REG_ADDR(PREI_USB_PHY_REG_A);
peri_b = (usb_peri_reg_t*)PREI_USB_PHY_REG_B;//CBUS_REG_ADDR(PREI_USB_PHY_REG_B);
// peri_c = (usb_peri_reg_t*)CBUS_REG_ADDR(PREI_USB_PHY_REG_C);
// peri_d = (usb_peri_reg_t*)CBUS_REG_ADDR(PREI_USB_PHY_REG_D);
peri_c = NULL;
peri_d = NULL;
if (port == USB_PHY_PORT_A) {
peri = peri_a;
port_idx = 0;
}else if(port == USB_PHY_PORT_B){
peri = peri_b;
port_idx = 1;
}
else if(port == USB_PHY_PORT_C){
peri = peri_c;
port_idx = 2;
}else if(port == USB_PHY_PORT_D){
peri = peri_d;
port_idx = 3;
}
adp_bc.d32 = peri->adp_bc;
if (adp_bc.b.device_sess_vld) {
_mdelay(T_DCD_TIMEOUT);
/* Turn on VDPSRC */
adp_bc.b.chrgsel = 0;
adp_bc.b.vdatdetenb = 1;
adp_bc.b.vdatsrcenb = 1;
adp_bc.b.dcd_enable = 0;
peri->adp_bc = adp_bc.d32;
/* SDP and CDP/DCP distinguish */
timeout_det = T_VDMSRC_EN;
while (timeout_det--) {
adp_bc.d32 = peri->adp_bc;
if (adp_bc.b.chg_det)
break;
_mdelay(1);
};
if (adp_bc.b.chg_det) {
/* Turn off VDPSRC */
adp_bc.d32 = peri->adp_bc;
adp_bc.b.vdatdetenb = 0;
adp_bc.b.vdatsrcenb = 0;
peri->adp_bc = adp_bc.d32;
/* Wait VDMSRC_DIS */
timeout_det = T_VDMSRC_DIS;
while (timeout_det--) {
adp_bc.d32 = peri->adp_bc;
if (!adp_bc.b.chg_det)
break;
_mdelay(1);
};
if (timeout_det <= 0)
printf("Time out for VDMSRC_DIS!");
/* Turn on VDMSRC */
adp_bc.d32 = peri->adp_bc;
adp_bc.b.chrgsel = 1;
adp_bc.b.vdatdetenb = 1;
adp_bc.b.vdatsrcenb = 1;
peri->adp_bc = adp_bc.d32;
_mdelay(T_VDMSRC_ON);
adp_bc.d32 = peri->adp_bc;
if (adp_bc.b.chg_det)
bc_mode = BC_MODE_DCP;
else
bc_mode = BC_MODE_CDP;
}
else{
bc_mode = BC_MODE_SDP;
}
adp_bc.d32 = peri->adp_bc;
adp_bc.b.vdatdetenb = 0;
adp_bc.b.vdatsrcenb = 0;
adp_bc.b.dcd_enable = 0;
peri->adp_bc = adp_bc.d32;
}
printf("Port %d detect usb battery charger mode: %s\n",port_idx, bc_name[bc_mode]);
usb_cfg->battery_charging_det_cb(bc_mode);
}
amlogic_usb_config_t * board_usb_start(int mode,int index)
{
if (mode < 0 || mode >= BOARD_USB_MODE_MAX||!g_usb_cfg[mode][index])
return 0;
set_usb_phy_clock(g_usb_cfg[mode][index]);
set_usb_phy_power(g_usb_cfg[mode][index],1);//on
if(mode == BOARD_USB_MODE_CHARGER &&
g_usb_cfg[mode][index]->battery_charging_det_cb)
usb_bc_detect(g_usb_cfg[mode][index]);
return g_usb_cfg[mode][index];
}
int board_usb_stop(int mode,int index)
{
printf("board_usb_stop cfg: %d\n",mode);
if (mode < 0 || mode >= BOARD_USB_MODE_MAX)
return 1;
set_usb_phy_power(g_usb_cfg[mode][index],0);//off
return 0;
}
int usb_index = 0;
void board_usb_init(amlogic_usb_config_t * usb_cfg,int mode)
{
if (mode < 0 || mode >= BOARD_USB_MODE_MAX || !usb_cfg)
return ;
if (mode == BOARD_USB_MODE_HOST) {
if (usb_index >= USB_PHY_PORT_MAX)
return;
g_usb_cfg[mode][usb_index] = usb_cfg;
usb_index++;
}else
g_usb_cfg[mode][0] = usb_cfg;
printf("register usb cfg[%d][%d] = %p\n",mode,(mode==BOARD_USB_MODE_HOST)?usb_index:0,usb_cfg);
}
int get_usb_count(void)
{
return usb_index;
}
#endif //CONFIG_USB_DWC_OTG_HCD