On Thu, Aug 09, 2012 at 08:04:44PM +0800, Yu Xu wrote: > The driver supports phy_init and phy_shutdown functions to > enable and disable phy for Marvell USB 3.0 controller. > > Signed-off-by: Yu Xu <yuxu@xxxxxxxxxxx> > --- > drivers/usb/phy/Kconfig | 8 + > drivers/usb/phy/Makefile | 1 + > drivers/usb/phy/mv_u3d_phy.c | 354 ++++++++++++++++++++++++++++++++++++++++++ > drivers/usb/phy/mv_u3d_phy.h | 105 +++++++++++++ > 4 files changed, 468 insertions(+) > create mode 100644 drivers/usb/phy/mv_u3d_phy.c > create mode 100644 drivers/usb/phy/mv_u3d_phy.h > > diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig > index e7cf84f..2838adb 100644 > --- a/drivers/usb/phy/Kconfig > +++ b/drivers/usb/phy/Kconfig > @@ -15,3 +15,11 @@ config USB_ISP1301 > > To compile this driver as a module, choose M here: the > module will be called isp1301. > + > +config MV_U3D_PHY > + bool "Marvell USB 3.0 PHY controller Driver" > + depends on USB_MV_U3D > + select USB_OTG_UTILS > + help > + Enable this to support Marvell USB 3.0 phy controller for Marvell > + SoC. > diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile > index eca095b..cf38f08 100644 > --- a/drivers/usb/phy/Makefile > +++ b/drivers/usb/phy/Makefile > @@ -5,3 +5,4 @@ > ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG > > obj-$(CONFIG_USB_ISP1301) += isp1301.o > +obj-$(CONFIG_MV_U3D_PHY) += mv_u3d_phy.o > diff --git a/drivers/usb/phy/mv_u3d_phy.c b/drivers/usb/phy/mv_u3d_phy.c > new file mode 100644 > index 0000000..99312e4 > --- /dev/null > +++ b/drivers/usb/phy/mv_u3d_phy.c > @@ -0,0 +1,354 @@ > +/* > + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2, as published by the Free Software Foundation. > + */ > + > +#include <linux/module.h> > +#include <linux/platform_device.h> > +#include <linux/clk.h> > +#include <linux/delay.h> > +#include <linux/err.h> > +#include <linux/io.h> > +#include <linux/usb/otg.h> > +#include <linux/platform_data/mv_usb.h> > + > +#include "mv_u3d_phy.h" > + > +/* > + * struct mv_u3d_phy - transceiver driver state > + * @phy: transceiver structure > + * @dev: The parent device supplied to the probe function > + * @clk: usb phy clock > + * @base: usb phy register memory base > + */ > +struct mv_u3d_phy { > + struct usb_phy phy; > + struct mv_usb_platform_data *plat; > + struct device *dev; > + struct clk *clk; > + void __iomem *base; > +}; > + > +static u32 mv_u3d_phy_read(void __iomem *base, u32 reg) > +{ > + void __iomem *addr, *data; > + > + addr = base; > + data = base + 0x4; > + > + writel_relaxed(reg, addr); > + return readl_relaxed(data); > +} > + > +static void mv_u3d_phy_set(void __iomem *base, u32 reg, u32 value) > +{ > + void __iomem *addr, *data; > + u32 tmp; > + > + addr = base; > + data = base + 0x4; > + > + writel_relaxed(reg, addr); > + tmp = readl_relaxed(data); > + tmp |= value; > + writel_relaxed(tmp, data); > +} > + > +static void mv_u3d_phy_clear(void __iomem *base, u32 reg, u32 value) > +{ > + void __iomem *addr, *data; > + u32 tmp; > + > + addr = base; > + data = base + 0x4; > + > + writel_relaxed(reg, addr); > + tmp = readl_relaxed(data); > + tmp &= ~value; > + writel_relaxed(tmp, data); > +} > + > +static void mv_u3d_phy_write(void __iomem *base, u32 reg, u32 value) > +{ > + void __iomem *addr, *data; > + > + addr = base; > + data = base + 0x4; > + > + writel_relaxed(reg, addr); > + writel_relaxed(value, data); > +} > + > +static void mv_u3d_phy_reg_print(void __iomem *base, u32 reg) > +{ > + u32 data; > + data = mv_u3d_phy_read(base, reg); > + pr_debug("phy reg 0x%x: phy data 0x%x\n", reg, data); > +} > + > +void mv_u3d_phy_shutdown(struct usb_phy *phy) > +{ > + struct mv_u3d_phy *mv_u3d_phy; > + void __iomem *base; > + u32 val; > + > + mv_u3d_phy = container_of(phy, struct mv_u3d_phy, phy); > + base = mv_u3d_phy->base; > + > + /* Power down Reference Analog current, bit 15 > + * Power down PLL, bit 14 > + * Power down Receiver, bit 13 > + * Power down Transmitter, bit 12 > + * of USB3_POWER_PLL_CONTROL register > + */ > + val = mv_u3d_phy_read(base, USB3_POWER_PLL_CONTROL); > + val &= ~(USB3_POWER_PLL_CONTROL_PU); > + mv_u3d_phy_write(base, USB3_POWER_PLL_CONTROL, val); > + > + if (mv_u3d_phy->clk) > + clk_disable(mv_u3d_phy->clk); > +} > + > +static int mv_u3d_phy_init(struct usb_phy *phy) > +{ > + struct mv_u3d_phy *mv_u3d_phy; > + void __iomem *base; > + u32 val, count; > + > + /* enable usb3 phy */ > + mv_u3d_phy = container_of(phy, struct mv_u3d_phy, phy); > + > + if (mv_u3d_phy->clk) > + clk_enable(mv_u3d_phy->clk); > + > + base = mv_u3d_phy->base; > + > + val = mv_u3d_phy_read(base, USB3_POWER_PLL_CONTROL); > + val &= ~(USB3_POWER_PLL_CONTROL_PU_MASK); > + val |= 0xF << USB3_POWER_PLL_CONTROL_PU_SHIFT; > + mv_u3d_phy_write(base, USB3_POWER_PLL_CONTROL, val); > + udelay(100); > + > + mv_u3d_phy_write(base, USB3_RESET_CONTROL, > + USB3_RESET_CONTROL_RESET_PIPE); > + udelay(100); > + > + mv_u3d_phy_write(base, USB3_RESET_CONTROL, > + USB3_RESET_CONTROL_RESET_PIPE > + | USB3_RESET_CONTROL_RESET_PHY); > + udelay(100); > + > + val = mv_u3d_phy_read(base, USB3_POWER_PLL_CONTROL); > + val &= ~(USB3_POWER_PLL_CONTROL_REF_FREF_SEL_MASK > + | USB3_POWER_PLL_CONTROL_PHY_MODE_MASK); > + val |= (USB3_PLL_25MHZ << USB3_POWER_PLL_CONTROL_REF_FREF_SEL_SHIFT) > + | (0x5 << USB3_POWER_PLL_CONTROL_PHY_MODE_SHIFT); > + mv_u3d_phy_write(base, USB3_POWER_PLL_CONTROL, val); > + udelay(100); > + > + mv_u3d_phy_clear(base, USB3_KVCO_CALI_CONTROL, > + USB3_KVCO_CALI_CONTROL_USE_MAX_PLL_RATE_MASK); > + udelay(100); > + > + val = mv_u3d_phy_read(base, USB3_SQUELCH_FFE); > + val &= ~(USB3_SQUELCH_FFE_FFE_CAP_SEL_MASK > + | USB3_SQUELCH_FFE_FFE_RES_SEL_MASK > + | USB3_SQUELCH_FFE_SQ_THRESH_IN_MASK); > + val |= ((0xD << USB3_SQUELCH_FFE_FFE_CAP_SEL_SHIFT) > + | (0x7 << USB3_SQUELCH_FFE_FFE_RES_SEL_SHIFT) > + | (0x8 << USB3_SQUELCH_FFE_SQ_THRESH_IN_SHIFT)); > + mv_u3d_phy_write(base, USB3_SQUELCH_FFE, val); > + udelay(100); > + > + val = mv_u3d_phy_read(base, USB3_GEN1_SET0); > + val &= ~USB3_GEN1_SET0_G1_TX_SLEW_CTRL_EN_MASK; > + val |= 1 << USB3_GEN1_SET0_G1_TX_EMPH_EN_SHIFT; > + mv_u3d_phy_write(base, USB3_GEN1_SET0, val); > + udelay(100); > + > + val = mv_u3d_phy_read(base, USB3_GEN2_SET0); > + val &= ~(USB3_GEN2_SET0_G2_TX_AMP_MASK > + | USB3_GEN2_SET0_G2_TX_EMPH_AMP_MASK > + | USB3_GEN2_SET0_G2_TX_SLEW_CTRL_EN_MASK); > + val |= ((0x14 << USB3_GEN2_SET0_G2_TX_AMP_SHIFT) > + | (1 << USB3_GEN2_SET0_G2_TX_AMP_ADJ_SHIFT) > + | (0xA << USB3_GEN2_SET0_G2_TX_EMPH_AMP_SHIFT) > + | (1 << USB3_GEN2_SET0_G2_TX_EMPH_EN_SHIFT)); > + mv_u3d_phy_write(base, USB3_GEN2_SET0, val); > + udelay(100); > + > + mv_u3d_phy_read(base, USB3_TX_EMPPH); > + val &= ~(USB3_TX_EMPPH_AMP_MASK > + | USB3_TX_EMPPH_EN_MASK > + | USB3_TX_EMPPH_AMP_FORCE_MASK > + | USB3_TX_EMPPH_PAR1_MASK > + | USB3_TX_EMPPH_PAR2_MASK); > + val |= ((0xB << USB3_TX_EMPPH_AMP_SHIFT) > + | (1 << USB3_TX_EMPPH_EN_SHIFT) > + | (1 << USB3_TX_EMPPH_AMP_FORCE_SHIFT) > + | (0x1C << USB3_TX_EMPPH_PAR1_SHIFT) > + | (1 << USB3_TX_EMPPH_PAR2_SHIFT)); > + > + mv_u3d_phy_write(base, USB3_TX_EMPPH, val); > + udelay(100); > + > + val = mv_u3d_phy_read(base, USB3_GEN2_SET1); > + val &= ~(USB3_GEN2_SET1_G2_RX_SELMUPI_MASK > + | USB3_GEN2_SET1_G2_RX_SELMUPF_MASK > + | USB3_GEN2_SET1_G2_RX_SELMUFI_MASK > + | USB3_GEN2_SET1_G2_RX_SELMUFF_MASK); > + val |= ((1 << USB3_GEN2_SET1_G2_RX_SELMUPI_SHIFT) > + | (1 << USB3_GEN2_SET1_G2_RX_SELMUPF_SHIFT) > + | (1 << USB3_GEN2_SET1_G2_RX_SELMUFI_SHIFT) > + | (1 << USB3_GEN2_SET1_G2_RX_SELMUFF_SHIFT)); > + mv_u3d_phy_write(base, USB3_GEN2_SET1, val); > + udelay(100); > + > + val = mv_u3d_phy_read(base, USB3_DIGITAL_LOOPBACK_EN); > + val &= ~USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_MASK; > + val |= 1 << USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_SHIFT; > + mv_u3d_phy_write(base, USB3_DIGITAL_LOOPBACK_EN, val); > + udelay(100); > + > + val = mv_u3d_phy_read(base, USB3_IMPEDANCE_TX_SSC); > + val &= ~USB3_IMPEDANCE_TX_SSC_SSC_AMP_MASK; > + val |= 0xC << USB3_IMPEDANCE_TX_SSC_SSC_AMP_SHIFT; > + mv_u3d_phy_write(base, USB3_IMPEDANCE_TX_SSC, val); > + udelay(100); > + > + val = mv_u3d_phy_read(base, USB3_IMPEDANCE_CALI_CTRL); > + val &= ~USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_MASK; > + val |= 0x4 << USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_SHIFT; > + mv_u3d_phy_write(base, USB3_IMPEDANCE_CALI_CTRL, val); > + udelay(100); > + > + val = mv_u3d_phy_read(base, USB3_PHY_ISOLATION_MODE); > + val &= ~(USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_MASK > + | USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_MASK > + | USB3_PHY_ISOLATION_MODE_TX_DRV_IDLE_MASK); > + val |= ((1 << USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_SHIFT) > + | (1 << USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_SHIFT)); > + mv_u3d_phy_write(base, USB3_PHY_ISOLATION_MODE, val); > + udelay(100); > + > + val = mv_u3d_phy_read(base, USB3_TXDETRX); > + val &= ~(USB3_TXDETRX_VTHSEL_MASK); > + val |= 0x1 << USB3_TXDETRX_VTHSEL_SHIFT; > + mv_u3d_phy_write(base, USB3_TXDETRX, val); > + udelay(100); > + > + mv_u3d_phy_reg_print(base, USB3_KVCO_CALI_CONTROL); I'm not sure you really want this constantly on your driver, because it introduces quite some overhead when you always need to readback the register before printing. Couldn't you just move this out of the driver and use perf for that kind of thing ? You could try something like: # perf probe --add mv_u3d_phy_write addr data # perf probe --add mv_u3d_phy_read addr data # perf record -gaR sleep 10 # it will record for 10 seconds # perf report I didn't test that, but I think it could work and it wouldn't cause a lot of overhead in your driver. > + pr_debug("%s: start calibration", __func__); drop all pr_* and convert them in to dev_* you have your dev pointer saved on your mv_u3d_phy structure. -- balbi
Attachment:
signature.asc
Description: Digital signature