blob: 0d0272c9b298fe76a7d91dde1aea7d85050078a1 [file] [log] [blame]
/*
* This file is part of the coreboot project.
*
* Copyright 2014 Google 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; version 2 of the License.
*
* 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.
*/
#include <device/mmio.h>
#include <console/console.h>
#include <delay.h>
#include <soc/clock.h>
#include <soc/iomap.h>
#include <soc/usb.h>
/**
* USB Hardware registers
*/
#define PHY_CTRL0_ADDR 0x000
#define PHY_CTRL1_ADDR 0x004
#define PHY_CTRL2_ADDR 0x008
#define PHY_CTRL3_ADDR 0x00C
#define PHY_CTRL4_ADDR 0x010
#define PHY_MISC_ADDR 0x024
#define PHY_IPG_ADDR 0x030
#define PHY_CTRL0_VAL 0xA4600015
#define PHY_CTRL1_VAL 0x09500000
#define PHY_CTRL2_VAL 0x00058180
#define PHY_CTRL3_VAL 0x6DB6DCD6
#define PHY_CTRL4_VAL 0x836DB6DB
#define PHY_MISC_VAL 0x3803FB0C
#define PHY_IPG_VAL 0x47323232
#define USB_HOST3_PHY_BASE ((void *)0x8a00000)
#define USB_HOST3_BALDUR_PHY_BASE ((void *)0xa6000)
#define GCC_USB3_RST_CTRL ((void *)0x0181E038)
#define DWC3_GCTL 0xc110
#define DWC3_GUSB3PIPECTL(n) (0xc2c0 + (n * 0x04))
#define DWC3_GUSB2PHYCFG(n) (0xc200 + (n * 0x04))
/* Global USB3 PIPE Control Register */
#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17)
#define DWC3_GCTL_CORESOFTRESET (1 << 11)
#define DWC3_GCTL_PRTCAPDIR(n) ((n) << 12)
#define DWC3_GCTL_PRTCAP_OTG 3
#define DWC3_DCTL_CSFTRST (1 << 30)
#define DWC3_GSNPSID 0xc120
#define DWC3_DCTL 0xc704
/* Global USB2 PHY Configuration Register */
#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6)
#define DWC3_GSNPSID_MASK 0xffff0000
#define DWC3_GEVTEN 0xc114
#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4)
#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3)
#define DWC3_GCTL_DISSCRAMBLE (1 << 3)
#define DWC3_GCTL_DSBLCLKGTNG (1 << 0)
#define DWC3_GCTL_U2RSTECN (1 << 16)
#define DWC3_REVISION_190A 0x5533190a
#define USB30_HS_PHY_CTRL 0x00000010
#define SW_SESSVLD (0x01 << 0x1C)
#define UTMI_OTG_VBUS_VALID (0x01 << 0x14)
#define USB30_SS_PHY_CTRL 0x00000030
#define LANE0_PWR_PRESENT (0x01 << 0x18)
static void setup_dwc3(void);
/**
* Write register.
*
* @param base - PHY base virtual address.
* @param offset - register offset.
* @param val - value to write.
*/
static inline void qscratch_write(void *base, u32 offset, u32 val)
{
write32(base + offset, val);
}
/**
* Write register and read back masked value to confirm it is written
*
* @param base - base virtual address.
* @param offset - register offset.
* @param mask - register bitmask specifying what should be updated
* @param val - value to write.
*/
static inline void qscratch_write_readback(void *base, u32 offset,
const u32 mask, u32 val)
{
u32 write_val, tmp = read32(base + offset);
tmp &= ~mask; /* retain other bits */
write_val = tmp | val;
write32(base + offset, write_val);
/* Read back to see if val was written */
tmp = read32(base + offset);
tmp &= mask; /* clear other bits */
if (tmp != val) {
printk(BIOS_INFO, "write: %x to QSCRATCH: %x FAILED\n",
val, offset);
}
}
static void dwc3_ipq40xx_enable_vbus_valid(void)
{
/* Enable VBUS valid for HS PHY*/
qscratch_write_readback((void *)0x8af8800, USB30_HS_PHY_CTRL,
SW_SESSVLD, SW_SESSVLD);
qscratch_write_readback((void *)0x8af8800, USB30_HS_PHY_CTRL,
UTMI_OTG_VBUS_VALID, UTMI_OTG_VBUS_VALID);
/* Enable VBUS valid for SS PHY*/
qscratch_write_readback((void *)0x8af8800, USB30_SS_PHY_CTRL,
LANE0_PWR_PRESENT, LANE0_PWR_PRESENT);
}
static void qcom_baldur_hs_phy_init(void)
{
u32 reg;
/* assert HS PHY POR reset */
reg = read32(GCC_USB3_RST_CTRL);
reg = reg | 0x10;
write32(GCC_USB3_RST_CTRL, reg);
mdelay(10);
/* assert HS PHY SRIF reset */
reg = read32(GCC_USB3_RST_CTRL);
reg = reg | 0x4;
write32(GCC_USB3_RST_CTRL, reg);
mdelay(10);
/* deassert HS PHY SRIF reset and program HS PHY registers */
reg = read32(GCC_USB3_RST_CTRL);
reg = reg & ~0x4;
write32(GCC_USB3_RST_CTRL, reg);
mdelay(10);
/* perform PHY register writes */
write32(USB_HOST3_BALDUR_PHY_BASE + PHY_CTRL0_ADDR, PHY_CTRL0_VAL);
write32(USB_HOST3_BALDUR_PHY_BASE + PHY_CTRL1_ADDR, PHY_CTRL1_VAL);
write32(USB_HOST3_BALDUR_PHY_BASE + PHY_CTRL2_ADDR, PHY_CTRL2_VAL);
write32(USB_HOST3_BALDUR_PHY_BASE + PHY_CTRL3_ADDR, PHY_CTRL3_VAL);
write32(USB_HOST3_BALDUR_PHY_BASE + PHY_CTRL4_ADDR, PHY_CTRL4_VAL);
write32(USB_HOST3_BALDUR_PHY_BASE + PHY_MISC_ADDR, PHY_MISC_VAL);
write32(USB_HOST3_BALDUR_PHY_BASE + PHY_IPG_ADDR, PHY_IPG_VAL);
mdelay(10);
/* de-assert USB3 HS PHY POR reset */
reg = read32(GCC_USB3_RST_CTRL);
reg = reg & ~0x10;
write32(GCC_USB3_RST_CTRL, reg);
}
static void qcom_uni_ss_phy_init(void)
{
u32 reg;
/* assert SS PHY POR reset */
reg = read32(GCC_USB3_RST_CTRL);
reg = reg | 0x20;
write32(GCC_USB3_RST_CTRL, reg);
mdelay(100);
/* deassert SS PHY POR reset */
reg = read32(GCC_USB3_RST_CTRL);
reg = reg & ~0x20;
write32(GCC_USB3_RST_CTRL, reg);
}
void setup_dwc3(void)
{
u32 reg;
u32 revision;
revision = read32(USB_HOST3_PHY_BASE + DWC3_GSNPSID);
/* This should read as U3 followed by revision number */
if ((revision & DWC3_GSNPSID_MASK) != 0x55330000)
printk(BIOS_INFO, "Error in reading Version\n");
printk(BIOS_INFO, "Version = %x\n", revision);
/* issue device SoftReset too */
write32(USB_HOST3_PHY_BASE + DWC3_DCTL, DWC3_DCTL_CSFTRST);
do {
reg = read32(USB_HOST3_PHY_BASE + DWC3_DCTL);
if (!(reg & DWC3_DCTL_CSFTRST))
break;
udelay(10);
} while (true);
printk(BIOS_INFO, "software reset done\n");
/* Before Resetting PHY, put Core in Reset */
reg = read32(USB_HOST3_PHY_BASE + DWC3_GCTL);
reg |= DWC3_GCTL_CORESOFTRESET;
write32(USB_HOST3_PHY_BASE + DWC3_GCTL, reg);
/* Assert USB3 PHY reset */
reg = read32(USB_HOST3_PHY_BASE + DWC3_GUSB3PIPECTL(0));
reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST;
write32(USB_HOST3_PHY_BASE + DWC3_GUSB3PIPECTL(0), reg);
/* Assert USB2 PHY reset */
reg = read32(USB_HOST3_PHY_BASE + DWC3_GUSB2PHYCFG(0));
reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST;
write32(USB_HOST3_PHY_BASE + DWC3_GUSB2PHYCFG(0), reg);
qcom_baldur_hs_phy_init();
qcom_uni_ss_phy_init();
mdelay(100);
/* Clear USB3 PHY reset */
reg = read32(USB_HOST3_PHY_BASE + DWC3_GUSB3PIPECTL(0));
reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST;
write32(USB_HOST3_PHY_BASE + DWC3_GUSB3PIPECTL(0), reg);
/* Clear USB2 PHY reset */
reg = read32(USB_HOST3_PHY_BASE + DWC3_GUSB2PHYCFG(0));
reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST;
write32(USB_HOST3_PHY_BASE + DWC3_GUSB2PHYCFG(0), reg);
mdelay(100);
/* After PHYs are stable we can take Core out of reset state */
reg = read32(USB_HOST3_PHY_BASE + DWC3_GCTL);
reg &= ~DWC3_GCTL_CORESOFTRESET;
write32(USB_HOST3_PHY_BASE + DWC3_GCTL, reg);
#if 0
/* Enable Suspend USB2.0 HS/FS/LS PHY (SusPHY) */
reg = read32(USB_HOST3_PHY_BASE + DWC3_GUSB2PHYCFG(0));
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
write32(USB_HOST3_PHY_BASE + DWC3_GUSB2PHYCFG(0), reg);
/* Enable Suspend USB3.0 SS PHY (Suspend_en) */
reg = read32(USB_HOST3_PHY_BASE + DWC3_GUSB3PIPECTL(0));
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
write32(USB_HOST3_PHY_BASE + DWC3_GUSB3PIPECTL(0), reg);
#endif
/* configure controller in Host mode */
reg = read32(USB_HOST3_PHY_BASE + DWC3_GCTL);
reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));
reg |= DWC3_GCTL_PRTCAPDIR(0x1); /* host mode */
write32(USB_HOST3_PHY_BASE + DWC3_GCTL, reg);
printk(BIOS_INFO, "USB Host mode reg = %x\n", reg);
reg = read32(USB_HOST3_PHY_BASE + DWC3_GCTL);
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
reg &= ~DWC3_GCTL_DISSCRAMBLE;
reg &= ~DWC3_GCTL_DSBLCLKGTNG;
/*
* WORKAROUND: DWC3 revisions <1.90a have a bug
* where the device can fail to connect at SuperSpeed
* and falls back to high-speed mode which causes
* the device to enter a Connect/Disconnect loop
*/
if (revision < DWC3_REVISION_190A)
reg |= DWC3_GCTL_U2RSTECN;
write32(USB_HOST3_PHY_BASE + DWC3_GCTL, reg);
}
void setup_usb_host1(void)
{
printk(BIOS_INFO, "Setting up USB HOST1 controller.\n");
setup_dwc3();
dwc3_ipq40xx_enable_vbus_valid();
}