| // 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"); |