blob: f9288cf8cb5d444c2464b37e2e2746a8f81697f8 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Support for asr usb phy driver
*
* Copyright 2022 ASR Microelectronics (Shanghai) Co., Ltd.
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/usb/phy.h>
#include <linux/platform_data/mv_usb.h>
#include <linux/cputype.h>
#include <soc/asr/addr-map.h>
/*
*
* THE BASE ADDRESSES
*
*/
#define USB_PHY_PORTB_OFFSET (0x100000)
#define PUPHY_REG_OFFSET (0x400)
#define PUPHY_REG00 (PUPHY_REG_OFFSET + (0x0 << 2))
#define PUPHY_REG01 (PUPHY_REG_OFFSET + (0x1 << 2))
#define PUPHY_REG02 (PUPHY_REG_OFFSET + (0x2 << 2))
#define PUPHY_REG03 (PUPHY_REG_OFFSET + (0x3 << 2))
#define PUPHY_REG04 (PUPHY_REG_OFFSET + (0x4 << 2))
#define PUPHY_REG05 (PUPHY_REG_OFFSET + (0x5 << 2))
#define PUPHY_REG06 (PUPHY_REG_OFFSET + (0x6 << 2))
#define PUPHY_REG07 (PUPHY_REG_OFFSET + (0x7 << 2))
#define PUPHY_REG08 (PUPHY_REG_OFFSET + (0x8 << 2))
#define PUPHY_REG09 (PUPHY_REG_OFFSET + (0x9 << 2))
#define PUPHY_REG0A (PUPHY_REG_OFFSET + (0xA << 2))
#define PUPHY_REG0B (PUPHY_REG_OFFSET + (0xB << 2))
#define PUPHY_REG0C (PUPHY_REG_OFFSET + (0xC << 2))
#define PUPHY_REG0D (PUPHY_REG_OFFSET + (0xD << 2))
#define PUPHY_REG0E (PUPHY_REG_OFFSET + (0xE << 2))
#define PUPHY_REG0F (PUPHY_REG_OFFSET + (0xF << 2))
#define PUPHY_REG10 (PUPHY_REG_OFFSET + (0x10 << 2))
#define PUPHY_REG11 (PUPHY_REG_OFFSET + (0x11 << 2))
#define PUPHY_REG13 (PUPHY_REG_OFFSET + (0x13 << 2))
#define PUPHY_REG15 (PUPHY_REG_OFFSET + (0x15 << 2))
#define PUPHY_REG16 (PUPHY_REG_OFFSET + (0x16 << 2))
#define PUPHY_REG17 (PUPHY_REG_OFFSET + (0x17 << 2))
#define PUPHY_REG18 (PUPHY_REG_OFFSET + (0x18 << 2))
#define PUPHY_REG19 (PUPHY_REG_OFFSET + (0x19 << 2))
#define PUPHY_REG1B (PUPHY_REG_OFFSET + (0x1B << 2))
#define PUPHY_REG1E (PUPHY_REG_OFFSET + (0x1E << 2))
#define PUPHY_REG20 (PUPHY_REG_OFFSET + (0x20 << 2))
#define PUPHY_REG21 (PUPHY_REG_OFFSET + (0x21 << 2))
#define PUPHY_REG2D (PUPHY_REG_OFFSET + (0x2D << 2))
#define PUPHY_REG06_SUSPEND_MASK (0xFF300)
#define PUPHY_REG06_SUSPEND_VAL (0xAA200)
#define PUPHY_REG06_SUSPEND_VAL2 (0x00000)
#define PHY_SUSPEND_PLL (0x1 << 13)
#define PHY_SUSPEND_PHY (0x1 << 12)
/*
*
* THE BASE ADDRESSES
*
*/
#define USB2_PHY0_OFFSET (0x0)
/* PHY REGS */
#define USB2_PHY0_REG00 (USB2_PHY0_OFFSET+0x00)
#define USB2_PHY0_REG01 (USB2_PHY0_OFFSET+0x04)
#define USB2_PHY0_REG02 (USB2_PHY0_OFFSET+0x08)
#define USB2_PHY0_REG03 (USB2_PHY0_OFFSET+0x0C)
#define USB2_PHY0_REG04 (USB2_PHY0_OFFSET+0x10)
#define USB2_PHY0_REG05 (USB2_PHY0_OFFSET+0x14)
#define USB2_PHY0_REG06 (USB2_PHY0_OFFSET+0x18)
#define USB2_PHY0_REG07 (USB2_PHY0_OFFSET+0x1C)
#define USB2_PHY0_REG08 (USB2_PHY0_OFFSET+0x20)
#define USB2_PHY0_REG09 (USB2_PHY0_OFFSET+0x24)
#define USB2_PHY0_REG0A (USB2_PHY0_OFFSET+0x28)
#define USB2_PHY0_REG0B (USB2_PHY0_OFFSET+0x2C)
#define USB2_PHY0_REG0C (USB2_PHY0_OFFSET+0x30)
#define USB2_PHY0_REG0D (USB2_PHY0_OFFSET+0x34)
#define USB2_PHY0_REG0E (USB2_PHY0_OFFSET+0x38)
#define USB2_PHY0_REG0F (USB2_PHY0_OFFSET+0x3C)
#define USB2_PHY0_REG10 (USB2_PHY0_OFFSET+0x40)
#ifdef CONFIG_CPU_ASR1901
#define USB2_PHY0_REG25 (USB2_PHY0_OFFSET+0x98)
#define USB2_PHY0_REG29 (USB2_PHY0_OFFSET+0x94)
#else
#define USB2_PHY0_REG25 (USB2_PHY0_OFFSET+0x94)
#define USB2_PHY0_REG29 (USB2_PHY0_OFFSET+0xA4)
#endif
#define USB2_PLL_BIT_RDY (0x1 << 0)
#define USB2_CFG_HS_SRCS_SEL (0x1 << 0)
#ifdef CONFIG_CPU_ASR1901
#define APMU_USB_PHY_READ 0x118
#define USB_VBUS_STS (0x1 << 2)
#else
#define APMU_USB_PHY_READ 0x7C
#define USB_VBUS_STS (0x1 << 15)
#endif
#define PORTA_TYPEC_CHECK_PIN_OFFSET (0x01e048)
#define PORTA_TYPEC_CHECK_REG_OFFSET (0x020)
#define PHY_USB2_PLL_BIT_RDY (0x1 << 0)
#define PHY_USB2_CFG_HS_SRCS_SEL (0x1 << 0)
#define PHY_USB2_SUSPEND_PLL (0x1 << 13)
#define PHY_USB2_SUSPEND_PHY (0x1 << 12)
#ifdef CONFIG_CPU_ASR1901
#define PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN (0x1 << 6)
#else
#define PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN (0x1 << 14)
#endif
#define PHY_USB2_DCP_DET_PULL_UP_DOWN ((0x1 << 11) | (0x1 << 8))
#define PHY_USB2_DCP_DET_PULL_UP_NONE ((0x1 << 11) | (0x1 << 10))
#define PHY_USB2_DCP_DET_PULL_DOWN_DOWN ((0x1 << 10) | (0x1 << 8))
#define PHY_USB2_DCP_DET_PULL_MASK (0xF << 8)
#define PHY_USB2_DRV_PULLDOWN_OVERRIDE_MASK (0xF << 8)
#define PHY_USB2_DRV_PULLDOWN_OVERRIDE_VAL (0x9 << 8)
#define PMUA_RTERM_CAL_REG (0x3f8)
#define APB_SPARE_REG24 (0x15c)
/*
* struct asr_usb3_phy - transceiver driver state
* @phy: transceiver structure
* @dev: The parent device supplied to the probe function
* @clk: usb phy clock
* @base: usb phy register memory base
*/
struct asr_usb3_phy {
struct usb_phy phy;
struct mv_usb_platform_data *pdata;
struct device *dev;
struct clk *clk;
void __iomem *base;
};
static u32 usb3_phy_res_size;
extern void dwc3_force_usb2_mode(void);
extern u32 dwc3_get_line_status(void);
extern u32 dwc3_usb_is_running(void);
static void usb31_rterm_cal_porta(void __iomem *base);
u32 usb31_rterm_cal_value;
static int asr_usb_host_phy_private(struct usb_phy *phy, u32 option)
{
struct asr_usb3_phy *asr_usb3_phy = container_of(phy, struct asr_usb3_phy, phy);
void __iomem *phy_base = asr_usb3_phy->base;
int count = 0x100000;
pr_info("asr usb phy host priviate: %d\n", option);
if (option) {
;
} else {
writel(readl(phy_base + USB2_PHY0_REG10) | 0x1,
phy_base + USB2_PHY0_REG10);
while((readl(phy_base + USB2_PHY0_REG10) & 0x1) && (count--));
if (unlikely(count == 0))
pr_err("phy rd_hshost_disc_raw clear failed\n");
}
return 0;
}
static int asr_usb_get_vbus(struct usb_phy *phy)
{
int ret;
u32 reg32;
void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
reg32 = __raw_readl(apmu_base + APMU_USB_PHY_READ);
if (reg32 & USB_VBUS_STS)
ret = 1;
else
ret = 0;
return ret;
}
static int asr_usb_phy_charger_detect(struct usb_phy *phy)
{
struct asr_usb3_phy *asr_usb3_phy;
void __iomem *base;
u32 reg32, reg32_ovrid;
int charger_type_bc12 = NULL_CHARGER;
if (dwc3_usb_is_running()) {
return DEFAULT_CHARGER;
}
asr_usb3_phy = container_of(phy, struct asr_usb3_phy, phy);
base = asr_usb3_phy->base;
reg32_ovrid = reg32 = readl(base + USB2_PHY0_REG25);
reg32 &= ~(PHY_USB2_DRV_PULLDOWN_OVERRIDE_MASK);
reg32 |= PHY_USB2_DRV_PULLDOWN_OVERRIDE_VAL;
writel(reg32, base + USB2_PHY0_REG25);
reg32 = readl(base + USB2_PHY0_REG29);
reg32 |= PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN;
writel(reg32, base + USB2_PHY0_REG29);
udelay(10);
reg32 = readl(base + USB2_PHY0_REG04);
reg32 &= ~(0xff << 8);
reg32 |= (0x12 << 8);
writel(reg32, base + USB2_PHY0_REG04);
if ((dwc3_get_line_status() & (0x3 << 8)) == (0x1 << 8)) {
charger_type_bc12 = NONE_STANDARD_CHARGER;
} else {
charger_type_bc12 = DCP_CHARGER;
}
/* restore reg29 and reg25 */
reg32 = readl(base + USB2_PHY0_REG29);
reg32 &= ~PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN;
writel(reg32, base + USB2_PHY0_REG29);
writel(reg32_ovrid, base + USB2_PHY0_REG25);
return charger_type_bc12;
}
int asr1903_usb3_phy_suspend(struct usb_phy *phy, int suspend)
{
struct asr_usb3_phy *asr_usb3_phy;
void __iomem *base;
u32 val;
asr_usb3_phy = container_of(phy, struct asr_usb3_phy, phy);
base = asr_usb3_phy->base;
pr_info("USB3 PHY set_suspend: %d...\n", suspend);
if (suspend) {
val = readl(base + USB2_PHY0_REG0B);
val &= ~(PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
writel(val, base + USB2_PHY0_REG0B);
val = readl(base + USB2_PHY0_REG0A);
val |= (PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
writel(val, base + USB2_PHY0_REG0A);
/* pulldown dp and pulldown dm */
val = readl(base + USB2_PHY0_REG25);
val &= ~PHY_USB2_DCP_DET_PULL_MASK;
val |= PHY_USB2_DCP_DET_PULL_DOWN_DOWN;
writel(val, base + USB2_PHY0_REG25);
val = readl(base + USB2_PHY0_REG29);
val |= PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN;
writel(val, base + USB2_PHY0_REG29);
} else {
/* disable dp/dm pull override */
val = readl(base + USB2_PHY0_REG29);
val &= ~PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN;
writel(val, base + USB2_PHY0_REG29);
val = readl(base + USB2_PHY0_REG04);
val &= ~(0xff << 8);
val |= (0x12 << 8);
writel(val, base + USB2_PHY0_REG04);
val = readl(base + USB2_PHY0_REG0A);
val &= ~(PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
writel(val, base + USB2_PHY0_REG0A);
}
pr_info("USB2 PHY set_suspend done\n");
return 0;
}
int asr_usb3_phy_suspend(struct usb_phy *phy, int suspend)
{
struct asr_usb3_phy *asr_usb3_phy;
void __iomem *base;
u32 val, typec_dir = USB_TYPEC_DIR_CC2;
int ret;
if (cpu_is_asr1903())
return asr1903_usb3_phy_suspend(phy, suspend);
asr_usb3_phy = container_of(phy, struct asr_usb3_phy, phy);
base = asr_usb3_phy->base;
pr_info("USB3 PHY set_suspend: %d...\n", suspend);
if (cpu_is_asr1901() || cpu_is_asr1906()) {
/* port B */
if (usb3_phy_res_size == (USB_PHY_PORTB_OFFSET + 0x1000))
base = base + USB_PHY_PORTB_OFFSET;
}
if (suspend) {
val = readl(base + USB2_PHY0_REG0B);
val &= ~(PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
writel(val, base + USB2_PHY0_REG0B);
val = readl(base + USB2_PHY0_REG0A);
val |= (PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
writel(val, base + USB2_PHY0_REG0A);
/* USB3 phy suspend */
val = readl(base + PUPHY_REG06);
val &= ~PUPHY_REG06_SUSPEND_MASK;
val |= PUPHY_REG06_SUSPEND_VAL;
writel(val, base + PUPHY_REG06);
/* pulldown dp and pulldown dm */
val = readl(base + USB2_PHY0_REG25);
val &= ~PHY_USB2_DCP_DET_PULL_MASK;
val |= PHY_USB2_DCP_DET_PULL_DOWN_DOWN;
writel(val, base + USB2_PHY0_REG25);
val = readl(base + USB2_PHY0_REG29);
val |= PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN;
writel(val, base + USB2_PHY0_REG29);
} else {
/* disable dp/dm pull override */
val = readl(base + USB2_PHY0_REG29);
val &= ~PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN;
writel(val, base + USB2_PHY0_REG29);
val = readl(base + USB2_PHY0_REG04);
val &= ~(0xff << 8);
val |= (0x12 << 8);
writel(val, base + USB2_PHY0_REG04);
/* USB3 phy resume */
val = readl(base + PUPHY_REG06);
val &= ~PUPHY_REG06_SUSPEND_VAL;
#if 0
if (cpu_is_asr1901() || cpu_is_asr1906())
/* override/cfg term to avoid drop to HS/SS from SSP */
val |= (0x3 << 8);
#endif
writel(val, base + PUPHY_REG06);
val = readl(base + USB2_PHY0_REG0A);
val &= ~(PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
writel(val, base + USB2_PHY0_REG0A);
if (cpu_is_asr1901() || cpu_is_asr1906()) {
ret = pxa_usb_extern_call(PXA_USB_DEV_OTG, typec, get_typec_dir, &typec_dir);
if (ret) {
/* with gpio typec dir */
pr_info("!!!!!!!get_typec_dir failed\n");
writel((readl(base + PUPHY_REG08) & (~((0x1<<1)))), base + PUPHY_REG08);
} else {
pr_info("sw set typec dir: %d\n", typec_dir);
/* sw write typec dir */
val = readl(base + PUPHY_REG08);
val |= (0x1 << 1);
if (typec_dir == USB_TYPEC_DIR_CC2)
val |= (0x1 << 2);
else
val &= ~(0x1 << 2);
writel(val, base + PUPHY_REG08);
}
usb31_rterm_cal_porta(base);
}
}
pr_info("USB3 PHY set_suspend done\n");
return 0;
}
int asr1903_usb3_phy_suspend2(struct usb_phy *phy, int suspend)
{
struct asr_usb3_phy *asr_usb3_phy;
void __iomem *base;
u32 val;
asr_usb3_phy = container_of(phy, struct asr_usb3_phy, phy);
base = asr_usb3_phy->base;
pr_info("USB3 PHY set_suspend2: %d...\n", suspend);
if (suspend) {
val = readl(base + USB2_PHY0_REG0B);
val &= ~(PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
writel(val, base + USB2_PHY0_REG0B);
val = readl(base + USB2_PHY0_REG0A);
val |= (PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
writel(val, base + USB2_PHY0_REG0A);
} else {
/* disable dp/dm pull override */
val = readl(base + USB2_PHY0_REG29);
val &= ~PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN;
writel(val, base + USB2_PHY0_REG29);
val = readl(base + USB2_PHY0_REG04);
val &= ~(0xff << 8);
val |= (0x12 << 8);
writel(val, base + USB2_PHY0_REG04);
val = readl(base + USB2_PHY0_REG0A);
val &= ~(PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
writel(val, base + USB2_PHY0_REG0A);
}
pr_info("USB2 PHY set_suspend2 done\n");
return 0;
}
extern u32 dwc3_is_superspeed_plus(void);
int asr_usb3_phy_suspend2(struct usb_phy *phy, int suspend)
{
struct asr_usb3_phy *asr_usb3_phy;
void __iomem *base;
u32 val, typec_dir = USB_TYPEC_DIR_CC2;
int ret;
if (cpu_is_asr1903())
return asr1903_usb3_phy_suspend2(phy, suspend);
asr_usb3_phy = container_of(phy, struct asr_usb3_phy, phy);
base = asr_usb3_phy->base;
pr_info("USB3 PHY set_suspend2: %d...\n", suspend);
if (cpu_is_asr1901() || cpu_is_asr1906()) {
/* port B */
if (usb3_phy_res_size == (USB_PHY_PORTB_OFFSET + 0x1000))
base = base + USB_PHY_PORTB_OFFSET;
}
if (suspend) {
val = readl(base + PUPHY_REG03);
val &= ~(0x1 << 11);
writel(val, base + PUPHY_REG03);
val = readl(base + USB2_PHY0_REG0B);
val &= ~(PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
writel(val, base + USB2_PHY0_REG0B);
val = readl(base + USB2_PHY0_REG0A);
val |= (PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
writel(val, base + USB2_PHY0_REG0A);
/* USB3 phy suspend */
val = readl(base + PUPHY_REG06);
val &= ~PUPHY_REG06_SUSPEND_MASK;
if (dwc3_is_superspeed_plus())
val |= PUPHY_REG06_SUSPEND_VAL2;
else
val |= PUPHY_REG06_SUSPEND_VAL;
writel(val, base + PUPHY_REG06);
} else {
val = readl(base + PUPHY_REG03);
val |= (0x1 << 11);
writel(val, base + PUPHY_REG03);
/* disable dp/dm pull override */
val = readl(base + USB2_PHY0_REG29);
val &= ~PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN;
writel(val, base + USB2_PHY0_REG29);
val = readl(base + USB2_PHY0_REG04);
val &= ~(0xff << 8);
val |= (0x12 << 8);
writel(val, base + USB2_PHY0_REG04);
/* USB3 phy resume */
val = readl(base + PUPHY_REG06);
val &= ~PUPHY_REG06_SUSPEND_MASK;
#if 0
if (cpu_is_asr1901() || cpu_is_asr1906())
/* override/cfg term to avoid drop to HS/SS from SSP */
val |= (0x3 << 8);
#endif
writel(val, base + PUPHY_REG06);
val = readl(base + USB2_PHY0_REG0A);
val &= ~(PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
writel(val, base + USB2_PHY0_REG0A);
if (cpu_is_asr1901() || cpu_is_asr1906()) {
ret = pxa_usb_extern_call(PXA_USB_DEV_OTG, typec, get_typec_dir, &typec_dir);
if (ret) {
/* with gpio typec dir */
pr_info("!!!!!!!get_typec_dir failed\n");
writel((readl(base + PUPHY_REG08) & (~((0x1<<1)))), base + PUPHY_REG08);
} else {
pr_info("sw set typec dir: %d\n", typec_dir);
/* sw write typec dir */
val = readl(base + PUPHY_REG08);
val |= (0x1 << 1);
if (typec_dir == USB_TYPEC_DIR_CC2)
val |= (0x1 << 2);
else
val &= ~(0x1 << 2);
writel(val, base + PUPHY_REG08);
}
usb31_rterm_cal_porta(base);
}
}
pr_info("USB3 PHY set_suspend2 done\n");
return 0;
}
static void asr1906_rterm_cal_porta(void __iomem *base)
{
u32 regval;
int timeout;
bool rc_restared = false;
void __iomem *apbs_base = regs_addr_get_va(REGS_ADDR_APBS);
do_reterm_cal:
pr_info("RTERM Test for ASR1906\n");
if (!(readl(base + PUPHY_REG20) & (0x1 << 24))) {
pr_info("skip lane0 rterm calibration: 0x%x\n", readl(base + PUPHY_REG20));
return;
}
timeout = 100;
while (timeout--) {
regval = readl(base + PUPHY_REG21);
if ((regval & (0x3 << 16)) == (0x3 << 16))
goto latch_out;
udelay(1);
}
if (timeout <= 0) {
pr_err("usbphy rterm cal failed\n");
if (!rc_restared) {
regval = readl(apbs_base + APB_SPARE_REG24);
regval &= ~0x1;
writel(regval, apbs_base + APB_SPARE_REG24);
regval = readl(apbs_base + APB_SPARE_REG24);
regval |= 0x1;
writel(regval, apbs_base + APB_SPARE_REG24);
udelay(100);
pr_err("apbs-reg24: 0x%x\n", readl(apbs_base + APB_SPARE_REG24));
rc_restared = true;
goto do_reterm_cal;
} else {
BUG();
}
}
latch_out:
/* latch rterm value */
writel((0x1 << 20), base + PUPHY_REG11);
pr_info("PUPHY21: 0x%x PUPHY11: 0x%x\n",
readl(base + PUPHY_REG21), readl(base + PUPHY_REG11));
}
static void asr1901_a0_rterm_cal_porta(void __iomem *base)
{
u32 regval, regval2;
void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
pr_info("RTERM Test for ASR1901 a0+\n");
if (!(readl(base + PUPHY_REG20) & (0x1 << 24))) {
pr_info("skip lane0 rterm calibration: 0x%x\n", readl(base + PUPHY_REG20));
return;
}
/* pll_reg2 set to 0xC0 */
writel(((readl(base + PUPHY_REG16) & 0xFFFF00FF) | 0xC000), base + PUPHY_REG16);
/* pll_reg7[5] of lane0, disable select refclk_100_n/p 100Mhz input */
writel((readl(base + PUPHY_REG17) & (~(0x1<<21))), base + PUPHY_REG17);
regval = readl(base + PUPHY_REG11);
writel((regval & 0xFFFF0000) | (0x13CB), base + PUPHY_REG11);
usb31_rterm_cal_value = regval = readl(apmu_base + PMUA_RTERM_CAL_REG);
if ((regval & (0x1 << 24)) != 0) {
pr_info("USB31 RTERM Done: 0x%x\n", regval);
} else {
regval = 0xCB00; /* default manu rterm value */
pr_info("!!!set manu reterm: 0x%x\n", regval);
}
regval2 = readl(base + PUPHY_REG11);
writel(((regval2 & 0xFFFFFF00) | ((regval & 0xFF00) >> 8)), base + PUPHY_REG11);
/* set force_rc_calib */
writel(readl(base + PUPHY_REG1E) | (0x1 << 1), base + PUPHY_REG1E);
#if 0
/* override/cfg term to avoid drop to HS/SS from SSP */
writel((readl(base + PUPHY_REG06) | (0x3<<8)), base + PUPHY_REG06);
#endif
}
static void usb31_rterm_cal_porta(void __iomem *base)
{
u32 regval, regval2;
int timeout = 1000000 / 5;
if (cpu_is_asr1901_a0_plus()) {
asr1901_a0_rterm_cal_porta(base);
return;
}
if (cpu_is_asr1906()) {
asr1906_rterm_cal_porta(base);
return;
}
pr_info("RTERM Test for USB PortA\n");
/* Override mpu_u3 to 0 */
writel((readl(base + PUPHY_REG08) | (0x1<<1)), base + PUPHY_REG08);
/* pll_reg2 set to 0xC0 */
writel(((readl(base + PUPHY_REG16) & 0xFFFF00FF) | 0xC000), base + PUPHY_REG16);
/* pll_reg7[5] of lane0, disable select refclk_100_n/p 100Mhz input */
writel((readl(base + PUPHY_REG17) & (~(0x1<<21))), base + PUPHY_REG17);
while (timeout--) {
udelay(5);
regval = readl(base + PUPHY_REG21);
/* read until readonly_reg3[0] == 1 */
if ((regval & (0x1 << 24)) != 0) {
pr_info("USB31 RTERM Done\n");
udelay(5);
regval = readl(base + PUPHY_REG21);
regval2 = readl(base + PUPHY_REG11);
writel(((regval2 & 0xFFFFFF00) | ((regval & 0xFF00) >> 8)), base + PUPHY_REG11);
/* set force_rc_calib */
writel(readl(base + PUPHY_REG1E) | (0x1 << 1), base + PUPHY_REG1E);
break;
}
}
if (timeout <= 0) {
pr_info("pu_test_info is 0x%x\n", readl(base + PUPHY_REG21));
WARN(1, "USB31 rterm cal timeout");
}
usb31_rterm_cal_value = readl(base + PUPHY_REG21);
/* Cancle mpu_u3 override */
writel((readl(base + PUPHY_REG08) & (~((0x1<<1)))), base + PUPHY_REG08);
#if 0
/* override/cfg term to avoid drop to HS/SS from SSP */
writel((readl(base + PUPHY_REG06) | (0x3<<8)), base + PUPHY_REG06);
#endif
}
static void usb31_rterm_cal_portb(void __iomem *base, void __iomem *base0)
{
u32 regval, regval2;
int timeout = 1000000 / 5;
pr_info("RTERM Test for USB PortB\n");
/* Override mpu_u3 to 0 */
writel((readl(base + PUPHY_REG06) | ((0x1<<17)|(0x1<<15))), base + PUPHY_REG06);
/* pll_reg2 set to 0xC0 */
writel(((readl(base0 + PUPHY_REG16) & 0xFFFF00FF) | 0xC000), base0 + PUPHY_REG16);
/* pll_reg7[5] of lane0, disable select refclk_100_n/p 100Mhz input */
writel((readl(base0 + PUPHY_REG17) & (~(0x1<<21))), base0 + PUPHY_REG17);
regval = readl(base + PUPHY_REG11);
if (cpu_is_asr1901_z1())
/* write rterm_cal_reg1 to 0x1399 */
writel((regval & 0xFFFF0000) | (0x1399), base + PUPHY_REG11);
else
/* write rterm_cal_reg1 to 0x1387 */
writel((regval & 0xFFFF0000) | (0x1387), base + PUPHY_REG11);
while (timeout--) {
udelay(5);
regval = readl(base0 + PUPHY_REG21);
/* read until readonly_reg3[0] == 1 */
if ((regval & (0x1 << 24)) != 0) {
pr_info("USB31 RTERM Done\n");
udelay(5);
regval = readl(base0 + PUPHY_REG21);
regval2 = readl(base + PUPHY_REG11);
writel(((regval2 & 0xFFFFFF00) | ((regval & 0xFF00) >> 8)), base + PUPHY_REG11);
/* set force_rc_calib */
writel(readl(base + PUPHY_REG1E) | (0x1 << 1), base + PUPHY_REG1E);
break;
}
}
if (timeout <= 0) {
pr_info("pu_test_info is 0x%x\n", readl(base0 + PUPHY_REG21));
WARN(1, "USB31 rterm cal timeout");
}
/* Cancle mpu_u3 override */
writel((readl(base + PUPHY_REG06) & (~((0x1<<17)|(0x1<<15)))), base + PUPHY_REG06);
#if 0
/* override/cfg term to avoid drop to HS/SS from SSP */
writel((readl(base + PUPHY_REG06) | (0x3<<8)), base + PUPHY_REG06);
#endif
}
static int asr_usb3_phy_dumpcfg(struct usb_phy *phy)
{
struct asr_usb3_phy *asr_usb3_phy;
void __iomem *base;
asr_usb3_phy = container_of(phy, struct asr_usb3_phy, phy);
base = asr_usb3_phy->base;
if (!cpu_is_asr1903()) {
pr_err_ratelimited("dwc3 phy: %08x: %08x | %08x: %08x | %08x: %08x\n",
(u32)(base + PUPHY_REG04), readl(base + PUPHY_REG04),
(u32)(base + PUPHY_REG20), readl(base + PUPHY_REG20),
(u32)(base + PUPHY_REG21), readl(base + PUPHY_REG21));
}
pr_err_ratelimited("dwc3 phy: %08x: %08x\n", (u32)(base + USB2_PHY0_REG0E), readl(base + USB2_PHY0_REG0E));
return 0;
}
static void usb30_phy_init(void __iomem *base)
{
u32 pll_wait_us = 400;
u32 val;
if (cpu_is_asr1903())
goto usb2_init;
/* USB PHY config
* step.1
* config phphy, sw init done, ref clk cfg=2
* open allclk en bits
*/
writel(0x97C, base + PUPHY_REG02);
/* step.2 wait for PUPHY PLL READY */
while((readl(base + PUPHY_REG02) & 0x1) != 0x1)
{
udelay(1);
pll_wait_us--;
if (pll_wait_us == 0)
break;
}
/* step.3 override pipe_phystatus to 0 if puphy pll not ready */
if (pll_wait_us == 0) {
dwc3_force_usb2_mode();
writel((readl(base + PUPHY_REG10) | (0x1 << 10)), base + PUPHY_REG10);
}
/* update for mode_2x and ls_mode */
if (cpu_is_asr1828() && (!cpu_is_asr1828_a0())) {
val = readl(base + PUPHY_REG10);
val &= ~(0x1 << 8);
writel(val, (base + PUPHY_REG10));
val = readl(base + PUPHY_REG13);
val &= ~(0x1 << 11);
writel(val, (base + PUPHY_REG13));
}
/* Write 1s to clear phy err status */
writel(0xFFFFFFFF, base + PUPHY_REG04);
usb2_init:
/* step.4 wait for USB2 PHY PLL READY */
pll_wait_us = 400;
while (((readl(base + USB2_PHY0_REG01) & USB2_PLL_BIT_RDY) != USB2_PLL_BIT_RDY)
&& (pll_wait_us--))
{
udelay(1);
}
/*
* Step 5&6
* Release usb2 phy internal reset and enable clock gating
*/
writel(0x60ef, base + USB2_PHY0_REG01);
writel(0x1C, base + USB2_PHY0_REG0D);
/*
* STEP.7
* 0x1: serial mode, 0x0: parallel mode
* USB2_PHY0_REG06 (USB2_BASE+0x18)
*
* serial mode
* writel((readl(base + USB2_PHY0_REG06) | 0x1), base + USB2_PHY0_REG06);
* parallel mode
*
* set to serial mode for stability
*/
writel(readl(base + USB2_PHY0_REG06) | (0x1), base + USB2_PHY0_REG06);
/* increase tx dac by 10% */
/* writel((readl(base + USB2_PHY0_REG29) & (~0x1F)) | (0x1B), base + USB2_PHY0_REG29); */
/* Write 1s to clear phy err status */
writel(0xFFFFFFFF, base + USB2_PHY0_REG0E);
}
static void usb31_phy_init(void __iomem *base)
{
u32 val;
u32 pll_wait_us = 400;
/*
* CONFIG PLL, default puphy clk is 100M clk for pcie
* should be config to usb 38.4M
*/
val = readl(base + PUPHY_REG16);
val &= 0xFFFF0FFF;
val |= (0x6 << 13); /* 38.4M */
writel(val, base + PUPHY_REG16);
val = readl(base + PUPHY_REG17);
val &= (~(0x1 << 21));
writel(val, base + PUPHY_REG17);
val = readl(base + PUPHY_REG03);
val |= (0x3 << 8);
writel(val, base + PUPHY_REG03);
if (cpu_is_asr1906())
val = 0x18C;
else
val = 0x18B;
writel(val, base + PUPHY_REG1B);
writel(0x8014, base + PUPHY_REG07);
#if 0
val = readl(base + PUPHY_REG07);
val &= ~(0x3ff<<2);
val |= ((512 & 0x3ff) << 2);
writel(val, base + PUPHY_REG07);
#endif
val = readl(base + PUPHY_REG03);
val |= (0x1 << 2);
writel(val, base + PUPHY_REG03);
val = 0x97dfdf30;
writel(val, base + PUPHY_REG18);
val = readl(base + PUPHY_REG19);
val &= ~(0x1 << 5 | 0x1 << 6);
writel(val, base + PUPHY_REG19);
/* USB PHY config
* step.1
* config phphy, sw init done, ref clk cfg=2
* open allclk en bits
*/
writel(0xB7C, base + PUPHY_REG02);
/* step.2 wait for PUPHY PLL READY */
while((readl(base + PUPHY_REG02) & 0x1) != 0x1)
{
udelay(1);
pll_wait_us--;
if (pll_wait_us == 0)
break;
}
/* step.3 override pipe_phystatus to 0 if puphy pll not ready */
if (pll_wait_us == 0) {
dwc3_force_usb2_mode();
writel((readl(base + PUPHY_REG10) | (0x1 << 10)), base + PUPHY_REG10);
}
/* settings for u1/u2/u3 */
if (!cpu_is_asr1901_z1()) {
val = readl(base + PUPHY_REG15);
val &= ~(0xF << 16);
val |= (0x3 << 16); /* */
writel(val, base + PUPHY_REG15);
val = readl(base + PUPHY_REG07);
val &= ~(0x3ff << 2);
val |= (200 << 2); /* */
writel(val, base + PUPHY_REG07);
if (cpu_is_asr1901_z2()) {
val = readl(base + PUPHY_REG2D);
val &= ~(0x7 << 22);
val |= (0X7 << 22); /* tx swing */
writel(val, base + PUPHY_REG2D);
} else if (cpu_is_asr1901_a0_plus()) {
val = readl(base + PUPHY_REG2D);
val &= ~(0x1ff << 16);
val |= (0X1C0 << 16); /* old rxeq value */
writel(val, base + PUPHY_REG2D);
} else if (cpu_is_asr1906()) {
val = readl(base + PUPHY_REG2D);
val &= ~(0x1ff << 16);
val |= (0X1f6 << 16); /* rxeq */
writel(val, base + PUPHY_REG2D);
} else {
val = readl(base + PUPHY_REG2D);
val &= ~(0x1ff << 16);
val |= (0X1f6 << 16); /* rxeq */
writel(val, base + PUPHY_REG2D);
}
}
/* Write 1s to clear phy err status */
writel(0xFFFFFFFF, base + PUPHY_REG04);
/* step.4 wait for USB2 PHY PLL READY */
pll_wait_us = 400;
while (((readl(base + USB2_PHY0_REG01) & USB2_PLL_BIT_RDY) != USB2_PLL_BIT_RDY)
&& (pll_wait_us--))
{
udelay(1);
}
/*
* Step 5&6
* Release usb2 phy internal reset and enable clock gating
*/
writel(0x60ef, base + USB2_PHY0_REG01);
writel(0x1C, base + USB2_PHY0_REG0D);
/* STEP.7
* 0x1: serial mode, 0x0: parallel mode
* USB2_PHY0_REG06 (USB2_BASE+0x18)
*
* serial mode
* writel((readl(base + USB2_PHY0_REG06) | 0x1), base + USB2_PHY0_REG06);
* parallel mode
*/
writel((readl(base + USB2_PHY0_REG06) & (~0x1)), base + USB2_PHY0_REG06);
/* Write 1s to clear phy err status */
writel(0xFFFFFFFF, base + USB2_PHY0_REG0E);
}
static int asr_usb3_phy_preinit(struct asr_usb3_phy *asr_phy)
{
unsigned int loops = 400;
void __iomem *base = asr_phy->base;
unsigned int val;
pr_info("%s\n", __func__);
/* WAIT FOR PHY PLL RDY */
loops = 400;
while (((readl(base + USB2_PHY0_REG01) & USB2_PLL_BIT_RDY) != USB2_PLL_BIT_RDY)
&& (loops--))
{
udelay(1);
}
if (loops == 0)
pr_err("!!!!!!!!!!phy pll not ready after 400us\n");
/* pulldown dp and pulldown dm */
val = readl(base + USB2_PHY0_REG25);
val &= ~PHY_USB2_DCP_DET_PULL_MASK;
val |= PHY_USB2_DCP_DET_PULL_DOWN_DOWN;
writel(val, base + USB2_PHY0_REG25);
val = readl(base + USB2_PHY0_REG29);
val |= PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN;
writel(val, base + USB2_PHY0_REG29);
/* for dwc3_get_line_status */
val = readl(base + USB2_PHY0_REG04);
val &= ~(0xff << 8);
val |= (0x12 << 8);
writel(val, base + USB2_PHY0_REG04);
return 0;
}
static int asr_usb3_phy_init(struct usb_phy *phy)
{
struct asr_usb3_phy *asr_usb3_phy;
void __iomem *base;
u32 val;
pr_info("USB3 PHY init start...\n");
/* enable usb3 phy */
asr_usb3_phy = container_of(phy, struct asr_usb3_phy, phy);
if (asr_usb3_phy->clk)
clk_prepare_enable(asr_usb3_phy->clk);
base = asr_usb3_phy->base;
if (cpu_is_asr1901() || cpu_is_asr1906()) {
/* port B */
if (usb3_phy_res_size == (USB_PHY_PORTB_OFFSET + 0x1000)) {
usb31_rterm_cal_portb((base + USB_PHY_PORTB_OFFSET), base);
usb31_phy_init(base + USB_PHY_PORTB_OFFSET);
} else { /* port A */
val = readl(base + PUPHY_REG08);
val |= 0x1;
writel(val, base + PUPHY_REG08);
/* pll_reg2 set to 0xC0 */
writel(((readl(base + PUPHY_REG16) & 0xFFFF00FF) | 0xC000), base + PUPHY_REG16);
/* pll_reg7[5] of lane0, disable select refclk_100_n/p 100Mhz input */
writel((readl(base + PUPHY_REG17) & (~(0x1<<21))), base + PUPHY_REG17);
usb31_phy_init(base);
}
} else {
usb30_phy_init(base);
}
pr_info("USB3 PHY init done...\n");
return 0;
}
void asr_usb3_phy_shutdown(struct usb_phy *phy)
{
struct asr_usb3_phy *asr_usb3_phy;
void __iomem *base;
asr_usb3_phy = container_of(phy, struct asr_usb3_phy, phy);
base = asr_usb3_phy->base;
pr_info("USB3 PHY shutdown ...\n");
writel(0x60ef, base + USB2_PHY0_REG01);
if (asr_usb3_phy->clk)
clk_disable_unprepare(asr_usb3_phy->clk);
pr_info("USB3 PHY shutdown done\n");
}
static int asr_usb3_phy_probe(struct platform_device *pdev)
{
struct asr_usb3_phy *mv_phy;
struct mv_usb_platform_data *pdata = pdev->dev.platform_data;
struct device *dev = &pdev->dev;
struct resource *res;
int ret;
if (pdata == NULL) {
dev_err(&pdev->dev, "failed to get platform data\n");
/* return -ENODEV; */
}
mv_phy = devm_kzalloc(dev, sizeof(*mv_phy), GFP_KERNEL);
if (!mv_phy)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mv_phy->base = ioremap(res->start, resource_size(res));
if (IS_ERR(mv_phy->base))
return PTR_ERR(mv_phy->base);
usb3_phy_res_size = resource_size(res);
mv_phy->pdata = pdata;
mv_phy->phy.dev = &pdev->dev;
mv_phy->phy.label = "asr-usb3-phy";
mv_phy->phy.init = asr_usb3_phy_init;
mv_phy->phy.shutdown = asr_usb3_phy_shutdown;
mv_phy->phy.get_vbus = asr_usb_get_vbus;
mv_phy->phy.set_suspend = asr_usb3_phy_suspend;
mv_phy->phy.set_suspend2 = asr_usb3_phy_suspend2;
mv_phy->phy.charger_detect = asr_usb_phy_charger_detect;
mv_phy->phy.dump_cfg = asr_usb3_phy_dumpcfg;
mv_phy->phy.phy_private2 = asr_usb_host_phy_private;
ret = usb_add_phy(&mv_phy->phy, USB_PHY_TYPE_USB3);
if (ret)
goto err;
mv_phy->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(mv_phy->clk))
return PTR_ERR(mv_phy->clk);
clk_prepare_enable(mv_phy->clk);
asr_usb3_phy_preinit(mv_phy);
platform_set_drvdata(pdev, mv_phy);
dev_info(&pdev->dev, "Initialized ASR USB 3.0 PHY\n");
err:
return ret;
}
static int asr_usb3_phy_remove(struct platform_device *pdev)
{
struct asr_usb3_phy *asr_usb3_phy = platform_get_drvdata(pdev);
usb_remove_phy(&asr_usb3_phy->phy);
platform_set_drvdata(pdev, NULL);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id asr_usb3_phy_id_table[] = {
{ .compatible = "asr,asr-usb3-phy" },
{}
};
MODULE_DEVICE_TABLE(of, asr_usb3_phy_id_table);
#endif
static struct platform_driver asr_usb3_phy_driver = {
.probe = asr_usb3_phy_probe,
.remove = asr_usb3_phy_remove,
.driver = {
.name = "asr-usb3-phy",
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = of_match_ptr(asr_usb3_phy_id_table),
#endif
},
};
static int __init asr_usb3_phydrv_init(void)
{
return platform_driver_register(&asr_usb3_phy_driver);
}
static void __exit asr_usb3_phydrv_exit(void)
{
platform_driver_unregister(&asr_usb3_phy_driver);
}
arch_initcall(asr_usb3_phydrv_init);
module_exit(asr_usb3_phydrv_exit);
MODULE_DESCRIPTION("ASR USB 3.0/3.1 PHY controller");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:asr-usb3-phy");