| /* |
| * Copyright (C) 2017 MediaTek Inc. |
| * |
| * This program is free software: you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * 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. |
| */ |
| |
| |
| #include <linux/clk.h> |
| #include <linux/io.h> |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/init.h> |
| #include <linux/platform_device.h> |
| #include <linux/io.h> |
| #include <linux/of.h> |
| #include <linux/of_irq.h> |
| #include <linux/of_address.h> |
| #include <linux/slab.h> |
| #include <linux/phy/phy.h> |
| #include <dt-bindings/phy/phy.h> |
| #include <linux/delay.h> |
| |
| #include "phy-mtk.h" |
| |
| #ifdef CONFIG_MTK_USB2JTAG_SUPPORT |
| #include <mt-plat/mtk_usb2jtag.h> |
| #endif |
| |
| #define MTK_USB_PHY_BASE (phy_drv->phy_base) |
| #define MTK_USB_PHY_PORT_BASE (instance->port_base) |
| #define MTK_USB_PHY_MISC_BASE (instance->sif_misc) |
| #define MTK_USB_PHY_FMREG_BASE (instance->sif_fmreg) |
| #define MTK_USB_PHY_SPLLC_BASE (instance->sif_spllc) |
| #define MTK_USB_PHY_U2_BASE (instance->sif_u2phy_com) |
| #define MTK_USB_PHY_CHIP_BASE (instance->sif_chip) |
| #define MTK_USB_PHY_U3PHYD_BASE (instance->sif_u3phyd) |
| #define MTK_USB_PHY_B2_BASE (instance->sif_u3phyd_bank2) |
| #define MTK_USB_PHY_PHYA_BASE (instance->sif_u3phya) |
| #define MTK_USB_PHY_PHYA_DA_BASE (instance->sif_u3phya_da) |
| |
| #include "phy-mtk-ssusb-reg.h" |
| |
| static DEFINE_MUTEX(prepare_lock); |
| |
| enum mt_phy_version { |
| MT_PHY_V1 = 1, |
| MT_PHY_V2, |
| }; |
| |
| static bool usb_enable_clock(struct mtk_phy_drv *u3phy, bool enable) |
| { |
| static int count; |
| |
| if (!u3phy->clk) |
| return false; |
| |
| mutex_lock(&prepare_lock); |
| phy_printk(K_INFO, "CG, enable<%d>, count<%d>\n", enable, count); |
| |
| if (enable && count == 0) { |
| if (clk_prepare_enable(u3phy->clk) != 0) |
| phy_printk(K_ERR, "clk enable fail\n"); |
| } else if (!enable && count == 1) { |
| clk_disable_unprepare(u3phy->clk); |
| } |
| |
| if (enable) |
| count++; |
| else |
| count = (count == 0) ? 0 : (count - 1); |
| |
| mutex_unlock(&prepare_lock); |
| return true; |
| } |
| |
| static void u3phywrite32(void __iomem *addr, int offset, int mask, int value) |
| { |
| int cur_value; |
| int new_value; |
| |
| cur_value = readl(addr); |
| new_value = (cur_value & (~mask)) | ((value << offset) & mask); |
| writel(new_value, addr); |
| } |
| |
| static int u3phyread32(void __iomem *addr) |
| { |
| return readl(addr); |
| } |
| |
| static void phy_advance_settings(struct mtk_phy_instance *instance) |
| { |
| /* special request from DE */ |
| u32 val, offset; |
| |
| /* RG_SSUSB_TX_EIDLE_CM, 0x11F40B20[31:28] */ |
| /* 4'b1000, ssusb_USB30_PHYA_regmap_T12FF_TPHY */ |
| val = 0x8; |
| offset = 0x20; |
| u3phywrite32( |
| (MTK_USB_PHY_PHYA_BASE + offset), 28, |
| (0xf<<28), val); |
| |
| /* rg_ssusb_cdr_bir_ltr, 0x11F4095C[20:16] 5'b01101 */ |
| /* ssusb_USB30_PHYD_regmap_T12FF_TPHY */ |
| val = 0xd; |
| offset = 0x5c; |
| u3phywrite32( |
| (MTK_USB_PHY_U3PHYD_BASE + offset), 16, |
| (0x1f<<16), val); |
| |
| /* HQA request */ |
| u3phywrite32( |
| U3D_USBPHYACR2, 11, (0x3<<11), 0x3); |
| |
| u3phywrite32(U3D_USBPHYACR6, RG_USB20_SQTH_OFST, |
| RG_USB20_SQTH, 0x2); |
| |
| u3phywrite32(U3D_USBPHYACR6, (28), |
| (0x1<<28), 0x1); |
| |
| u3phywrite32(U3D_PHYD_EQ_EYE3, (24), |
| (0x7<<24), 0x1); |
| } |
| |
| static void phy_efuse_settings(struct mtk_phy_instance *instance) |
| { |
| u32 evalue; |
| |
| evalue = (get_devinfo_with_index(108) & (0x1f<<0)) >> 0; |
| if (evalue) { |
| phy_printk(K_INFO, "RG_USB20_INTR_CAL=0x%x\n", |
| evalue); |
| u3phywrite32(U3D_USBPHYACR1, |
| RG_USB20_INTR_CAL_OFST, |
| RG_USB20_INTR_CAL, evalue); |
| } |
| evalue = (get_devinfo_with_index(107) & (0x3f << 16)) >> 16; |
| if (evalue) { |
| phy_printk(K_INFO, "RG_SSUSB_IEXT_INTR_CTRL=0x%x\n", |
| evalue); |
| u3phywrite32(U3D_USB30_PHYA_REG0, |
| RG_SSUSB_IEXT_INTR_CTRL_OFST, |
| RG_SSUSB_IEXT_INTR_CTRL, evalue); |
| } |
| evalue = (get_devinfo_with_index(107) & (0x1f << 8)) >> 8; |
| if (evalue) { |
| phy_printk(K_INFO, "rg_ssusb_rx_impsel=0x%x\n", |
| evalue); |
| u3phywrite32(U3D_PHYD_IMPCAL1, |
| RG_SSUSB_RX_IMPSEL_OFST, |
| RG_SSUSB_RX_IMPSEL, evalue); |
| } |
| evalue = (get_devinfo_with_index(107) & (0x1f << 0)) >> 0; |
| if (evalue) { |
| phy_printk(K_INFO, "g_ssusb_tx_impsel=0x%x (R50)\n", |
| evalue); |
| |
| if (evalue < 6) |
| evalue += 2; |
| else if (evalue >= 6 && evalue < 15) |
| evalue += 3; |
| else |
| evalue = 15; |
| phy_printk(K_INFO, "g_ssusb_tx_impsel=0x%x (R45)\n", |
| evalue); |
| u3phywrite32(U3D_PHYD_IMPCAL0, |
| RG_SSUSB_TX_IMPSEL_OFST, |
| RG_SSUSB_TX_IMPSEL, evalue); |
| } |
| } |
| |
| static int phy_slew_rate_calibration(struct mtk_phy_instance *instance) |
| { |
| int i = 0; |
| int fgRet = 0; |
| int u4FmOut = 0; |
| int u4Tmp = 0; |
| |
| phy_printk(K_DEBUG, "%s\n", __func__); |
| |
| /* enable USB ring oscillator */ |
| u3phywrite32(U3D_USBPHYACR5, RG_USB20_HSTX_SRCAL_EN_OFST, |
| RG_USB20_HSTX_SRCAL_EN, 1); |
| |
| /* wait 1us */ |
| udelay(1); |
| |
| /* Enable free run clock */ |
| u3phywrite32(RG_SSUSB_SIFSLV_FMMONR1, RG_FRCK_EN_OFST, |
| RG_FRCK_EN, 0x1); |
| /* Setting cyclecnt */ |
| u3phywrite32(RG_SSUSB_SIFSLV_FMCR0, RG_CYCLECNT_OFST, |
| RG_CYCLECNT, 0x400); |
| /* Enable frequency meter */ |
| u3phywrite32(RG_SSUSB_SIFSLV_FMCR0, RG_FREQDET_EN_OFST, |
| RG_FREQDET_EN, 0x1); |
| phy_printk(K_DEBUG, "Freq_Valid=(0x%08X)\n", |
| u3phyread32(RG_SSUSB_SIFSLV_FMMONR1)); |
| |
| mdelay(1); |
| |
| /* wait for FM detection done, set 10ms timeout */ |
| for (i = 0; i < 10; i++) { |
| u4FmOut = u3phyread32(RG_SSUSB_SIFSLV_FMMONR0); |
| phy_printk(K_DEBUG, "FM_OUT u4FmOut = %d(0x%08X)\n", |
| u4FmOut, u4FmOut); |
| if (u4FmOut != 0) { |
| fgRet = 0; |
| phy_printk(K_DEBUG, "FM detect loop = %d\n", i); |
| break; |
| } |
| fgRet = 1; |
| mdelay(1); |
| } |
| u3phywrite32(RG_SSUSB_SIFSLV_FMCR0, RG_FREQDET_EN_OFST, |
| RG_FREQDET_EN, 0); |
| u3phywrite32(RG_SSUSB_SIFSLV_FMMONR1, RG_FRCK_EN_OFST, |
| RG_FRCK_EN, 0); |
| |
| if (u4FmOut == 0) { |
| u3phywrite32(U3D_USBPHYACR5, RG_USB20_HSTX_SRCTRL_OFST, |
| RG_USB20_HSTX_SRCTRL, 0x4); |
| fgRet = 1; |
| } else { |
| u4Tmp = (1024 * U3D_PHY_REF_CK * U2_SR_COEF); |
| u4Tmp = u4Tmp / (u4FmOut * 1000); |
| phy_printk(K_DEBUG, "SR cali u1SrCalVal = %d\n", u4Tmp); |
| u3phywrite32(U3D_USBPHYACR5, RG_USB20_HSTX_SRCTRL_OFST, |
| RG_USB20_HSTX_SRCTRL, u4Tmp); |
| } |
| |
| u3phywrite32(U3D_USBPHYACR5, RG_USB20_HSTX_SRCAL_EN_OFST, |
| RG_USB20_HSTX_SRCAL_EN, 0); |
| |
| return fgRet; |
| } |
| |
| #ifdef CONFIG_MTK_UART_USB_SWITCH |
| static bool bFirstUartCheck = true; |
| static bool phy_check_in_uart_mode(struct mtk_phy_instance *instance); |
| #endif |
| |
| static int phy_init_soc(struct mtk_phy_instance *instance) |
| { |
| struct mtk_phy_drv *phy_drv = instance->phy_drv; |
| |
| phy_printk(K_DEBUG, "%s\n", __func__); |
| usb_enable_clock(phy_drv, true); |
| udelay(250); |
| u3phywrite32(U3D_USB30_PHYA_REG1, RG_SSUSB_VUSB10_ON_OFST, |
| RG_SSUSB_VUSB10_ON, 1); |
| |
| #ifdef CONFIG_MTK_UART_USB_SWITCH |
| if ((bFirstUartCheck && phy_check_in_uart_mode(instance)) || |
| instance->uart_mode) { |
| phy_printk(K_ALET, "%s+ already in UART_MODE\n", __func__); |
| bFirstUartCheck = false; |
| instance->uart_mode = 1; |
| goto reg_done; |
| } else |
| bFirstUartCheck = false; |
| #endif |
| |
| #ifdef CONFIG_MTK_USB2JTAG_SUPPORT |
| if (usb2jtag_mode()) { |
| phy_printk(K_INFO, "%s, bypass in usb2jtag mode\n", __func__); |
| return 0; |
| } |
| #endif |
| |
| /*switch to USB function. (system register, force ip into usb mode) */ |
| u3phywrite32(U3D_U2PHYDTM0, FORCE_UART_EN_OFST, |
| FORCE_UART_EN, 0); |
| u3phywrite32(U3D_U2PHYDTM1, RG_UART_EN_OFST, |
| RG_UART_EN, 0); |
| u3phywrite32(U3D_U2PHYACR4, RG_USB20_GPIO_CTL_OFST, |
| RG_USB20_GPIO_CTL, 0); |
| u3phywrite32(U3D_U2PHYACR4, USB20_GPIO_MODE_OFST, |
| USB20_GPIO_MODE, 0); |
| u3phywrite32(U3D_U2PHYDTM0, RG_UART_MODE_OFST, |
| RG_UART_MODE, 0); |
| u3phywrite32(U3D_USBPHYACR6, RG_USB20_BC11_SW_EN_OFST, |
| RG_USB20_BC11_SW_EN, 0); |
| u3phywrite32(U3D_U2PHYACR4, RG_USB20_DP_100K_MODE_OFST, |
| RG_USB20_DP_100K_MODE, 1); |
| u3phywrite32(U3D_U2PHYACR4, USB20_DP_100K_EN_OFST, |
| USB20_DP_100K_EN, 0); |
| u3phywrite32(U3D_U2PHYACR4, RG_USB20_DM_100K_EN_OFST, |
| RG_USB20_DM_100K_EN, 0); |
| u3phywrite32(U3D_USBPHYACR6, RG_USB20_OTG_VBUSCMP_EN_OFST, |
| RG_USB20_OTG_VBUSCMP_EN, 0); |
| u3phywrite32(U3D_U2PHYDTM0, FORCE_SUSPENDM_OFST, |
| FORCE_SUSPENDM, 0); |
| |
| udelay(800); |
| |
| u3phywrite32(U3D_U2PHYDTM1, FORCE_VBUSVALID_OFST, |
| FORCE_VBUSVALID, 1); |
| u3phywrite32(U3D_U2PHYDTM1, FORCE_AVALID_OFST, |
| FORCE_AVALID, 1); |
| u3phywrite32(U3D_U2PHYDTM1, FORCE_SESSEND_OFST, |
| FORCE_SESSEND, 1); |
| |
| #ifdef CONFIG_MTK_UART_USB_SWITCH |
| reg_done: |
| #endif |
| usb_enable_clock(phy_drv, false); |
| |
| return 0; |
| } |
| |
| |
| static void phy_savecurrent(struct mtk_phy_instance *instance) |
| { |
| struct mtk_phy_drv *phy_drv = instance->phy_drv; |
| |
| phy_printk(K_ALET, "%s\n", __func__); |
| if (instance->sib_mode) { |
| phy_printk(K_INFO, "%s sib_mode can't savecurrent\n", __func__); |
| return; |
| } |
| |
| #ifdef CONFIG_MTK_UART_USB_SWITCH |
| if (instance->uart_mode) |
| goto reg_done; |
| #endif |
| |
| #ifdef CONFIG_MTK_USB2JTAG_SUPPORT |
| if (usb2jtag_mode()) { |
| phy_printk(K_INFO, "%s, bypass in usb2jtag mode\n", __func__); |
| return; |
| } |
| #endif |
| |
| u3phywrite32(U3D_U2PHYDTM0, FORCE_UART_EN_OFST, |
| FORCE_UART_EN, 0); |
| u3phywrite32(U3D_U2PHYDTM1, RG_UART_EN_OFST, |
| RG_UART_EN, 0); |
| u3phywrite32(U3D_U2PHYACR4, RG_USB20_GPIO_CTL_OFST, |
| RG_USB20_GPIO_CTL, 0); |
| u3phywrite32(U3D_U2PHYACR4, USB20_GPIO_MODE_OFST, |
| USB20_GPIO_MODE, 0); |
| u3phywrite32(U3D_USBPHYACR6, RG_USB20_BC11_SW_EN_OFST, |
| RG_USB20_BC11_SW_EN, 0); |
| /*u3phywrite32(U3D_USBPHYACR6, |
| *RG_USB20_OTG_VBUSCMP_EN_OFST, |
| *RG_USB20_OTG_VBUSCMP_EN, 0); |
| */ |
| u3phywrite32(U3D_U2PHYDTM0, RG_SUSPENDM_OFST, |
| RG_SUSPENDM, 1); |
| u3phywrite32(U3D_U2PHYDTM0, FORCE_SUSPENDM_OFST, |
| FORCE_SUSPENDM, 1); |
| udelay(2000); |
| u3phywrite32(U3D_U2PHYDTM0, RG_DPPULLDOWN_OFST, |
| RG_DPPULLDOWN, 1); |
| u3phywrite32(U3D_U2PHYDTM0, RG_DMPULLDOWN_OFST, |
| RG_DMPULLDOWN, 1); |
| u3phywrite32(U3D_U2PHYDTM0, RG_XCVRSEL_OFST, |
| RG_XCVRSEL, 0x1); |
| u3phywrite32(U3D_U2PHYDTM0, RG_TERMSEL_OFST, |
| RG_TERMSEL, 1); |
| u3phywrite32(U3D_U2PHYDTM0, RG_DATAIN_OFST, |
| RG_DATAIN, 0); |
| u3phywrite32(U3D_U2PHYDTM0, FORCE_DP_PULLDOWN_OFST, |
| FORCE_DP_PULLDOWN, 1); |
| u3phywrite32(U3D_U2PHYDTM0, FORCE_DM_PULLDOWN_OFST, |
| FORCE_DM_PULLDOWN, 1); |
| u3phywrite32(U3D_U2PHYDTM0, FORCE_XCVRSEL_OFST, |
| FORCE_XCVRSEL, 1); |
| u3phywrite32(U3D_U2PHYDTM0, FORCE_TERMSEL_OFST, |
| FORCE_TERMSEL, 1); |
| u3phywrite32(U3D_U2PHYDTM0, FORCE_DATAIN_OFST, |
| FORCE_DATAIN, 1); |
| udelay(800); |
| |
| u3phywrite32(U3D_U2PHYDTM0, RG_SUSPENDM_OFST, |
| RG_SUSPENDM, 0); |
| |
| udelay(1); |
| |
| u3phywrite32(U3D_U2PHYDTM1, RG_VBUSVALID_OFST, |
| RG_VBUSVALID, 0); |
| u3phywrite32(U3D_U2PHYDTM1, RG_AVALID_OFST, |
| RG_AVALID, 0); |
| u3phywrite32(U3D_U2PHYDTM1, RG_SESSEND_OFST, |
| RG_SESSEND, 1); |
| |
| #ifdef CONFIG_MTK_UART_USB_SWITCH |
| reg_done: |
| #endif |
| u3phywrite32(U3D_USB30_PHYA_REG1, RG_SSUSB_VUSB10_ON_OFST, |
| RG_SSUSB_VUSB10_ON, 1); |
| |
| udelay(10); |
| usb_enable_clock(phy_drv, false); |
| } |
| |
| #define VAL_MAX_WIDTH_2 0x3 |
| #define VAL_MAX_WIDTH_3 0x7 |
| |
| extern unsigned int usb_mode; |
| static void usb_phy_tuning(struct mtk_phy_instance *instance) |
| { |
| s32 u2_vrt_ref, u2_term_ref, u2_enhance; |
| s32 host_u2_vrt_ref, host_u2_term_ref, host_u2_enhance; |
| struct device_node *of_node; |
| |
| if (!instance->phy_tuning.inited) { |
| instance->phy_tuning.u2_vrt_ref = 6; |
| instance->phy_tuning.u2_term_ref = 6; |
| instance->phy_tuning.u2_enhance = 1; |
| |
| instance->phy_tuning.host_u2_vrt_ref = 6; |
| instance->phy_tuning.host_u2_term_ref = 6; |
| instance->phy_tuning.host_u2_enhance = 1; |
| |
| of_node = of_find_compatible_node(NULL, NULL, |
| instance->phycfg->tuning_node_name); |
| if (of_node) { |
| /* value won't be updated if property not being found */ |
| of_property_read_u32(of_node, "u2_vrt_ref", |
| (u32 *) &instance->phy_tuning.u2_vrt_ref); |
| of_property_read_u32(of_node, "u2_term_ref", |
| (u32 *) &instance->phy_tuning.u2_term_ref); |
| of_property_read_u32(of_node, "u2_enhance", |
| (u32 *) &instance->phy_tuning.u2_enhance); |
| |
| of_property_read_u32(of_node, "host_u2_vrt_ref", |
| (u32 *) &instance->phy_tuning.host_u2_vrt_ref); |
| of_property_read_u32(of_node, "host_u2_term_ref", |
| (u32 *) &instance->phy_tuning.host_u2_term_ref); |
| of_property_read_u32(of_node, "host_u2_enhance", |
| (u32 *) &instance->phy_tuning.host_u2_enhance); |
| |
| } |
| instance->phy_tuning.inited = true; |
| } |
| |
| if (usb_mode == 0){ //host |
| u3phywrite32(U3D_USBPHYACR6, RG_USB20_DISCTH_OFST,RG_USB20_DISCTH, 0xD); |
| u3phywrite32(U3D_USBPHYACR1, RG_USB20_INTR_CAL_OFST,RG_USB20_INTR_CAL, 0X1E); |
| u2_vrt_ref = instance->phy_tuning.host_u2_vrt_ref; |
| u2_term_ref = instance->phy_tuning.host_u2_term_ref; |
| u2_enhance = instance->phy_tuning.host_u2_enhance; |
| } |
| else{ //device |
| u2_vrt_ref = instance->phy_tuning.u2_vrt_ref; |
| u2_term_ref = instance->phy_tuning.u2_term_ref; |
| u2_enhance = instance->phy_tuning.u2_enhance; |
| } |
| |
| if (u2_vrt_ref != -1) { |
| if (u2_vrt_ref <= VAL_MAX_WIDTH_3) { |
| u3phywrite32(U3D_USBPHYACR1, |
| RG_USB20_VRT_VREF_SEL_OFST, |
| RG_USB20_VRT_VREF_SEL, u2_vrt_ref); |
| } |
| } |
| if (u2_term_ref != -1) { |
| if (u2_term_ref <= VAL_MAX_WIDTH_3) { |
| u3phywrite32(U3D_USBPHYACR1, |
| RG_USB20_TERM_VREF_SEL_OFST, |
| RG_USB20_TERM_VREF_SEL, u2_term_ref); |
| } |
| } |
| if (u2_enhance != -1) { |
| if (u2_enhance <= VAL_MAX_WIDTH_2) { |
| u3phywrite32(U3D_USBPHYACR6, |
| RG_USB20_PHY_REV_6_OFST, |
| RG_USB20_PHY_REV_6, u2_enhance); |
| } |
| } |
| |
| phy_printk(K_INFO, "%s - SSUSB TX EYE Tuning\n", __func__); |
| u3phywrite32(U3D_PHYD_MIX6, RG_SSUSB_IDRVSEL_OFST, |
| RG_SSUSB_IDRVSEL, 0x19); |
| u3phywrite32(U3D_PHYD_MIX6, RG_SSUSB_FORCE_IDRVSEL_OFST, |
| RG_SSUSB_FORCE_IDRVSEL, 1); |
| |
| u3phywrite32(U3D_PHYD_MIX6, RG_SSUSB_IDEMSEL_OFST, |
| RG_SSUSB_IDEMSEL, 0x1a); |
| u3phywrite32(U3D_PHYD_MIX6, RG_SSUSB_FORCE_IDEMSEL_OFST, |
| RG_SSUSB_FORCE_IDEMSEL, 1); |
| } |
| |
| |
| static void phy_recover(struct mtk_phy_instance *instance) |
| { |
| struct mtk_phy_drv *phy_drv = instance->phy_drv; |
| |
| phy_printk(K_INFO, "%s+\n", __func__); |
| usb_enable_clock(phy_drv, true); |
| udelay(250); |
| u3phywrite32(U3D_USB30_PHYA_REG1, RG_SSUSB_VUSB10_ON_OFST, |
| RG_SSUSB_VUSB10_ON, 1); |
| |
| #ifdef CONFIG_MTK_UART_USB_SWITCH |
| if (instance->uart_mode) |
| return; |
| #endif |
| |
| #ifdef CONFIG_MTK_USB2JTAG_SUPPORT |
| if (usb2jtag_mode()) { |
| phy_printk(K_INFO, "%s, bypass in usb2jtag mode\n", __func__); |
| return; |
| } |
| #endif |
| |
| u3phywrite32(U3D_U2PHYDTM0, FORCE_UART_EN_OFST, |
| FORCE_UART_EN, 0); |
| u3phywrite32(U3D_U2PHYDTM1, RG_UART_EN_OFST, |
| RG_UART_EN, 0); |
| u3phywrite32(U3D_U2PHYACR4, RG_USB20_GPIO_CTL_OFST, |
| RG_USB20_GPIO_CTL, 0); |
| u3phywrite32(U3D_U2PHYACR4, USB20_GPIO_MODE_OFST, |
| USB20_GPIO_MODE, 0); |
| |
| u3phywrite32(U3D_U2PHYDTM0, FORCE_SUSPENDM_OFST, |
| FORCE_SUSPENDM, 0); |
| u3phywrite32(U3D_U2PHYDTM0, RG_DPPULLDOWN_OFST, |
| RG_DPPULLDOWN, 0); |
| u3phywrite32(U3D_U2PHYDTM0, RG_DMPULLDOWN_OFST, |
| RG_DMPULLDOWN, 0); |
| u3phywrite32(U3D_U2PHYDTM0, RG_XCVRSEL_OFST, |
| RG_XCVRSEL, 0); |
| u3phywrite32(U3D_U2PHYDTM0, RG_TERMSEL_OFST, |
| RG_TERMSEL, 0); |
| u3phywrite32(U3D_U2PHYDTM0, RG_DATAIN_OFST, |
| RG_DATAIN, 0); |
| u3phywrite32(U3D_U2PHYDTM0, FORCE_DP_PULLDOWN_OFST, |
| FORCE_DP_PULLDOWN, 0); |
| u3phywrite32(U3D_U2PHYDTM0, FORCE_DM_PULLDOWN_OFST, |
| FORCE_DM_PULLDOWN, 0); |
| u3phywrite32(U3D_U2PHYDTM0, FORCE_XCVRSEL_OFST, |
| FORCE_XCVRSEL, 0); |
| u3phywrite32(U3D_U2PHYDTM0, FORCE_TERMSEL_OFST, |
| FORCE_TERMSEL, 0); |
| u3phywrite32(U3D_U2PHYDTM0, FORCE_DATAIN_OFST, |
| FORCE_DATAIN, 0); |
| u3phywrite32(U3D_USBPHYACR6, RG_USB20_BC11_SW_EN_OFST, |
| RG_USB20_BC11_SW_EN, 0); |
| /*u3phywrite32(U3D_USBPHYACR6, RG_USB20_OTG_VBUSCMP_EN_OFST, |
| * RG_USB20_OTG_VBUSCMP_EN, 1); |
| */ |
| /* Wait 800 usec */ |
| udelay(800); |
| |
| u3phywrite32(U3D_U2PHYDTM1, RG_VBUSVALID_OFST, |
| RG_VBUSVALID, 1); |
| u3phywrite32(U3D_U2PHYDTM1, RG_AVALID_OFST, |
| RG_AVALID, 1); |
| u3phywrite32(U3D_U2PHYDTM1, RG_SESSEND_OFST, |
| RG_SESSEND, 0); |
| |
| phy_efuse_settings(instance); |
| |
| u3phywrite32(U3D_USBPHYACR6, RG_USB20_DISCTH_OFST, |
| RG_USB20_DISCTH, 0x7); |
| |
| usb_phy_tuning(instance); |
| phy_advance_settings(instance); |
| |
| phy_slew_rate_calibration(instance); |
| |
| phy_printk(K_INFO, "%s-\n", __func__); |
| } |
| |
| |
| static int charger_detect_init(struct mtk_phy_instance *instance) |
| { |
| struct mtk_phy_drv *phy_drv = instance->phy_drv; |
| |
| |
| phy_printk(K_INFO, "%s+\n", __func__); |
| |
| usb_enable_clock(phy_drv, true); |
| udelay(250); |
| u3phywrite32(U3D_USBPHYACR6, RG_USB20_BC11_SW_EN_OFST, |
| RG_USB20_BC11_SW_EN, 1); |
| udelay(1); |
| usb_enable_clock(phy_drv, false); |
| |
| phy_printk(K_INFO, "%s-\n", __func__); |
| return 0; |
| } |
| |
| static int charger_detect_release(struct mtk_phy_instance *instance) |
| { |
| struct mtk_phy_drv *phy_drv = instance->phy_drv; |
| |
| phy_printk(K_INFO, "%s+\n", __func__); |
| |
| usb_enable_clock(phy_drv, true); |
| udelay(250); |
| u3phywrite32(U3D_USBPHYACR6, RG_USB20_BC11_SW_EN_OFST, |
| RG_USB20_BC11_SW_EN, 0); |
| udelay(1); |
| usb_enable_clock(phy_drv, false); |
| |
| phy_printk(K_INFO, "%s-\n", __func__); |
| return 0; |
| } |
| |
| static void phy_charger_switch_bc11(struct mtk_phy_instance *instance, |
| bool on) |
| { |
| if (on) |
| charger_detect_init(instance); |
| else |
| charger_detect_release(instance); |
| } |
| |
| static void phy_dpdm_pulldown(struct mtk_phy_instance *instance, |
| bool enable) |
| { |
| struct mtk_phy_drv *phy_drv = instance->phy_drv; |
| |
| phy_printk(K_INFO, "%s+\n", __func__); |
| |
| usb_enable_clock(phy_drv, true); |
| udelay(250); |
| if (enable) { |
| u3phywrite32(U3D_U2PHYDTM0, RG_DPPULLDOWN_OFST, |
| RG_DPPULLDOWN, 1); |
| u3phywrite32(U3D_U2PHYDTM0, RG_DMPULLDOWN_OFST, |
| RG_DMPULLDOWN, 1); |
| |
| u3phywrite32(U3D_USBPHYACR6, |
| RG_USB20_PHY_REV_OFST, (0x2<<24), 0x0); |
| } else { |
| u3phywrite32(U3D_U2PHYDTM0, RG_DPPULLDOWN_OFST, |
| RG_DPPULLDOWN, 0); |
| u3phywrite32(U3D_U2PHYDTM0, RG_DMPULLDOWN_OFST, |
| RG_DMPULLDOWN, 0); |
| |
| /*For water detection function, |
| *need to set DPPULLDOWN and DMPULLDOWN 0 |
| *Also adjust RG_USB20_PHY_REV to prevent power leakage |
| */ |
| u3phywrite32(U3D_USBPHYACR6, |
| RG_USB20_PHY_REV_OFST, (0x2<<24), 0x2); |
| } |
| udelay(1); |
| usb_enable_clock(phy_drv, false); |
| |
| phy_printk(K_INFO, "%s-\n", __func__); |
| } |
| |
| static int phy_lpm_enable(struct mtk_phy_instance *instance, bool on) |
| { |
| phy_printk(K_DEBUG, "%s+ = %d\n", __func__, on); |
| |
| if (on) |
| u3phywrite32(U3D_U2PHYDCR1, RG_USB20_SW_PLLMODE_OFST, |
| RG_USB20_SW_PLLMODE, 0x1); |
| else |
| u3phywrite32(U3D_U2PHYDCR1, RG_USB20_SW_PLLMODE_OFST, |
| RG_USB20_SW_PLLMODE, 0x0); |
| |
| return 0; |
| } |
| |
| static int phy_host_mode(struct mtk_phy_instance *instance, bool on) |
| { |
| phy_printk(K_DEBUG, "%s+ = %d\n", __func__, on); |
| |
| return 0; |
| } |
| |
| static int phy_ioread(struct mtk_phy_instance *instance, u32 reg) |
| { |
| if (reg < instance->port_rgsz) |
| return readl(instance->sif_u2phy_com + reg); |
| else |
| return 0; |
| } |
| |
| static int phy_iowrite(struct mtk_phy_instance *instance, |
| u32 val, u32 reg) |
| { |
| if (reg < instance->port_rgsz) |
| writel(val, instance->sif_u2phy_com + reg); |
| |
| return 0; |
| } |
| |
| |
| static bool phy_u3_loop_back_test(struct mtk_phy_instance *instance) |
| { |
| int reg; |
| bool loop_back_ret = false; |
| struct mtk_phy_drv *phy_drv = instance->phy_drv; |
| int r_pipe0, r_rx0, rx_mix0, r_t2rlb; |
| |
| /* VA10 is shared by U3/UFS */ |
| /* default on and set voltage by PMIC */ |
| /* off/on in SPM suspend/resume */ |
| |
| usb_enable_clock(phy_drv, true); |
| |
| r_pipe0 = readl(U3D_PHYD_PIPE0); |
| r_rx0 = readl(U3D_PHYD_RX0); |
| rx_mix0 = readl(U3D_PHYD_MIX0); |
| r_t2rlb = readl(U3D_PHYD_T2RLB); |
| |
| writel((readl(U3D_PHYD_PIPE0) & ~(0x01<<30)) | 0x01<<30, |
| U3D_PHYD_PIPE0); |
| writel((readl(U3D_PHYD_PIPE0) & ~(0x01<<28)) | 0x00<<28, |
| U3D_PHYD_PIPE0); |
| writel((readl(U3D_PHYD_PIPE0) & ~(0x03<<26)) | 0x01<<26, |
| U3D_PHYD_PIPE0); |
| writel((readl(U3D_PHYD_PIPE0) & ~(0x03<<24)) | 0x00<<24, |
| U3D_PHYD_PIPE0); |
| writel((readl(U3D_PHYD_PIPE0) & ~(0x01<<22)) | 0x00<<22, |
| U3D_PHYD_PIPE0); |
| writel((readl(U3D_PHYD_PIPE0) & ~(0x01<<21)) | 0x00<<21, |
| U3D_PHYD_PIPE0); |
| writel((readl(U3D_PHYD_PIPE0) & ~(0x01<<20)) | 0x01<<20, |
| U3D_PHYD_PIPE0); |
| mdelay(10); |
| |
| /*T2R loop back disable*/ |
| writel((readl(U3D_PHYD_RX0)&~(0x01<<15)) | 0x00<<15, |
| U3D_PHYD_RX0); |
| mdelay(10); |
| |
| /* TSEQ lock detect threshold */ |
| writel((readl(U3D_PHYD_MIX0) & ~(0x07<<24)) | 0x07<<24, |
| U3D_PHYD_MIX0); |
| /* set default TSEQ polarity check value = 1 */ |
| writel((readl(U3D_PHYD_MIX0) & ~(0x01<<28)) | 0x01<<28, |
| U3D_PHYD_MIX0); |
| /* TSEQ polarity check enable */ |
| writel((readl(U3D_PHYD_MIX0) & ~(0x01<<29)) | 0x01<<29, |
| U3D_PHYD_MIX0); |
| /* TSEQ decoder enable */ |
| writel((readl(U3D_PHYD_MIX0) & ~(0x01<<30)) | 0x01<<30, |
| U3D_PHYD_MIX0); |
| mdelay(10); |
| |
| /* set T2R loop back TSEQ length (x 16us) */ |
| writel((readl(U3D_PHYD_T2RLB) & ~(0xff<<0)) | 0xF0<<0, |
| U3D_PHYD_T2RLB); |
| /* set T2R loop back BDAT reset period (x 16us) */ |
| writel((readl(U3D_PHYD_T2RLB) & ~(0x0f<<12)) | 0x0F<<12, |
| U3D_PHYD_T2RLB); |
| /* T2R loop back pattern select */ |
| writel((readl(U3D_PHYD_T2RLB) & ~(0x03<<8)) | 0x00<<8, |
| U3D_PHYD_T2RLB); |
| mdelay(10); |
| |
| /* T2R loop back serial mode */ |
| writel((readl(U3D_PHYD_RX0) & ~(0x01<<13)) | 0x01<<13, |
| U3D_PHYD_RX0); |
| /* T2R loop back parallel mode = 0 */ |
| writel((readl(U3D_PHYD_RX0) & ~(0x01<<12)) | 0x00<<12, |
| U3D_PHYD_RX0); |
| /* T2R loop back mode enable */ |
| writel((readl(U3D_PHYD_RX0) & ~(0x01<<11)) | 0x01<<11, |
| U3D_PHYD_RX0); |
| /* T2R loop back enable */ |
| writel((readl(U3D_PHYD_RX0) & ~(0x01<<15)) | 0x01<<15, |
| U3D_PHYD_RX0); |
| mdelay(100); |
| |
| reg = u3phyread32(SSUSB_SIFSLV_U3PHYD_BASE + 0xb4); |
| phy_printk(K_INFO, "rb back : 0x%x\n", reg); |
| phy_printk(K_INFO, "rb t2rlb_lock : %d\n", (reg >> 2) & 0x01); |
| phy_printk(K_INFO, "rb t2rlb_pass : %d\n", (reg >> 3) & 0x01); |
| phy_printk(K_INFO, "rb t2rlb_passth: %d\n", (reg >> 4) & 0x01); |
| |
| if ((reg & 0x0E) == 0x0E) |
| loop_back_ret = true; |
| else |
| loop_back_ret = false; |
| |
| writel(r_rx0, U3D_PHYD_RX0); |
| writel(r_pipe0, U3D_PHYD_PIPE0); |
| writel(rx_mix0, U3D_PHYD_MIX0); |
| writel(r_t2rlb, U3D_PHYD_T2RLB); |
| |
| usb_enable_clock(phy_drv, false); |
| return loop_back_ret; |
| } |
| |
| |
| #ifdef CONFIG_MTK_SIB_USB_SWITCH |
| static void phy_sib_enable(struct mtk_phy_instance *instance, bool enable) |
| { |
| struct mtk_phy_drv *phy_drv = instance->phy_drv; |
| |
| phy_printk(K_DEBUG, "usb_phy_sib_enable_switch =%d\n", enable); |
| |
| usb_enable_clock(phy_drv, true); |
| udelay(250); |
| |
| u3phywrite32(U3D_USB30_PHYA_REG1, RG_SSUSB_VUSB10_ON_OFST, |
| RG_SSUSB_VUSB10_ON, 1); |
| |
| /* SSUSB_SIFSLV_IPPC_BASE SSUSB_IP_SW_RST = 0 */ |
| writel(0x00031000, (void __iomem *)phy_drv->ippc_base + 0x0); |
| /* SSUSB_IP_HOST_PDN = 0 */ |
| writel(0x00000000, (void __iomem *)phy_drv->ippc_base + 0x4); |
| /* SSUSB_IP_DEV_PDN = 0 */ |
| writel(0x00000000, (void __iomem *)phy_drv->ippc_base + 0x8); |
| /* SSUSB_IP_PCIE_PDN = 0 */ |
| writel(0x00000000, (void __iomem *)phy_drv->ippc_base + 0xC); |
| /* SSUSB_U3_PORT_DIS/SSUSB_U3_PORT_PDN = 0*/ |
| writel(0x0000000C, (void __iomem *)phy_drv->ippc_base + 0x30); |
| |
| /* |
| * USBMAC mode is 0x62910002 (bit 1) |
| * MDSIB mode is 0x62910008 (bit 3) |
| * 0x0629 just likes a signature. Can't be removed. |
| */ |
| if (enable) { |
| writel(0x62910008, MTK_USB_PHY_CHIP_BASE); |
| instance->sib_mode = true; |
| } else { |
| writel(0x62910002, MTK_USB_PHY_CHIP_BASE); |
| instance->sib_mode = false; |
| } |
| } |
| |
| static bool phy_sib_switch_status(struct mtk_phy_instance *instance) |
| { |
| struct mtk_phy_drv *phy_drv = instance->phy_drv; |
| int reg; |
| bool ret; |
| |
| usb_enable_clock(phy_drv, true); |
| udelay(250); |
| |
| u3phywrite32(U3D_USB30_PHYA_REG1, RG_SSUSB_VUSB10_ON_OFST, |
| RG_SSUSB_VUSB10_ON, 1); |
| |
| reg = u3phyread32(MTK_USB_PHY_CHIP_BASE); |
| phy_printk(K_DEBUG, "%s reg=%d\n", __func__, reg); |
| |
| if (reg == 0x62910008) |
| ret = true; |
| else |
| ret = false; |
| |
| return ret; |
| } |
| |
| #endif |
| |
| #ifdef CONFIG_MTK_UART_USB_SWITCH |
| #include <linux/phy/mediatek/mtk_usb_phy.h> |
| |
| static void phy_dump_usb2uart_reg(struct mtk_phy_instance *instance) |
| { |
| struct mtk_phy_drv *phy_drv = instance->phy_drv; |
| |
| usb_enable_clock(phy_drv, true); |
| udelay(250); |
| |
| phy_printk(K_ALET, "addr: 0x18, value: 0x%x\n", |
| u3phyread32(U3D_USBPHYACR6)); |
| phy_printk(K_ALET, "addr: 0x20, value: 0x%x\n", |
| u3phyread32(U3D_U2PHYACR4)); |
| phy_printk(K_ALET, "addr: 0x68, value: 0x%x\n", |
| u3phyread32(U3D_U2PHYDTM0)); |
| phy_printk(K_ALET, "addr: 0x6C, value: 0x%x\n", |
| u3phyread32(U3D_U2PHYDTM1)); |
| |
| usb_enable_clock(phy_drv, false); |
| } |
| |
| bool phy_check_in_uart_mode(struct mtk_phy_instance *instance) |
| { |
| struct mtk_phy_drv *phy_drv = instance->phy_drv; |
| int usb_port_mode; |
| |
| usb_enable_clock(phy_drv, true); |
| udelay(250); |
| |
| usb_port_mode = u3phyread32(U3D_U2PHYDTM0) >> RG_UART_MODE_OFST; |
| |
| usb_enable_clock(phy_drv, false); |
| phy_printk(K_ALET, "%s - usb_port_mode = %d, ", |
| __func__, usb_port_mode); |
| phy_printk(K_ALET, "%s - instance->uart_mode = %d\n", |
| __func__, instance->uart_mode); |
| |
| if (usb_port_mode == 0x1) |
| return true; |
| else |
| return false; |
| } |
| |
| static int phy_switch_usb2uart_gpio(enum PORT_MODE portmode) |
| { |
| struct device_node *node; |
| void __iomem *gpio_base; |
| |
| /* SET USB to UART GPIO to UART0 */ |
| node = of_find_compatible_node(NULL, NULL, "mediatek,gpio"); |
| if (!node) { |
| pr_info("error: can't find GPIO node\n"); |
| return -EINVAL; |
| } |
| |
| gpio_base = of_iomap(node, 0); |
| if (!gpio_base) { |
| pr_info("error: iomap fail for GPIO\n"); |
| return -EINVAL; |
| } |
| |
| if (portmode == PORT_MODE_UART) |
| writel((readl(gpio_base + RG_GPIO_SELECT) | |
| (GPIO_SEL_UART0<<GPIO_SEL_OFFSET)), |
| gpio_base + RG_GPIO_SELECT); |
| else |
| writel((readl(gpio_base + RG_GPIO_SELECT) & |
| ~(GPIO_SEL_MASK<<GPIO_SEL_OFFSET)), |
| gpio_base + RG_GPIO_SELECT); |
| |
| return 0; |
| } |
| |
| static void phy_switch_to_uart(struct mtk_phy_instance *instance) |
| { |
| struct mtk_phy_drv *phy_drv = instance->phy_drv; |
| |
| if (phy_check_in_uart_mode(instance)) { |
| phy_printk(K_DEBUG, "%s+ UART_MODE\n", __func__); |
| return; |
| } |
| |
| phy_printk(K_DEBUG, "%s+ USB_MODE\n", __func__); |
| |
| usb_enable_clock(phy_drv, true); |
| udelay(250); |
| |
| u3phywrite32(U3D_USBPHYACR6, RG_USB20_BC11_SW_EN_OFST, |
| RG_USB20_BC11_SW_EN, 0); |
| u3phywrite32(U3D_U2PHYDTM0, RG_SUSPENDM_OFST, |
| RG_SUSPENDM, 1); |
| u3phywrite32(U3D_U2PHYDTM0, FORCE_SUSPENDM_OFST, |
| FORCE_SUSPENDM, 1); |
| u3phywrite32(U3D_U2PHYDTM0, RG_UART_MODE_OFST, |
| RG_UART_MODE, 1); |
| u3phywrite32(U3D_U2PHYDTM1, RG_UART_EN_OFST, |
| RG_UART_EN, 1); |
| u3phywrite32(U3D_U2PHYDTM0, FORCE_UART_EN_OFST, |
| FORCE_UART_EN, 1); |
| u3phywrite32(U3D_U2PHYDTM0, FORCE_UART_TX_OE_OFST, |
| FORCE_UART_TX_OE, 1); |
| u3phywrite32(U3D_U2PHYDTM0, FORCE_UART_BIAS_EN_OFST, |
| FORCE_UART_BIAS_EN, 1); |
| u3phywrite32(U3D_U2PHYDTM1, RG_UART_EN_OFST, |
| RG_UART_EN, 1); |
| u3phywrite32(U3D_U2PHYDTM1, RG_UART_TX_OE_OFST, |
| RG_UART_TX_OE, 1); |
| u3phywrite32(U3D_U2PHYDTM1, RG_UART_BIAS_EN_OFST, |
| RG_UART_BIAS_EN, 1); |
| u3phywrite32(U3D_U2PHYACR4, RG_USB20_DM_100K_EN_OFST, |
| RG_USB20_DM_100K_EN, 1); |
| u3phywrite32(U3D_U2PHYDTM0, RG_DPPULLDOWN_OFST, |
| RG_DPPULLDOWN, 0); |
| u3phywrite32(U3D_U2PHYDTM0, RG_DMPULLDOWN_OFST, |
| RG_DMPULLDOWN, 0); |
| |
| usb_enable_clock(phy_drv, false); |
| |
| phy_switch_usb2uart_gpio(PORT_MODE_UART); |
| |
| instance->uart_mode = true; |
| } |
| |
| |
| static void phy_switch_to_usb(struct mtk_phy_instance *instance) |
| { |
| instance->uart_mode = false; |
| |
| u3phywrite32(U3D_U2PHYDTM0, FORCE_UART_EN_OFST, |
| FORCE_UART_EN, 0); |
| |
| phy_switch_usb2uart_gpio(PORT_MODE_USB); |
| |
| phy_init_soc(instance); |
| } |
| #endif |
| |
| #ifdef CONFIG_MTK_USB2JTAG_SUPPORT |
| int usb2jtag_usb_init(void) |
| { |
| struct device_node *node = NULL; |
| void __iomem *usb3_sif2_base; |
| u32 temp; |
| |
| pr_notice("[USB2JTAG] %s ++\n", __func__); |
| |
| node = of_find_compatible_node(NULL, NULL, "mediatek,usb3"); |
| if (!node) { |
| pr_notice("[USB2JTAG] map node @ mediatek,usb3 failed\n"); |
| return -1; |
| } |
| |
| usb3_sif2_base = of_iomap(node, 2); |
| if (!usb3_sif2_base) { |
| pr_notice("[USB2JTAG] iomap usb3_sif2_base failed\n"); |
| return -1; |
| } |
| |
| /* rg_usb20_gpio_ctl: bit[9] = 1 */ |
| temp = readl(usb3_sif2_base + 0x320); |
| writel(temp | (1 << 9), usb3_sif2_base + 0x320); |
| |
| /* RG_USB20_BC11_SW_EN: bit[23] = 0 */ |
| temp = readl(usb3_sif2_base + 0x318); |
| writel(temp & ~(1 << 23), usb3_sif2_base + 0x318); |
| |
| /* RG_USB20_BGR_EN: bit[0] = 1 */ |
| temp = readl(usb3_sif2_base + 0x300); |
| writel(temp | (1 << 0), usb3_sif2_base + 0x300); |
| |
| /* rg_sifslv_mac_bandgap_en: bit[17] = 0 */ |
| temp = readl(usb3_sif2_base + 0x308); |
| writel(temp & ~(1 << 17), usb3_sif2_base + 0x308); |
| |
| /* wait stable */ |
| mdelay(1); |
| |
| iounmap(usb3_sif2_base); |
| |
| return 0; |
| } |
| #endif |
| |
| static int mtk_phy_drv_init(struct platform_device *pdev, |
| struct mtk_phy_drv *mtkphy) |
| { |
| int ret = 0; |
| struct device *dev = &pdev->dev; |
| struct resource *res; |
| |
| res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sif_base"); |
| if (!res) |
| return -EINVAL; |
| |
| mtkphy->phy_base = devm_ioremap(dev, res->start, resource_size(res)); |
| if (IS_ERR(mtkphy->phy_base)) { |
| dev_info(dev, "failed to remap phy regs\n"); |
| return PTR_ERR(mtkphy->phy_base); |
| } |
| |
| res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ippc"); |
| if (!res) |
| return -EINVAL; |
| |
| mtkphy->ippc_base = devm_ioremap(dev, res->start, resource_size(res)); |
| if (IS_ERR(mtkphy->ippc_base)) { |
| dev_info(dev, "failed to remap ippc_base regs\n"); |
| return PTR_ERR(mtkphy->ippc_base); |
| } |
| |
| mtkphy->clk = devm_clk_get(dev, "ref_clk"); |
| if (IS_ERR(mtkphy->clk)) { |
| if (PTR_ERR(mtkphy->clk) == -EPROBE_DEFER) { |
| dev_info(dev, "mtkphy->clk EPROBE_DEFER\n"); |
| return -EPROBE_DEFER; |
| } |
| mtkphy->clk = NULL; |
| } |
| |
| return ret; |
| } |
| |
| static int mtk_phy_drv_exit(struct platform_device *pdev, |
| struct mtk_phy_drv *mtkphy) |
| { |
| if (!IS_ERR_OR_NULL(mtkphy->clk)) |
| clk_disable_unprepare(mtkphy->clk); |
| return 0; |
| } |
| |
| static int phy_inst_init(struct mtk_phy_instance *instance) |
| { |
| struct mtk_phy_drv *phy_drv = instance->phy_drv; |
| void __iomem *port_base; |
| |
| phy_printk(K_DEBUG, "%s+\n", __func__); |
| |
| port_base = instance->port_base; |
| |
| if (phy_drv->phycfg->version == MT_PHY_V1) { |
| instance->sif_misc = NULL; |
| instance->sif_fmreg = phy_drv->phy_base + 0x100; |
| instance->sif_u2phy_com = port_base; |
| if (instance->phycfg->port_type == PHY_TYPE_USB3) { |
| instance->sif_spllc = phy_drv->phy_base; |
| instance->sif_chip = NULL; |
| instance->sif_u3phyd = port_base + 0x100; |
| instance->sif_u3phyd_bank2 = port_base + 0x200; |
| instance->sif_u3phya = port_base + 0x300; |
| instance->sif_u3phya_da = port_base + 0x400; |
| } |
| instance->port_rgsz = 0x800; |
| } else if (phy_drv->phycfg->version == MT_PHY_V2) { |
| instance->sif_misc = port_base; |
| instance->sif_fmreg = port_base + 0x100; |
| instance->sif_u2phy_com = port_base + 0x300; |
| instance->port_rgsz = 0x500; |
| if (instance->phycfg->port_type == PHY_TYPE_USB3) { |
| instance->sif_spllc = port_base + 0x700; |
| instance->sif_chip = port_base + 0x800; |
| instance->sif_u3phyd = port_base + 0x900; |
| instance->sif_u3phyd_bank2 = port_base + 0xa00; |
| instance->sif_u3phya = port_base + 0xb00; |
| instance->sif_u3phya_da = port_base + 0xc00; |
| instance->port_rgsz = 0x1000; |
| } |
| } |
| return 0; |
| } |
| |
| |
| static const struct mtk_phy_interface ssusb_phys[] = { |
| { |
| .name = "port0", |
| .tuning_node_name = "mediatek,phy_tuning", |
| .port_num = 0, |
| .reg_offset = 0, |
| .port_type = PHY_TYPE_USB3, |
| .usb_phy_inst_init = phy_inst_init, |
| .usb_phy_init = phy_init_soc, |
| .usb_phy_savecurrent = phy_savecurrent, |
| .usb_phy_recover = phy_recover, |
| .usb_phy_switch_to_bc11 = phy_charger_switch_bc11, |
| .usb_phy_dpdm_pulldown = phy_dpdm_pulldown, |
| .usb_phy_lpm_enable = phy_lpm_enable, |
| .usb_phy_host_mode = phy_host_mode, |
| .usb_phy_io_read = phy_ioread, |
| .usb_phy_io_write = phy_iowrite, |
| #ifdef CONFIG_MTK_UART_USB_SWITCH |
| .usb_phy_switch_to_usb = phy_switch_to_usb, |
| .usb_phy_switch_to_uart = phy_switch_to_uart, |
| .usb_phy_check_in_uart_mode = phy_check_in_uart_mode, |
| .usb_phy_dump_usb2uart_reg = phy_dump_usb2uart_reg, |
| #endif |
| #ifdef CONFIG_MTK_SIB_USB_SWITCH |
| .usb_phy_sib_enable_switch = phy_sib_enable, |
| .usb_phy_sib_switch_status = phy_sib_switch_status, |
| #endif |
| .usb_phy_u3_loop_back_test = phy_u3_loop_back_test, |
| }, |
| }; |
| |
| static const struct mtk_usbphy_config ssusb_phy_config = { |
| .phys = ssusb_phys, |
| .num_phys = 1, |
| .version = MT_PHY_V2, |
| .usb_drv_init = mtk_phy_drv_init, |
| .usb_drv_exit = mtk_phy_drv_exit, |
| }; |
| |
| const struct of_device_id mtk_phy_of_match[] = { |
| { |
| .compatible = "mediatek,mt6853-phy", |
| .data = &ssusb_phy_config, |
| }, |
| { }, |
| }; |