/*
*
* phy-hi3516cv500-usb.c
*
* usb phy driver special for hi3516cv500
*
* Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED.
*
* 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, see .
*
*/
#include
#include
#include
#include
#include "phy-hisi-usb.h"
/* offset 0x140 */
#define USB2_UTMI_PCTRL (0x1 << 15)
#define USB2_PHY_TEST_SRST_REQ (0x1 << 14)
#define USB2_UTMI_CKSEL (0x1 << 13)
#define USB2_UTMI_CKEN (0x1 << 12)
#define USB2_REF_CKEN (0x1 << 9)
#define USB2_BUS_CKEN (0x1 << 8)
#define USB2_VCC_SRST_REQ (0x1 << 3)
#define USB2_PHY_CKEN (0x1 << 2)
#define USB2_PHY_PORT_TREQ (0x1 << 1)
#define USB2_PHY_REQ (0x1 << 0)
#define REG_GUSB3PIPECTL0 0xc2c0
#define PCS_SSP_SOFT_RESET (0x1 << 31)
#define PORT_DISABLE_SUSPEND (0x1 << 17)
#define REG_GCTL 0xc110
#define PORT_CAP_DIR (0x3 << 12)
#define PORT_SET_HOST (0x1 << 12)
#define GTXTHRCFG 0xc108
#define USB2_G_TXTHRCFG 0x23100000
#define GRXTHRCFG 0xc10c
#define USB2_G_RXTHRCFG 0x23100000
#define USB2_INNO_PHY_BASE_REG 0x10110000
#define USB2_PHY_CLK_OUTPUT_REG 0x18
#define USB2_PHY_CLK_OUTPUT_VAL 0x0c
#define USB2_VBUS_IO_BASE_REG 0x10ff0000
#define USB2_VBUS_IO_OFFSET 0x40
#define USB2_VBUS_IO_VAL 0x431
#define HS_HIGH_HEIGHT_TUNING_OFFSET 0x8
#define HS_HIGH_HEIGHT_TUNING_MASK (0x7 << 4)
#define HS_HIGH_HEIGHT_TUNING_VAL (0x5 << 4)
#define PRE_EMPHASIS_TUNING_OFFSET 0x0
#define PRE_EMPHASIS_TUNING_MASK (0x7 << 0)
#define PRE_EMPHASIS_TUNING_VAL (0x7 << 0)
#define PRE_EMPHASIS_STRENGTH_OFFSET 0x14
#define PRE_EMPHASIS_STRENGTH_MASK (0x7 << 2)
#define PRE_EMPHASIS_STRENGTH_VAL (0x3 << 2)
#define HS_SLEW_RATE_TUNING_OFFSET 0x74
#define HS_SLEW_RATE_TUNING_MASK (0x7 << 1)
#define HS_SLEW_RATE_TUNING_VAL (0x7 << 1)
#define DISCONNECT_TRIGGER_OFFSET 0x10
#define DISCONNECT_TRIGGER_MASK (0xf << 4)
#define DISCONNECT_TRIGGER_VAL (0xd << 4)
static uintptr_t xhci_base = 0;
int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor)
{
if ((hccr == NULL) || (hcor == NULL))
return -EINVAL;
xhci_base = USB3_CTRL_REG_BASE;
*hccr = (struct xhci_hccr *)(xhci_base);
*hcor = (struct xhci_hcor *)((uintptr_t) *hccr +
HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
return 0;
}
void usb2_eye_config(void)
{
unsigned int reg;
/* HS eye height tuning */
reg = readl(USB2_INNO_PHY_BASE_REG + HS_HIGH_HEIGHT_TUNING_OFFSET);
reg &= ~HS_HIGH_HEIGHT_TUNING_MASK;
reg |= HS_HIGH_HEIGHT_TUNING_VAL;
writel(reg, USB2_INNO_PHY_BASE_REG + HS_HIGH_HEIGHT_TUNING_OFFSET);
/* Pre-emphasis tuning */
reg = readl(USB2_INNO_PHY_BASE_REG + PRE_EMPHASIS_TUNING_OFFSET);
reg &= ~PRE_EMPHASIS_TUNING_MASK;
reg |= PRE_EMPHASIS_TUNING_VAL;
writel(reg, USB2_INNO_PHY_BASE_REG + PRE_EMPHASIS_TUNING_OFFSET);
/* Pre-emphasis strength */
reg = readl(USB2_INNO_PHY_BASE_REG + PRE_EMPHASIS_STRENGTH_OFFSET);
reg &= ~PRE_EMPHASIS_STRENGTH_MASK;
reg |= PRE_EMPHASIS_STRENGTH_VAL;
writel(reg, USB2_INNO_PHY_BASE_REG + PRE_EMPHASIS_STRENGTH_OFFSET);
/* HS driver slew rate tunning */
reg = readl(USB2_INNO_PHY_BASE_REG + HS_SLEW_RATE_TUNING_OFFSET);
reg &= ~HS_SLEW_RATE_TUNING_MASK;
reg |= HS_SLEW_RATE_TUNING_VAL;
writel(reg, USB2_INNO_PHY_BASE_REG + HS_SLEW_RATE_TUNING_OFFSET);
/* HOST disconnects detection trigger point */
reg = readl(USB2_INNO_PHY_BASE_REG + DISCONNECT_TRIGGER_OFFSET);
reg &= ~DISCONNECT_TRIGGER_MASK;
reg |= DISCONNECT_TRIGGER_VAL;
writel(reg, USB2_INNO_PHY_BASE_REG + DISCONNECT_TRIGGER_OFFSET);
}
void phy_hiusb_init_clk(int index)
{
unsigned int reg;
/* set inno phy output clock */
writel(USB2_PHY_CLK_OUTPUT_VAL, USB2_INNO_PHY_BASE_REG +
USB2_PHY_CLK_OUTPUT_REG);
udelay(U_LEVEL1);
/* open phy ref cken */
reg = readl(CRG_REG_BASE + REG_CRG80);
reg |= USB2_PHY_CKEN;
writel(reg, CRG_REG_BASE + REG_CRG80);
udelay(U_LEVEL1);
/* open utmi pctrl */
reg = readl(CRG_REG_BASE + REG_CRG80);
reg &= ~USB2_UTMI_PCTRL;
writel(reg, CRG_REG_BASE + REG_CRG80);
udelay(U_LEVEL1);
/* open utmi cksel */
reg = readl(CRG_REG_BASE + REG_CRG80);
reg &= ~USB2_UTMI_CKSEL;
writel(reg, CRG_REG_BASE + REG_CRG80);
udelay(U_LEVEL1);
/* open utmi cken */
reg = readl(CRG_REG_BASE + REG_CRG80);
reg |= USB2_UTMI_CKEN;
writel(reg, CRG_REG_BASE + REG_CRG80);
udelay(U_LEVEL1);
/* open controller ref cken */
reg = readl(CRG_REG_BASE + REG_CRG80);
reg |= USB2_REF_CKEN;
writel(reg, CRG_REG_BASE + REG_CRG80);
udelay(U_LEVEL1);
/* open bus cken */
reg = readl(CRG_REG_BASE + REG_CRG80);
reg |= USB2_BUS_CKEN;
writel(reg, CRG_REG_BASE + REG_CRG80);
udelay(U_LEVEL6);
/* cancel POR reset */
reg = readl(CRG_REG_BASE + REG_CRG80);
reg &= ~USB2_PHY_REQ;
writel(reg, CRG_REG_BASE + REG_CRG80);
udelay(U_LEVEL6);
/* cancel TPOR reset */
reg = readl(CRG_REG_BASE + REG_CRG80);
reg &= ~USB2_PHY_PORT_TREQ;
writel(reg, CRG_REG_BASE + REG_CRG80);
udelay(U_LEVEL6);
}
void phy_hiusb_init(int index)
{
unsigned int reg;
/* usb phy reset */
reg = readl(CRG_REG_BASE + REG_CRG80);
reg |= USB2_PHY_TEST_SRST_REQ;
writel(reg, CRG_REG_BASE + REG_CRG80);
udelay(U_LEVEL5);
/* cancel usb phy srst */
reg = readl(CRG_REG_BASE + REG_CRG80);
reg &= ~USB2_PHY_TEST_SRST_REQ;
writel(reg, CRG_REG_BASE + REG_CRG80);
udelay(U_LEVEL2);
/* usb2 vcc reset */
reg = readl(CRG_REG_BASE + REG_CRG80);
reg |= USB2_VCC_SRST_REQ;
writel(reg, CRG_REG_BASE + REG_CRG80);
udelay(U_LEVEL6);
phy_hiusb_init_clk(index);
/* cancel vcc reset */
reg = readl(CRG_REG_BASE + REG_CRG80);
reg &= ~USB2_VCC_SRST_REQ;
writel(reg, CRG_REG_BASE + REG_CRG80);
udelay(U_LEVEL6);
/* usb2 test vbus using gpio */
writel(USB2_VBUS_IO_VAL, USB2_VBUS_IO_BASE_REG + USB2_VBUS_IO_OFFSET);
udelay(U_LEVEL2);
/* USB2 Controller configs */
reg = readl(USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
reg |= PCS_SSP_SOFT_RESET;
writel(reg, USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
udelay(U_LEVEL2);
reg = readl(USB3_CTRL_REG_BASE + REG_GCTL);
reg &= ~PORT_CAP_DIR;
reg |= PORT_SET_HOST; /* [13:12] 01: Host; 10: Device; 11: OTG */
writel(reg, USB3_CTRL_REG_BASE + REG_GCTL);
udelay(U_LEVEL2);
reg = readl(USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
reg &= ~PCS_SSP_SOFT_RESET;
reg &= ~PORT_DISABLE_SUSPEND; /* disable suspend */
writel(reg, USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
udelay(U_LEVEL2);
writel(USB2_G_TXTHRCFG, USB3_CTRL_REG_BASE + GTXTHRCFG);
writel(USB2_G_RXTHRCFG, USB3_CTRL_REG_BASE + GRXTHRCFG);
udelay(U_LEVEL2);
/* USB2 eye config */
usb2_eye_config();
}
EXPORT_SYMBOL(phy_hiusb_init);
void xhci_hcd_stop(int index)
{
unsigned int reg;
/* usb2 vcc reset */
reg = readl(CRG_REG_BASE + REG_CRG80);
reg |= USB2_VCC_SRST_REQ;
writel(reg, CRG_REG_BASE + REG_CRG80);
udelay(U_LEVEL6);
}
EXPORT_SYMBOL(xhci_hcd_stop);