sdk-hwV1.3/lichee/brandy-2.0/u-boot-2018/drivers/usb/host/ehci-sunxi.c

387 lines
11 KiB
C

/*
* SPDX-License-Identifier: GPL-2.0+
* (C) Copyright 20016-2020
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* wangwei <wangwei@allwinnertech.com>
*
*/
#include <common.h>
#include <asm/arch/clock.h>
//#include <asm/arch/usb_phy.h>
#include <asm/io.h>
#include <dm.h>
#include <asm/arch/gpio.h>
#include <asm/gpio.h>
#include <asm/arch/clock.h>
#include "ehci.h"
#define SUNXI_USB_CTRL 0x800
#define SUNXI_USB_PHY_CTRL 0x810
#define HOST_MAXNUM 5
static uintptr_t usb_vbase = SUNXI_EHCI0_BASE;
unsigned int usb_drv_vbus_gpio = -1;
typedef struct _ehci_config
{
uintptr_t 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];
}ehci_config_t;
static ehci_config_t ehci_cfg[HOST_MAXNUM] = {
{SUNXI_EHCI0_BASE, USBEHCI0_RST_BIT, USBEHCI0_GATIING_BIT, USBPHY0_RST_BIT, USBPHY0_SCLK_GATING_BIT, 1, "USB-DRD"},
#ifdef SUNXI_EHCI1_BASE
{SUNXI_EHCI1_BASE, USBEHCI1_RST_BIT, USBEHCI1_GATIING_BIT, USBPHY1_RST_BIT, USBPHY1_SCLK_GATING_BIT, 0, "USB1-Host"},
#endif
#ifdef SUNXI_EHCI2_BASE
{SUNXI_EHCI2_BASE, USBEHCI2_RST_BIT, USBEHCI2_GATIING_BIT, USBPHY2_RST_BIT, USBPHY2_SCLK_GATING_BIT, 0, "USB2-Host"},
#endif
#ifdef SUNXI_EHCI3_BASE
{SUNXI_EHCI3_BASE, USBEHCI3_RST_BIT, USBEHCI3_GATIING_BIT, USBPHY3_RST_BIT, USBPHY3_SCLK_GATING_BIT, 0, "USB3-Host"},
#endif
};
/*Port:port+num<fun><pull><drv><data>*/
ulong config_usb_pin(int index)
{
if (index == 0)
usb_drv_vbus_gpio = sunxi_name_to_gpio(CONFIG_USB0_VBUS_PIN);
else if (index == 1)
usb_drv_vbus_gpio = sunxi_name_to_gpio(CONFIG_USB1_VBUS_PIN);
else if (index == 2)
usb_drv_vbus_gpio = sunxi_name_to_gpio(CONFIG_USB2_VBUS_PIN);
else
usb_drv_vbus_gpio = sunxi_name_to_gpio(CONFIG_USB3_VBUS_PIN);
if (usb_drv_vbus_gpio == -1)
return 0;
/*usbc0 port:PB0<0><1><default><default>*/
/*usbc1 port:PB1<0><1><default><default>*/
sunxi_gpio_set_cfgpin(usb_drv_vbus_gpio, 0);
sunxi_gpio_set_pull(usb_drv_vbus_gpio, SUNXI_GPIO_PULL_UP);
/* set cfg, ouput */
sunxi_gpio_set_cfgpin(usb_drv_vbus_gpio, 1);
/* reserved is pull down */
sunxi_gpio_set_pull(usb_drv_vbus_gpio, 2);
return 0;
}
void sunxi_set_vbus(int onoff)
{
if (usb_drv_vbus_gpio == -1)
return;
gpio_set_value(usb_drv_vbus_gpio, onoff);
return;
}
s32 __attribute__((weak)) axp_usb_vbus_output(void){ return 0;}
int alloc_pin(int index)
{
if (axp_usb_vbus_output())
return 0;
config_usb_pin(index);
return 0;
}
void free_pin(void)
{
return;
}
u32 open_usb_clock(int index)
{
u32 reg_value = 0;
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
#if !IS_ENABLED(CONFIG_MACH_SUN8IW11)
/* Bus reset and gating for ehci */
reg_value = readl(&ccm->usb_gate_reset);
reg_value |= (1 << ehci_cfg[index].bus_soft_reset_ofs);
reg_value |= (1 << ehci_cfg[index].bus_clk_gating_ofs);
writel(reg_value, &ccm->usb_gate_reset);
#else
reg_value = readl(&ccm->ahb_reset0_cfg);
reg_value |= (1 << ehci_cfg[index].bus_soft_reset_ofs);
writel(reg_value, &ccm->ahb_reset0_cfg);
reg_value = readl(&ccm->ahb_gate0);
reg_value |= (1 << ehci_cfg[index].bus_clk_gating_ofs);
writel(reg_value, &ccm->ahb_gate0);
#endif
#if defined(CONFIG_MACH_SUN50IW9)
reg_value = readl(&ccm->usb2_clk_cfg);
reg_value |= (1 << ehci_cfg[2].phy_slk_gatimg_ofs);
reg_value |= (1 << ehci_cfg[2].phy_reset_ofs);
writel(reg_value, &ccm->usb2_clk_cfg);
#endif
/* open clk for usb phy */
if (index == 0) {
#if !IS_ENABLED(CONFIG_MACH_SUN8IW11)
reg_value = readl(&ccm->usb0_clk_cfg);
reg_value |= (1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value |= (1 << ehci_cfg[index].phy_reset_ofs);
writel(reg_value, &ccm->usb0_clk_cfg);
#else
reg_value = readl(&ccm->usb_clk_cfg);
reg_value |= (1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value |= (1 << ehci_cfg[index].phy_reset_ofs);
writel(reg_value, &ccm->usb_clk_cfg);
#endif
} else if (index == 1) {
#if !IS_ENABLED(CONFIG_MACH_SUN8IW11)
reg_value = readl(&ccm->usb1_clk_cfg);
reg_value |= (1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value |= (1 << ehci_cfg[index].phy_reset_ofs);
writel(reg_value, &ccm->usb1_clk_cfg);
#else
reg_value = readl(&ccm->usb_clk_cfg);
reg_value |= (1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value |= (1 << ehci_cfg[index].phy_reset_ofs);
writel(reg_value, &ccm->usb_clk_cfg);
#endif
} else if (index == 2) {
#if defined(CONFIG_MACH_SUN50IW9)
/*noting to do*/
#else
#ifdef SUNXI_EHCI2_BASE
#if !IS_ENABLED(CONFIG_MACH_SUN8IW11)
reg_value = readl(&ccm->usb2_clk_cfg);
reg_value |= (1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value |= (1 << ehci_cfg[index].phy_reset_ofs);
writel(reg_value, &ccm->usb2_clk_cfg);
#else
reg_value = readl(&ccm->usb_clk_cfg);
reg_value |= (1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value |= (1 << ehci_cfg[index].phy_reset_ofs);
writel(reg_value, &ccm->usb_clk_cfg);
#endif
#endif
#endif
} else {
#ifdef SUNXI_EHCI3_BASE
#if !IS_ENABLED(CONFIG_MACH_SUN8IW11)
reg_value = readl(&ccm->usb3_clk_cfg);
reg_value |= (1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value |= (1 << ehci_cfg[index].phy_reset_ofs);
writel(reg_value, &ccm->usb3_clk_cfg);
#else
reg_value = readl(&ccm->usb_clk_cfg);
reg_value |= (1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value |= (1 << ehci_cfg[index].phy_reset_ofs);
writel(reg_value, &ccm->usb_clk_cfg);
#endif
#endif
}
printf("config usb clk ok\n");
return 0;
}
u32 close_usb_clock(int index)
{
u32 reg_value = 0;
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
/* Bus reset and gating for ehci */
#if !IS_ENABLED(CONFIG_MACH_SUN8IW11)
reg_value = readl(&ccm->usb_gate_reset);
reg_value &= ~(1 << ehci_cfg[index].bus_soft_reset_ofs);
reg_value &= ~(1 << ehci_cfg[index].bus_clk_gating_ofs);
writel(reg_value, &ccm->usb_gate_reset);
#else
reg_value = readl(&ccm->ahb_reset0_cfg);
reg_value &= ~(1 << ehci_cfg[index].bus_soft_reset_ofs);
writel(reg_value, &ccm->ahb_reset0_cfg);
reg_value = readl(&ccm->ahb_gate0);
reg_value &= ~(1 << ehci_cfg[index].bus_clk_gating_ofs);
writel(reg_value, &ccm->ahb_gate0);
#endif
/* close clk for usb phy */
if (index == 0) {
#if !IS_ENABLED(CONFIG_MACH_SUN8IW11)
reg_value = readl(&ccm->usb0_clk_cfg);
reg_value &= ~(1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value &= ~(1 << ehci_cfg[index].phy_reset_ofs);
writel(reg_value, &ccm->usb0_clk_cfg);
#else
reg_value = readl(&ccm->usb_clk_cfg);
reg_value &= ~(1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value &= ~(1 << ehci_cfg[index].phy_reset_ofs);
writel(reg_value, &ccm->usb_clk_cfg);
#endif
} else if (index == 1) {
#if !IS_ENABLED(CONFIG_MACH_SUN8IW11)
reg_value = readl(&ccm->usb1_clk_cfg);
reg_value &= ~(1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value &= ~(1 << ehci_cfg[index].phy_reset_ofs);
writel(reg_value, &ccm->usb1_clk_cfg);
#else
reg_value = readl(&ccm->usb_clk_cfg);
reg_value &= ~(1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value &= ~(1 << ehci_cfg[index].phy_reset_ofs);
writel(reg_value, &ccm->usb_clk_cfg);
#endif
} else if (index == 2) {
#if defined(CONFIG_MACH_SUN50IW9)
/*noting to do*/
#else
#ifdef SUNXI_EHCI2_BASE
#if !IS_ENABLED(CONFIG_MACH_SUN8IW11)
reg_value = readl(&ccm->usb2_clk_cfg);
reg_value &= ~(1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value &= ~(1 << ehci_cfg[index].phy_reset_ofs);
writel(reg_value, &ccm->usb2_clk_cfg);
#else
reg_value = readl(&ccm->usb_clk_cfg);
reg_value &= ~(1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value &= ~(1 << ehci_cfg[index].phy_reset_ofs);
writel(reg_value, &ccm->usb_clk_cfg);
#endif
#endif
#endif
} else {
#ifdef SUNXI_EHCI3_BASE
#if !IS_ENABLED(CONFIG_MACH_SUN8IW11)
reg_value = readl(&ccm->usb3_clk_cfg);
reg_value &= ~(1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value &= ~(1 << ehci_cfg[index].phy_reset_ofs);
writel(reg_value, &ccm->usb3_clk_cfg);
#else
reg_value = readl(&ccm->usb_clk_cfg);
reg_value &= ~(1 << ehci_cfg[index].phy_slk_gatimg_ofs);
reg_value &= ~(1 << ehci_cfg[index].phy_reset_ofs);
writel(reg_value, &ccm->usb_clk_cfg);
#endif
#endif
}
return 0;
}
void usb_passby(int index, u32 enable)
{
unsigned long reg_value = 0;
uintptr_t 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 = readl((void *)(SUNXI_USBOTG_BASE + 0x420));
reg_value &= ~(0x01);
writel(reg_value, (void *)(SUNXI_USBOTG_BASE + 0x420));
}
#if defined(CONFIG_MACH_SUN50IW11)
if (index == 1) {
reg_value = readl((void *)(SUNXI_USB1_BASE + 0x420));
reg_value &= ~(0x01);
writel(reg_value, (void *)(SUNXI_USB1_BASE + 0x420));
}
#endif
reg_value = readl((void *)(ehci_vbase + SUNXI_USB_PHY_CTRL));
#if !IS_ENABLED(CONFIG_MACH_SUN8IW11)
reg_value &= ~(0x01 << 3);
#else
reg_value &= ~(0x01 << 1);
#endif
writel(reg_value, (void *)(ehci_vbase + SUNXI_USB_PHY_CTRL));
reg_value = readl((void *)(ehci_vbase + SUNXI_USB_CTRL));
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 */
}
writel(reg_value, (void *)(ehci_vbase + SUNXI_USB_CTRL));
return;
}
int sunxi_start_ehci(int index)
{
if(alloc_pin(index))
return -1;
open_usb_clock(index);
usb_passby(index, 1);
sunxi_set_vbus(1);
mdelay(800);
return 0;
}
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 *)((uintptr_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;
}