SmartAudio/lichee/brandy/u-boot-2014.07/drivers/usb/host/ehci-sunxi.c

466 lines
11 KiB
C
Raw Normal View History

2018-07-13 01:31:50 +00:00
/*
* (C) Copyright 20016-2020
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* wangwei <wangwei@allwinnertech.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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 <common.h>
#include <pci.h>
#include <usb.h>
#include <asm/io.h>
#include <usb/ehci-fsl.h>
#include <sys_config.h>
#include <asm/arch/ccmu.h>
#include "ehci.h"
#include "ehci-sunxi.h"
static u32 usb_vbase = SUNXI_EHCI1_BASE;
static u32 usb_vbus_handle = 0;
typedef struct _ehci_config
{
u32 ehci_base;
u32 bus_soft_reset_ofs;
u32 bus_clk_gating_ofs;
u32 phy_reset_ofs;
u32 phy_slk_gatimg_ofs;
u32 usb0_support;
char name[32];
char node[32];
}ehci_config_t;
#ifdef CONFIG_USB_EHCI_SUNXI
static ehci_config_t ehci_cfg[] = {
{SUNXI_EHCI0_BASE, USBEHCI0_RST_BIT, USBEHCI0_GATIING_BIT, USBPHY0_RST_BIT, USBPHY0_SCLK_GATING_BIT, 1, "ehci0", "/soc/usbc0"},
#ifndef CONFIG_ARCH_SUN50IW6P1
{SUNXI_EHCI1_BASE, USBEHCI1_RST_BIT, USBEHCI1_GATIING_BIT, USBPHY1_RST_BIT, USBPHY1_SCLK_GATING_BIT, 0, "ehci1", "/soc/usbc1"},
#endif
};
#endif
/*
*******************************************************************************
* pin_init
*
* Description:
* void
*
* Parameters:
* void
*
* Return value:
* void
*
* note:
* void
*
*******************************************************************************
*/
ulong config_usb_pin(char *path, char *prop)
{
user_gpio_set_t usbc_gpio;
int status = -1;
ulong pin_handle = 0;
do{
if(fdt_get_one_gpio(path,prop,&usbc_gpio))
{
break;
}
pin_handle = gpio_request(&usbc_gpio, 1);
if(!pin_handle)
{
break;
}
/* set config, ouput */
if(gpio_set_one_pin_io_status(pin_handle, 1, NULL))
{
break;
}
/* reserved is pull down */
if(gpio_set_one_pin_pull(pin_handle, 2, NULL))
{
break;
}
status = 0;
}while(0);
printf("config usb pin %s\n",status?"fail":"success");
return status ? 0 : pin_handle;
}
s32 __attribute__((weak)) axp_usb_vbus_output(void){ return 0;}
int alloc_pin(int index)
{
if (axp_usb_vbus_output())
return 0;
usb_vbus_handle = config_usb_pin(ehci_cfg[index].node,
"usb_drv_vbus_gpio");
return usb_vbus_handle ? 0:-1;
}
/*
*******************************************************************************
* pin_exit
*
* Description:
* void
*
* Parameters:
* void
*
* Return value:
* void
*
* note:
* void
*
*******************************************************************************
*/
void free_pin(void)
{
if(usb_vbus_handle)
gpio_release(usb_vbus_handle, 0);
usb_vbus_handle = 0;
return;
}
/*
*******************************************************************************
* open_usb_clock
*
* Description:
*
*
* Parameters:
* void
*
* Return value:
* void
*
* note:
* void
*
*******************************************************************************
*/
#if defined(SUNXI_NCAT_VERSION1)
u32 open_usb_clock(int index)
{
u32 reg_value = 0;
u32 ccmu_base = SUNXI_CCM_BASE;
/* Bus reset and gating for ehci */
reg_value = USBC_Readl(ccmu_base + CCMU_USB_BUS_GATING_RST);
reg_value |= (1 << ehci_cfg[index].bus_soft_reset_ofs);
reg_value |= (1 << ehci_cfg[index].bus_clk_gating_ofs);
USBC_Writel(reg_value, (ccmu_base + CCMU_USB_BUS_GATING_RST));
/* open clk for usb phy */
if (index == 0) {
reg_value = USBC_Readl(ccmu_base + CCMU_USB0_CLK);
reg_value |= (1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value |= (1 << ehci_cfg[index].phy_reset_ofs);
USBC_Writel(reg_value, (ccmu_base + CCMU_USB0_CLK));
} else if (index == 1) {
#ifndef CONFIG_ARCH_SUN50IW6P1
reg_value = USBC_Readl(ccmu_base + CCMU_USB1_CLK);
reg_value |= (1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value |= (1 << ehci_cfg[index].phy_reset_ofs);
USBC_Writel(reg_value, (ccmu_base + CCMU_USB1_CLK));
#endif
}
printf("config usb clk ok\n");
return 0;
}
/*
*******************************************************************************
* close_usb_clock
*
* Description:
*
*
* Parameters:
* void
*
* Return value:
* void
*
* note:
* void
*
*******************************************************************************
*/
u32 close_usb_clock(int index)
{
u32 reg_value = 0;
u32 ccmu_base = SUNXI_CCM_BASE;
/* Bus reset and gating for ehci */
reg_value = USBC_Readl(ccmu_base + CCMU_USB_BUS_GATING_RST);
reg_value &= ~(1 << ehci_cfg[index].bus_soft_reset_ofs);
reg_value &= ~(1 << ehci_cfg[index].bus_clk_gating_ofs);
USBC_Writel(reg_value, (ccmu_base + CCMU_USB_BUS_GATING_RST));
/* close clk for usb phy */
if (index == 0) {
reg_value = USBC_Readl(ccmu_base + CCMU_USB0_CLK);
reg_value &= ~(1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value &= ~(1 << ehci_cfg[index].phy_reset_ofs);
USBC_Writel(reg_value, (ccmu_base + CCMU_USB0_CLK));
} else if (index == 1) {
#ifndef CONFIG_ARCH_SUN50IW6P1
reg_value = USBC_Readl(ccmu_base + CCMU_USB1_CLK);
reg_value &= ~(1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value &= ~(1 << ehci_cfg[index].phy_reset_ofs);
USBC_Writel(reg_value, (ccmu_base + CCMU_USB1_CLK));
#endif
}
return 0;
}
#else
u32 open_usb_clock(int index)
{
u32 reg_value = 0;
u32 ccmu_base = SUNXI_CCM_BASE;
/* BUS clk gating for USB EHCI */
reg_value = USBC_Readl(ccmu_base + BUS_CLK_GATING_REG);
reg_value |= (1 << ehci_cfg[index].bus_soft_reset_ofs);
USBC_Writel(reg_value, (ccmu_base + BUS_CLK_GATING_REG));
/* Bus soft reset for USB EHCI */
reg_value = USBC_Readl(ccmu_base + BUS_SOFTWARE_RESET_REG);
reg_value |= (1 << ehci_cfg[index].bus_clk_gating_ofs);
USBC_Writel(reg_value, (ccmu_base + BUS_SOFTWARE_RESET_REG));
/* open clock for USB PHY */
reg_value = USBC_Readl(ccmu_base + USBPHY_CONFIG_REG);
reg_value |= (1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value |= (1 << ehci_cfg[index].phy_reset_ofs);
USBC_Writel(reg_value, (ccmu_base + USBPHY_CONFIG_REG));
printf("config usb clk ok\n");
return 0;
}
/*
*******************************************************************************
* close_usb_clock
*
* Description:
*
*
* Parameters:
* void
*
* Return value:
* void
*
* note:
* void
*
*******************************************************************************
*/
u32 close_usb_clock(int index)
{
u32 reg_value = 0;
u32 ccmu_base = SUNXI_CCM_BASE;
/* Bus soft reset for USB EHCI */
reg_value = USBC_Readl(ccmu_base + BUS_SOFTWARE_RESET_REG);
reg_value &= ~(1 << ehci_cfg[index].bus_soft_reset_ofs);
USBC_Writel(reg_value, (ccmu_base + BUS_SOFTWARE_RESET_REG));
/* BUS clk gating for USB EHCI */
reg_value = USBC_Readl(ccmu_base + BUS_CLK_GATING_REG);
reg_value &= ~(1 << ehci_cfg[index].bus_clk_gating_ofs);
USBC_Writel(reg_value, (ccmu_base + BUS_CLK_GATING_REG));
/* close clock for USB PHY */
reg_value = USBC_Readl(ccmu_base + USBPHY_CONFIG_REG);
/* PHY0 */
reg_value &= ~(1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value &= ~(1 << ehci_cfg[index].phy_reset_ofs);
USBC_Writel(reg_value, (ccmu_base + USBPHY_CONFIG_REG));
return 0;
}
#endif
/*
*******************************************************************************
* enable_usb_passby
*
* Description:
* void
*
* Parameters:
* void
*
* Return value:
* void
*
* note:
* void
*
*******************************************************************************
*/
void usb_passby(int index, u32 enable)
{
unsigned long reg_value = 0;
u32 ehci_vbase = ehci_cfg[index].ehci_base;
if(ehci_cfg[index].usb0_support)
{
/* the default mode of usb0 is OTG,so change it here. */
reg_value = USBC_Readl(SUNXI_USBOTG_BASE + 0x420);
reg_value &= ~(0x01);
USBC_Writel(reg_value, (SUNXI_USBOTG_BASE + 0x420));
}
reg_value = USBC_Readl(ehci_vbase + 0x810);
reg_value &= ~(0x01<<1);
USBC_Writel(reg_value, (ehci_vbase + 0x810));
reg_value = USBC_Readl(ehci_vbase + SUNXI_USB_PMU_IRQ_ENABLE);
if(enable){
reg_value |= (1 << 10); /* AHB Master interface INCR8 enable */
reg_value |= (1 << 9); /* AHB Master interface burst type INCR4 enable */
reg_value |= (1 << 8); /* AHB Master interface INCRX align enable */
reg_value |= (1 << 0); /* ULPI bypass enable */
}else if(!enable){
reg_value &= ~(1 << 10); /* AHB Master interface INCR8 disable */
reg_value &= ~(1 << 9); /* AHB Master interface burst type INCR4 disable */
reg_value &= ~(1 << 8); /* AHB Master interface INCRX align disable */
reg_value &= ~(1 << 0); /* ULPI bypass disable */
}
USBC_Writel(reg_value, (ehci_vbase + SUNXI_USB_PMU_IRQ_ENABLE));
return;
}
void sunxi_set_vbus(int on_off)
{
if(usb_vbus_handle)
gpio_write_one_pin_value(usb_vbus_handle, on_off, NULL);
return;
}
/*
*******************************************************************************
* sunxi_start_ehci
*
* Description:
* void
*
* Parameters:
* void
*
* Return value:
* void
*
* note:
* void
*
*******************************************************************************
*/
int sunxi_start_ehci(int index)
{
if(alloc_pin(index))
return -1;
open_usb_clock(index);
usb_passby(index, 1);
sunxi_set_vbus(1);
__msdelay(800);
return 0;
}
/*
*******************************************************************************
* sunxi_stop_ehci
*
* Description:
* void
*
* Parameters:
* void
*
* Return value:
* void
*
* note:
* void
*
*******************************************************************************
*/
void sunxi_stop_ehci(int index)
{
sunxi_set_vbus(0);
usb_passby(index, 0);
close_usb_clock(index);
free_pin();
return;
}
/*
* Create the appropriate control structures to manage
* a new EHCI host controller.
*/
int ehci_hcd_init(int index, enum usb_init_type init,
struct ehci_hccr **hccr, struct ehci_hcor **hcor)
{
printf("start sunxi %s...\n", ehci_cfg[index].name);
if(index > ARRAY_SIZE(ehci_cfg))
{
printf("the index is too large\n");
return -1;
}
usb_vbase = ehci_cfg[index].ehci_base;
if(sunxi_start_ehci(index))
{
return -1;
}
*hccr = (struct ehci_hccr *)usb_vbase;
*hcor = (struct ehci_hcor *)((uint32_t) (*hccr) +
HC_LENGTH(ehci_readl(&((*hccr)->cr_capbase))));
printf("sunxi %s init ok...\n", ehci_cfg[index].name);
return 0;
}
/*
* Destroy the appropriate control structures corresponding
* the the EHCI host controller.
*/
int ehci_hcd_stop(int index)
{
sunxi_stop_ehci(index);
printf("stop sunxi %s ok...\n", ehci_cfg[index].name);
return 0;
}