Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.8 345 lines 9.2 kB view raw
1/* 2 * Copyright (C) 2011 Marvell International Ltd. All rights reserved. 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms and conditions of the GNU General Public License, 6 * version 2, as published by the Free Software Foundation. 7 */ 8 9#include <linux/module.h> 10#include <linux/platform_device.h> 11#include <linux/clk.h> 12#include <linux/delay.h> 13#include <linux/err.h> 14#include <linux/io.h> 15#include <linux/usb/otg.h> 16#include <linux/platform_data/mv_usb.h> 17 18#include "mv_u3d_phy.h" 19 20/* 21 * struct mv_u3d_phy - transceiver driver state 22 * @phy: transceiver structure 23 * @dev: The parent device supplied to the probe function 24 * @clk: usb phy clock 25 * @base: usb phy register memory base 26 */ 27struct mv_u3d_phy { 28 struct usb_phy phy; 29 struct mv_usb_platform_data *plat; 30 struct device *dev; 31 struct clk *clk; 32 void __iomem *base; 33}; 34 35static u32 mv_u3d_phy_read(void __iomem *base, u32 reg) 36{ 37 void __iomem *addr, *data; 38 39 addr = base; 40 data = base + 0x4; 41 42 writel_relaxed(reg, addr); 43 return readl_relaxed(data); 44} 45 46static void mv_u3d_phy_set(void __iomem *base, u32 reg, u32 value) 47{ 48 void __iomem *addr, *data; 49 u32 tmp; 50 51 addr = base; 52 data = base + 0x4; 53 54 writel_relaxed(reg, addr); 55 tmp = readl_relaxed(data); 56 tmp |= value; 57 writel_relaxed(tmp, data); 58} 59 60static void mv_u3d_phy_clear(void __iomem *base, u32 reg, u32 value) 61{ 62 void __iomem *addr, *data; 63 u32 tmp; 64 65 addr = base; 66 data = base + 0x4; 67 68 writel_relaxed(reg, addr); 69 tmp = readl_relaxed(data); 70 tmp &= ~value; 71 writel_relaxed(tmp, data); 72} 73 74static void mv_u3d_phy_write(void __iomem *base, u32 reg, u32 value) 75{ 76 void __iomem *addr, *data; 77 78 addr = base; 79 data = base + 0x4; 80 81 writel_relaxed(reg, addr); 82 writel_relaxed(value, data); 83} 84 85void mv_u3d_phy_shutdown(struct usb_phy *phy) 86{ 87 struct mv_u3d_phy *mv_u3d_phy; 88 void __iomem *base; 89 u32 val; 90 91 mv_u3d_phy = container_of(phy, struct mv_u3d_phy, phy); 92 base = mv_u3d_phy->base; 93 94 /* Power down Reference Analog current, bit 15 95 * Power down PLL, bit 14 96 * Power down Receiver, bit 13 97 * Power down Transmitter, bit 12 98 * of USB3_POWER_PLL_CONTROL register 99 */ 100 val = mv_u3d_phy_read(base, USB3_POWER_PLL_CONTROL); 101 val &= ~(USB3_POWER_PLL_CONTROL_PU); 102 mv_u3d_phy_write(base, USB3_POWER_PLL_CONTROL, val); 103 104 if (mv_u3d_phy->clk) 105 clk_disable(mv_u3d_phy->clk); 106} 107 108static int mv_u3d_phy_init(struct usb_phy *phy) 109{ 110 struct mv_u3d_phy *mv_u3d_phy; 111 void __iomem *base; 112 u32 val, count; 113 114 /* enable usb3 phy */ 115 mv_u3d_phy = container_of(phy, struct mv_u3d_phy, phy); 116 117 if (mv_u3d_phy->clk) 118 clk_enable(mv_u3d_phy->clk); 119 120 base = mv_u3d_phy->base; 121 122 val = mv_u3d_phy_read(base, USB3_POWER_PLL_CONTROL); 123 val &= ~(USB3_POWER_PLL_CONTROL_PU_MASK); 124 val |= 0xF << USB3_POWER_PLL_CONTROL_PU_SHIFT; 125 mv_u3d_phy_write(base, USB3_POWER_PLL_CONTROL, val); 126 udelay(100); 127 128 mv_u3d_phy_write(base, USB3_RESET_CONTROL, 129 USB3_RESET_CONTROL_RESET_PIPE); 130 udelay(100); 131 132 mv_u3d_phy_write(base, USB3_RESET_CONTROL, 133 USB3_RESET_CONTROL_RESET_PIPE 134 | USB3_RESET_CONTROL_RESET_PHY); 135 udelay(100); 136 137 val = mv_u3d_phy_read(base, USB3_POWER_PLL_CONTROL); 138 val &= ~(USB3_POWER_PLL_CONTROL_REF_FREF_SEL_MASK 139 | USB3_POWER_PLL_CONTROL_PHY_MODE_MASK); 140 val |= (USB3_PLL_25MHZ << USB3_POWER_PLL_CONTROL_REF_FREF_SEL_SHIFT) 141 | (0x5 << USB3_POWER_PLL_CONTROL_PHY_MODE_SHIFT); 142 mv_u3d_phy_write(base, USB3_POWER_PLL_CONTROL, val); 143 udelay(100); 144 145 mv_u3d_phy_clear(base, USB3_KVCO_CALI_CONTROL, 146 USB3_KVCO_CALI_CONTROL_USE_MAX_PLL_RATE_MASK); 147 udelay(100); 148 149 val = mv_u3d_phy_read(base, USB3_SQUELCH_FFE); 150 val &= ~(USB3_SQUELCH_FFE_FFE_CAP_SEL_MASK 151 | USB3_SQUELCH_FFE_FFE_RES_SEL_MASK 152 | USB3_SQUELCH_FFE_SQ_THRESH_IN_MASK); 153 val |= ((0xD << USB3_SQUELCH_FFE_FFE_CAP_SEL_SHIFT) 154 | (0x7 << USB3_SQUELCH_FFE_FFE_RES_SEL_SHIFT) 155 | (0x8 << USB3_SQUELCH_FFE_SQ_THRESH_IN_SHIFT)); 156 mv_u3d_phy_write(base, USB3_SQUELCH_FFE, val); 157 udelay(100); 158 159 val = mv_u3d_phy_read(base, USB3_GEN1_SET0); 160 val &= ~USB3_GEN1_SET0_G1_TX_SLEW_CTRL_EN_MASK; 161 val |= 1 << USB3_GEN1_SET0_G1_TX_EMPH_EN_SHIFT; 162 mv_u3d_phy_write(base, USB3_GEN1_SET0, val); 163 udelay(100); 164 165 val = mv_u3d_phy_read(base, USB3_GEN2_SET0); 166 val &= ~(USB3_GEN2_SET0_G2_TX_AMP_MASK 167 | USB3_GEN2_SET0_G2_TX_EMPH_AMP_MASK 168 | USB3_GEN2_SET0_G2_TX_SLEW_CTRL_EN_MASK); 169 val |= ((0x14 << USB3_GEN2_SET0_G2_TX_AMP_SHIFT) 170 | (1 << USB3_GEN2_SET0_G2_TX_AMP_ADJ_SHIFT) 171 | (0xA << USB3_GEN2_SET0_G2_TX_EMPH_AMP_SHIFT) 172 | (1 << USB3_GEN2_SET0_G2_TX_EMPH_EN_SHIFT)); 173 mv_u3d_phy_write(base, USB3_GEN2_SET0, val); 174 udelay(100); 175 176 mv_u3d_phy_read(base, USB3_TX_EMPPH); 177 val &= ~(USB3_TX_EMPPH_AMP_MASK 178 | USB3_TX_EMPPH_EN_MASK 179 | USB3_TX_EMPPH_AMP_FORCE_MASK 180 | USB3_TX_EMPPH_PAR1_MASK 181 | USB3_TX_EMPPH_PAR2_MASK); 182 val |= ((0xB << USB3_TX_EMPPH_AMP_SHIFT) 183 | (1 << USB3_TX_EMPPH_EN_SHIFT) 184 | (1 << USB3_TX_EMPPH_AMP_FORCE_SHIFT) 185 | (0x1C << USB3_TX_EMPPH_PAR1_SHIFT) 186 | (1 << USB3_TX_EMPPH_PAR2_SHIFT)); 187 188 mv_u3d_phy_write(base, USB3_TX_EMPPH, val); 189 udelay(100); 190 191 val = mv_u3d_phy_read(base, USB3_GEN2_SET1); 192 val &= ~(USB3_GEN2_SET1_G2_RX_SELMUPI_MASK 193 | USB3_GEN2_SET1_G2_RX_SELMUPF_MASK 194 | USB3_GEN2_SET1_G2_RX_SELMUFI_MASK 195 | USB3_GEN2_SET1_G2_RX_SELMUFF_MASK); 196 val |= ((1 << USB3_GEN2_SET1_G2_RX_SELMUPI_SHIFT) 197 | (1 << USB3_GEN2_SET1_G2_RX_SELMUPF_SHIFT) 198 | (1 << USB3_GEN2_SET1_G2_RX_SELMUFI_SHIFT) 199 | (1 << USB3_GEN2_SET1_G2_RX_SELMUFF_SHIFT)); 200 mv_u3d_phy_write(base, USB3_GEN2_SET1, val); 201 udelay(100); 202 203 val = mv_u3d_phy_read(base, USB3_DIGITAL_LOOPBACK_EN); 204 val &= ~USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_MASK; 205 val |= 1 << USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_SHIFT; 206 mv_u3d_phy_write(base, USB3_DIGITAL_LOOPBACK_EN, val); 207 udelay(100); 208 209 val = mv_u3d_phy_read(base, USB3_IMPEDANCE_TX_SSC); 210 val &= ~USB3_IMPEDANCE_TX_SSC_SSC_AMP_MASK; 211 val |= 0xC << USB3_IMPEDANCE_TX_SSC_SSC_AMP_SHIFT; 212 mv_u3d_phy_write(base, USB3_IMPEDANCE_TX_SSC, val); 213 udelay(100); 214 215 val = mv_u3d_phy_read(base, USB3_IMPEDANCE_CALI_CTRL); 216 val &= ~USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_MASK; 217 val |= 0x4 << USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_SHIFT; 218 mv_u3d_phy_write(base, USB3_IMPEDANCE_CALI_CTRL, val); 219 udelay(100); 220 221 val = mv_u3d_phy_read(base, USB3_PHY_ISOLATION_MODE); 222 val &= ~(USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_MASK 223 | USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_MASK 224 | USB3_PHY_ISOLATION_MODE_TX_DRV_IDLE_MASK); 225 val |= ((1 << USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_SHIFT) 226 | (1 << USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_SHIFT)); 227 mv_u3d_phy_write(base, USB3_PHY_ISOLATION_MODE, val); 228 udelay(100); 229 230 val = mv_u3d_phy_read(base, USB3_TXDETRX); 231 val &= ~(USB3_TXDETRX_VTHSEL_MASK); 232 val |= 0x1 << USB3_TXDETRX_VTHSEL_SHIFT; 233 mv_u3d_phy_write(base, USB3_TXDETRX, val); 234 udelay(100); 235 236 dev_dbg(mv_u3d_phy->dev, "start calibration\n"); 237 238calstart: 239 /* Perform Manual Calibration */ 240 mv_u3d_phy_set(base, USB3_KVCO_CALI_CONTROL, 241 1 << USB3_KVCO_CALI_CONTROL_CAL_START_SHIFT); 242 243 mdelay(1); 244 245 count = 0; 246 while (1) { 247 val = mv_u3d_phy_read(base, USB3_KVCO_CALI_CONTROL); 248 if (val & (1 << USB3_KVCO_CALI_CONTROL_CAL_DONE_SHIFT)) 249 break; 250 else if (count > 50) { 251 dev_dbg(mv_u3d_phy->dev, "calibration failure, retry...\n"); 252 goto calstart; 253 } 254 count++; 255 mdelay(1); 256 } 257 258 /* active PIPE interface */ 259 mv_u3d_phy_write(base, USB3_PIPE_SM_CTRL, 260 1 << USB3_PIPE_SM_CTRL_PHY_INIT_DONE); 261 262 return 0; 263} 264 265static int mv_u3d_phy_probe(struct platform_device *pdev) 266{ 267 struct mv_u3d_phy *mv_u3d_phy; 268 struct mv_usb_platform_data *pdata; 269 struct device *dev = &pdev->dev; 270 struct resource *res; 271 void __iomem *phy_base; 272 int ret; 273 274 pdata = pdev->dev.platform_data; 275 if (!pdata) { 276 dev_err(&pdev->dev, "%s: no platform data defined\n", __func__); 277 return -EINVAL; 278 } 279 280 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 281 if (!res) { 282 dev_err(dev, "missing mem resource\n"); 283 return -ENODEV; 284 } 285 286 phy_base = devm_request_and_ioremap(dev, res); 287 if (!phy_base) { 288 dev_err(dev, "%s: register mapping failed\n", __func__); 289 return -ENXIO; 290 } 291 292 mv_u3d_phy = devm_kzalloc(dev, sizeof(*mv_u3d_phy), GFP_KERNEL); 293 if (!mv_u3d_phy) 294 return -ENOMEM; 295 296 mv_u3d_phy->dev = &pdev->dev; 297 mv_u3d_phy->plat = pdata; 298 mv_u3d_phy->base = phy_base; 299 mv_u3d_phy->phy.dev = mv_u3d_phy->dev; 300 mv_u3d_phy->phy.label = "mv-u3d-phy"; 301 mv_u3d_phy->phy.init = mv_u3d_phy_init; 302 mv_u3d_phy->phy.shutdown = mv_u3d_phy_shutdown; 303 304 ret = usb_add_phy(&mv_u3d_phy->phy, USB_PHY_TYPE_USB3); 305 if (ret) 306 goto err; 307 308 if (!mv_u3d_phy->clk) 309 mv_u3d_phy->clk = clk_get(mv_u3d_phy->dev, "u3dphy"); 310 311 platform_set_drvdata(pdev, mv_u3d_phy); 312 313 dev_info(&pdev->dev, "Initialized Marvell USB 3.0 PHY\n"); 314err: 315 return ret; 316} 317 318static int __exit mv_u3d_phy_remove(struct platform_device *pdev) 319{ 320 struct mv_u3d_phy *mv_u3d_phy = platform_get_drvdata(pdev); 321 322 usb_remove_phy(&mv_u3d_phy->phy); 323 324 if (mv_u3d_phy->clk) { 325 clk_put(mv_u3d_phy->clk); 326 mv_u3d_phy->clk = NULL; 327 } 328 329 return 0; 330} 331 332static struct platform_driver mv_u3d_phy_driver = { 333 .probe = mv_u3d_phy_probe, 334 .remove = mv_u3d_phy_remove, 335 .driver = { 336 .name = "mv-u3d-phy", 337 .owner = THIS_MODULE, 338 }, 339}; 340 341module_platform_driver(mv_u3d_phy_driver); 342MODULE_DESCRIPTION("Marvell USB 3.0 PHY controller"); 343MODULE_AUTHOR("Yu Xu <yuxu@marvell.com>"); 344MODULE_LICENSE("GPL"); 345MODULE_ALIAS("platform:mv-u3d-phy");