blob: 322f54fda004455ce21f5d43a33696a6de738823 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0+
/*
* Meson GXL USB3 PHY driver
*
* Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
* Copyright (C) 2018 BayLibre, SAS
* Author: Neil Armstrong <narmstron@baylibre.com>
*/
#include <common.h>
#include <asm/io.h>
#include <bitfield.h>
#include <dm.h>
#include <errno.h>
#include <generic-phy.h>
#include <regmap.h>
#include <clk.h>
#include <asm/arch/usb.h>
#include <linux/libfdt.h>
//#include <asm/gpio.h>
#include <asm-generic/gpio.h>
#include <linux/compat.h>
#include <linux/bitfield.h>
struct phy_aml_usb3_priv {
unsigned int base_addr;
unsigned int usb3_port_num;
};
#if 0
static int
phy_aml_usb3_set_host_mode(struct phy_aml_usb3_priv *priv)
{
return 0;
}
#endif /* 0 */
static int phy_aml_usb3_power_on(struct phy *phy){
int ret;
struct gpio_desc desc;
const char *usb_vbus_gpioname;
const void *fdt = gd->fdt_blob;
usb_vbus_gpioname = fdt_getprop(fdt, dev_of_offset(phy->dev), "gpio-vbus-power", NULL);
debug("usb_vbus_gpioname=%s\n", usb_vbus_gpioname);
ret = dm_gpio_lookup_name(usb_vbus_gpioname, &desc);
if (ret) {
printf("%s: not found\n", usb_vbus_gpioname);
return ret;
}
ret = dm_gpio_request(&desc, usb_vbus_gpioname);
if (ret) {
dm_gpio_free(phy->dev, &desc);
dm_gpio_request(&desc, usb_vbus_gpioname);
pr_info("%s: after usb stop, retry request gpio\n",
usb_vbus_gpioname);
}
ret = dm_gpio_set_dir_flags(&desc, GPIOD_IS_OUT);
if (ret) {
pr_err("%s: failed to set direction\n", usb_vbus_gpioname);
return ret;
}
dm_gpio_set_value(&desc, 1);
return 0;
}
static int phy_aml_usb3_power_off(struct phy *phy)
{
return 0;
}
static int phy_aml_usb3_init(struct phy *phy)
{
usb_r1_t r1 = {.d32 = 0};
usb_r2_t r2 = {.d32 = 0};
usb_r3_t r3 = {.d32 = 0};
int i;
unsigned int u3portnum;
struct phy_aml_usb3_priv *priv = dev_get_priv(phy->dev);
struct usb_aml_regs *usb_aml_reg;
#if 0
u32 data = 0;
#endif
dev_read_u32(phy->dev, "portnum", &u3portnum);
priv->usb3_port_num = u3portnum;
priv->base_addr = dev_read_addr(phy->dev);
if (priv->base_addr == FDT_ADDR_T_NONE) {
pr_err("Coun't get usb3 base addr\n");
return -1;
}
debug("usb3_phy: portnum=%d, base addr = 0x%08x\n",
u3portnum, priv->base_addr);
if (u3portnum == 0) {
usb_aml_reg = (struct usb_aml_regs *)((ulong)priv->base_addr);
r1.d32 = usb_aml_reg->usb_r1;
r1.b.u3h_fladj_30mhz_reg = 0x20;
usb_aml_reg->usb_r1 = r1.d32;
udelay(100);
}
for (i = 0; i < u3portnum; i++) {
usb_aml_reg = (struct usb_aml_regs *)((ulong)priv->base_addr+i*PHY_REGISTER_SIZE);
r3.d32 = usb_aml_reg->usb_r3;
r3.b.p30_ssc_en = 1;
r3.b.p30_ref_ssp_en = 1;
usb_aml_reg->usb_r3 = r3.d32;
udelay(2);
r2.d32 = usb_aml_reg->usb_r2;
r2.b.p30_pcs_tx_deemph_3p5db = 0x15;
r2.b.p30_pcs_tx_deemph_6db = 0x20;
usb_aml_reg->usb_r2 = r2.d32;
udelay(2);
r1.d32 = usb_aml_reg->usb_r1;
r1.b.u3h_host_port_power_control_present = 1;
r1.b.u3h_fladj_30mhz_reg = 32;
usb_aml_reg->usb_r1 = r1.d32;
udelay(2);
#if 0
/*
* WORKAROUND: There is SSPHY suspend bug due to which USB enumerates
* in HS mode instead of SS mode. Workaround it by asserting
* LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus mode
*/
data = cr_bus_read(0x102d);
data |= (1 << 7);
cr_bus_write(0x102D, data);
data = cr_bus_read(0x1010);
data &= ~0xff0;
data |= 0x20;
cr_bus_write(0x1010, data);
/*
* Fix RX Equalization setting as follows
* LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0
* LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1
* LANE0.RX_OVRD_IN_HI.RX_EQ set to 3
* LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1
*/
data = cr_bus_read(0x1006);
data &= ~(1 << 6);
data |= (1 << 7);
data &= ~(0x7 << 8);
data |= (0x3 << 8);
data |= (0x1 << 11);
cr_bus_write(0x1006, data);
/*
* Set EQ and TX launch amplitudes as follows
* LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22
* LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127
* LANE0.TX_OVRD_DRV_LO.EN set to 1.
*/
data = cr_bus_read(0x1002);
data &= ~0x3f80;
data |= (0x16 << 7);
data &= ~0x7f;
data |= (0x7f | (1 << 14));
cr_bus_write(0x1002, data);
/*
* TX_FULL_SWING to 127
*/
data = cr_bus_read(0x30);
data &= ~(0xf << 4);
cr_bus_write(0x30, data);
/*
* TX_FULL_SWING to 127
*/
r1.d32 = usb_aml_reg->usb_r1;
r1.b.p30_pcs_tx_swing_full = 127;
r1.b.u3h_fladj_30mhz_reg = 0x20;
usb_aml_reg->usb_r1 = r1.d32;
udelay(2);
/*
* TX_DEEMPH_3_5DB to 22
*/
r2.d32 = usb_aml_reg->usb_r2;
r2.b.p30_pcs_tx_deemph_3p5db = 22;
usb_aml_reg->usb_r2 = r2.d32;
udelay(2);
/*
* LOS_BIAS to 0x5
* LOS_LEVEL to 0x9
*/
r3.d32 = usb_aml_reg->usb_r3;
r3.b.p30_los_bias = 0x5;
r3.b.p30_los_level = 0x9;
r3.b.p30_ssc_en = 1;
r3.b.p30_ssc_range = 2;
usb_aml_reg->usb_r3 = r3.d32;
#endif
}
return 0;
}
struct phy_ops phy_aml_usb3_phy_ops = {
.init = phy_aml_usb3_init,
.power_on = phy_aml_usb3_power_on,
.power_off = phy_aml_usb3_power_off,
};
int phy_aml_usb3_probe(struct udevice *dev)
{
return 0;
}
static const struct udevice_id phy_aml_usb3_phy_ids[] = {
{ .compatible = "amlogic, amlogic-new-usb3-v2" },
{ }
};
U_BOOT_DRIVER(meson_gxl_usb3_phy) = {
.name = "amlogic_new_usb3_phy",
.id = UCLASS_PHY,
.of_match = phy_aml_usb3_phy_ids,
.probe = phy_aml_usb3_probe,
.ops = &phy_aml_usb3_phy_ops,
.priv_auto_alloc_size = sizeof(struct phy_aml_usb3_priv),
};