1*08f13e7cSLubomir Rintel // SPDX-License-Identifier: GPL-2.0 2*08f13e7cSLubomir Rintel /* 3*08f13e7cSLubomir Rintel * Copyright (C) 2011 Marvell International Ltd. All rights reserved. 4*08f13e7cSLubomir Rintel * Copyright (C) 2018,2019 Lubomir Rintel <lkundrak@v3.sk> 5*08f13e7cSLubomir Rintel */ 6*08f13e7cSLubomir Rintel 7*08f13e7cSLubomir Rintel #include <linux/delay.h> 8*08f13e7cSLubomir Rintel #include <linux/io.h> 9*08f13e7cSLubomir Rintel #include <linux/module.h> 10*08f13e7cSLubomir Rintel #include <linux/phy/phy.h> 11*08f13e7cSLubomir Rintel #include <linux/platform_device.h> 12*08f13e7cSLubomir Rintel #include <linux/soc/mmp/cputype.h> 13*08f13e7cSLubomir Rintel 14*08f13e7cSLubomir Rintel #define USB2_PLL_REG0 0x4 15*08f13e7cSLubomir Rintel #define USB2_PLL_REG1 0x8 16*08f13e7cSLubomir Rintel #define USB2_TX_REG0 0x10 17*08f13e7cSLubomir Rintel #define USB2_TX_REG1 0x14 18*08f13e7cSLubomir Rintel #define USB2_TX_REG2 0x18 19*08f13e7cSLubomir Rintel #define USB2_RX_REG0 0x20 20*08f13e7cSLubomir Rintel #define USB2_RX_REG1 0x24 21*08f13e7cSLubomir Rintel #define USB2_RX_REG2 0x28 22*08f13e7cSLubomir Rintel #define USB2_ANA_REG0 0x30 23*08f13e7cSLubomir Rintel #define USB2_ANA_REG1 0x34 24*08f13e7cSLubomir Rintel #define USB2_ANA_REG2 0x38 25*08f13e7cSLubomir Rintel #define USB2_DIG_REG0 0x3C 26*08f13e7cSLubomir Rintel #define USB2_DIG_REG1 0x40 27*08f13e7cSLubomir Rintel #define USB2_DIG_REG2 0x44 28*08f13e7cSLubomir Rintel #define USB2_DIG_REG3 0x48 29*08f13e7cSLubomir Rintel #define USB2_TEST_REG0 0x4C 30*08f13e7cSLubomir Rintel #define USB2_TEST_REG1 0x50 31*08f13e7cSLubomir Rintel #define USB2_TEST_REG2 0x54 32*08f13e7cSLubomir Rintel #define USB2_CHARGER_REG0 0x58 33*08f13e7cSLubomir Rintel #define USB2_OTG_REG0 0x5C 34*08f13e7cSLubomir Rintel #define USB2_PHY_MON0 0x60 35*08f13e7cSLubomir Rintel #define USB2_RESETVE_REG0 0x64 36*08f13e7cSLubomir Rintel #define USB2_ICID_REG0 0x78 37*08f13e7cSLubomir Rintel #define USB2_ICID_REG1 0x7C 38*08f13e7cSLubomir Rintel 39*08f13e7cSLubomir Rintel /* USB2_PLL_REG0 */ 40*08f13e7cSLubomir Rintel 41*08f13e7cSLubomir Rintel /* This is for Ax stepping */ 42*08f13e7cSLubomir Rintel #define USB2_PLL_FBDIV_SHIFT_MMP3 0 43*08f13e7cSLubomir Rintel #define USB2_PLL_FBDIV_MASK_MMP3 (0xFF << 0) 44*08f13e7cSLubomir Rintel 45*08f13e7cSLubomir Rintel #define USB2_PLL_REFDIV_SHIFT_MMP3 8 46*08f13e7cSLubomir Rintel #define USB2_PLL_REFDIV_MASK_MMP3 (0xF << 8) 47*08f13e7cSLubomir Rintel 48*08f13e7cSLubomir Rintel #define USB2_PLL_VDD12_SHIFT_MMP3 12 49*08f13e7cSLubomir Rintel #define USB2_PLL_VDD18_SHIFT_MMP3 14 50*08f13e7cSLubomir Rintel 51*08f13e7cSLubomir Rintel /* This is for B0 stepping */ 52*08f13e7cSLubomir Rintel #define USB2_PLL_FBDIV_SHIFT_MMP3_B0 0 53*08f13e7cSLubomir Rintel #define USB2_PLL_REFDIV_SHIFT_MMP3_B0 9 54*08f13e7cSLubomir Rintel #define USB2_PLL_VDD18_SHIFT_MMP3_B0 14 55*08f13e7cSLubomir Rintel #define USB2_PLL_FBDIV_MASK_MMP3_B0 0x01FF 56*08f13e7cSLubomir Rintel #define USB2_PLL_REFDIV_MASK_MMP3_B0 0x3E00 57*08f13e7cSLubomir Rintel 58*08f13e7cSLubomir Rintel #define USB2_PLL_CAL12_SHIFT_MMP3 0 59*08f13e7cSLubomir Rintel #define USB2_PLL_CALI12_MASK_MMP3 (0x3 << 0) 60*08f13e7cSLubomir Rintel 61*08f13e7cSLubomir Rintel #define USB2_PLL_VCOCAL_START_SHIFT_MMP3 2 62*08f13e7cSLubomir Rintel 63*08f13e7cSLubomir Rintel #define USB2_PLL_KVCO_SHIFT_MMP3 4 64*08f13e7cSLubomir Rintel #define USB2_PLL_KVCO_MASK_MMP3 (0x7<<4) 65*08f13e7cSLubomir Rintel 66*08f13e7cSLubomir Rintel #define USB2_PLL_ICP_SHIFT_MMP3 8 67*08f13e7cSLubomir Rintel #define USB2_PLL_ICP_MASK_MMP3 (0x7<<8) 68*08f13e7cSLubomir Rintel 69*08f13e7cSLubomir Rintel #define USB2_PLL_LOCK_BYPASS_SHIFT_MMP3 12 70*08f13e7cSLubomir Rintel 71*08f13e7cSLubomir Rintel #define USB2_PLL_PU_PLL_SHIFT_MMP3 13 72*08f13e7cSLubomir Rintel #define USB2_PLL_PU_PLL_MASK (0x1 << 13) 73*08f13e7cSLubomir Rintel 74*08f13e7cSLubomir Rintel #define USB2_PLL_READY_MASK_MMP3 (0x1 << 15) 75*08f13e7cSLubomir Rintel 76*08f13e7cSLubomir Rintel /* USB2_TX_REG0 */ 77*08f13e7cSLubomir Rintel #define USB2_TX_IMPCAL_VTH_SHIFT_MMP3 8 78*08f13e7cSLubomir Rintel #define USB2_TX_IMPCAL_VTH_MASK_MMP3 (0x7 << 8) 79*08f13e7cSLubomir Rintel 80*08f13e7cSLubomir Rintel #define USB2_TX_RCAL_START_SHIFT_MMP3 13 81*08f13e7cSLubomir Rintel 82*08f13e7cSLubomir Rintel /* USB2_TX_REG1 */ 83*08f13e7cSLubomir Rintel #define USB2_TX_CK60_PHSEL_SHIFT_MMP3 0 84*08f13e7cSLubomir Rintel #define USB2_TX_CK60_PHSEL_MASK_MMP3 (0xf << 0) 85*08f13e7cSLubomir Rintel 86*08f13e7cSLubomir Rintel #define USB2_TX_AMP_SHIFT_MMP3 4 87*08f13e7cSLubomir Rintel #define USB2_TX_AMP_MASK_MMP3 (0x7 << 4) 88*08f13e7cSLubomir Rintel 89*08f13e7cSLubomir Rintel #define USB2_TX_VDD12_SHIFT_MMP3 8 90*08f13e7cSLubomir Rintel #define USB2_TX_VDD12_MASK_MMP3 (0x3 << 8) 91*08f13e7cSLubomir Rintel 92*08f13e7cSLubomir Rintel /* USB2_TX_REG2 */ 93*08f13e7cSLubomir Rintel #define USB2_TX_DRV_SLEWRATE_SHIFT 10 94*08f13e7cSLubomir Rintel 95*08f13e7cSLubomir Rintel /* USB2_RX_REG0 */ 96*08f13e7cSLubomir Rintel #define USB2_RX_SQ_THRESH_SHIFT_MMP3 4 97*08f13e7cSLubomir Rintel #define USB2_RX_SQ_THRESH_MASK_MMP3 (0xf << 4) 98*08f13e7cSLubomir Rintel 99*08f13e7cSLubomir Rintel #define USB2_RX_SQ_LENGTH_SHIFT_MMP3 10 100*08f13e7cSLubomir Rintel #define USB2_RX_SQ_LENGTH_MASK_MMP3 (0x3 << 10) 101*08f13e7cSLubomir Rintel 102*08f13e7cSLubomir Rintel /* USB2_ANA_REG1*/ 103*08f13e7cSLubomir Rintel #define USB2_ANA_PU_ANA_SHIFT_MMP3 14 104*08f13e7cSLubomir Rintel 105*08f13e7cSLubomir Rintel /* USB2_OTG_REG0 */ 106*08f13e7cSLubomir Rintel #define USB2_OTG_PU_OTG_SHIFT_MMP3 3 107*08f13e7cSLubomir Rintel 108*08f13e7cSLubomir Rintel struct mmp3_usb_phy { 109*08f13e7cSLubomir Rintel struct phy *phy; 110*08f13e7cSLubomir Rintel void __iomem *base; 111*08f13e7cSLubomir Rintel }; 112*08f13e7cSLubomir Rintel 113*08f13e7cSLubomir Rintel static unsigned int u2o_get(void __iomem *base, unsigned int offset) 114*08f13e7cSLubomir Rintel { 115*08f13e7cSLubomir Rintel return readl_relaxed(base + offset); 116*08f13e7cSLubomir Rintel } 117*08f13e7cSLubomir Rintel 118*08f13e7cSLubomir Rintel static void u2o_set(void __iomem *base, unsigned int offset, 119*08f13e7cSLubomir Rintel unsigned int value) 120*08f13e7cSLubomir Rintel { 121*08f13e7cSLubomir Rintel u32 reg; 122*08f13e7cSLubomir Rintel 123*08f13e7cSLubomir Rintel reg = readl_relaxed(base + offset); 124*08f13e7cSLubomir Rintel reg |= value; 125*08f13e7cSLubomir Rintel writel_relaxed(reg, base + offset); 126*08f13e7cSLubomir Rintel readl_relaxed(base + offset); 127*08f13e7cSLubomir Rintel } 128*08f13e7cSLubomir Rintel 129*08f13e7cSLubomir Rintel static void u2o_clear(void __iomem *base, unsigned int offset, 130*08f13e7cSLubomir Rintel unsigned int value) 131*08f13e7cSLubomir Rintel { 132*08f13e7cSLubomir Rintel u32 reg; 133*08f13e7cSLubomir Rintel 134*08f13e7cSLubomir Rintel reg = readl_relaxed(base + offset); 135*08f13e7cSLubomir Rintel reg &= ~value; 136*08f13e7cSLubomir Rintel writel_relaxed(reg, base + offset); 137*08f13e7cSLubomir Rintel readl_relaxed(base + offset); 138*08f13e7cSLubomir Rintel } 139*08f13e7cSLubomir Rintel 140*08f13e7cSLubomir Rintel static int mmp3_usb_phy_init(struct phy *phy) 141*08f13e7cSLubomir Rintel { 142*08f13e7cSLubomir Rintel struct mmp3_usb_phy *mmp3_usb_phy = phy_get_drvdata(phy); 143*08f13e7cSLubomir Rintel void __iomem *base = mmp3_usb_phy->base; 144*08f13e7cSLubomir Rintel 145*08f13e7cSLubomir Rintel if (cpu_is_mmp3_a0()) { 146*08f13e7cSLubomir Rintel u2o_clear(base, USB2_PLL_REG0, (USB2_PLL_FBDIV_MASK_MMP3 147*08f13e7cSLubomir Rintel | USB2_PLL_REFDIV_MASK_MMP3)); 148*08f13e7cSLubomir Rintel u2o_set(base, USB2_PLL_REG0, 149*08f13e7cSLubomir Rintel 0xd << USB2_PLL_REFDIV_SHIFT_MMP3 150*08f13e7cSLubomir Rintel | 0xf0 << USB2_PLL_FBDIV_SHIFT_MMP3); 151*08f13e7cSLubomir Rintel } else if (cpu_is_mmp3_b0()) { 152*08f13e7cSLubomir Rintel u2o_clear(base, USB2_PLL_REG0, USB2_PLL_REFDIV_MASK_MMP3_B0 153*08f13e7cSLubomir Rintel | USB2_PLL_FBDIV_MASK_MMP3_B0); 154*08f13e7cSLubomir Rintel u2o_set(base, USB2_PLL_REG0, 155*08f13e7cSLubomir Rintel 0xd << USB2_PLL_REFDIV_SHIFT_MMP3_B0 156*08f13e7cSLubomir Rintel | 0xf0 << USB2_PLL_FBDIV_SHIFT_MMP3_B0); 157*08f13e7cSLubomir Rintel } else { 158*08f13e7cSLubomir Rintel dev_err(&phy->dev, "unsupported silicon revision\n"); 159*08f13e7cSLubomir Rintel return -ENODEV; 160*08f13e7cSLubomir Rintel } 161*08f13e7cSLubomir Rintel 162*08f13e7cSLubomir Rintel u2o_clear(base, USB2_PLL_REG1, USB2_PLL_PU_PLL_MASK 163*08f13e7cSLubomir Rintel | USB2_PLL_ICP_MASK_MMP3 164*08f13e7cSLubomir Rintel | USB2_PLL_KVCO_MASK_MMP3 165*08f13e7cSLubomir Rintel | USB2_PLL_CALI12_MASK_MMP3); 166*08f13e7cSLubomir Rintel u2o_set(base, USB2_PLL_REG1, 1 << USB2_PLL_PU_PLL_SHIFT_MMP3 167*08f13e7cSLubomir Rintel | 1 << USB2_PLL_LOCK_BYPASS_SHIFT_MMP3 168*08f13e7cSLubomir Rintel | 3 << USB2_PLL_ICP_SHIFT_MMP3 169*08f13e7cSLubomir Rintel | 3 << USB2_PLL_KVCO_SHIFT_MMP3 170*08f13e7cSLubomir Rintel | 3 << USB2_PLL_CAL12_SHIFT_MMP3); 171*08f13e7cSLubomir Rintel 172*08f13e7cSLubomir Rintel u2o_clear(base, USB2_TX_REG0, USB2_TX_IMPCAL_VTH_MASK_MMP3); 173*08f13e7cSLubomir Rintel u2o_set(base, USB2_TX_REG0, 2 << USB2_TX_IMPCAL_VTH_SHIFT_MMP3); 174*08f13e7cSLubomir Rintel 175*08f13e7cSLubomir Rintel u2o_clear(base, USB2_TX_REG1, USB2_TX_VDD12_MASK_MMP3 176*08f13e7cSLubomir Rintel | USB2_TX_AMP_MASK_MMP3 177*08f13e7cSLubomir Rintel | USB2_TX_CK60_PHSEL_MASK_MMP3); 178*08f13e7cSLubomir Rintel u2o_set(base, USB2_TX_REG1, 3 << USB2_TX_VDD12_SHIFT_MMP3 179*08f13e7cSLubomir Rintel | 4 << USB2_TX_AMP_SHIFT_MMP3 180*08f13e7cSLubomir Rintel | 4 << USB2_TX_CK60_PHSEL_SHIFT_MMP3); 181*08f13e7cSLubomir Rintel 182*08f13e7cSLubomir Rintel u2o_clear(base, USB2_TX_REG2, 3 << USB2_TX_DRV_SLEWRATE_SHIFT); 183*08f13e7cSLubomir Rintel u2o_set(base, USB2_TX_REG2, 2 << USB2_TX_DRV_SLEWRATE_SHIFT); 184*08f13e7cSLubomir Rintel 185*08f13e7cSLubomir Rintel u2o_clear(base, USB2_RX_REG0, USB2_RX_SQ_THRESH_MASK_MMP3); 186*08f13e7cSLubomir Rintel u2o_set(base, USB2_RX_REG0, 0xa << USB2_RX_SQ_THRESH_SHIFT_MMP3); 187*08f13e7cSLubomir Rintel 188*08f13e7cSLubomir Rintel u2o_set(base, USB2_ANA_REG1, 0x1 << USB2_ANA_PU_ANA_SHIFT_MMP3); 189*08f13e7cSLubomir Rintel 190*08f13e7cSLubomir Rintel u2o_set(base, USB2_OTG_REG0, 0x1 << USB2_OTG_PU_OTG_SHIFT_MMP3); 191*08f13e7cSLubomir Rintel 192*08f13e7cSLubomir Rintel return 0; 193*08f13e7cSLubomir Rintel } 194*08f13e7cSLubomir Rintel 195*08f13e7cSLubomir Rintel static int mmp3_usb_phy_calibrate(struct phy *phy) 196*08f13e7cSLubomir Rintel { 197*08f13e7cSLubomir Rintel struct mmp3_usb_phy *mmp3_usb_phy = phy_get_drvdata(phy); 198*08f13e7cSLubomir Rintel void __iomem *base = mmp3_usb_phy->base; 199*08f13e7cSLubomir Rintel int loops; 200*08f13e7cSLubomir Rintel 201*08f13e7cSLubomir Rintel /* 202*08f13e7cSLubomir Rintel * PLL VCO and TX Impedance Calibration Timing: 203*08f13e7cSLubomir Rintel * 204*08f13e7cSLubomir Rintel * _____________________________________ 205*08f13e7cSLubomir Rintel * PU __________| 206*08f13e7cSLubomir Rintel * _____________________________ 207*08f13e7cSLubomir Rintel * VCOCAL START _________| 208*08f13e7cSLubomir Rintel * ___ 209*08f13e7cSLubomir Rintel * REG_RCAL_START ________________| |________|_______ 210*08f13e7cSLubomir Rintel * | 200us | 400us | 40| 400us | USB PHY READY 211*08f13e7cSLubomir Rintel */ 212*08f13e7cSLubomir Rintel 213*08f13e7cSLubomir Rintel udelay(200); 214*08f13e7cSLubomir Rintel u2o_set(base, USB2_PLL_REG1, 1 << USB2_PLL_VCOCAL_START_SHIFT_MMP3); 215*08f13e7cSLubomir Rintel udelay(400); 216*08f13e7cSLubomir Rintel u2o_set(base, USB2_TX_REG0, 1 << USB2_TX_RCAL_START_SHIFT_MMP3); 217*08f13e7cSLubomir Rintel udelay(40); 218*08f13e7cSLubomir Rintel u2o_clear(base, USB2_TX_REG0, 1 << USB2_TX_RCAL_START_SHIFT_MMP3); 219*08f13e7cSLubomir Rintel udelay(400); 220*08f13e7cSLubomir Rintel 221*08f13e7cSLubomir Rintel loops = 0; 222*08f13e7cSLubomir Rintel while ((u2o_get(base, USB2_PLL_REG1) & USB2_PLL_READY_MASK_MMP3) == 0) { 223*08f13e7cSLubomir Rintel mdelay(1); 224*08f13e7cSLubomir Rintel loops++; 225*08f13e7cSLubomir Rintel if (loops > 100) { 226*08f13e7cSLubomir Rintel dev_err(&phy->dev, "PLL_READY not set after 100mS.\n"); 227*08f13e7cSLubomir Rintel return -ETIMEDOUT; 228*08f13e7cSLubomir Rintel } 229*08f13e7cSLubomir Rintel } 230*08f13e7cSLubomir Rintel 231*08f13e7cSLubomir Rintel return 0; 232*08f13e7cSLubomir Rintel } 233*08f13e7cSLubomir Rintel 234*08f13e7cSLubomir Rintel static const struct phy_ops mmp3_usb_phy_ops = { 235*08f13e7cSLubomir Rintel .init = mmp3_usb_phy_init, 236*08f13e7cSLubomir Rintel .calibrate = mmp3_usb_phy_calibrate, 237*08f13e7cSLubomir Rintel .owner = THIS_MODULE, 238*08f13e7cSLubomir Rintel }; 239*08f13e7cSLubomir Rintel 240*08f13e7cSLubomir Rintel static const struct of_device_id mmp3_usb_phy_of_match[] = { 241*08f13e7cSLubomir Rintel { .compatible = "marvell,mmp3-usb-phy", }, 242*08f13e7cSLubomir Rintel { }, 243*08f13e7cSLubomir Rintel }; 244*08f13e7cSLubomir Rintel MODULE_DEVICE_TABLE(of, mmp3_usb_phy_of_match); 245*08f13e7cSLubomir Rintel 246*08f13e7cSLubomir Rintel static int mmp3_usb_phy_probe(struct platform_device *pdev) 247*08f13e7cSLubomir Rintel { 248*08f13e7cSLubomir Rintel struct device *dev = &pdev->dev; 249*08f13e7cSLubomir Rintel struct resource *resource; 250*08f13e7cSLubomir Rintel struct mmp3_usb_phy *mmp3_usb_phy; 251*08f13e7cSLubomir Rintel struct phy_provider *provider; 252*08f13e7cSLubomir Rintel 253*08f13e7cSLubomir Rintel mmp3_usb_phy = devm_kzalloc(dev, sizeof(*mmp3_usb_phy), GFP_KERNEL); 254*08f13e7cSLubomir Rintel if (!mmp3_usb_phy) 255*08f13e7cSLubomir Rintel return -ENOMEM; 256*08f13e7cSLubomir Rintel 257*08f13e7cSLubomir Rintel resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); 258*08f13e7cSLubomir Rintel mmp3_usb_phy->base = devm_ioremap_resource(dev, resource); 259*08f13e7cSLubomir Rintel if (IS_ERR(mmp3_usb_phy->base)) { 260*08f13e7cSLubomir Rintel dev_err(dev, "failed to remap PHY regs\n"); 261*08f13e7cSLubomir Rintel return PTR_ERR(mmp3_usb_phy->base); 262*08f13e7cSLubomir Rintel } 263*08f13e7cSLubomir Rintel 264*08f13e7cSLubomir Rintel mmp3_usb_phy->phy = devm_phy_create(dev, NULL, &mmp3_usb_phy_ops); 265*08f13e7cSLubomir Rintel if (IS_ERR(mmp3_usb_phy->phy)) { 266*08f13e7cSLubomir Rintel dev_err(dev, "failed to create PHY\n"); 267*08f13e7cSLubomir Rintel return PTR_ERR(mmp3_usb_phy->phy); 268*08f13e7cSLubomir Rintel } 269*08f13e7cSLubomir Rintel 270*08f13e7cSLubomir Rintel phy_set_drvdata(mmp3_usb_phy->phy, mmp3_usb_phy); 271*08f13e7cSLubomir Rintel provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 272*08f13e7cSLubomir Rintel if (IS_ERR(provider)) { 273*08f13e7cSLubomir Rintel dev_err(dev, "failed to register PHY provider\n"); 274*08f13e7cSLubomir Rintel return PTR_ERR(provider); 275*08f13e7cSLubomir Rintel } 276*08f13e7cSLubomir Rintel 277*08f13e7cSLubomir Rintel return 0; 278*08f13e7cSLubomir Rintel } 279*08f13e7cSLubomir Rintel 280*08f13e7cSLubomir Rintel static struct platform_driver mmp3_usb_phy_driver = { 281*08f13e7cSLubomir Rintel .probe = mmp3_usb_phy_probe, 282*08f13e7cSLubomir Rintel .driver = { 283*08f13e7cSLubomir Rintel .name = "mmp3-usb-phy", 284*08f13e7cSLubomir Rintel .of_match_table = mmp3_usb_phy_of_match, 285*08f13e7cSLubomir Rintel }, 286*08f13e7cSLubomir Rintel }; 287*08f13e7cSLubomir Rintel module_platform_driver(mmp3_usb_phy_driver); 288*08f13e7cSLubomir Rintel 289*08f13e7cSLubomir Rintel MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); 290*08f13e7cSLubomir Rintel MODULE_DESCRIPTION("Marvell MMP3 USB PHY Driver"); 291*08f13e7cSLubomir Rintel MODULE_LICENSE("GPL v2"); 292