diff --git a/arch/arm64/boot/dts/amlogic/mesong12a.dtsi b/arch/arm64/boot/dts/amlogic/mesong12a.dtsi index 1cbce22c4b4a..cc118488ba9f 100644 --- a/arch/arm64/boot/dts/amlogic/mesong12a.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesong12a.dtsi @@ -369,7 +369,7 @@ status = "disable"; reg = <0x0 0xffe09080 0x0 0x20>; phy-reg = <0xff646000>; - phy-reg-size = <0x4>; + phy-reg-size = <0x2000>; usb2-phy-reg = <0xffe09000>; usb2-phy-reg-size = <0x80>; interrupts = <0 16 4>; diff --git a/drivers/amlogic/usb/phy/phy-aml-new-usb3-v2.c b/drivers/amlogic/usb/phy/phy-aml-new-usb3-v2.c index fd06de21eea1..8e5648d68de6 100644 --- a/drivers/amlogic/usb/phy/phy-aml-new-usb3-v2.c +++ b/drivers/amlogic/usb/phy/phy-aml-new-usb3-v2.c @@ -120,6 +120,124 @@ void aml_new_usb_v2_init(void) } EXPORT_SYMBOL(aml_new_usb_v2_init); +static void cr_bus_addr(unsigned int addr) +{ + union phy3_r4 phy_r4 = {.d32 = 0}; + union phy3_r5 phy_r5 = {.d32 = 0}; + unsigned long timeout_jiffies; + + phy_r4.b.phy_cr_data_in = addr; + writel(phy_r4.d32, g_phy_v2->phy3_cfg_r4); + + phy_r4.b.phy_cr_cap_addr = 0; + writel(phy_r4.d32, g_phy_v2->phy3_cfg_r4); + phy_r4.b.phy_cr_cap_addr = 1; + writel(phy_r4.d32, g_phy_v2->phy3_cfg_r4); + timeout_jiffies = jiffies + + msecs_to_jiffies(1000); + do { + phy_r5.d32 = readl(g_phy_v2->phy3_cfg_r5); + } while (phy_r5.b.phy_cr_ack == 0 && + time_is_after_jiffies(timeout_jiffies)); + + phy_r4.b.phy_cr_cap_addr = 0; + writel(phy_r4.d32, g_phy_v2->phy3_cfg_r4); + timeout_jiffies = jiffies + + msecs_to_jiffies(1000); + do { + phy_r5.d32 = readl(g_phy_v2->phy3_cfg_r5); + } while (phy_r5.b.phy_cr_ack == 1 && + time_is_after_jiffies(timeout_jiffies)); +} + +static int cr_bus_read(unsigned int addr) +{ + int data; + union phy3_r4 phy_r4 = {.d32 = 0}; + union phy3_r5 phy_r5 = {.d32 = 0}; + unsigned long timeout_jiffies; + + cr_bus_addr(addr); + + phy_r4.b.phy_cr_read = 0; + writel(phy_r4.d32, g_phy_v2->phy3_cfg_r4); + phy_r4.b.phy_cr_read = 1; + writel(phy_r4.d32, g_phy_v2->phy3_cfg_r4); + + timeout_jiffies = jiffies + + msecs_to_jiffies(1000); + do { + phy_r5.d32 = readl(g_phy_v2->phy3_cfg_r5); + } while (phy_r5.b.phy_cr_ack == 0 && + time_is_after_jiffies(timeout_jiffies)); + + data = phy_r5.b.phy_cr_data_out; + + phy_r4.b.phy_cr_read = 0; + writel(phy_r4.d32, g_phy_v2->phy3_cfg_r4); + timeout_jiffies = jiffies + + msecs_to_jiffies(1000); + do { + phy_r5.d32 = readl(g_phy_v2->phy3_cfg_r5); + } while (phy_r5.b.phy_cr_ack == 1 && + time_is_after_jiffies(timeout_jiffies)); + + return data; +} + +static void cr_bus_write(unsigned int addr, unsigned int data) +{ + union phy3_r4 phy_r4 = {.d32 = 0}; + union phy3_r5 phy_r5 = {.d32 = 0}; + unsigned long timeout_jiffies; + + cr_bus_addr(addr); + + phy_r4.b.phy_cr_data_in = data; + writel(phy_r4.d32, g_phy_v2->phy3_cfg_r4); + + phy_r4.b.phy_cr_cap_data = 0; + writel(phy_r4.d32, g_phy_v2->phy3_cfg_r4); + phy_r4.b.phy_cr_cap_data = 1; + writel(phy_r4.d32, g_phy_v2->phy3_cfg_r4); + timeout_jiffies = jiffies + + msecs_to_jiffies(1000); + do { + phy_r5.d32 = readl(g_phy_v2->phy3_cfg_r5); + } while (phy_r5.b.phy_cr_ack == 0 && + time_is_after_jiffies(timeout_jiffies)); + + phy_r4.b.phy_cr_cap_data = 0; + writel(phy_r4.d32, g_phy_v2->phy3_cfg_r4); + timeout_jiffies = jiffies + + msecs_to_jiffies(1000); + do { + phy_r5.d32 = readl(g_phy_v2->phy3_cfg_r5); + } while (phy_r5.b.phy_cr_ack == 1 && + time_is_after_jiffies(timeout_jiffies)); + + phy_r4.b.phy_cr_write = 0; + writel(phy_r4.d32, g_phy_v2->phy3_cfg_r4); + phy_r4.b.phy_cr_write = 1; + writel(phy_r4.d32, g_phy_v2->phy3_cfg_r4); + timeout_jiffies = jiffies + + msecs_to_jiffies(1000); + do { + phy_r5.d32 = readl(g_phy_v2->phy3_cfg_r5); + } while (phy_r5.b.phy_cr_ack == 0 && + time_is_after_jiffies(timeout_jiffies)); + + phy_r4.b.phy_cr_write = 0; + writel(phy_r4.d32, g_phy_v2->phy3_cfg_r4); + timeout_jiffies = jiffies + + msecs_to_jiffies(1000); + do { + phy_r5.d32 = readl(g_phy_v2->phy3_cfg_r5); + } while (phy_r5.b.phy_cr_ack == 1 && + time_is_after_jiffies(timeout_jiffies)); +} + + static int amlogic_new_usb3_init(struct usb_phy *x) { struct amlogic_usb_v2 *phy = phy_to_amlusb(x); @@ -127,7 +245,10 @@ static int amlogic_new_usb3_init(struct usb_phy *x) union usb_r2_v2 r2 = {.d32 = 0}; union usb_r3_v2 r3 = {.d32 = 0}; union usb_r5_v2 r5 = {.d32 = 0}; + union phy3_r2 p3_r2 = {.d32 = 0}; + union phy3_r1 p3_r1 = {.d32 = 0}; int i = 0; + u32 data = 0; if (phy->suspend_flag) { if (phy->phy.flags == AML_USB3_PHY_ENABLE) @@ -155,6 +276,7 @@ static int amlogic_new_usb3_init(struct usb_phy *x) if (phy->phy.flags == AML_USB3_PHY_ENABLE) { r3.d32 = readl(usb_new_aml_regs_v2.usb_r_v2[3]); r3.b.p30_ssc_en = 1; + r3.b.p30_ssc_range = 2; r3.b.p30_ref_ssp_en = 1; writel(r3.d32, usb_new_aml_regs_v2.usb_r_v2[3]); udelay(2); @@ -166,8 +288,74 @@ static int amlogic_new_usb3_init(struct usb_phy *x) r1.d32 = readl(usb_new_aml_regs_v2.usb_r_v2[1]); r1.b.u3h_host_port_power_control_present = 1; r1.b.u3h_fladj_30mhz_reg = 0x26; + r1.b.p30_pcs_tx_swing_full = 127; writel(r1.d32, usb_new_aml_regs_v2.usb_r_v2[1]); udelay(2); + p3_r2.d32 = readl(phy->phy3_cfg_r2); + p3_r2.b.phy_tx_vboost_lvl = 0x4; + writel(p3_r2.d32, phy->phy3_cfg_r2); + udelay(2); + /* + * 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); + + /* + * MPLL_LOOP_CTL.PROP_CNTRL + */ + data = cr_bus_read(0x30); + data &= ~(0xf << 4); + data |= (0x8 << 4); + cr_bus_write(0x30, data); + udelay(2); + + /* + * LOS_BIAS to 0x5 + * LOS_LEVEL to 0x9 + */ + p3_r1.d32 = readl(phy->phy3_cfg_r1); + p3_r1.b.phy_los_bias = 0x4; + p3_r1.b.phy_los_level = 0x9; + writel(p3_r1.d32, phy->phy3_cfg_r1); } return 0; @@ -391,6 +579,14 @@ static int amlogic_new_usb3_v2_probe(struct platform_device *pdev) phy->regs = phy_base; phy->phy3_cfg = phy3_base; phy->usb2_phy_cfg = usb2_phy_base; + phy->phy3_cfg_r1 = (void __iomem *) + ((unsigned long)phy->phy3_cfg + 4 * 1); + phy->phy3_cfg_r2 = (void __iomem *) + ((unsigned long)phy->phy3_cfg + 4 * 2); + phy->phy3_cfg_r4 = (void __iomem *) + ((unsigned long)phy->phy3_cfg + 4 * 4); + phy->phy3_cfg_r5 = (void __iomem *) + ((unsigned long)phy->phy3_cfg + 4 * 5); phy->portnum = portnum; phy->suspend_flag = 0; phy->phy.dev = phy->dev; diff --git a/include/linux/amlogic/usb-v2.h b/include/linux/amlogic/usb-v2.h index 09f833caace5..25a68cde2d14 100644 --- a/include/linux/amlogic/usb-v2.h +++ b/include/linux/amlogic/usb-v2.h @@ -164,6 +164,10 @@ struct amlogic_usb_v2 { void __iomem *reset_regs; void __iomem *phy_cfg[4]; void __iomem *phy3_cfg; + void __iomem *phy3_cfg_r1; + void __iomem *phy3_cfg_r2; + void __iomem *phy3_cfg_r4; + void __iomem *phy3_cfg_r5; void __iomem *usb2_phy_cfg; u32 pll_setting[3]; /* Set VBus Power though GPIO */ @@ -177,4 +181,62 @@ struct amlogic_usb_v2 { struct clk *clk; }; +union phy3_r1 { + /** raw register data */ + uint32_t d32; + /** register bits */ + struct { + unsigned phy_tx1_term_offset:5; + unsigned phy_tx0_term_offset:5; + unsigned phy_rx1_eq:3; + unsigned phy_rx0_eq:3; + unsigned phy_los_level:5; + unsigned phy_los_bias:3; + unsigned phy_ref_clkdiv2:1; + unsigned phy_mpll_multiplier:7; + } b; +}; + + +union phy3_r2 { + /** raw register data */ + uint32_t d32; + /** register bits */ + struct { + unsigned pcs_tx_deemph_gen2_6db:6; + unsigned pcs_tx_deemph_gen2_3p5db:6; + unsigned pcs_tx_deemph_gen1:6; + unsigned phy_tx_vboost_lvl:3; + unsigned reserved:11; + } b; +}; + + +union phy3_r4 { + /** raw register data */ + uint32_t d32; + /** register bits */ + struct { + unsigned phy_cr_write:1; + unsigned phy_cr_read:1; + unsigned phy_cr_data_in:16; + unsigned phy_cr_cap_data:1; + unsigned phy_cr_cap_addr:1; + unsigned reserved:12; + } b; +}; + +union phy3_r5 { + /** raw register data */ + uint32_t d32; + /** register bits */ + struct { + unsigned phy_cr_data_out:16; + unsigned phy_cr_ack:1; + unsigned phy_bs_out:1; + unsigned reserved:14; + } b; +}; + + #endif