| /* |
| * Copyright (C) 2013 Marvell Inc. |
| * |
| * Author: |
| * Chao Xie <xiechao.mail@gmail.com> |
| * |
| * This software is licensed under the terms of the GNU General Public |
| * License version 2, as published by the Free Software Foundation, and |
| * may be copied, distributed, and modified under those terms. |
| * |
| * 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/resource.h> |
| #include <linux/delay.h> |
| #include <linux/slab.h> |
| #include <linux/of.h> |
| #include <linux/of_device.h> |
| #include <linux/io.h> |
| #include <linux/err.h> |
| #include <linux/clk.h> |
| #include <linux/export.h> |
| #include <linux/module.h> |
| #include <linux/platform_device.h> |
| #include <linux/platform_data/mv_usb.h> |
| #include <linux/usb/phy.h> |
| #include <linux/usb/mv_usb2_phy.h> |
| #include <dt-bindings/usb/mv_usb_phy.h> |
| #include <linux/features.h> |
| #include <linux/cputype.h> |
| #include <linux/usb/ehci_def.h> |
| #include <soc/asr/regs-addr.h> |
| |
| #include <linux/pm_qos.h> |
| #include "phy-mv-usb.h" |
| #include <linux/usb/composite.h> |
| |
| /* phy regs */ |
| /* for pxa910 and mmp2, there is no revision register */ |
| #define PHY_55NM_REVISION 0x0 |
| #define PHY_55NM_CTRL 0x4 |
| #define PHY_55NM_PLL 0x8 |
| #define PHY_55NM_TX 0xc |
| #define PHY_55NM_RX 0x10 |
| #define PHY_55NM_IVREF 0x14 |
| #define PHY_55NM_T0 0x18 |
| #define PHY_55NM_T1 0x1c |
| #define PHY_55NM_T2 0x20 |
| #define PHY_55NM_T3 0x24 |
| #define PHY_55NM_T4 0x28 |
| #define PHY_55NM_T5 0x2c |
| #define PHY_55NM_RESERVE 0x30 |
| #define PHY_55NM_USB_INT 0x34 |
| #define PHY_55NM_DBG_CTL 0x38 |
| #define PHY_55NM_OTG_ADDON 0x3c |
| |
| /* For UTMICTRL Register */ |
| #define PHY_55NM_CTRL_USB_CLK_EN (1 << 31) |
| /* pxa168 */ |
| #define PHY_55NM_CTRL_SUSPEND_SET1 (1 << 30) |
| #define PHY_55NM_CTRL_SUSPEND_SET2 (1 << 29) |
| #define PHY_55NM_CTRL_RXBUF_PDWN (1 << 24) |
| #define PHY_55NM_CTRL_TXBUF_PDWN (1 << 11) |
| |
| #define PHY_55NM_CTRL_INPKT_DELAY_SHIFT 30 |
| #define PHY_55NM_CTRL_INPKT_DELAY_SOF_SHIFT 28 |
| #define PHY_55NM_CTRL_PU_REF_SHIFT 20 |
| #define PHY_55NM_CTRL_ARC_PULLDN_SHIFT 12 |
| #define PHY_55NM_CTRL_PLL_PWR_UP_SHIFT 1 |
| #define PHY_55NM_CTRL_PWR_UP_SHIFT 0 |
| |
| /* For UTMI_PLL Register */ |
| #define PHY_55NM_PLL_PLLCALI12_SHIFT 29 |
| #define PHY_55NM_PLL_PLLCALI12_MASK (0x3 << 29) |
| |
| #define PHY_55NM_PLL_PLLVDD18_SHIFT 27 |
| #define PHY_55NM_PLL_PLLVDD18_MASK (0x3 << 27) |
| |
| #define PHY_55NM_PLL_PLLVDD12_SHIFT 25 |
| #define PHY_55NM_PLL_PLLVDD12_MASK (0x3 << 25) |
| |
| #define PHY_55NM_PLL_PLL_READY (0x1 << 23) |
| #define PHY_55NM_PLL_KVCO_EXT (0x1 << 22) |
| #define PHY_55NM_PLL_VCOCAL_START (0x1 << 21) |
| |
| #define PHY_55NM_PLL_KVCO_SHIFT 15 |
| #define PHY_55NM_PLL_KVCO_MASK (0x7 << 15) |
| |
| #define PHY_55NM_PLL_ICP_SHIFT 12 |
| #define PHY_55NM_PLL_ICP_MASK (0x7 << 12) |
| |
| #define PHY_55NM_PLL_FBDIV_SHIFT 4 |
| #define PHY_55NM_PLL_FBDIV_MASK (0xFF << 4) |
| |
| #define PHY_55NM_PLL_REFDIV_SHIFT 0 |
| #define PHY_55NM_PLL_REFDIV_MASK (0xF << 0) |
| |
| /* For UTMI_TX Register */ |
| #define PHY_55NM_TX_REG_EXT_FS_RCAL_SHIFT 27 |
| #define PHY_55NM_TX_REG_EXT_FS_RCAL_MASK (0xf << 27) |
| |
| #define PHY_55NM_TX_REG_EXT_FS_RCAL_EN_SHIFT 26 |
| #define PHY_55NM_TX_REG_EXT_FS_RCAL_EN_MASK (0x1 << 26) |
| |
| #define PHY_55NM_TX_TXVDD12_SHIFT 22 |
| #define PHY_55NM_TX_TXVDD12_MASK (0x3 << 22) |
| |
| #define PHY_55NM_TX_CK60_PHSEL_SHIFT 17 |
| #define PHY_55NM_TX_CK60_PHSEL_MASK (0xf << 17) |
| |
| #define PHY_55NM_TX_IMPCAL_VTH_SHIFT 14 |
| #define PHY_55NM_TX_IMPCAL_VTH_MASK (0x7 << 14) |
| |
| #define PHY_55NM_TX_REG_RCAL_START (0x1 << 12) |
| |
| #define PHY_55NM_TX_LOW_VDD_EN_SHIFT 11 |
| |
| #define PHY_55NM_TX_AMP_SHIFT 0 |
| #define PHY_55NM_TX_AMP_MASK (0x7 << 0) |
| |
| /* For UTMI_RX Register */ |
| #define PHY_55NM_RX_REG_SQ_LENGTH_SHIFT 15 |
| #define PHY_55NM_RX_REG_SQ_LENGTH_MASK (0x3 << 15) |
| |
| #define PHY_55NM_RX_SQ_THRESH_SHIFT 4 |
| #define PHY_55NM_RX_SQ_THRESH_MASK (0xf << 4) |
| |
| /* For UTMI_OTG_ADDON Register. Only for pxa168 */ |
| #define PHY_55NM_OTG_ADDON_OTG_ON (1 << 0) |
| |
| |
| /* For pxa988 the register mapping are changed*/ |
| #define PHY_40NM_PLL0 0x4 |
| #define PHY_40NM_PLL1 0x8 |
| #define PHY_40NM_TX0 0x10 |
| #define PHY_40NM_TX1 0x14 |
| #define PHY_40NM_TX2 0x18 |
| #define PHY_40NM_RX0 0x20 |
| #define PHY_40NM_RX1 0x24 |
| #define PHY_40NM_RX2 0x28 |
| #define PHY_40NM_ANA0 0x30 |
| #define PHY_40NM_ANA1 0x34 |
| #define PHY_40NM_DIG0 0x3c |
| #define PHY_40NM_DIG1 0x40 |
| #define PHY_40NM_DIG2 0x44 |
| #define PHY_40NM_T0 0x4c |
| #define PHY_40NM_T1 0x50 |
| #define PHY_40NM_CHARGE0 0x58 |
| #define PHY_40NM_OTG 0x5C |
| #define PHY_40NM_PHY_MON 0x60 |
| #define PHY_40NM_RESERVE0 0x64 |
| #define PHY_40NM_CTRL 0x104 |
| #define PHY_40NM_STATUS_A0C 0x108 |
| #define PHY_40NM_STATUS 0x128 |
| |
| /* default values are got from spec */ |
| #define PHY_40NM_PLL0_DEFAULT 0x5A78 |
| #define PHY_40NM_PLL1_DEFAULT 0x0231 |
| #define PHY_40NM_TX0_DEFAULT 0x0488 |
| #define PHY_40NM_TX1_DEFAULT 0x05B0 |
| #define PHY_40NM_TX2_DEFAULT 0x02FF |
| #define PHY_40NM_RX0_DEFAULT 0xAA71 |
| #define PHY_40NM_RX1_DEFAULT 0x3892 |
| #define PHY_40NM_RX2_DEFAULT 0x0125 |
| #define PHY_40NM_ANA1_DEFAULT 0x1680 |
| #define PHY_40NM_OTG_DEFAULT 0x0 |
| #define PHY_40NM_CTRL_DEFAULT 0x00801000 |
| #define PHY_40NM_CTRL_OTG_DEFAULT 0x0398F000 |
| |
| #define PHY_40NM_PLL0_PLLVDD18(x) (((x) & 0x3) << 14) |
| #define PHY_40NM_PLL0_REFDIV(x) (((x) & 0x1f) << 9) |
| #define PHY_40NM_PLL0_FBDIV(x) (((x) & 0x1ff) << 0) |
| |
| #define PHY_40NM_PLL1_PLL_READY (0x1 << 15) |
| #define PHY_40NM_PLL1_PLL_CONTROL_BY_PIN (0x1 << 14) |
| #define PHY_40NM_PLL1_PU_PLL (0x1 << 13) |
| #define PHY_40NM_PLL1_PLL_LOCK_BYPASS (0x1 << 12) |
| #define PHY_40NM_PLL1_DLL_RESET (0x1 << 11) |
| #define PHY_40NM_PLL1_ICP(x) (((x) & 0x7) << 8) |
| #define PHY_40NM_PLL1_KVCO_EXT (0x1 << 7) |
| #define PHY_40NM_PLL1_KVCO(x) (((x) & 0x7) << 4) |
| #define PHY_40NM_PLL1_CLK_BLK_EN (0x1 << 3) |
| #define PHY_40NM_PLL1_VCOCAL_START (0x1 << 2) |
| #define PHY_40NM_PLL1_PLLCAL12(x) (((x) & 0x3) << 0) |
| |
| #define PHY_40NM_TX0_TXDATA_BLK_EN (0x1 << 14) |
| #define PHY_40NM_TX0_RCAL_START (0x1 << 13) |
| #define PHY_40NM_TX0_EXT_HS_RCAL_EN (0x1 << 12) |
| #define PHY_40NM_TX0_EXT_FS_RCAL_EN (0x1 << 11) |
| #define PHY_40NM_TX0_IMPCAL_VTH(x) (((x) & 0x7) << 8) |
| #define PHY_40NM_TX0_EXT_HS_RCAL(x) (((x) & 0xf) << 4) |
| #define PHY_40NM_TX0_EXT_FS_RCAL(x) (((x) & 0xf) << 0) |
| |
| #define PHY_40NM_TX1_TXVDD15(x) (((x) & 0x3) << 10) |
| #define PHY_40NM_TX1_TXVDD12(x) (((x) & 0x3) << 8) |
| #define PHY_40NM_TX1_LOWVDD_EN (0x1 << 7) |
| #define PHY_40NM_TX1_AMP(x) (((x) & 0x7) << 4) |
| #define PHY_40NM_TX1_CK60_PHSEL(x) (((x) & 0xf) << 0) |
| |
| #define PHY_40NM_TX2_DRV_SLEWRATE(x) (((x) & 0x3) << 10) |
| #define PHY_40NM_TX2_IMP_CAL_DLY(x) (((x) & 0x3) << 8) |
| #define PHY_40NM_TX2_FSDRV_EN(x) (((x) & 0xf) << 4) |
| #define PHY_40NM_TX2_HSDEV_EN(x) (((x) & 0xf) << 0) |
| |
| #define PHY_40NM_RX0_PHASE_FREEZE_DLY (0x1 << 15) |
| #define PHY_40NM_RX0_USQ_LENGTH (0x1 << 14) |
| #define PHY_40NM_RX0_ACQ_LENGTH(x) (((x) & 0x3) << 12) |
| #define PHY_40NM_RX0_SQ_LENGTH(x) (((x) & 0x3) << 10) |
| #define PHY_40NM_RX0_DISCON_THRESH(x) (((x) & 0x3) << 8) |
| #define PHY_40NM_RX0_SQ_THRESH(x) (((x) & 0xf) << 4) |
| #define PHY_40NM_RX0_LPF_COEF(x) (((x) & 0x3) << 2) |
| #define PHY_40NM_RX0_INTPI(x) (((x) & 0x3) << 0) |
| |
| #define PHY_40NM_RX1_EARLY_VOS_ON_EN (0x1 << 13) |
| #define PHY_40NM_RX1_RXDATA_BLOCK_EN (0x1 << 12) |
| #define PHY_40NM_RX1_EDGE_DET_EN (0x1 << 11) |
| #define PHY_40NM_RX1_CAP_SEL(x) (((x) & 0x7) << 8) |
| #define PHY_40NM_RX1_RXDATA_BLOCK_LENGTH(x) (((x) & 0x3) << 6) |
| #define PHY_40NM_RX1_EDGE_DET_SEL(x) (((x) & 0x3) << 4) |
| #define PHY_40NM_RX1_CDR_COEF_SEL (0x1 << 3) |
| #define PHY_40NM_RX1_CDR_FASTLOCK_EN (0x1 << 2) |
| #define PHY_40NM_RX1_S2TO3_DLY_SEL(x) (((x) & 0x3) << 0) |
| |
| #define PHY_40NM_RX2_USQ_FILTER (0x1 << 8) |
| #define PHY_40NM_RX2_SQ_CM_SEL (0x1 << 7) |
| #define PHY_40NM_RX2_SAMPLER_CTRL (0x1 << 6) |
| #define PHY_40NM_RX2_SQ_BUFFER_EN (0x1 << 5) |
| #define PHY_40NM_RX2_SQ_ALWAYS_ON (0x1 << 4) |
| #define PHY_40NM_RX2_RXVDD18(x) (((x) & 0x3) << 2) |
| #define PHY_40NM_RX2_RXVDD12(x) (((x) & 0x3) << 0) |
| |
| #define PHY_40NM_ANA0_BG_VSEL(x) (((x) & 0x3) << 8) |
| #define PHY_40NM_ANA0_DIG_SEL(x) (((x) & 0x3) << 6) |
| #define PHY_40NM_ANA0_TOPVDD18(x) (((x) & 0x3) << 4) |
| #define PHY_40NM_ANA0_VDD_USB2_DIG_TOP_SEL (0x1 << 3) |
| #define PHY_40NM_ANA0_IPTAT_SEL(x) (((x) & 0x7) << 0) |
| |
| #define PHY_40NM_ANA1_PU_ANA (0x1 << 14) |
| #define PHY_40NM_ANA1_ANA_CONTROL_BY_PIN (0x1 << 13) |
| #define PHY_40NM_ANA1_SEL_LPFR (0x1 << 12) |
| #define PHY_40NM_ANA1_V2I_EXT (0x1 << 11) |
| #define PHY_40NM_ANA1_V2I(x) (((x) & 0x7) << 8) |
| #define PHY_40NM_ANA1_R_ROTATE_SEL (0x1 << 7) |
| #define PHY_40NM_ANA1_STRESS_TEST_MODE (0x1 << 6) |
| #define PHY_40NM_ANA1_TESTMON_ANA(x) (((x) & 0x3f) << 0) |
| |
| #define PHY_40NM_DIG0_FIFO_UF (0x1 << 15) |
| #define PHY_40NM_DIG0_FIFO_OV (0x1 << 14) |
| #define PHY_40NM_DIG0_FS_EOP_MODE (0x1 << 13) |
| #define PHY_40NM_DIG0_HOST_DISCON_SEL1 (0x1 << 12) |
| #define PHY_40NM_DIG0_HOST_DISCON_SEL0 (0x1 << 11) |
| #define PHY_40NM_DIG0_FORCE_END_EN (0x1 << 10) |
| #define PHY_40NM_DIG0_SYNCDET_WINDOW_EN (0x1 << 8) |
| #define PHY_40NM_DIG0_CLK_SUSPEND_EN (0x1 << 7) |
| #define PHY_40NM_DIG0_HS_DRIBBLE_EN (0x1 << 6) |
| #define PHY_40NM_DIG0_SYNC_NUM(x) (((x) & 0x3) << 4) |
| #define PHY_40NM_DIG0_FIFO_FILL_NUM(x) (((x) & 0xf) << 0) |
| |
| #define PHY_40NM_DIG1_FS_RX_ERROR_MODE2 (0x1 << 15) |
| #define PHY_40NM_DIG1_FS_RX_ERROR_MODE1 (0x1 << 14) |
| #define PHY_40NM_DIG1_FS_RX_ERROR_MODE (0x1 << 13) |
| #define PHY_40NM_DIG1_CLK_OUT_SEL (0x1 << 12) |
| #define PHY_40NM_DIG1_EXT_TX_CLK_SEL (0x1 << 11) |
| #define PHY_40NM_DIG1_ARC_DPDM_MODE (0x1 << 10) |
| #define PHY_40NM_DIG1_DP_PULLDOWN (0x1 << 9) |
| #define PHY_40NM_DIG1_DM_PULLDOWN (0x1 << 8) |
| #define PHY_40NM_DIG1_SYNC_IGNORE_SQ (0x1 << 7) |
| #define PHY_40NM_DIG1_SQ_RST_RX (0x1 << 6) |
| #define PHY_40NM_DIG1_MON_SEL(x) (((x) & 0x3f) << 0) |
| |
| #define PHY_40NM_DIG2_PAD_STRENGTH(x) (((x) & 0x1f) << 8) |
| #define PHY_40NM_DIG2_LONG_EOP (0x1 << 5) |
| #define PHY_40NM_DIG2_NOVBUS_DPDM00 (0x1 << 4) |
| #define PHY_40NM_DIG2_ALIGN_FS_OUTEN (0x1 << 2) |
| #define PHY_40NM_DIG2_HS_HDL_SYNC (0x1 << 1) |
| #define PHY_40NM_DIG2_FS_HDL_OPMD (0x1 << 0) |
| |
| #define PHY_40NM_CHARGE0_ENABLE_SWITCH (0x1 << 3) |
| #define PHY_40NM_CHARGE0_PU_CHRG_DTC (0x1 << 2) |
| #define PHY_40NM_CHARGE0_TESTMON_CHRGDTC(x) (((x) & 0x3) << 0) |
| |
| #define PHY_40NM_OTG_TESTMODE(x) (((x) & 0x7) << 0) |
| #define PHY_40NM_OTG_CONTROL_BY_PIN (0x1 << 4) |
| #define PHY_40NM_OTG_PU (0x1 << 3) |
| |
| #define PHY_40NM_CDP_EN (0x1 << 2) |
| #define PHY_40NM_DCP_EN (0x1 << 3) |
| #define PHY_40NM_PD_EN (0x1 << 4) |
| #define PHY_40NM_CDP_DM_AUTO_SWITCH (0x1 << 5) |
| #define PHY_40NM_ENABLE_SWITCH_DM (0x1 << 6) |
| #define PHY_40NM_ENABLE_SWITCH_DP (0x1 << 7) |
| #define PHY_40NM_VDAT_CHARGE (0x3 << 8) |
| #define PHY_40NM_VSRC_CHARGE (0x3 << 10) |
| #define PHY_40NM_DP_DM_SWAP_CTRL (0x1 << 15) |
| #define PHY_40NM_CHARGER_STATUS_MASK (0x1 << 4) |
| |
| #define PHY_40NM_CTRL_AVALID_CONTROL (0x3 << 24) |
| #define PHY_40NM_CTRL_PU_PLL (0x1 << 1) |
| #define PHY_40NM_CTRL_PU (0x1 << 0) |
| |
| /* USB PXA1928 PHY mapping */ |
| #define PHY_28NM_PLL_REG0 0x0 |
| #define PHY_28NM_PLL_REG1 0x4 |
| #define PHY_28NM_CAL_REG 0x8 |
| #define PHY_28NM_TX_REG0 0x0C |
| #define PHY_28NM_TX_REG1 0x10 |
| #define PHY_28NM_RX_REG0 0x14 |
| #define PHY_28NM_RX_REG1 0x18 |
| #define PHY_28NM_DIG_REG0 0x1C |
| #define PHY_28NM_DIG_REG1 0x20 |
| #define PHY_28NM_TEST_REG0 0x24 |
| #define PHY_28NM_TEST_REG1 0x28 |
| #define PHY_28NM_MOC_REG 0x2C |
| #define PHY_28NM_PHY_RESERVE 0x30 |
| #define PHY_28NM_OTG_REG 0x34 |
| #define PHY_28NM_CHRG_DET 0x38 |
| #define PHY_28NM_CTRL_REG0 0xC4 |
| #define PHY_28NM_CTRL_REG1 0xC8 |
| #define PHY_28NM_CTRL_REG2 0xD4 |
| #define PHY_28NM_CTRL_REG3 0xDC |
| #define PHY_28NM_VBUSVALID_CTRL_SHIFT 4 |
| |
| /* PHY_28NM_PLL_REG0 */ |
| #define PHY_28NM_PLL_READY_MASK (0x1 << 31) |
| |
| #define PHY_28NM_PLL_SELLPFR_SHIFT 28 |
| #define PHY_28NM_PLL_SELLPFR_MASK (0x3 << 28) |
| |
| #define PHY_28NM_PLL_FBDIV_SHIFT 16 |
| #define PHY_28NM_PLL_FBDIV_MASK (0x1ff << 16) |
| |
| #define PHY_28NM_PLL_ICP_SHIFT 8 |
| #define PHY_28NM_PLL_ICP_MASK (0x7 << 8) |
| |
| #define PHY_28NM_PLL_REFDIV_SHIFT 0 |
| #define PHY_28NM_PLL_REFDIV_MASK 0x7f |
| |
| /* PHY_28NM_PLL_REG1 */ |
| #define PHY_28NM_PLL_PU_BY_REG_SHIFT 1 |
| #define PHY_28NM_PLL_PU_BY_REG_MASK (0x1 << 1) |
| |
| #define PHY_28NM_PLL_PU_PLL_SHIFT 0 |
| #define PHY_28NM_PLL_PU_PLL_MASK (0x1 << 0) |
| |
| /* PHY_28NM_CAL_REG */ |
| #define PHY_28NM_PLL_PLLCAL_DONE_SHIFT 31 |
| #define PHY_28NM_PLL_PLLCAL_DONE_MASK (0x1 << 31) |
| |
| #define PHY_28NM_PLL_IMPCAL_DONE_SHIFT 23 |
| #define PHY_28NM_PLL_IMPCAL_DONE_MASK (0x1 << 23) |
| |
| #define PHY_28NM_PLL_KVCO_SHIFT 16 |
| #define PHY_28NM_PLL_KVCO_MASK (0x7 << 16) |
| |
| #define PHY_28NM_PLL_CAL12_SHIFT 20 |
| #define PHY_28NM_PLL_CAL12_MASK (0x3 << 20) |
| |
| #define PHY_28NM_IMPCAL_VTH_SHIFT 8 |
| #define PHY_28NM_IMPCAL_VTH_MASK (0x7 << 8) |
| |
| #define PHY_28NM_PLLCAL_START_SHIFT 22 |
| #define PHY_28NM_IMPCAL_START_SHIFT 13 |
| |
| /* PHY_28NM_TX_REG0 */ |
| #define PHY_28NM_TX_PU_BY_REG_SHIFT 25 |
| |
| #define PHY_28NM_TX_PU_ANA_SHIFT 24 |
| |
| #define PHY_28NM_TX_AMP_SHIFT 20 |
| #define PHY_28NM_TX_AMP_MASK (0x7 << 20) |
| |
| /* PHY_28NM_RX_REG0 */ |
| #define PHY_28NM_RX_SQ_THRESH_SHIFT 0 |
| #define PHY_28NM_RX_SQ_THRESH_MASK (0xf << 0) |
| |
| /* PHY_28NM_RX_REG1 */ |
| #define PHY_28NM_RX_SQCAL_DONE_SHIFT 31 |
| #define PHY_28NM_RX_SQCAL_DONE_MASK (0x1 << 31) |
| |
| /* PHY_28NM_DIG_REG0 */ |
| #define PHY_28NM_DIG_BITSTAFFING_ERR_MASK (0x1 << 31) |
| #define PHY_28NM_DIG_SYNC_ERR_MASK (0x1 << 30) |
| |
| #define PHY_28NM_DIG_SQ_FILT_SHIFT 16 |
| #define PHY_28NM_DIG_SQ_FILT_MASK (0x7 << 16) |
| |
| #define PHY_28NM_DIG_SQ_BLK_SHIFT 12 |
| #define PHY_28NM_DIG_SQ_BLK_MASK (0x7 << 12) |
| |
| #define PHY_28NM_DIG_SYNC_NUM_SHIFT 0 |
| #define PHY_28NM_DIG_SYNC_NUM_MASK (0x3 << 0) |
| |
| #define PHY_28NM_PLL_LOCK_BYPASS_SHIFT 7 |
| |
| /* PHY_28NM_OTG_REG */ |
| #define PHY_28NM_OTG_CONTROL_BY_PIN_SHIFT 5 |
| #define PHY_28NM_OTG_PU_OTG_SHIFT 4 |
| |
| #define PHY_28NM_CHGDTC_ENABLE_SWITCH_DM_SHIFT_28 13 |
| #define PHY_28NM_CHGDTC_ENABLE_SWITCH_DP_SHIFT_28 12 |
| #define PHY_28NM_CHGDTC_VSRC_CHARGE_SHIFT_28 10 |
| #define PHY_28NM_CHGDTC_VDAT_CHARGE_SHIFT_28 8 |
| #define PHY_28NM_CHGDTC_CDP_DM_AUTO_SWITCH_SHIFT_28 7 |
| #define PHY_28NM_CHGDTC_DP_DM_SWAP_SHIFT_28 6 |
| #define PHY_28NM_CHGDTC_PU_CHRG_DTC_SHIFT_28 5 |
| #define PHY_28NM_CHGDTC_PD_EN_SHIFT_28 4 |
| #define PHY_28NM_CHGDTC_DCP_EN_SHIFT_28 3 |
| #define PHY_28NM_CHGDTC_CDP_EN_SHIFT_28 2 |
| #define PHY_28NM_CHGDTC_TESTMON_CHRGDTC_SHIFT_28 0 |
| |
| #define PHY_28NM_CTRL1_CHRG_DTC_OUT_SHIFT_28 4 |
| #define PHY_28NM_CTRL1_VBUSDTC_OUT_SHIFT_28 2 |
| #define PHY_28NM_RX_SQCAL_START_SHIFT 4 |
| #define PHY_28NM_RX_SQCAL_START_MASK (0x1 << 4) |
| |
| #define PHY_28NM_CTRL3_OVERWRITE_SHIFT 0 |
| #define PHY_28NM_CTRL3_VBUS_VALID_SHIFT 4 |
| #define PHY_28NM_CTRL3_AVALID_SHIFT 5 |
| #define PHY_28NM_CTRL3_BVALID_SHIFT 6 |
| |
| #define PHY_28NM_HSIC_PLL_CTRL01 0x1c |
| #define PHY_28NM_HSIC_PLL_CTRL2 0x20 |
| #define PHY_28NM_HSIC_INT 0x28 |
| |
| #if defined(CONFIG_CPU_ASR18XX) |
| #define PHY_28NM_HSIC_CTL0 0x08 |
| #define PHY_28NM_HSIC_TEST2 0x18 |
| #endif |
| |
| #define PHY_28NM_HSIC_PLL_SELLPFR_SHIFT 26 |
| #define PHY_28NM_HSIC_PLL_FBDIV_SHIFT 0 |
| #define PHY_28NM_HSIC_PLL_REFDIV_SHIFT 9 |
| |
| #define PHY_28NM_HSIC_S2H_PU_PLL_SHIFT 10 |
| #define PHY_28NM_HSIC_H2S_PLL_LOCK_SHIFT 15 |
| #define PHY_28NM_HSIC_CTRL 0x08 |
| #define PHY_28NM_HSIC_S2H_HSIC_ENABLE_SHIFT 7 |
| #define PHY_28NM_HSIC_S2H_HSIC_CONN_BYP_SHIFT 3 |
| #define PHY_28NM_HSIC_IMPCAL_CAL 0x18 |
| #define PHY_28NM_HSIC_USB_CTRL 0x24 |
| #define PHY_28NM_HSIC_CONNECT_INT_BIT (0x2) |
| #define PHY_28NM_HSIC_INT_HS_READY (0x4) |
| |
| #if defined(CONFIG_CPU_ASR18XX) |
| #define PHY_28NM_HSIC_H2S_IMPCAL_DONE_SHIFT 11 |
| #else |
| #define PHY_28NM_HSIC_H2S_IMPCAL_DONE_SHIFT 27 |
| #endif |
| #if defined(CONFIG_CPU_ASR18XX) |
| #define PHY_28NM_HSIC_PLL_CTRL1_ICP_SHIFT 0 |
| #define PHY_28NM_HSIC_CTL0_S2H_DRV_SE0_4RESUME 14 |
| #define PHY_28NM_HSIC_CTL0_S2H_BUS_RESET_EN 5 |
| #define PHY_28NM_HSIC_DPPULLDOWN_CTRL_DEVICE (0x1 << 5) |
| #define PHY_28NM_HSIC_DPPULLDOWN_CTRL_MASK (0x3 << 5) |
| #endif |
| |
| #define PHY_28NMHP_PHY_REG00 (0x00) |
| #define PHY_28NMHP_PHY_REG01 (0x04) |
| #define PHY_28NMHP_PHY_REG02 (0x08) |
| #define PHY_28NMHP_PHY_REG03 (0x0C) |
| #define PHY_28NMHP_PHY_REG04 (0x10) |
| #define PHY_28NMHP_PHY_REG05 (0x14) |
| #define PHY_28NMHP_PHY_REG06 (0x18) |
| #define PHY_28NMHP_PHY_REG07 (0x1C) |
| #define PHY_28NMHP_PHY_REG08 (0x20) |
| #define PHY_28NMHP_PHY_REG09 (0x24) |
| #define PHY_28NMHP_PHY_REG0A (0x28) |
| #define PHY_28NMHP_PHY_REG0B (0x2C) |
| #define PHY_28NMHP_PHY_REG0C (0x30) |
| #define PHY_28NMHP_PHY_REG0D (0x34) |
| #define PHY_28NMHP_PHY_REG0E (0x38) |
| #define PHY_28NMHP_PHY_REG10 (0x40) |
| #define PHY_28NMHP_PHY_REG22 (0x88) |
| #define PHY_28NMHP_PHY_REG23 (0x8C) |
| #define PHY_28NMHP_PHY_REG25 (0x94) |
| #define PHY_28NMHP_PHY_REG28 (0xA0) |
| #define PHY_28NMHP_PHY_REG29 (0xA4) |
| #define PHY_28NMHP_PHY_REG33 (0xCC) |
| |
| #define PHY_28NMHP_PLL_BIT_RDY (0x1 << 0) |
| #define PHY_28NMHP_CFG_HS_SRCS_SEL (0x1 << 0) |
| #define PHY_28NMHP_SUSPEND_PLL (0x1 << 13) |
| #define PHY_28NMHP_SUSPEND_PHY (0x1 << 12) |
| #define PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN (0x1 << 14) |
| #define PHY_28NMHP_DCP_DET_PULL_UP_DOWN ((0x1 << 11) | (0x1 << 8)) |
| #define PHY_28NMHP_DCP_DET_PULL_UP_NONE ((0x1 << 11) | (0x1 << 10)) |
| #define PHY_28NMHP_DCP_DET_PULL_DOWN_DOWN ((0x1 << 10) | (0x1 << 8)) |
| #define PHY_28NMHP_DCP_DET_PULL_MASK (0xF << 8) |
| #define PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_MASK (0xF << 8) |
| #define PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_VAL (0x9 << 8) |
| #define APMU_USB_CLK_CTRL (0x5C) |
| |
| static int _mv_usb2_phy_55nm_init(struct mv_usb2_phy *mv_phy) |
| { |
| struct platform_device *pdev = mv_phy->pdev; |
| unsigned int loops = 0; |
| void __iomem *base = mv_phy->base; |
| unsigned int val; |
| |
| val = readl(base + PHY_55NM_CTRL); |
| /* Initialize the USB PHY power */ |
| if (mv_phy->drv_data.phy_rev == REV_PXA910) { |
| val |= (1 << PHY_55NM_CTRL_INPKT_DELAY_SOF_SHIFT) |
| | (1 << PHY_55NM_CTRL_PU_REF_SHIFT); |
| } |
| |
| val |= (1 << PHY_55NM_CTRL_PLL_PWR_UP_SHIFT) |
| | (1 << PHY_55NM_CTRL_PWR_UP_SHIFT); |
| writel(val, base + PHY_55NM_CTRL); |
| |
| /* UTMI_PLL settings */ |
| val = readl(base + PHY_55NM_PLL); |
| val &= ~(PHY_55NM_PLL_PLLVDD18_MASK |
| | PHY_55NM_PLL_PLLVDD12_MASK |
| | PHY_55NM_PLL_PLLCALI12_MASK |
| | PHY_55NM_PLL_FBDIV_MASK |
| | PHY_55NM_PLL_REFDIV_MASK |
| | PHY_55NM_PLL_ICP_MASK |
| | PHY_55NM_PLL_KVCO_MASK |
| | PHY_55NM_PLL_VCOCAL_START); |
| |
| val |= (0xee << PHY_55NM_PLL_FBDIV_SHIFT) |
| | (0xb << PHY_55NM_PLL_REFDIV_SHIFT) |
| | (3 << PHY_55NM_PLL_PLLVDD18_SHIFT) |
| | (3 << PHY_55NM_PLL_PLLVDD12_SHIFT) |
| | (3 << PHY_55NM_PLL_PLLCALI12_SHIFT) |
| | (1 << PHY_55NM_PLL_ICP_SHIFT) |
| | (3 << PHY_55NM_PLL_KVCO_SHIFT); |
| writel(val, base + PHY_55NM_PLL); |
| |
| /* UTMI_TX */ |
| val = readl(base + PHY_55NM_TX); |
| val &= ~(PHY_55NM_TX_REG_EXT_FS_RCAL_EN_MASK |
| | PHY_55NM_TX_TXVDD12_MASK |
| | PHY_55NM_TX_CK60_PHSEL_MASK |
| | PHY_55NM_TX_IMPCAL_VTH_MASK |
| | PHY_55NM_TX_REG_EXT_FS_RCAL_MASK |
| | PHY_55NM_TX_AMP_MASK |
| | PHY_55NM_TX_REG_RCAL_START); |
| val |= (3 << PHY_55NM_TX_TXVDD12_SHIFT) |
| | (4 << PHY_55NM_TX_CK60_PHSEL_SHIFT) |
| | (4 << PHY_55NM_TX_IMPCAL_VTH_SHIFT) |
| | (8 << PHY_55NM_TX_REG_EXT_FS_RCAL_SHIFT) |
| | (3 << PHY_55NM_TX_AMP_SHIFT); |
| writel(val, base + PHY_55NM_TX); |
| |
| /* UTMI_RX */ |
| val = readl(base + PHY_55NM_RX); |
| val &= ~(PHY_55NM_RX_SQ_THRESH_MASK |
| | PHY_55NM_RX_REG_SQ_LENGTH_MASK); |
| val |= (7 << PHY_55NM_RX_SQ_THRESH_SHIFT) |
| | (2 << PHY_55NM_RX_REG_SQ_LENGTH_SHIFT); |
| writel(val, base + PHY_55NM_RX); |
| |
| /* UTMI_IVREF */ |
| if (mv_phy->drv_data.phy_rev == REV_PXA168) |
| /* |
| * Fixing Microsoft Altair board interface with NEC hub issue - |
| * Set UTMI_IVREF from 0x4a3 to 0x4bf. |
| */ |
| writel(0x4bf, base + PHY_55NM_IVREF); |
| |
| /* |
| * Toggle VCOCAL_START bit of UTMI_PLL. It is active at rising edge. |
| * It is low level by UTMI_PLL initialization above. |
| */ |
| val = readl(base + PHY_55NM_PLL); |
| /* |
| * Delay 200us for low level, and 40us for high level. |
| * Make sure we can get a effective rising edege. |
| * It should be set to low after issue rising edge. |
| * The delay value is suggested by DE. |
| */ |
| udelay(300); |
| val |= PHY_55NM_PLL_VCOCAL_START; |
| writel(val, base + PHY_55NM_PLL); |
| udelay(40); |
| val &= ~PHY_55NM_PLL_VCOCAL_START; |
| writel(val, base + PHY_55NM_PLL); |
| |
| /* |
| * Toggle REG_RCAL_START bit of UTMI_TX. |
| * it is low level by UTMI_TX initialization above. |
| */ |
| val = readl(base + PHY_55NM_TX); |
| /* same as VCOCAL_START, except it is triggered by low->high->low */ |
| udelay(400); |
| val |= PHY_55NM_TX_REG_RCAL_START; |
| writel(val, base + PHY_55NM_TX); |
| udelay(40); |
| val &= ~PHY_55NM_TX_REG_RCAL_START; |
| writel(val, base + PHY_55NM_TX); |
| udelay(400); |
| |
| /* Make sure PHY PLL is ready */ |
| loops = 0; |
| while (1) { |
| val = readl(base + PHY_55NM_PLL); |
| if (val & PHY_55NM_PLL_PLL_READY) |
| break; |
| udelay(1000); |
| loops++; |
| if (loops > 100) { |
| dev_warn(&pdev->dev, "calibrate timeout, UTMI_PLL %x\n", |
| readl(base + PHY_55NM_PLL)); |
| return -ETIME; |
| } |
| } |
| |
| if (mv_phy->drv_data.phy_rev == REV_PXA168) { |
| val = readl(base + PHY_55NM_RESERVE); |
| val |= (1 << 5); |
| writel(val, base + PHY_55NM_RESERVE); |
| /* Turn on UTMI PHY OTG extension */ |
| writel(PHY_55NM_OTG_ADDON_OTG_ON, |
| base + PHY_55NM_OTG_ADDON); |
| } |
| |
| return 0; |
| } |
| |
| static int _mv_usb2_phy_55nm_shutdown(struct mv_usb2_phy *mv_phy) |
| { |
| void __iomem *base = mv_phy->base; |
| unsigned int val; |
| |
| if (mv_phy->drv_data.phy_rev == REV_PXA168) |
| writel(0, base + PHY_55NM_OTG_ADDON); |
| |
| val = readl(base + PHY_55NM_CTRL); |
| val &= ~(PHY_55NM_CTRL_RXBUF_PDWN |
| | PHY_55NM_CTRL_TXBUF_PDWN |
| | PHY_55NM_CTRL_USB_CLK_EN |
| | (1 << PHY_55NM_CTRL_PWR_UP_SHIFT) |
| | (1 << PHY_55NM_CTRL_PLL_PWR_UP_SHIFT)); |
| |
| writel(val, base + PHY_55NM_CTRL); |
| |
| return 0; |
| } |
| |
| static int _mv_usb2_phy_40nm_init(struct mv_usb2_phy *mv_phy) |
| { |
| void __iomem *base = mv_phy->base; |
| |
| u16 tmp16; |
| u32 tmp32; |
| |
| /* Program 0xd4207004[8:0]= 0xF0 */ |
| /* Program 0xd4207004[13:9]=0xD */ |
| writew((PHY_40NM_PLL0_DEFAULT |
| & (~PHY_40NM_PLL0_FBDIV(~0)) |
| & (~PHY_40NM_PLL0_REFDIV(~0))) |
| | PHY_40NM_PLL0_FBDIV(0xF0) |
| | PHY_40NM_PLL0_REFDIV(0xD), base + PHY_40NM_PLL0); |
| |
| /* Program 0xd4207008[11:8]=0x1 */ |
| /* Program 0xd4207008[14]=0x0 */ |
| /* Program 0xd4207008[13]=0x1 */ |
| /* Program 0xd4207008[12]=0x1 */ |
| /* Program 0xd4207008[1:0]=0x3 */ |
| writew((PHY_40NM_PLL1_DEFAULT |
| & (~PHY_40NM_PLL1_ICP(~0)) |
| & (~PHY_40NM_PLL1_PLL_CONTROL_BY_PIN) |
| & (~PHY_40NM_PLL1_PLLCAL12(~0))) |
| | PHY_40NM_PLL1_ICP(0x1) |
| | PHY_40NM_PLL1_PU_PLL |
| | PHY_40NM_PLL1_PLL_LOCK_BYPASS |
| | PHY_40NM_PLL1_PLLCAL12(3), base + PHY_40NM_PLL1); |
| |
| /* Program 0xd4207014[6:4]=0x5 */ |
| /* Program 0xd4207014[9:8]=0x3 */ |
| /* Program 0xd4207014[3:0]=0x4 */ |
| writew((PHY_40NM_TX1_DEFAULT |
| & (~PHY_40NM_TX1_AMP(~0)) |
| & (~PHY_40NM_TX1_TXVDD12(~0)) |
| & (~PHY_40NM_TX1_CK60_PHSEL(~0))) |
| | PHY_40NM_TX1_AMP(5) |
| | PHY_40NM_TX1_TXVDD12(3) |
| | PHY_40NM_TX1_CK60_PHSEL(4), base + PHY_40NM_TX1); |
| |
| /* Program 0xd4207018[11:10]=0x2 */ |
| writew((PHY_40NM_TX2_DEFAULT & (~PHY_40NM_TX2_DRV_SLEWRATE(3))) |
| | PHY_40NM_TX2_DRV_SLEWRATE(2), base + PHY_40NM_TX2); |
| |
| /* Program 0xd4207020[7:4]=0xa */ |
| writew((PHY_40NM_RX0_DEFAULT |
| & (~PHY_40NM_RX0_SQ_THRESH(~0))) |
| | PHY_40NM_RX0_SQ_THRESH(0xA), base + PHY_40NM_RX0); |
| |
| /* Program 0xd4207034[14]=0x1 */ |
| writew(PHY_40NM_ANA1_DEFAULT |
| | PHY_40NM_ANA1_PU_ANA, base + PHY_40NM_ANA1); |
| |
| /* Program 0xd420705c[3]=0x1 */ |
| writew(PHY_40NM_OTG_DEFAULT |
| | PHY_40NM_OTG_PU, base + PHY_40NM_OTG); |
| |
| /* Program 0xD4207104[1] = 0x1 */ |
| /* Program 0xD4207104[0] = 0x1 */ |
| tmp32 = readl(base + PHY_40NM_CTRL); |
| writel(tmp32 | PHY_40NM_CTRL_PU_PLL |
| | PHY_40NM_CTRL_PU, base + PHY_40NM_CTRL); |
| |
| /* Wait for 200us */ |
| udelay(200); |
| |
| /* Program 0xd4207008[2]=0x1 */ |
| tmp16 = readw(base + PHY_40NM_PLL1); |
| writew(tmp16 |
| | PHY_40NM_PLL1_VCOCAL_START, base + PHY_40NM_PLL1); |
| |
| /* Wait for 400us */ |
| udelay(400); |
| |
| /* Polling 0xd4207008[15]=0x1 */ |
| while ((readw(base + PHY_40NM_PLL1) |
| & PHY_40NM_PLL1_PLL_READY) == 0) |
| pr_info("polling usb phy\n"); |
| |
| /* Program 0xd4207010[13]=0x1 */ |
| tmp16 = readw(base + PHY_40NM_TX0); |
| writew(tmp16 | PHY_40NM_TX0_RCAL_START, base + PHY_40NM_TX0); |
| |
| /* Wait for 40us */ |
| udelay(40); |
| |
| /* Program 0xd4207010[13]=0x0 */ |
| tmp16 = readw(base + PHY_40NM_TX0); |
| writew(tmp16 & (~PHY_40NM_TX0_RCAL_START), base + PHY_40NM_TX0); |
| |
| /* Wait for 400us */ |
| udelay(400); |
| |
| pr_info("usb phy inited %x!\n", readl(base + PHY_40NM_CTRL)); |
| |
| return 0; |
| } |
| |
| static void _mv_usb2_phy_40nm_shutdown(struct mv_usb2_phy *mv_phy) |
| { |
| void __iomem *base = mv_phy->base; |
| u16 tmp16; |
| u32 tmp32; |
| |
| pr_info("usb phy deinit!\n"); |
| |
| /* Program 0xd4207008[14:13]=0x0 */ |
| tmp16 = readw(base + PHY_40NM_PLL1); |
| writew(tmp16 |
| & (~PHY_40NM_PLL1_PLL_CONTROL_BY_PIN) |
| & (~PHY_40NM_PLL1_PU_PLL), base + PHY_40NM_PLL1); |
| |
| /* Program 0xd4207034[14]=0x0 */ |
| tmp16 = readw(base + PHY_40NM_ANA1); |
| writew(tmp16 |
| & (~PHY_40NM_ANA1_PU_ANA), base + PHY_40NM_ANA1); |
| |
| /* Program 0xD4207104[1] = 0x0 */ |
| /* Program 0xD4207104[0] = 0x0 */ |
| tmp32 = readl(base + PHY_40NM_CTRL); |
| writel(tmp32 |
| & (~PHY_40NM_CTRL_PU_PLL) |
| & (~PHY_40NM_CTRL_PU), base + PHY_40NM_CTRL); |
| } |
| |
| #if defined(CONFIG_CPU_ASR18XX) |
| static int asr_usb_host_phy_private(struct usb_phy *phy, u32 option) |
| { |
| struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy); |
| void __iomem *phy_base = mv_phy->base; |
| int count = 0x100000; |
| |
| pr_info("asr usb phy host priviate: %d\n", option); |
| |
| if (option) { |
| /* |
| * set cfg_en_hstsof, cfg_discon_det_en |
| * clear rb_hshost_disc_raw |
| */ |
| if (cpu_is_asr1803_z1()) { |
| writel(readl(phy_base + PHY_28NMHP_PHY_REG04) | 0x1, |
| phy_base + PHY_28NMHP_PHY_REG04); |
| writel(readl(phy_base + PHY_28NMHP_PHY_REG08) | (0x1 << 9), |
| phy_base + PHY_28NMHP_PHY_REG08); |
| } |
| } else { |
| /* |
| * clar cfg_en_hstsof, cfg_discon_det_en and rb_hshost_disc_raw |
| */ |
| if (cpu_is_asr1803_z1()) { |
| writel(readl(phy_base + PHY_28NMHP_PHY_REG04) & (~0x1), |
| phy_base + PHY_28NMHP_PHY_REG04); |
| writel(readl(phy_base + PHY_28NMHP_PHY_REG08) & (~(0x1 << 9)), |
| phy_base + PHY_28NMHP_PHY_REG08); |
| } |
| writel(readl(phy_base + PHY_28NMHP_PHY_REG10) | 0x1, |
| phy_base + PHY_28NMHP_PHY_REG10); |
| while((readl(phy_base + PHY_28NMHP_PHY_REG10) & 0x1) && (count--)); |
| if (unlikely(count == 0)) |
| pr_err("phy rd_hshost_disc_raw clear failed\n"); |
| } |
| |
| return 0; |
| |
| } |
| #endif |
| |
| #if defined(CONFIG_CPU_ASR18XX) |
| static int asr_usb_phy_softrst_private(struct usb_phy *phy, u32 arg) |
| { |
| struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy); |
| void __iomem *phy_base = mv_phy->base; |
| u32 regval; |
| static u32 phy_sts_rcd = 0; |
| |
| if (cpu_is_asr1806()) { |
| regval = readl(phy_base + PHY_28NMHP_PHY_REG0E); |
| phy_sts_rcd = phy_sts_rcd << 1; |
| if ((regval & (0x3 << 13)) == (0x3 << 13)) { |
| phy_sts_rcd |= 0x1; |
| pr_err("phy sts: 0x%x, 0x%x\n", regval, phy_sts_rcd); |
| if ((phy_sts_rcd & 0x3) == 0x3) |
| goto do_softrst; |
| else |
| goto out_no_softrst; |
| } |
| } else if (cpu_is_asr1803()) { |
| regval = readl(phy_base + PHY_28NMHP_PHY_REG04); |
| regval &= ~(0xff << 8); |
| regval |= (0x9 << 8); |
| writel(regval, phy_base + PHY_28NMHP_PHY_REG04); |
| |
| regval = readl(phy_base + PHY_28NMHP_PHY_REG33); |
| if (regval & (0x1 << 21)) { |
| pr_err("phy reg_cc err: 0x%x\n", regval); |
| goto do_softrst; |
| } |
| } |
| /* no err */ |
| return 0; |
| |
| do_softrst: |
| regval = readl(phy_base + PHY_28NMHP_PHY_REG01); |
| regval &= ~(0x3 << 6); |
| writel(regval, phy_base + PHY_28NMHP_PHY_REG01); |
| udelay(10); |
| regval = readl(phy_base + PHY_28NMHP_PHY_REG01); |
| regval |= (0x3 << 6); |
| writel(regval, phy_base + PHY_28NMHP_PHY_REG01); |
| |
| out_no_softrst: |
| /* Write 1s to clear phy err status */ |
| writel(0xFFFFFFFF, phy_base + PHY_28NMHP_PHY_REG0E); |
| |
| return 1; |
| } |
| #endif |
| |
| static int _mv_usb2_phy_28nmhp_init(struct mv_usb2_phy *mv_phy) |
| { |
| unsigned int loops = 200; |
| void __iomem *base = mv_phy->base; |
| unsigned int val; |
| void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU); |
| |
| #if defined(CONFIG_CPU_ASR18XX) |
| if (cpu_is_asr1803() || cpu_is_asr1806()) |
| mv_phy->phy.phy_private2 = asr_usb_host_phy_private; |
| #endif |
| |
| #if defined(CONFIG_CPU_ASR18XX) |
| if (cpu_is_asr1803() || cpu_is_asr1806()) |
| mv_phy->phy.phy_private3 = asr_usb_phy_softrst_private; |
| #endif |
| |
| /* WAIT FOR PHY PLL RDY */ |
| while (((readl(base + PHY_28NMHP_PHY_REG01) & PHY_28NMHP_PLL_BIT_RDY) != |
| PHY_28NMHP_PLL_BIT_RDY) |
| && (loops--)) |
| udelay(1); |
| if (loops == 0) |
| pr_err("!!!!!!!!!!phy pll not ready after 200us\n"); |
| |
| if (cpu_is_asr1802s()) { |
| /* pullup dp and pulldown dm */ |
| val = readl(base + PHY_28NMHP_PHY_REG28); |
| val &= ~PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN; |
| writel(val, base + PHY_28NMHP_PHY_REG28); |
| } else if (cpu_is_asr1803() || cpu_is_asr1806()) { |
| /* pullup dp and pulldown dm */ |
| val = readl(base + PHY_28NMHP_PHY_REG29); |
| val &= ~PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN; |
| writel(val, base + PHY_28NMHP_PHY_REG29); |
| } |
| |
| /* Release usb2 phy internal reset and enable clock gating */ |
| writel(0x60ef, base + PHY_28NMHP_PHY_REG01); |
| #if 0 |
| /* enable phy hw reset for asr1806 */ |
| if (cpu_is_asr1806()) |
| writel(0xC01C, base + PHY_28NMHP_PHY_REG0D); |
| else |
| #endif |
| writel(0x1C, base + PHY_28NMHP_PHY_REG0D); |
| /* |
| * This is 1802s only |
| * 1803 has new reg -> threshold map table |
| * HS RX sequelch threshold: 4 -> 7 for yield rate |
| */ |
| if (cpu_is_asr1802s()) |
| writel(0x7468, base + PHY_28NMHP_PHY_REG23); |
| else if (cpu_is_asr1803() || cpu_is_asr1806()) { |
| /* set to serial mode for stability */ |
| writel(readl(base + PHY_28NMHP_PHY_REG06) | (0x1), base + PHY_28NMHP_PHY_REG06); |
| |
| /* increase tx dac by 10%, will add back if needed in some special cases */ |
| /* writel((readl(base + PHY_28NMHP_PHY_REG29) & (~0x1F)) | (0x1B), base + PHY_28NMHP_PHY_REG29); */ |
| } |
| |
| val = readl(base + PHY_28NMHP_PHY_REG0A); |
| val &= ~(PHY_28NMHP_SUSPEND_PLL | PHY_28NMHP_SUSPEND_PHY); |
| writel(val, base + PHY_28NMHP_PHY_REG0A); |
| |
| /* Write 1s to clear phy err status */ |
| writel(0xFFFFFFFF, base + PHY_28NMHP_PHY_REG0E); |
| |
| /* rls controller */ |
| if (cpu_is_asr1806()) { |
| writel(readl(apmu_base + APMU_USB_CLK_CTRL) | (0x1 << 2), (apmu_base + APMU_USB_CLK_CTRL)); |
| pr_info("asr1806 usbphy done\n"); |
| } |
| return 0; |
| } |
| |
| static int _mv_usb2_phy_28nmhp_preinit(struct mv_usb2_phy *mv_phy) |
| { |
| unsigned int loops = 400; |
| void __iomem *base = mv_phy->base; |
| unsigned int val; |
| |
| pr_info("%s\n", __func__); |
| /* WAIT FOR PHY PLL RDY */ |
| while (((readl(base + PHY_28NMHP_PHY_REG01) & PHY_28NMHP_PLL_BIT_RDY) != |
| PHY_28NMHP_PLL_BIT_RDY) |
| && (loops--)) |
| udelay(1); |
| if (loops == 0) |
| pr_err("!!!!!!!!!!phy pll not ready after 200us\n"); |
| |
| if (cpu_is_asr1802s()) { |
| /* pullup dp and pulldown dm */ |
| val = readl(base + PHY_28NMHP_PHY_REG25); |
| val &= ~PHY_28NMHP_DCP_DET_PULL_MASK; |
| val |= PHY_28NMHP_DCP_DET_PULL_DOWN_DOWN; |
| writel(val, base + PHY_28NMHP_PHY_REG25); |
| |
| val = readl(base + PHY_28NMHP_PHY_REG28); |
| val |= PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN; |
| writel(val, base + PHY_28NMHP_PHY_REG28); |
| } else if (cpu_is_asr1803()) { |
| /* pullup dp and pulldown dm */ |
| val = readl(base + PHY_28NMHP_PHY_REG25); |
| val &= ~PHY_28NMHP_DCP_DET_PULL_MASK; |
| val |= PHY_28NMHP_DCP_DET_PULL_DOWN_DOWN; |
| writel(val, base + PHY_28NMHP_PHY_REG25); |
| |
| val = readl(base + PHY_28NMHP_PHY_REG29); |
| val |= PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN; |
| writel(val, base + PHY_28NMHP_PHY_REG29); |
| } |
| return 0; |
| } |
| |
| static void _mv_usb2_phy_28nmhp_shutdown(struct mv_usb2_phy *mv_phy) |
| { |
| void __iomem *base = mv_phy->base; |
| unsigned int val; |
| |
| #if defined(CONFIG_CPU_ASR18XX) |
| if (cpu_is_asr1803() || cpu_is_asr1806()) |
| mv_phy->phy.phy_private2 = NULL; |
| #endif |
| |
| #if defined(CONFIG_CPU_ASR18XX) |
| if (cpu_is_asr1803() || cpu_is_asr1806()) |
| mv_phy->phy.phy_private3 = NULL; |
| #endif |
| |
| val = readl(base + PHY_28NMHP_PHY_REG0B); |
| val &= ~(PHY_28NMHP_SUSPEND_PLL | PHY_28NMHP_SUSPEND_PHY); |
| writel(val, base + PHY_28NMHP_PHY_REG0B); |
| |
| val = readl(base + PHY_28NMHP_PHY_REG0A); |
| val |= (PHY_28NMHP_SUSPEND_PLL | PHY_28NMHP_SUSPEND_PHY); |
| writel(val, base + PHY_28NMHP_PHY_REG0A); |
| |
| if (cpu_is_asr1802s()) { |
| /* pulldown dp and pulldown dm */ |
| val = readl(base + PHY_28NMHP_PHY_REG25); |
| val &= ~PHY_28NMHP_DCP_DET_PULL_MASK; |
| val |= PHY_28NMHP_DCP_DET_PULL_DOWN_DOWN; |
| writel(val, base + PHY_28NMHP_PHY_REG25); |
| |
| val = readl(base + PHY_28NMHP_PHY_REG28); |
| val |= PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN; |
| writel(val, base + PHY_28NMHP_PHY_REG28); |
| } else if (cpu_is_asr1803()) { |
| /* pulldown dp and pulldown dm */ |
| val = readl(base + PHY_28NMHP_PHY_REG25); |
| val &= ~PHY_28NMHP_DCP_DET_PULL_MASK; |
| val |= PHY_28NMHP_DCP_DET_PULL_DOWN_DOWN; |
| writel(val, base + PHY_28NMHP_PHY_REG25); |
| |
| val = readl(base + PHY_28NMHP_PHY_REG29); |
| val |= PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN; |
| writel(val, base + PHY_28NMHP_PHY_REG29); |
| } |
| } |
| |
| static int mv_usb2_phy_suspend(struct usb_phy *phy, int suspend) |
| { |
| struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy); |
| void __iomem *base = mv_phy->base; |
| unsigned int val; |
| |
| pr_info("USB2 PHY set_suspend: %d...\n", suspend); |
| |
| if (suspend) { |
| val = readl(base + PHY_28NMHP_PHY_REG0B); |
| val &= ~(PHY_28NMHP_SUSPEND_PLL | PHY_28NMHP_SUSPEND_PHY); |
| writel(val, base + PHY_28NMHP_PHY_REG0B); |
| |
| val = readl(base + PHY_28NMHP_PHY_REG0A); |
| val |= (PHY_28NMHP_SUSPEND_PLL | PHY_28NMHP_SUSPEND_PHY); |
| writel(val, base + PHY_28NMHP_PHY_REG0A); |
| |
| /* pulldown dp and pulldown dm */ |
| val = readl(base + PHY_28NMHP_PHY_REG25); |
| val &= ~PHY_28NMHP_DCP_DET_PULL_MASK; |
| val |= PHY_28NMHP_DCP_DET_PULL_DOWN_DOWN; |
| writel(val, base + PHY_28NMHP_PHY_REG25); |
| |
| val = readl(base + PHY_28NMHP_PHY_REG29); |
| val |= PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN; |
| writel(val, base + PHY_28NMHP_PHY_REG29); |
| } else { |
| val = readl(base + PHY_28NMHP_PHY_REG0A); |
| val &= ~(PHY_28NMHP_SUSPEND_PLL | PHY_28NMHP_SUSPEND_PHY); |
| writel(val, base + PHY_28NMHP_PHY_REG0A); |
| |
| /* disable pullup dp and pulldown dm */ |
| val = readl(base + PHY_28NMHP_PHY_REG29); |
| val &= ~PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN; |
| writel(val, base + PHY_28NMHP_PHY_REG29); |
| |
| /* Write 1s to clear phy err status */ |
| writel(0xFFFFFFFF, base + PHY_28NMHP_PHY_REG0E); |
| } |
| |
| return 0; |
| } |
| |
| static int mv_usb2_phy_suspend2(struct usb_phy *phy, int suspend) |
| { |
| struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy); |
| void __iomem *base = mv_phy->base; |
| unsigned int val; |
| |
| pr_info("USB2 PHY set_suspend2: %d...\n", suspend); |
| |
| if (suspend) { |
| val = readl(base + PHY_28NMHP_PHY_REG0B); |
| val &= ~(PHY_28NMHP_SUSPEND_PLL | PHY_28NMHP_SUSPEND_PHY); |
| writel(val, base + PHY_28NMHP_PHY_REG0B); |
| |
| val = readl(base + PHY_28NMHP_PHY_REG0A); |
| val |= (PHY_28NMHP_SUSPEND_PLL | PHY_28NMHP_SUSPEND_PHY); |
| writel(val, base + PHY_28NMHP_PHY_REG0A); |
| } else { |
| val = readl(base + PHY_28NMHP_PHY_REG0A); |
| val &= ~(PHY_28NMHP_SUSPEND_PLL | PHY_28NMHP_SUSPEND_PHY); |
| writel(val, base + PHY_28NMHP_PHY_REG0A); |
| |
| /* disable pullup dp and pulldown dm */ |
| val = readl(base + PHY_28NMHP_PHY_REG29); |
| val &= ~PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN; |
| writel(val, base + PHY_28NMHP_PHY_REG29); |
| |
| /* Write 1s to clear phy err status */ |
| writel(0xFFFFFFFF, base + PHY_28NMHP_PHY_REG0E); |
| } |
| |
| return 0; |
| } |
| |
| static int mv_usb2_phy_dumpcfg(struct usb_phy *phy) |
| { |
| struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy); |
| void __iomem *base = mv_phy->base; |
| u32 i; |
| |
| pr_info("usb phy regs\n"); |
| for (i = 0x0; i < 0x100; i += 32) { |
| pr_info("0x%08x: %08x %08x %08x %08x %08x %08x %08x %08x\n", i, |
| readl(base + i + 0), |
| readl(base + i + 4), |
| readl(base + i + 8), |
| readl(base + i + 12), |
| readl(base + i + 16), |
| readl(base + i + 20), |
| readl(base + i + 24), |
| readl(base + i + 28)); |
| } |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_USB_GADGET_CHARGE_ONLY |
| void usb_phy_force_dp_dm(struct usb_phy *phy, bool is_force) |
| { |
| struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy); |
| void __iomem *base = mv_phy->base; |
| u16 reg16; |
| |
| /* chips that has otg do not need to force dp dm down */ |
| if (has_feat_force_dpdm()) { |
| reg16 = readw(base + PHY_40NM_DIG1); |
| if (is_force) { |
| reg16 = (reg16 & (~PHY_40NM_DIG1_ARC_DPDM_MODE)) |
| | PHY_40NM_DIG1_DP_PULLDOWN |
| | PHY_40NM_DIG1_DM_PULLDOWN; |
| } else { |
| reg16 = (reg16 | PHY_40NM_DIG1_ARC_DPDM_MODE) |
| & (~PHY_40NM_DIG1_DP_PULLDOWN) |
| & (~PHY_40NM_DIG1_DM_PULLDOWN); |
| } |
| |
| writew(reg16, base + PHY_40NM_DIG1); |
| pr_info("dp_dm force? %s, reg=%x\n", |
| is_force ? "yes" : "no", reg16); |
| } |
| } |
| #endif /* CONFIG_USB_GADGET_CHARGE_ONLY */ |
| |
| static int _mv_usb2_phy_28nm_init(struct mv_usb2_phy *mv_phy) |
| { |
| struct platform_device *pdev = mv_phy->pdev; |
| unsigned int loops = 0; |
| void __iomem *base = mv_phy->base; |
| unsigned int tmp, val; |
| |
| if (cpu_is_pxa1826()) { |
| /* PHY_28NM_PLL_REG0 */ |
| writel(readl(base + PHY_28NM_PLL_REG0) & |
| ~(PHY_28NM_PLL_SELLPFR_MASK |
| | PHY_28NM_PLL_FBDIV_MASK |
| | PHY_28NM_PLL_REFDIV_MASK), |
| base + PHY_28NM_PLL_REG0); |
| writel(readl(base + PHY_28NM_PLL_REG0) | |
| (0x1 << PHY_28NM_PLL_SELLPFR_SHIFT |
| | 0xf0 << PHY_28NM_PLL_FBDIV_SHIFT |
| | 0xd << PHY_28NM_PLL_REFDIV_SHIFT), |
| base + PHY_28NM_PLL_REG0); |
| } else { |
| /* PHY_28NM_PLL_REG0 */ |
| writel(readl(base + PHY_28NM_PLL_REG0) & |
| ~(PHY_28NM_PLL_SELLPFR_MASK |
| | PHY_28NM_PLL_FBDIV_MASK |
| | PHY_28NM_PLL_ICP_MASK |
| | PHY_28NM_PLL_REFDIV_MASK), |
| base + PHY_28NM_PLL_REG0); |
| writel(readl(base + PHY_28NM_PLL_REG0) | |
| (0x1 << PHY_28NM_PLL_SELLPFR_SHIFT |
| | 0xf0 << PHY_28NM_PLL_FBDIV_SHIFT |
| | 0x3 << PHY_28NM_PLL_ICP_SHIFT |
| | 0xd << PHY_28NM_PLL_REFDIV_SHIFT), |
| base + PHY_28NM_PLL_REG0); |
| } |
| |
| /* PHY_28NM_PLL_REG1 */ |
| writel(readl(base + PHY_28NM_PLL_REG1) & |
| ~(PHY_28NM_PLL_PU_PLL_MASK |
| | PHY_28NM_PLL_PU_BY_REG_MASK), |
| base + PHY_28NM_PLL_REG1); |
| |
| writel(readl(base + PHY_28NM_PLL_REG1) | |
| (0x1 << PHY_28NM_PLL_PU_PLL_SHIFT |
| | 0x1 << PHY_28NM_PLL_PU_BY_REG_SHIFT), |
| base + PHY_28NM_PLL_REG1); |
| |
| if (cpu_is_pxa1826()) { |
| /* set TX AMP to 445mv */ |
| if (system_is_prod_mode()) |
| writel((((readl(base + PHY_28NM_TX_REG0)) |
| & (~PHY_28NM_TX_AMP_MASK)) |
| | (0x4 << PHY_28NM_TX_AMP_SHIFT)), |
| base + PHY_28NM_TX_REG0); |
| |
| writel(readl(base + PHY_28NM_TX_REG0) |
| | (0x3 << PHY_28NM_TX_PU_ANA_SHIFT), |
| base + PHY_28NM_TX_REG0); |
| } else { |
| /* PHY_28NM_TX_REG0 */ |
| writel(readl(base + PHY_28NM_TX_REG0) & |
| ~PHY_28NM_TX_AMP_MASK, |
| base + PHY_28NM_TX_REG0); |
| writel(readl(base + PHY_28NM_TX_REG0) | |
| (0x1 << PHY_28NM_TX_PU_BY_REG_SHIFT |
| | 0x3 << PHY_28NM_TX_AMP_SHIFT |
| | 0x1 << PHY_28NM_TX_PU_ANA_SHIFT), |
| base + PHY_28NM_TX_REG0); |
| } |
| |
| /* PHY_28NM_RX_REG0 */ |
| writel(readl(base + PHY_28NM_RX_REG0) & |
| ~PHY_28NM_RX_SQ_THRESH_MASK, |
| base + PHY_28NM_RX_REG0); |
| |
| writel(readl(base + PHY_28NM_RX_REG0) | |
| 0xa << PHY_28NM_RX_SQ_THRESH_SHIFT, |
| base + PHY_28NM_RX_REG0); |
| |
| /* PHY_28NM_DIG_REG0 */ |
| writel(readl(base + PHY_28NM_DIG_REG0) & |
| ~(PHY_28NM_DIG_BITSTAFFING_ERR_MASK |
| | PHY_28NM_DIG_SYNC_ERR_MASK |
| | PHY_28NM_DIG_SQ_FILT_MASK |
| | PHY_28NM_DIG_SQ_BLK_MASK |
| | PHY_28NM_DIG_SYNC_NUM_MASK), |
| base + PHY_28NM_DIG_REG0); |
| |
| if (cpu_is_pxa1928_b0()) { |
| writel(readl(base + PHY_28NM_DIG_REG0) | |
| (0x0 << PHY_28NM_DIG_SQ_FILT_SHIFT |
| | 0x0 << PHY_28NM_DIG_SQ_BLK_SHIFT |
| | 0x1 << PHY_28NM_DIG_SYNC_NUM_SHIFT), |
| base + PHY_28NM_DIG_REG0); |
| } else { |
| writel(readl(base + PHY_28NM_DIG_REG0) | |
| (0x1 << PHY_28NM_DIG_SQ_FILT_SHIFT |
| | 0x0 << PHY_28NM_DIG_SQ_BLK_SHIFT |
| | 0x2 << PHY_28NM_DIG_SYNC_NUM_SHIFT), |
| base + PHY_28NM_DIG_REG0); |
| } |
| |
| if (mv_phy->drv_data.phy_flag & MV_PHY_FLAG_PLL_LOCK_BYPASS) |
| writel(readl(base + PHY_28NM_DIG_REG0) |
| | (0x1 << PHY_28NM_PLL_LOCK_BYPASS_SHIFT), |
| base + PHY_28NM_DIG_REG0); |
| |
| /* PHY_28NM_OTG_REG */ |
| writel(readl(base + PHY_28NM_OTG_REG) | |
| 0x1 << PHY_28NM_OTG_PU_OTG_SHIFT, |
| base + PHY_28NM_OTG_REG); |
| |
| writel(readl(base + PHY_28NM_OTG_REG) & |
| ~(0x1 << PHY_28NM_OTG_CONTROL_BY_PIN_SHIFT), |
| base + PHY_28NM_OTG_REG); |
| |
| /* Calibration Timing |
| * ____________________________ |
| * CAL START ___| |
| * ____________________ |
| * CAL_DONE ___________| |
| * | 400us | |
| */ |
| |
| /* IMP Calibrate */ |
| writel(readl(base + PHY_28NM_CAL_REG) | |
| 1 << PHY_28NM_IMPCAL_START_SHIFT, |
| base + PHY_28NM_CAL_REG); |
| /* Wait For Calibrate PHY */ |
| udelay(400); |
| |
| /* Make sure PHY Calibration is ready */ |
| loops = 0; |
| do { |
| tmp = readl(base + PHY_28NM_CAL_REG); |
| val = PHY_28NM_PLL_IMPCAL_DONE_MASK; |
| tmp &= val; |
| if (tmp == val) |
| break; |
| udelay(1000); |
| } while ((loops++) && (loops < 100)); |
| if (loops >= 100) |
| dev_warn(&pdev->dev, "USB PHY Calibrate not done after 100mS."); |
| writel(readl(base + PHY_28NM_CAL_REG) & |
| ~(1 << PHY_28NM_IMPCAL_START_SHIFT), |
| base + PHY_28NM_CAL_REG); |
| |
| /* PLL Calibrate */ |
| writel(readl(base + PHY_28NM_CAL_REG) | |
| 1 << PHY_28NM_PLLCAL_START_SHIFT, |
| base + PHY_28NM_CAL_REG); |
| udelay(400); |
| loops = 0; |
| do { |
| tmp = readl(base + PHY_28NM_CAL_REG); |
| val = PHY_28NM_PLL_PLLCAL_DONE_MASK; |
| tmp &= val; |
| if (tmp == val) |
| break; |
| udelay(1000); |
| } while ((loops++) && (loops < 100)); |
| if (loops >= 100) |
| dev_warn(&pdev->dev, "USB PHY Calibrate not done after 100mS."); |
| writel(readl(base + PHY_28NM_CAL_REG) & |
| ~(1 << PHY_28NM_PLLCAL_START_SHIFT), |
| base + PHY_28NM_CAL_REG); |
| |
| /* SQ Calibrate */ |
| writel(readl(base + PHY_28NM_RX_REG1) | |
| 1 << PHY_28NM_RX_SQCAL_START_SHIFT, |
| base + PHY_28NM_RX_REG1); |
| /* Wait For Calibrate PHY */ |
| udelay(400); |
| /* Make sure PHY Calibration is ready */ |
| loops = 0; |
| do { |
| tmp = readl(base + PHY_28NM_RX_REG1); |
| val = PHY_28NM_RX_SQCAL_DONE_MASK; |
| tmp &= val; |
| if (tmp == val) |
| break; |
| |
| udelay(1000); |
| } while ((loops++) && (loops < 100)); |
| if (loops >= 100) |
| dev_warn(&pdev->dev, "USB PHY Calibrate not done after 100mS."); |
| writel(readl(base + PHY_28NM_RX_REG1) & |
| ~(1 << PHY_28NM_RX_SQCAL_START_SHIFT), |
| base + PHY_28NM_RX_REG1); |
| |
| |
| /* Make sure PHY PLL is ready */ |
| loops = 0; |
| tmp = readl(base + PHY_28NM_PLL_REG0); |
| while (!(tmp & PHY_28NM_PLL_READY_MASK)) { |
| udelay(1000); |
| loops++; |
| if (loops > 200) { |
| dev_warn(&pdev->dev, "PLL_READY not set after 200mS."); |
| break; |
| } |
| } |
| |
| if (cpu_is_pxa1826()) { |
| val = readl(base + PHY_28NM_CTRL_REG0); |
| writel(val | (3 << PHY_28NM_VBUSVALID_CTRL_SHIFT), |
| base + PHY_28NM_CTRL_REG0); |
| } else if (cpu_is_pxa1928()) { |
| writel(readl(base + PHY_28NM_CTRL_REG3) | |
| (0x1 << PHY_28NM_CTRL3_OVERWRITE_SHIFT |
| | 0x1 << PHY_28NM_CTRL3_VBUS_VALID_SHIFT |
| | 0x1 << PHY_28NM_CTRL3_AVALID_SHIFT |
| | 0x1 << PHY_28NM_CTRL3_BVALID_SHIFT), |
| base + PHY_28NM_CTRL_REG3); |
| } |
| |
| return 0; |
| } |
| |
| static void _mv_usb2_phy_28nm_shutdown(struct mv_usb2_phy *mv_phy) |
| { |
| void __iomem *base = mv_phy->base; |
| #if defined(CONFIG_CPU_ASR18XX) |
| struct mv_otg_regs *otg_regs = (struct mv_otg_regs *)mv_phy->phy.io_op_regs; |
| #endif |
| unsigned int val; |
| u32 tmp; |
| |
| if (cpu_is_pxa1826()) { |
| val = readl(base + PHY_28NM_CTRL_REG0); |
| writel(val & (~(3 << PHY_28NM_VBUSVALID_CTRL_SHIFT)), |
| base + PHY_28NM_CTRL_REG0); |
| } else if (cpu_is_pxa1928()) { |
| writel(readl(base + PHY_28NM_CTRL_REG3) & |
| ~(0x1 << PHY_28NM_CTRL3_OVERWRITE_SHIFT |
| | 0x1 << PHY_28NM_CTRL3_VBUS_VALID_SHIFT |
| | 0x1 << PHY_28NM_CTRL3_AVALID_SHIFT |
| | 0x1 << PHY_28NM_CTRL3_BVALID_SHIFT), |
| base + PHY_28NM_CTRL_REG3); |
| } |
| |
| /*need clear suspendm for power leakage if we use 28nm phy*/ |
| if (cpu_is_pxa1826()) { |
| tmp = readl(&otg_regs->portsc[0]); |
| tmp |= PORTSCX_PHY_LOW_POWER_SPD; |
| writel(tmp, &otg_regs->portsc[0]); |
| } |
| |
| val = readw(base + PHY_28NM_PLL_REG1); |
| val &= ~PHY_28NM_PLL_PU_PLL_MASK; |
| writew(val, base + PHY_28NM_PLL_REG1); |
| |
| /* power down PHY Analog part */ |
| val = readw(base + PHY_28NM_TX_REG0); |
| val &= ~(0x1 << PHY_28NM_TX_PU_ANA_SHIFT); |
| writew(val, base + PHY_28NM_TX_REG0); |
| |
| /* power down PHY OTG part */ |
| val = readw(base + PHY_28NM_OTG_REG); |
| val &= ~(0x1 << PHY_28NM_OTG_PU_OTG_SHIFT); |
| writew(val, base + PHY_28NM_OTG_REG); |
| |
| } |
| |
| #ifdef CONFIG_USB_MV_HSIC_UDC |
| #if defined(CONFIG_CPU_ASR18XX) |
| #define HSIC_UDC_PORTSC_OFFSET (0x184) |
| static int mv_hsic_phy_private(struct usb_phy *phy) |
| { |
| struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy); |
| u32 hsic_int, hsic_ctrl; |
| u32 status; |
| int count, ret = 0; |
| void __iomem *phy_base = mv_phy->base; |
| void __iomem *udc_reg_base = phy->io_op_regs; |
| |
| count = 10000; |
| do { |
| hsic_int = __raw_readl(phy_base + PHY_28NM_HSIC_INT); |
| count--; |
| udelay(1); |
| } while ((count >= 0) |
| && !(hsic_int & (PHY_28NM_HSIC_CONNECT_INT_BIT))); |
| |
| if (count <= 0) { |
| pr_err("HSIC HS_CONNETC NOT set: hsic_int 0x%x\n", |
| hsic_int); |
| |
| hsic_ctrl = __raw_readl(phy_base + PHY_28NM_HSIC_CTRL); |
| pr_info("hsic_ctrl: 0x%x\n", hsic_ctrl); |
| if (!((0x1 << 31) & hsic_ctrl)) { |
| ret = -EPROTO; |
| goto out; |
| } |
| } else { |
| pr_info("HSIC HS_CONNECT set\n"); |
| } |
| |
| __raw_writel(__raw_readl(phy_base + PHY_28NM_HSIC_INT) | PHY_28NM_HSIC_CONNECT_INT_BIT, |
| phy_base + PHY_28NM_HSIC_INT); |
| |
| /* test mode: force enable hs */ |
| status = __raw_readl((void __iomem *)((u32)udc_reg_base + HSIC_UDC_PORTSC_OFFSET)); |
| status &= ~(0xf << 16 | 0x1 << 24); |
| status |= (0x5 << 16); |
| __raw_writel(status, (void __iomem *)((u32)udc_reg_base + HSIC_UDC_PORTSC_OFFSET)); |
| status = __raw_readl((void __iomem *)((u32)udc_reg_base + HSIC_UDC_PORTSC_OFFSET)); |
| |
| /* disable test mode */ |
| status = __raw_readl((void __iomem *)((u32)udc_reg_base + HSIC_UDC_PORTSC_OFFSET)); |
| status &= ~(0xf << 16 | 0x1 << 24); |
| __raw_writel(status, (void __iomem *)((u32)udc_reg_base + HSIC_UDC_PORTSC_OFFSET)); |
| |
| /* check HS ready */ |
| count = 20000; |
| do { |
| hsic_int = __raw_readl(phy_base + PHY_28NM_HSIC_INT); |
| count--; |
| udelay(1); |
| } while ((count >= 0) |
| && !(hsic_int & (PHY_28NM_HSIC_INT_HS_READY))); |
| |
| if (count <= 0) { |
| pr_err("HSIC HS_READY not set: hsic_int 0x%x\n", |
| hsic_int); |
| ret = -EPROTO; |
| } else { |
| pr_info("HSIC HS_READY set\n"); |
| } |
| |
| out: |
| pr_info("%s: hsic_int: 0x%x, portsc: 0x%x\n", __func__, hsic_int, |
| __raw_readl((void __iomem *)((u32)udc_reg_base + HSIC_UDC_PORTSC_OFFSET))); |
| |
| if ((hsic_int & (PHY_28NM_HSIC_INT_HS_READY)) |
| || (hsic_int & (PHY_28NM_HSIC_CONNECT_INT_BIT))) |
| __raw_writel(__raw_readl(phy_base + PHY_28NM_HSIC_INT) | |
| (PHY_28NM_HSIC_INT_HS_READY| PHY_28NM_HSIC_CONNECT_INT_BIT), |
| phy_base + PHY_28NM_HSIC_INT); |
| |
| return ret; |
| } |
| #endif |
| |
| /* HSIC device mode phy init code */ |
| static int _mv_usb2_phy_hsic_init(struct mv_usb2_phy *mv_phy) |
| { |
| struct platform_device *pdev = mv_phy->pdev; |
| void __iomem *base = mv_phy->base; |
| int loops; |
| |
| dev_info(&pdev->dev, "hsic_dev_phy_init !!!\n"); |
| #if defined(CONFIG_CPU_ASR18XX) |
| mv_phy->phy.need_forceHS = false; |
| mv_phy->phy.is_hsic = true; |
| mv_phy->phy.phy_private = mv_hsic_phy_private; |
| mv_phy->phy.phy_reg_base = base; |
| #endif |
| |
| /* SET DPPULLDOWN_CTRL TO 0b'01 */ |
| writel(((readl(base + PHY_28NM_HSIC_USB_CTRL) & |
| ~(PHY_28NM_HSIC_DPPULLDOWN_CTRL_MASK)) |
| | PHY_28NM_HSIC_DPPULLDOWN_CTRL_DEVICE), |
| base + PHY_28NM_HSIC_USB_CTRL); |
| |
| /* Set reference clock */ |
| writel(readl(base + PHY_28NM_HSIC_PLL_CTRL01) & |
| ~(0x3 << PHY_28NM_HSIC_PLL_SELLPFR_SHIFT |
| | 0x1ff << PHY_28NM_HSIC_PLL_FBDIV_SHIFT |
| | 0x7f << PHY_28NM_HSIC_PLL_REFDIV_SHIFT), |
| base + PHY_28NM_HSIC_PLL_CTRL01); |
| |
| writel(readl(base + PHY_28NM_HSIC_PLL_CTRL01) | |
| (0x1 << PHY_28NM_HSIC_PLL_SELLPFR_SHIFT |
| | 0xf0 << PHY_28NM_HSIC_PLL_FBDIV_SHIFT |
| | 0xd << PHY_28NM_HSIC_PLL_REFDIV_SHIFT), |
| base + PHY_28NM_HSIC_PLL_CTRL01); |
| |
| /* Turn on PLL */ |
| writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) | |
| 0x1 << PHY_28NM_HSIC_S2H_PU_PLL_SHIFT, |
| base + PHY_28NM_HSIC_PLL_CTRL2); |
| if (cpu_is_pxa1826()) { |
| writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) & |
| ~((0xf<<PHY_28NM_HSIC_PLL_CTRL1_ICP_SHIFT) | |
| (0x1<<PHY_28NM_HSIC_CTL0_S2H_DRV_SE0_4RESUME)), |
| base + PHY_28NM_HSIC_PLL_CTRL2); |
| writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) | |
| 0x8 << PHY_28NM_HSIC_PLL_CTRL1_ICP_SHIFT, |
| base + PHY_28NM_HSIC_PLL_CTRL2); |
| } |
| |
| /* Make sure PHY PLL is locked */ |
| udelay(400); |
| |
| loops = 0; |
| while (!(readl(base + PHY_28NM_HSIC_PLL_CTRL2) |
| & (0x1 << PHY_28NM_HSIC_H2S_PLL_LOCK_SHIFT))) { |
| mdelay(1); |
| loops++; |
| if (loops > 100) { |
| dev_warn(&pdev->dev, "HSIC PHY PLL not locked after 100mS."); |
| break; |
| } |
| } |
| |
| /* Enable HSIC PHY */ |
| writel(((readl(base + PHY_28NM_HSIC_CTRL) | |
| (0x1 << PHY_28NM_HSIC_S2H_HSIC_ENABLE_SHIFT)) & |
| ~(0x1 << PHY_28NM_HSIC_S2H_HSIC_CONN_BYP_SHIFT)), |
| base + PHY_28NM_HSIC_CTRL); |
| |
| /* Calibration Timing for EDEN |
| * ____________________________ |
| * CAL START ___| |
| * ____________________ |
| * CAL_DONE ___________| |
| * | 400us | |
| */ |
| /* Wait For Calibrate PHY */ |
| udelay(400); |
| |
| /* Make sure PHY Calibration is ready */ |
| loops = 0; |
| while (!(readl(base + PHY_28NM_HSIC_IMPCAL_CAL) |
| & (0x1 << PHY_28NM_HSIC_H2S_IMPCAL_DONE_SHIFT))) { |
| mdelay(1); |
| loops++; |
| if (loops > 100) { |
| dev_warn(&pdev->dev, "HSIC PHY READY not set after 100mS."); |
| break; |
| } |
| } |
| |
| return 0; |
| } |
| #else |
| #if defined(CONFIG_CPU_ASR18XX) |
| static int host_hsic_connected, hsic_init_connected, port_reset_done; |
| static int mv_hsic_phy_private(struct usb_phy *phy) |
| { |
| #ifdef CONFIG_USB_EHCI_MV_U2H_HSIC |
| struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy); |
| u32 hsic_int; |
| u32 status; |
| int count; |
| void __iomem *phy_base = mv_phy->base; |
| struct ehci_regs *ehci_regs = (struct ehci_regs *)phy->io_op_regs; |
| |
| hsic_int = __raw_readl(phy_base + 0x28); |
| |
| if (!host_hsic_connected) { |
| count = 10000; |
| do { |
| hsic_int = __raw_readl(phy_base + PHY_28NM_HSIC_INT); |
| count--; |
| udelay(1); |
| } while ((count >= 0) |
| && !(hsic_int & (PHY_28NM_HSIC_CONNECT_INT_BIT))); |
| |
| if (count <= 0) { |
| pr_err("HSIC HS_CONNECT NOT SET: hsic_int 0x%x\n", |
| hsic_int); |
| goto out; |
| } else { |
| host_hsic_connected = 1; |
| pr_info("HSIC HS_CONNECT set\n"); |
| } |
| |
| __raw_writel(__raw_readl(phy_base + PHY_28NM_HSIC_INT) | |
| PHY_28NM_HSIC_CONNECT_INT_BIT, |
| phy_base + PHY_28NM_HSIC_INT); |
| } |
| |
| if (phy->need_forceHS == true && host_hsic_connected) { |
| phy->need_forceHS = false; |
| |
| /* test mode: force enable hs */ |
| status = __raw_readl(&ehci_regs->port_status[0]); |
| if ((status & (0x3 << 26)) == (0x2 << 26)) { |
| pr_info("%s: already in hs mode\n", __func__); |
| goto port_reset; |
| } |
| |
| status &= ~(0xf << 16 | 0x1 << 24); |
| status |= (0x5 << 16); |
| __raw_writel(status, &ehci_regs->port_status[0]); |
| status = __raw_readl(&ehci_regs->port_status[0]); |
| |
| /* disable test mode */ |
| status = __raw_readl(&ehci_regs->port_status[0]); |
| status &= ~(0xf << 16 | 0x1 << 24); |
| __raw_writel(status, &ehci_regs->port_status[0]); |
| |
| /* check HS ready */ |
| count = 10000; |
| do { |
| hsic_int = __raw_readl(phy_base + PHY_28NM_HSIC_INT); |
| count--; |
| udelay(1); |
| } while ((count >= 0) |
| && !(hsic_int & (PHY_28NM_HSIC_INT_HS_READY))); |
| |
| if (count <= 0) { |
| pr_err("HS_READY not set: hsic_int 0x%x, portsc: 0x%x\n", |
| hsic_int, __raw_readl(&ehci_regs->port_status[0])); |
| } |
| |
| status = __raw_readl(&ehci_regs->port_status[0]); |
| status |= (0x1 << 1); |
| __raw_writel(status, &ehci_regs->port_status[0]); |
| |
| port_reset: |
| if (!port_reset_done && !hsic_init_connected) { |
| pr_info("%s: do port reset\n", __func__); |
| /* issue port reset */ |
| status = __raw_readl(&ehci_regs->port_status[0]); |
| status |= (1 << 8); |
| status &= ~(1 << 2); |
| __raw_writel(status, &ehci_regs->port_status[0]); |
| |
| /* check reset done */ |
| count = 0x10000; |
| do { |
| status = __raw_readl(&ehci_regs->port_status[0]); |
| count--; |
| } while ((count >= 0) && !(status & PORT_RESET)); |
| if (count <= 0) { |
| pr_err("HSIC port reset not done: port_status 0x%x\n", status); |
| goto out; |
| } |
| port_reset_done = 1; |
| } |
| } |
| |
| out: |
| if ((hsic_int & (PHY_28NM_HSIC_INT_HS_READY)) || (hsic_int & (PHY_28NM_HSIC_CONNECT_INT_BIT))) |
| __raw_writel(__raw_readl(phy_base + PHY_28NM_HSIC_INT) | |
| (PHY_28NM_HSIC_INT_HS_READY | PHY_28NM_HSIC_CONNECT_INT_BIT), |
| phy_base + PHY_28NM_HSIC_INT); |
| #endif |
| return 0; |
| |
| } |
| #endif |
| |
| static int _mv_usb2_phy_hsic_init(struct mv_usb2_phy *mv_phy) |
| { |
| struct platform_device *pdev = mv_phy->pdev; |
| void __iomem *base = mv_phy->base; |
| int loops; |
| |
| #if defined(CONFIG_CPU_ASR18XX) |
| mv_phy->phy.need_forceHS = false; |
| mv_phy->phy.is_hsic = true; |
| mv_phy->phy.phy_private = mv_hsic_phy_private; |
| #endif |
| |
| dev_info(&pdev->dev, "hsic_host_phy_init !!!\n"); |
| |
| /* Set reference clock */ |
| writel(readl(base + PHY_28NM_HSIC_PLL_CTRL01) & |
| ~(0x3 << PHY_28NM_HSIC_PLL_SELLPFR_SHIFT |
| | 0x1ff << PHY_28NM_HSIC_PLL_FBDIV_SHIFT |
| | 0x7f << PHY_28NM_HSIC_PLL_REFDIV_SHIFT), |
| base + PHY_28NM_HSIC_PLL_CTRL01); |
| |
| writel(readl(base + PHY_28NM_HSIC_PLL_CTRL01) | |
| (0x1 << PHY_28NM_HSIC_PLL_SELLPFR_SHIFT |
| | 0xf0 << PHY_28NM_HSIC_PLL_FBDIV_SHIFT |
| | 0xd << PHY_28NM_HSIC_PLL_REFDIV_SHIFT), |
| base + PHY_28NM_HSIC_PLL_CTRL01); |
| |
| /* Turn on PLL */ |
| writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) | |
| 0x1 << PHY_28NM_HSIC_S2H_PU_PLL_SHIFT, |
| base + PHY_28NM_HSIC_PLL_CTRL2); |
| if (cpu_is_pxa1826()) { |
| writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) & |
| ~(0xf<<PHY_28NM_HSIC_PLL_CTRL1_ICP_SHIFT), |
| base + PHY_28NM_HSIC_PLL_CTRL2); |
| writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) | |
| 0x8 << PHY_28NM_HSIC_PLL_CTRL1_ICP_SHIFT, |
| base + PHY_28NM_HSIC_PLL_CTRL2); |
| } |
| |
| /* Make sure PHY PLL is locked */ |
| udelay(400); |
| |
| loops = 0; |
| while (!(readl(base + PHY_28NM_HSIC_PLL_CTRL2) & |
| (0x1 << PHY_28NM_HSIC_H2S_PLL_LOCK_SHIFT))) { |
| mdelay(1); |
| loops++; |
| if (loops > 100) { |
| dev_warn(&pdev->dev, "HSIC PHY PLL not locked after 100mS."); |
| break; |
| } |
| } |
| /* Enable HSIC PHY */ |
| if (cpu_is_pxa1826()) { |
| writel(readl(base + PHY_28NM_HSIC_CTRL) | |
| (0x1 << PHY_28NM_HSIC_S2H_HSIC_ENABLE_SHIFT | |
| 0x1<<PHY_28NM_HSIC_CTL0_S2H_DRV_SE0_4RESUME), |
| base + PHY_28NM_HSIC_CTRL); |
| } else { |
| writel(readl(base + PHY_28NM_HSIC_CTRL) | |
| 0x1 << PHY_28NM_HSIC_S2H_HSIC_ENABLE_SHIFT, |
| base + PHY_28NM_HSIC_CTRL); |
| } |
| |
| /* Calibration Timing for EDEN |
| * ____________________________ |
| * CAL START ___| |
| * ____________________ |
| * CAL_DONE ___________| |
| * | 400us | |
| */ |
| /* Wait For Calibrate PHY */ |
| udelay(400); |
| |
| /* Make sure PHY Calibration is ready */ |
| loops = 0; |
| while (!(readl(base + PHY_28NM_HSIC_IMPCAL_CAL) & |
| (0x1 << PHY_28NM_HSIC_H2S_IMPCAL_DONE_SHIFT))) { |
| mdelay(1); |
| loops++; |
| if (loops > 100) { |
| dev_warn(&pdev->dev, "HSIC PHY READY not set after 100mS."); |
| break; |
| } |
| } |
| if (cpu_is_pxa1826()) { |
| writel(readl(base + PHY_28NM_HSIC_CTRL) | |
| 0x1 << PHY_28NM_HSIC_CTL0_S2H_BUS_RESET_EN, |
| base + PHY_28NM_HSIC_CTRL); |
| } |
| |
| /* Waiting for HSIC connect int*/ |
| loops = 0; |
| host_hsic_connected = 1; |
| hsic_init_connected = 1; |
| port_reset_done = 1; |
| while (!(readl(base + PHY_28NM_HSIC_INT) & |
| PHY_28NM_HSIC_CONNECT_INT_BIT)) { |
| mdelay(1); |
| loops++; |
| if (loops > 100) { |
| dev_warn(&pdev->dev, "HSIC wait for connect interrupt timeout."); |
| host_hsic_connected = 0; |
| hsic_init_connected = 0; |
| port_reset_done = 0; |
| break; |
| } |
| } |
| |
| return 0; |
| } |
| #endif |
| static void _mv_usb2_phy_hsic_shutdown(struct mv_usb2_phy *mv_phy) |
| { |
| struct platform_device *pdev = mv_phy->pdev; |
| void __iomem *base = mv_phy->base; |
| |
| dev_info(&pdev->dev, "hsic_phy_shutdown !!!\n"); |
| #if defined(CONFIG_CPU_ASR18XX) |
| mv_phy->phy.need_forceHS = false; |
| mv_phy->phy.is_hsic = false; |
| mv_phy->phy.phy_private = NULL; |
| |
| #ifndef CONFIG_USB_MV_HSIC_UDC |
| host_hsic_connected = 0; |
| hsic_init_connected = 0; |
| port_reset_done = 0; |
| #endif |
| |
| #endif |
| writel(readl(base + PHY_28NM_HSIC_CTRL) & |
| ~(0x1 << PHY_28NM_HSIC_S2H_HSIC_ENABLE_SHIFT), |
| base + PHY_28NM_HSIC_CTRL); |
| } |
| |
| static int mv_usb2_phy_init(struct usb_phy *phy) |
| { |
| struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy); |
| int ret = 0; |
| |
| clk_enable(mv_phy->clk); |
| switch (mv_phy->drv_data.phy_type) { |
| case PHY_40LP: |
| ret = _mv_usb2_phy_40nm_init(mv_phy); |
| break; |
| case PHY_28LP: |
| ret = _mv_usb2_phy_28nm_init(mv_phy); |
| break; |
| case PHY_55LP: |
| ret = _mv_usb2_phy_55nm_init(mv_phy); |
| break; |
| case PHY_28LP_HSIC: |
| ret = _mv_usb2_phy_hsic_init(mv_phy); |
| break; |
| case PHY_28HP: |
| ret = _mv_usb2_phy_28nmhp_init(mv_phy); |
| break; |
| |
| default: |
| pr_err("No such phy type supported!\n"); |
| break; |
| } |
| |
| return ret; |
| } |
| |
| static void mv_usb2_phy_shutdown(struct usb_phy *phy) |
| { |
| struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy); |
| |
| switch (mv_phy->drv_data.phy_type) { |
| case PHY_40LP: |
| _mv_usb2_phy_40nm_shutdown(mv_phy); |
| break; |
| case PHY_28LP: |
| _mv_usb2_phy_28nm_shutdown(mv_phy); |
| break; |
| case PHY_55LP: |
| _mv_usb2_phy_55nm_shutdown(mv_phy); |
| break; |
| case PHY_28LP_HSIC: |
| _mv_usb2_phy_hsic_shutdown(mv_phy); |
| break; |
| case PHY_28HP: |
| _mv_usb2_phy_28nmhp_shutdown(mv_phy); |
| break; |
| |
| default: |
| pr_err("No such phy type supported!\n"); |
| break; |
| } |
| |
| clk_disable(mv_phy->clk); |
| } |
| |
| /* |
| * can only detect DEFAULT_CHARGER, DCP_CHARGER, SDP_CHARGER |
| * for other charger types, need to be confirmed by USB enumuration |
| */ |
| static int _mv_usb2_phy_40nm_charger_detect(struct mv_usb2_phy *mv_phy) |
| { |
| void __iomem *base = mv_phy->base; |
| int charger_type_bc12 = NULL_CHARGER; |
| u16 reg16; |
| |
| /* debounce time for slowly insering USB cable */ |
| msleep(80); |
| |
| /*set PU_ANA*/ |
| reg16 = readw(base + PHY_40NM_ANA1); |
| reg16 |= PHY_40NM_ANA1_PU_ANA; |
| writew(reg16, base + PHY_40NM_ANA1); |
| |
| /* power up the charger detector */ |
| reg16 = readw(base + PHY_40NM_CHARGE0); |
| reg16 |= PHY_40NM_CHARGE0_ENABLE_SWITCH | |
| PHY_40NM_CHARGE0_PU_CHRG_DTC; |
| writew(reg16, base + PHY_40NM_CHARGE0); |
| usleep_range(2, 5); |
| |
| /* |
| * primary detect: |
| * to detect if there is a charger at upstream port |
| */ |
| reg16 = readw(base + PHY_40NM_RESERVE0); |
| reg16 &= ~(PHY_40NM_CDP_EN | PHY_40NM_DCP_EN | |
| PHY_40NM_CDP_DM_AUTO_SWITCH | |
| PHY_40NM_DP_DM_SWAP_CTRL); |
| writew(reg16, base + PHY_40NM_RESERVE0); |
| |
| reg16 = readw(base + PHY_40NM_RESERVE0); |
| reg16 |= (PHY_40NM_PD_EN | PHY_40NM_ENABLE_SWITCH_DM | |
| PHY_40NM_ENABLE_SWITCH_DP | |
| PHY_40NM_VDAT_CHARGE | |
| PHY_40NM_VSRC_CHARGE); |
| writew(reg16, base + PHY_40NM_RESERVE0); |
| /* wait 20 us */ |
| usleep_range(20, 30); |
| |
| /* read charger detector output register */ |
| if (readl(base + PHY_40NM_CTRL) & PHY_40NM_CTRL_AVALID_CONTROL) |
| reg16 = readw(base + PHY_40NM_STATUS_A0C); |
| else |
| reg16 = readw(base + PHY_40NM_STATUS); |
| if (reg16 & PHY_40NM_CHARGER_STATUS_MASK) { |
| /* |
| * secondary detect: |
| * detect if the upstream port is a dedicated |
| * charger or a USB2 host port |
| */ |
| reg16 = readw(base + PHY_40NM_RESERVE0); |
| reg16 |= PHY_40NM_DP_DM_SWAP_CTRL; |
| writew(reg16, base + PHY_40NM_RESERVE0); |
| /* wait 20us */ |
| usleep_range(20, 30); |
| |
| /* read charger detector output register */ |
| if (readl(base + PHY_40NM_CTRL) |
| & PHY_40NM_CTRL_AVALID_CONTROL) |
| reg16 = readw(base + |
| PHY_40NM_STATUS_A0C); |
| else |
| reg16 = readw(base + |
| PHY_40NM_STATUS); |
| if (reg16 & PHY_40NM_CHARGER_STATUS_MASK) |
| charger_type_bc12 = DCP_CHARGER; |
| else |
| charger_type_bc12 = CDP_CHARGER; |
| } else |
| charger_type_bc12 = DEFAULT_CHARGER; |
| |
| /*disable switch, shutdown the charger detector*/ |
| reg16 = readw(base + PHY_40NM_CHARGE0); |
| reg16 &= ~(PHY_40NM_CHARGE0_ENABLE_SWITCH | |
| PHY_40NM_CHARGE0_PU_CHRG_DTC); |
| writew(reg16, base + PHY_40NM_CHARGE0); |
| return charger_type_bc12; |
| } |
| |
| static int _mv_usb2_phy_28nm_charger_detect(struct mv_usb2_phy *mv_phy) |
| { |
| void __iomem *base = mv_phy->base; |
| int charger_type_bc12 = NULL_CHARGER; |
| u32 reg32; |
| |
| /* Power up Analog */ |
| reg32 = readl(base + PHY_28NM_TX_REG0); |
| reg32 |= (1 << PHY_28NM_TX_PU_BY_REG_SHIFT | |
| 1 << PHY_28NM_TX_PU_ANA_SHIFT); |
| writel(reg32, base + PHY_28NM_TX_REG0); |
| |
| /* Power up Charger Detector */ |
| reg32 = readl(base + PHY_28NM_CHRG_DET); |
| reg32 |= (1 << PHY_28NM_CHGDTC_PU_CHRG_DTC_SHIFT_28); |
| writel(reg32, base + PHY_28NM_CHRG_DET); |
| |
| usleep_range(2, 5); |
| |
| /* Primary detection */ |
| reg32 = readl(base + PHY_28NM_CHRG_DET); |
| reg32 &= ~(1 << PHY_28NM_CHGDTC_DP_DM_SWAP_SHIFT_28 | |
| 3 << PHY_28NM_CHGDTC_VSRC_CHARGE_SHIFT_28 | |
| 3 << PHY_28NM_CHGDTC_VDAT_CHARGE_SHIFT_28 | |
| 1 << PHY_28NM_CHGDTC_CDP_DM_AUTO_SWITCH_SHIFT_28 | |
| 1 << PHY_28NM_CHGDTC_DCP_EN_SHIFT_28 | |
| 1 << PHY_28NM_CHGDTC_CDP_EN_SHIFT_28); |
| writel(reg32, base + PHY_28NM_CHRG_DET); |
| |
| reg32 = readl(base + PHY_28NM_CHRG_DET); |
| reg32 |= (1 << PHY_28NM_CHGDTC_VSRC_CHARGE_SHIFT_28 | |
| 1 << PHY_28NM_CHGDTC_VDAT_CHARGE_SHIFT_28 | |
| 1 << PHY_28NM_CHGDTC_PD_EN_SHIFT_28); |
| writel(reg32, base + PHY_28NM_CHRG_DET); |
| |
| /* Enable swtich DM/DP */ |
| reg32 = readl(base + PHY_28NM_CHRG_DET); |
| reg32 |= (1 << PHY_28NM_CHGDTC_ENABLE_SWITCH_DM_SHIFT_28 | |
| 1 << PHY_28NM_CHGDTC_ENABLE_SWITCH_DP_SHIFT_28); |
| writel(reg32, base + PHY_28NM_CHRG_DET); |
| |
| if ((readl(base + PHY_28NM_CTRL_REG1)) & |
| (1 << PHY_28NM_CTRL1_CHRG_DTC_OUT_SHIFT_28)) { |
| /* We have CHRG_DTC_OUT set. |
| * Now proceed with Secondary Detection |
| */ |
| |
| msleep(60); |
| |
| reg32 = readl(base + PHY_28NM_CHRG_DET); |
| reg32 |= (1 << PHY_28NM_CHGDTC_DP_DM_SWAP_SHIFT_28); |
| writel(reg32, base + PHY_28NM_CHRG_DET); |
| |
| msleep(80); |
| |
| if ((readl(base + PHY_28NM_CTRL_REG1)) & |
| (1 << PHY_28NM_CTRL1_CHRG_DTC_OUT_SHIFT_28)) |
| charger_type_bc12 = DCP_CHARGER; |
| else |
| charger_type_bc12 = CDP_CHARGER; |
| } else |
| charger_type_bc12 = DEFAULT_CHARGER; |
| |
| /* Disable swtich DM/DP */ |
| reg32 = readl(base + PHY_28NM_CHRG_DET); |
| reg32 &= ~(1<<PHY_28NM_CHGDTC_ENABLE_SWITCH_DM_SHIFT_28 | |
| 1 << PHY_28NM_CHGDTC_ENABLE_SWITCH_DP_SHIFT_28); |
| writel(reg32, base + PHY_28NM_CHRG_DET); |
| |
| /* Power down Charger Detector */ |
| reg32 = readl(base + PHY_28NM_CHRG_DET); |
| reg32 &= ~(1 << PHY_28NM_CHGDTC_PU_CHRG_DTC_SHIFT_28); |
| writel(reg32, base + PHY_28NM_CHRG_DET); |
| |
| return charger_type_bc12; |
| } |
| |
| static int _mv_usb2_phy_28nmhp_charger_detect(struct mv_usb2_phy *mv_phy) |
| { |
| void __iomem *base = mv_phy->base; |
| int charger_type_bc12 = NULL_CHARGER; |
| u32 reg32; |
| struct mv_otg_regs *otg_regs = (struct mv_otg_regs *)mv_phy->phy.io_op_regs; |
| |
| if ((otg_regs->usbcmd & 0x1) != 0x1) |
| return DEFAULT_CHARGER; |
| |
| reg32 = readl(base + PHY_28NMHP_PHY_REG25); |
| reg32 &= ~(PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_MASK); |
| reg32 |= PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_VAL; |
| writel(reg32, base + PHY_28NMHP_PHY_REG25); |
| |
| if (cpu_is_asr1802s()) { |
| /* pullup dp and pulldown dm */ |
| reg32 = readl(base + PHY_28NMHP_PHY_REG25); |
| reg32 &= ~PHY_28NMHP_DCP_DET_PULL_MASK; |
| reg32 |= PHY_28NMHP_DCP_DET_PULL_UP_DOWN; |
| writel(reg32, base + PHY_28NMHP_PHY_REG25); |
| |
| reg32 = readl(base + PHY_28NMHP_PHY_REG28); |
| reg32 |= PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN; |
| writel(reg32, base + PHY_28NMHP_PHY_REG28); |
| } else { |
| /* pullup dp and pulldown dm */ |
| reg32 = readl(base + PHY_28NMHP_PHY_REG25); |
| reg32 &= ~PHY_28NMHP_DCP_DET_PULL_MASK; |
| reg32 |= PHY_28NMHP_DCP_DET_PULL_UP_DOWN; |
| writel(reg32, base + PHY_28NMHP_PHY_REG25); |
| |
| reg32 = readl(base + PHY_28NMHP_PHY_REG29); |
| reg32 |= PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN; |
| writel(reg32, base + PHY_28NMHP_PHY_REG29); |
| } |
| |
| udelay(5); |
| if ((readl(&otg_regs->portsc[0]) & (0x3 << 10)) == (0x2 << 10)) |
| charger_type_bc12 = NONE_STANDARD_CHARGER; |
| else |
| charger_type_bc12 = DCP_CHARGER; |
| if (cpu_is_asr1802s()) { |
| reg32 = readl(base + PHY_28NMHP_PHY_REG28); |
| reg32 &= ~PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN; |
| writel(reg32, base + PHY_28NMHP_PHY_REG28); |
| } else { |
| reg32 = readl(base + PHY_28NMHP_PHY_REG29); |
| reg32 &= ~PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN; |
| writel(reg32, base + PHY_28NMHP_PHY_REG29); |
| } |
| |
| return charger_type_bc12; |
| } |
| |
| static int mv_usb2_phy_charger_detect(struct usb_phy *phy) |
| { |
| struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy); |
| int ret; |
| |
| switch (mv_phy->drv_data.phy_type) { |
| case PHY_40LP: |
| ret = _mv_usb2_phy_40nm_charger_detect(mv_phy); |
| break; |
| case PHY_28LP: |
| ret = _mv_usb2_phy_28nm_charger_detect(mv_phy); |
| break; |
| case PHY_28HP: |
| ret = _mv_usb2_phy_28nmhp_charger_detect(mv_phy); |
| break; |
| |
| default: |
| pr_err("Such phy does not support to detect charger type!\n"); |
| ret = DEFAULT_CHARGER; |
| break; |
| } |
| |
| return ret; |
| } |
| #if defined(CONFIG_CPU_ASR18XX) |
| #define APMU_SD_ROT_WAKE_CLR 0x7C |
| #define USB_VBUS_STS (1<<15) |
| static int mv_usb2_phy_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_SD_ROT_WAKE_CLR); |
| if (reg32&USB_VBUS_STS) |
| ret = 1; |
| else |
| ret = 0; |
| |
| return ret; |
| |
| } |
| #endif |
| static const struct of_device_id mv_usbphy_dt_match[]; |
| |
| static int mv_usb2_get_phydata(struct platform_device *pdev, |
| struct mv_usb2_phy *mv_phy) |
| { |
| struct device_node *np = pdev->dev.of_node; |
| const struct of_device_id *match; |
| u32 phy_rev; |
| |
| match = of_match_device(mv_usbphy_dt_match, &pdev->dev); |
| if (!match) |
| return -ENODEV; |
| |
| mv_phy->drv_data.phy_type = (unsigned long)match->data; |
| |
| if (!of_property_read_u32(np, "marvell,usb2-phy-rev", &phy_rev)) |
| mv_phy->drv_data.phy_rev = phy_rev; |
| else |
| pr_info("No PHY revision found, use the default setting!"); |
| |
| if (of_property_read_bool(np, "marvell,pll-lock-bypass")) |
| mv_phy->drv_data.phy_flag |= MV_PHY_FLAG_PLL_LOCK_BYPASS; |
| |
| return 0; |
| } |
| |
| static void mv_usb2_phy_bind_device(struct mv_usb2_phy *mv_phy) |
| { |
| const char *device_name; |
| |
| struct device_node *np = (mv_phy->phy.dev)->of_node; |
| |
| if (!of_property_read_string(np, "marvell,udc-name", &device_name)) |
| usb_bind_phy(device_name, MV_USB2_PHY_INDEX, |
| dev_name(mv_phy->phy.dev)); |
| |
| if (!of_property_read_string(np, "marvell,ehci-name", &device_name)) |
| usb_bind_phy(device_name, MV_USB2_PHY_INDEX, |
| dev_name(mv_phy->phy.dev)); |
| |
| if (!of_property_read_string(np, "marvell,otg-name", &device_name)) |
| usb_bind_phy(device_name, MV_USB2_PHY_INDEX, |
| dev_name(mv_phy->phy.dev)); |
| } |
| |
| static int mv_usb2_phy_probe(struct platform_device *pdev) |
| { |
| struct mv_usb2_phy *mv_phy; |
| struct resource *r; |
| int ret = 0; |
| |
| mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL); |
| if (mv_phy == NULL) { |
| dev_err(&pdev->dev, "failed to allocate memory\n"); |
| return -ENOMEM; |
| } |
| |
| mv_phy->pdev = pdev; |
| |
| ret = mv_usb2_get_phydata(pdev, mv_phy); |
| if (ret) { |
| dev_err(&pdev->dev, "No matching phy founded\n"); |
| return ret; |
| } |
| |
| mv_phy->clk = devm_clk_get(&pdev->dev, NULL); |
| if (IS_ERR(mv_phy->clk)) { |
| dev_err(&pdev->dev, "failed to get clock.\n"); |
| return PTR_ERR(mv_phy->clk); |
| } |
| clk_prepare(mv_phy->clk); |
| |
| r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| if (r == NULL) { |
| dev_err(&pdev->dev, "no phy I/O memory resource defined\n"); |
| return -ENODEV; |
| } |
| mv_phy->base = devm_ioremap_resource(&pdev->dev, r); |
| if (mv_phy->base == NULL) { |
| dev_err(&pdev->dev, "error map register base\n"); |
| return -EBUSY; |
| } |
| |
| mv_phy->phy.dev = &pdev->dev; |
| mv_phy->phy.label = "mv-usb2"; |
| mv_phy->phy.type = USB_PHY_TYPE_USB2; |
| mv_phy->phy.init = mv_usb2_phy_init; |
| mv_phy->phy.shutdown = mv_usb2_phy_shutdown; |
| mv_phy->phy.charger_detect = mv_usb2_phy_charger_detect; |
| if (cpu_is_asr1806()) { |
| mv_phy->phy.set_suspend = mv_usb2_phy_suspend; |
| mv_phy->phy.set_suspend2 = mv_usb2_phy_suspend2; |
| } |
| mv_phy->phy.dump_cfg = mv_usb2_phy_dumpcfg; |
| |
| mv_phy->phy.io_priv = mv_phy->base; |
| |
| mv_usb2_phy_bind_device(mv_phy); |
| |
| usb_add_phy_dev(&mv_phy->phy); |
| |
| if (mv_phy->drv_data.phy_type == PHY_28HP) { |
| clk_enable(mv_phy->clk); |
| _mv_usb2_phy_28nmhp_preinit(mv_phy); |
| } |
| |
| platform_set_drvdata(pdev, mv_phy); |
| #if defined(CONFIG_CPU_ASR18XX) |
| mv_phy->phy.is_hsic = false; |
| mv_phy->phy.get_vbus = mv_usb2_phy_get_vbus; |
| #endif |
| pr_info("usb2 phy ok\n"); |
| return 0; |
| } |
| |
| static int mv_usb2_phy_remove(struct platform_device *pdev) |
| { |
| struct mv_usb2_phy *mv_phy = platform_get_drvdata(pdev); |
| |
| usb_remove_phy(&mv_phy->phy); |
| |
| clk_unprepare(mv_phy->clk); |
| |
| platform_set_drvdata(pdev, NULL); |
| |
| return 0; |
| } |
| |
| static const struct of_device_id mv_usbphy_dt_match[] = { |
| { .compatible = "marvell,usb2-phy-55lp", .data = (void *)PHY_55LP }, |
| { .compatible = "marvell,usb2-phy-40lp", .data = (void *)PHY_40LP }, |
| { .compatible = "marvell,usb2-phy-28lp", .data = (void *)PHY_28LP }, |
| { .compatible = "marvell,hsic-phy-28lp", .data = (void *)PHY_28LP_HSIC }, |
| { .compatible = "asr,usb2-phy-28hp", .data = (void *)PHY_28HP }, |
| {}, |
| }; |
| MODULE_DEVICE_TABLE(of, mv_usbphy_dt_match); |
| |
| static struct platform_driver mv_usb2_phy_driver = { |
| .probe = mv_usb2_phy_probe, |
| .remove = mv_usb2_phy_remove, |
| .driver = { |
| .name = "mv-usb2-phy", |
| .owner = THIS_MODULE, |
| .of_match_table = of_match_ptr(mv_usbphy_dt_match), |
| }, |
| }; |
| |
| #ifndef CONFIG_USB_DWC2 |
| module_platform_driver(mv_usb2_phy_driver); |
| #else |
| static int __init asr_usb2_phydrv_init(void) |
| { |
| return platform_driver_register(&mv_usb2_phy_driver); |
| } |
| |
| static void __exit asr_usb2_phydrv_exit(void) |
| { |
| platform_driver_unregister(&mv_usb2_phy_driver); |
| } |
| |
| arch_initcall(asr_usb2_phydrv_init); |
| module_exit(asr_usb2_phydrv_exit); |
| #endif |
| MODULE_ALIAS("platform: mv_usb2"); |
| MODULE_AUTHOR("Marvell Inc."); |
| MODULE_DESCRIPTION("Marvell USB2 phy driver"); |
| MODULE_LICENSE("GPL v2"); |