Message ID | 20190830220743.439670-16-lkundrak@v3.sk (mailing list archive) |
---|---|
State | Not Applicable, archived |
Headers | show |
Series | Initial support for Marvell MMP3 SoC | expand |
Hi, On 31/08/19 3:37 AM, Lubomir Rintel wrote: Change the $subject to "phy: Add USB2 PHY driver for Marvell MMP3 SoC" > This is the USB2 PHY as found on the Marvell MMP3 SoC. Based on Marvell GPL > release. Just have "Add PHY driver for the USB2 PHY found on Marvell MMP3 SoC." here. > > While at that, also add a MAINTAINERS entry including the other MMP PHY > driver. Adding MAINTAINERS could be a different patch. > > Signed-off-by: Lubomir Rintel <lkundrak@v3.sk> Once the commit log is fixed: Acked-by: Kishon Vijay Abraham I <kishon@ti.com> Thanks Kishon > --- > MAINTAINERS | 7 + > drivers/phy/marvell/Kconfig | 11 ++ > drivers/phy/marvell/Makefile | 1 + > drivers/phy/marvell/phy-mmp3-usb.c | 291 +++++++++++++++++++++++++++++ > 4 files changed, 310 insertions(+) > create mode 100644 drivers/phy/marvell/phy-mmp3-usb.c > > diff --git a/MAINTAINERS b/MAINTAINERS > index 176ef19f0b9db..449349f8f20bf 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -10799,6 +10799,13 @@ F: arch/arm/boot/dts/mmp* > F: arch/arm/mach-mmp/ > F: linux/soc/mmp/ > > +MMP USB PHY DRIVERS > +R: Lubomir Rintel <lkundrak@v3.sk> > +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) > +S: Maintained > +F: drivers/phy/marvell/phy-mmp3-usb.c > +F: drivers/phy/marvell/phy-pxa-usb.c > + > MMU GATHER AND TLB INVALIDATION > M: Will Deacon <will@kernel.org> > M: "Aneesh Kumar K.V" <aneesh.kumar@linux.ibm.com> > diff --git a/drivers/phy/marvell/Kconfig b/drivers/phy/marvell/Kconfig > index 0e1642419c0bf..d33ef35b3e51b 100644 > --- a/drivers/phy/marvell/Kconfig > +++ b/drivers/phy/marvell/Kconfig > @@ -102,3 +102,14 @@ config PHY_PXA_USB > The PHY driver will be used by Marvell udc/ehci/otg driver. > > To compile this driver as a module, choose M here. > + > +config PHY_MMP3_USB > + tristate "Marvell MMP3 USB PHY Driver" > + depends on MACH_MMP3_DT || COMPILE_TEST > + select GENERIC_PHY > + help > + Enable this to support Marvell MMP3 USB PHY driver for Marvell > + SoC. This driver will do the PHY initialization and shutdown. > + The PHY driver will be used by Marvell udc/ehci/otg driver. > + > + To compile this driver as a module, choose M here. > diff --git a/drivers/phy/marvell/Makefile b/drivers/phy/marvell/Makefile > index 434eb9ca6cc3f..5a106b1549f41 100644 > --- a/drivers/phy/marvell/Makefile > +++ b/drivers/phy/marvell/Makefile > @@ -2,6 +2,7 @@ > obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o > obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o > obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o > +obj-$(CONFIG_PHY_MMP3_USB) += phy-mmp3-usb.o > obj-$(CONFIG_PHY_MVEBU_A3700_COMPHY) += phy-mvebu-a3700-comphy.o > obj-$(CONFIG_PHY_MVEBU_A3700_UTMI) += phy-mvebu-a3700-utmi.o > obj-$(CONFIG_PHY_MVEBU_A38X_COMPHY) += phy-armada38x-comphy.o > diff --git a/drivers/phy/marvell/phy-mmp3-usb.c b/drivers/phy/marvell/phy-mmp3-usb.c > new file mode 100644 > index 0000000000000..499869595a582 > --- /dev/null > +++ b/drivers/phy/marvell/phy-mmp3-usb.c > @@ -0,0 +1,291 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. > + * Copyright (C) 2018,2019 Lubomir Rintel <lkundrak@v3.sk> > + */ > + > +#include <linux/delay.h> > +#include <linux/io.h> > +#include <linux/module.h> > +#include <linux/phy/phy.h> > +#include <linux/platform_device.h> > +#include <linux/soc/mmp/cputype.h> > + > +#define USB2_PLL_REG0 0x4 > +#define USB2_PLL_REG1 0x8 > +#define USB2_TX_REG0 0x10 > +#define USB2_TX_REG1 0x14 > +#define USB2_TX_REG2 0x18 > +#define USB2_RX_REG0 0x20 > +#define USB2_RX_REG1 0x24 > +#define USB2_RX_REG2 0x28 > +#define USB2_ANA_REG0 0x30 > +#define USB2_ANA_REG1 0x34 > +#define USB2_ANA_REG2 0x38 > +#define USB2_DIG_REG0 0x3C > +#define USB2_DIG_REG1 0x40 > +#define USB2_DIG_REG2 0x44 > +#define USB2_DIG_REG3 0x48 > +#define USB2_TEST_REG0 0x4C > +#define USB2_TEST_REG1 0x50 > +#define USB2_TEST_REG2 0x54 > +#define USB2_CHARGER_REG0 0x58 > +#define USB2_OTG_REG0 0x5C > +#define USB2_PHY_MON0 0x60 > +#define USB2_RESETVE_REG0 0x64 > +#define USB2_ICID_REG0 0x78 > +#define USB2_ICID_REG1 0x7C > + > +/* USB2_PLL_REG0 */ > + > +/* This is for Ax stepping */ > +#define USB2_PLL_FBDIV_SHIFT_MMP3 0 > +#define USB2_PLL_FBDIV_MASK_MMP3 (0xFF << 0) > + > +#define USB2_PLL_REFDIV_SHIFT_MMP3 8 > +#define USB2_PLL_REFDIV_MASK_MMP3 (0xF << 8) > + > +#define USB2_PLL_VDD12_SHIFT_MMP3 12 > +#define USB2_PLL_VDD18_SHIFT_MMP3 14 > + > +/* This is for B0 stepping */ > +#define USB2_PLL_FBDIV_SHIFT_MMP3_B0 0 > +#define USB2_PLL_REFDIV_SHIFT_MMP3_B0 9 > +#define USB2_PLL_VDD18_SHIFT_MMP3_B0 14 > +#define USB2_PLL_FBDIV_MASK_MMP3_B0 0x01FF > +#define USB2_PLL_REFDIV_MASK_MMP3_B0 0x3E00 > + > +#define USB2_PLL_CAL12_SHIFT_MMP3 0 > +#define USB2_PLL_CALI12_MASK_MMP3 (0x3 << 0) > + > +#define USB2_PLL_VCOCAL_START_SHIFT_MMP3 2 > + > +#define USB2_PLL_KVCO_SHIFT_MMP3 4 > +#define USB2_PLL_KVCO_MASK_MMP3 (0x7<<4) > + > +#define USB2_PLL_ICP_SHIFT_MMP3 8 > +#define USB2_PLL_ICP_MASK_MMP3 (0x7<<8) > + > +#define USB2_PLL_LOCK_BYPASS_SHIFT_MMP3 12 > + > +#define USB2_PLL_PU_PLL_SHIFT_MMP3 13 > +#define USB2_PLL_PU_PLL_MASK (0x1 << 13) > + > +#define USB2_PLL_READY_MASK_MMP3 (0x1 << 15) > + > +/* USB2_TX_REG0 */ > +#define USB2_TX_IMPCAL_VTH_SHIFT_MMP3 8 > +#define USB2_TX_IMPCAL_VTH_MASK_MMP3 (0x7 << 8) > + > +#define USB2_TX_RCAL_START_SHIFT_MMP3 13 > + > +/* USB2_TX_REG1 */ > +#define USB2_TX_CK60_PHSEL_SHIFT_MMP3 0 > +#define USB2_TX_CK60_PHSEL_MASK_MMP3 (0xf << 0) > + > +#define USB2_TX_AMP_SHIFT_MMP3 4 > +#define USB2_TX_AMP_MASK_MMP3 (0x7 << 4) > + > +#define USB2_TX_VDD12_SHIFT_MMP3 8 > +#define USB2_TX_VDD12_MASK_MMP3 (0x3 << 8) > + > +/* USB2_TX_REG2 */ > +#define USB2_TX_DRV_SLEWRATE_SHIFT 10 > + > +/* USB2_RX_REG0 */ > +#define USB2_RX_SQ_THRESH_SHIFT_MMP3 4 > +#define USB2_RX_SQ_THRESH_MASK_MMP3 (0xf << 4) > + > +#define USB2_RX_SQ_LENGTH_SHIFT_MMP3 10 > +#define USB2_RX_SQ_LENGTH_MASK_MMP3 (0x3 << 10) > + > +/* USB2_ANA_REG1*/ > +#define USB2_ANA_PU_ANA_SHIFT_MMP3 14 > + > +/* USB2_OTG_REG0 */ > +#define USB2_OTG_PU_OTG_SHIFT_MMP3 3 > + > +struct mmp3_usb_phy { > + struct phy *phy; > + void __iomem *base; > +}; > + > +static unsigned int u2o_get(void __iomem *base, unsigned int offset) > +{ > + return readl_relaxed(base + offset); > +} > + > +static void u2o_set(void __iomem *base, unsigned int offset, > + unsigned int value) > +{ > + u32 reg; > + > + reg = readl_relaxed(base + offset); > + reg |= value; > + writel_relaxed(reg, base + offset); > + readl_relaxed(base + offset); > +} > + > +static void u2o_clear(void __iomem *base, unsigned int offset, > + unsigned int value) > +{ > + u32 reg; > + > + reg = readl_relaxed(base + offset); > + reg &= ~value; > + writel_relaxed(reg, base + offset); > + readl_relaxed(base + offset); > +} > + > +static int mmp3_usb_phy_init(struct phy *phy) > +{ > + struct mmp3_usb_phy *mmp3_usb_phy = phy_get_drvdata(phy); > + void __iomem *base = mmp3_usb_phy->base; > + > + if (cpu_is_mmp3_a0()) { > + u2o_clear(base, USB2_PLL_REG0, (USB2_PLL_FBDIV_MASK_MMP3 > + | USB2_PLL_REFDIV_MASK_MMP3)); > + u2o_set(base, USB2_PLL_REG0, > + 0xd << USB2_PLL_REFDIV_SHIFT_MMP3 > + | 0xf0 << USB2_PLL_FBDIV_SHIFT_MMP3); > + } else if (cpu_is_mmp3_b0()) { > + u2o_clear(base, USB2_PLL_REG0, USB2_PLL_REFDIV_MASK_MMP3_B0 > + | USB2_PLL_FBDIV_MASK_MMP3_B0); > + u2o_set(base, USB2_PLL_REG0, > + 0xd << USB2_PLL_REFDIV_SHIFT_MMP3_B0 > + | 0xf0 << USB2_PLL_FBDIV_SHIFT_MMP3_B0); > + } else { > + dev_err(&phy->dev, "unsupported silicon revision\n"); > + return -ENODEV; > + } > + > + u2o_clear(base, USB2_PLL_REG1, USB2_PLL_PU_PLL_MASK > + | USB2_PLL_ICP_MASK_MMP3 > + | USB2_PLL_KVCO_MASK_MMP3 > + | USB2_PLL_CALI12_MASK_MMP3); > + u2o_set(base, USB2_PLL_REG1, 1 << USB2_PLL_PU_PLL_SHIFT_MMP3 > + | 1 << USB2_PLL_LOCK_BYPASS_SHIFT_MMP3 > + | 3 << USB2_PLL_ICP_SHIFT_MMP3 > + | 3 << USB2_PLL_KVCO_SHIFT_MMP3 > + | 3 << USB2_PLL_CAL12_SHIFT_MMP3); > + > + u2o_clear(base, USB2_TX_REG0, USB2_TX_IMPCAL_VTH_MASK_MMP3); > + u2o_set(base, USB2_TX_REG0, 2 << USB2_TX_IMPCAL_VTH_SHIFT_MMP3); > + > + u2o_clear(base, USB2_TX_REG1, USB2_TX_VDD12_MASK_MMP3 > + | USB2_TX_AMP_MASK_MMP3 > + | USB2_TX_CK60_PHSEL_MASK_MMP3); > + u2o_set(base, USB2_TX_REG1, 3 << USB2_TX_VDD12_SHIFT_MMP3 > + | 4 << USB2_TX_AMP_SHIFT_MMP3 > + | 4 << USB2_TX_CK60_PHSEL_SHIFT_MMP3); > + > + u2o_clear(base, USB2_TX_REG2, 3 << USB2_TX_DRV_SLEWRATE_SHIFT); > + u2o_set(base, USB2_TX_REG2, 2 << USB2_TX_DRV_SLEWRATE_SHIFT); > + > + u2o_clear(base, USB2_RX_REG0, USB2_RX_SQ_THRESH_MASK_MMP3); > + u2o_set(base, USB2_RX_REG0, 0xa << USB2_RX_SQ_THRESH_SHIFT_MMP3); > + > + u2o_set(base, USB2_ANA_REG1, 0x1 << USB2_ANA_PU_ANA_SHIFT_MMP3); > + > + u2o_set(base, USB2_OTG_REG0, 0x1 << USB2_OTG_PU_OTG_SHIFT_MMP3); > + > + return 0; > +} > + > +static int mmp3_usb_phy_calibrate(struct phy *phy) > +{ > + struct mmp3_usb_phy *mmp3_usb_phy = phy_get_drvdata(phy); > + void __iomem *base = mmp3_usb_phy->base; > + int loops; > + > + /* > + * PLL VCO and TX Impedance Calibration Timing: > + * > + * _____________________________________ > + * PU __________| > + * _____________________________ > + * VCOCAL START _________| > + * ___ > + * REG_RCAL_START ________________| |________|_______ > + * | 200us | 400us | 40| 400us | USB PHY READY > + */ > + > + udelay(200); > + u2o_set(base, USB2_PLL_REG1, 1 << USB2_PLL_VCOCAL_START_SHIFT_MMP3); > + udelay(400); > + u2o_set(base, USB2_TX_REG0, 1 << USB2_TX_RCAL_START_SHIFT_MMP3); > + udelay(40); > + u2o_clear(base, USB2_TX_REG0, 1 << USB2_TX_RCAL_START_SHIFT_MMP3); > + udelay(400); > + > + loops = 0; > + while ((u2o_get(base, USB2_PLL_REG1) & USB2_PLL_READY_MASK_MMP3) == 0) { > + mdelay(1); > + loops++; > + if (loops > 100) { > + dev_err(&phy->dev, "PLL_READY not set after 100mS.\n"); > + return -ETIMEDOUT; > + } > + } > + > + return 0; > +} > + > +static const struct phy_ops mmp3_usb_phy_ops = { > + .init = mmp3_usb_phy_init, > + .calibrate = mmp3_usb_phy_calibrate, > + .owner = THIS_MODULE, > +}; > + > +static const struct of_device_id mmp3_usb_phy_of_match[] = { > + { .compatible = "marvell,mmp3-usb-phy", }, > + { }, > +}; > +MODULE_DEVICE_TABLE(of, mmp3_usb_phy_of_match); > + > +static int mmp3_usb_phy_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct resource *resource; > + struct mmp3_usb_phy *mmp3_usb_phy; > + struct phy_provider *provider; > + > + mmp3_usb_phy = devm_kzalloc(dev, sizeof(*mmp3_usb_phy), GFP_KERNEL); > + if (!mmp3_usb_phy) > + return -ENOMEM; > + > + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + mmp3_usb_phy->base = devm_ioremap_resource(dev, resource); > + if (IS_ERR(mmp3_usb_phy->base)) { > + dev_err(dev, "failed to remap PHY regs\n"); > + return PTR_ERR(mmp3_usb_phy->base); > + } > + > + mmp3_usb_phy->phy = devm_phy_create(dev, NULL, &mmp3_usb_phy_ops); > + if (IS_ERR(mmp3_usb_phy->phy)) { > + dev_err(dev, "failed to create PHY\n"); > + return PTR_ERR(mmp3_usb_phy->phy); > + } > + > + phy_set_drvdata(mmp3_usb_phy->phy, mmp3_usb_phy); > + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); > + if (IS_ERR(provider)) { > + dev_err(dev, "failed to register PHY provider\n"); > + return PTR_ERR(provider); > + } > + > + return 0; > +} > + > +static struct platform_driver mmp3_usb_phy_driver = { > + .probe = mmp3_usb_phy_probe, > + .driver = { > + .name = "mmp3-usb-phy", > + .of_match_table = mmp3_usb_phy_of_match, > + }, > +}; > +module_platform_driver(mmp3_usb_phy_driver); > + > +MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); > +MODULE_DESCRIPTION("Marvell MMP3 USB PHY Driver"); > +MODULE_LICENSE("GPL v2"); >
diff --git a/MAINTAINERS b/MAINTAINERS index 176ef19f0b9db..449349f8f20bf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10799,6 +10799,13 @@ F: arch/arm/boot/dts/mmp* F: arch/arm/mach-mmp/ F: linux/soc/mmp/ +MMP USB PHY DRIVERS +R: Lubomir Rintel <lkundrak@v3.sk> +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: drivers/phy/marvell/phy-mmp3-usb.c +F: drivers/phy/marvell/phy-pxa-usb.c + MMU GATHER AND TLB INVALIDATION M: Will Deacon <will@kernel.org> M: "Aneesh Kumar K.V" <aneesh.kumar@linux.ibm.com> diff --git a/drivers/phy/marvell/Kconfig b/drivers/phy/marvell/Kconfig index 0e1642419c0bf..d33ef35b3e51b 100644 --- a/drivers/phy/marvell/Kconfig +++ b/drivers/phy/marvell/Kconfig @@ -102,3 +102,14 @@ config PHY_PXA_USB The PHY driver will be used by Marvell udc/ehci/otg driver. To compile this driver as a module, choose M here. + +config PHY_MMP3_USB + tristate "Marvell MMP3 USB PHY Driver" + depends on MACH_MMP3_DT || COMPILE_TEST + select GENERIC_PHY + help + Enable this to support Marvell MMP3 USB PHY driver for Marvell + SoC. This driver will do the PHY initialization and shutdown. + The PHY driver will be used by Marvell udc/ehci/otg driver. + + To compile this driver as a module, choose M here. diff --git a/drivers/phy/marvell/Makefile b/drivers/phy/marvell/Makefile index 434eb9ca6cc3f..5a106b1549f41 100644 --- a/drivers/phy/marvell/Makefile +++ b/drivers/phy/marvell/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o +obj-$(CONFIG_PHY_MMP3_USB) += phy-mmp3-usb.o obj-$(CONFIG_PHY_MVEBU_A3700_COMPHY) += phy-mvebu-a3700-comphy.o obj-$(CONFIG_PHY_MVEBU_A3700_UTMI) += phy-mvebu-a3700-utmi.o obj-$(CONFIG_PHY_MVEBU_A38X_COMPHY) += phy-armada38x-comphy.o diff --git a/drivers/phy/marvell/phy-mmp3-usb.c b/drivers/phy/marvell/phy-mmp3-usb.c new file mode 100644 index 0000000000000..499869595a582 --- /dev/null +++ b/drivers/phy/marvell/phy-mmp3-usb.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * Copyright (C) 2018,2019 Lubomir Rintel <lkundrak@v3.sk> + */ + +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/soc/mmp/cputype.h> + +#define USB2_PLL_REG0 0x4 +#define USB2_PLL_REG1 0x8 +#define USB2_TX_REG0 0x10 +#define USB2_TX_REG1 0x14 +#define USB2_TX_REG2 0x18 +#define USB2_RX_REG0 0x20 +#define USB2_RX_REG1 0x24 +#define USB2_RX_REG2 0x28 +#define USB2_ANA_REG0 0x30 +#define USB2_ANA_REG1 0x34 +#define USB2_ANA_REG2 0x38 +#define USB2_DIG_REG0 0x3C +#define USB2_DIG_REG1 0x40 +#define USB2_DIG_REG2 0x44 +#define USB2_DIG_REG3 0x48 +#define USB2_TEST_REG0 0x4C +#define USB2_TEST_REG1 0x50 +#define USB2_TEST_REG2 0x54 +#define USB2_CHARGER_REG0 0x58 +#define USB2_OTG_REG0 0x5C +#define USB2_PHY_MON0 0x60 +#define USB2_RESETVE_REG0 0x64 +#define USB2_ICID_REG0 0x78 +#define USB2_ICID_REG1 0x7C + +/* USB2_PLL_REG0 */ + +/* This is for Ax stepping */ +#define USB2_PLL_FBDIV_SHIFT_MMP3 0 +#define USB2_PLL_FBDIV_MASK_MMP3 (0xFF << 0) + +#define USB2_PLL_REFDIV_SHIFT_MMP3 8 +#define USB2_PLL_REFDIV_MASK_MMP3 (0xF << 8) + +#define USB2_PLL_VDD12_SHIFT_MMP3 12 +#define USB2_PLL_VDD18_SHIFT_MMP3 14 + +/* This is for B0 stepping */ +#define USB2_PLL_FBDIV_SHIFT_MMP3_B0 0 +#define USB2_PLL_REFDIV_SHIFT_MMP3_B0 9 +#define USB2_PLL_VDD18_SHIFT_MMP3_B0 14 +#define USB2_PLL_FBDIV_MASK_MMP3_B0 0x01FF +#define USB2_PLL_REFDIV_MASK_MMP3_B0 0x3E00 + +#define USB2_PLL_CAL12_SHIFT_MMP3 0 +#define USB2_PLL_CALI12_MASK_MMP3 (0x3 << 0) + +#define USB2_PLL_VCOCAL_START_SHIFT_MMP3 2 + +#define USB2_PLL_KVCO_SHIFT_MMP3 4 +#define USB2_PLL_KVCO_MASK_MMP3 (0x7<<4) + +#define USB2_PLL_ICP_SHIFT_MMP3 8 +#define USB2_PLL_ICP_MASK_MMP3 (0x7<<8) + +#define USB2_PLL_LOCK_BYPASS_SHIFT_MMP3 12 + +#define USB2_PLL_PU_PLL_SHIFT_MMP3 13 +#define USB2_PLL_PU_PLL_MASK (0x1 << 13) + +#define USB2_PLL_READY_MASK_MMP3 (0x1 << 15) + +/* USB2_TX_REG0 */ +#define USB2_TX_IMPCAL_VTH_SHIFT_MMP3 8 +#define USB2_TX_IMPCAL_VTH_MASK_MMP3 (0x7 << 8) + +#define USB2_TX_RCAL_START_SHIFT_MMP3 13 + +/* USB2_TX_REG1 */ +#define USB2_TX_CK60_PHSEL_SHIFT_MMP3 0 +#define USB2_TX_CK60_PHSEL_MASK_MMP3 (0xf << 0) + +#define USB2_TX_AMP_SHIFT_MMP3 4 +#define USB2_TX_AMP_MASK_MMP3 (0x7 << 4) + +#define USB2_TX_VDD12_SHIFT_MMP3 8 +#define USB2_TX_VDD12_MASK_MMP3 (0x3 << 8) + +/* USB2_TX_REG2 */ +#define USB2_TX_DRV_SLEWRATE_SHIFT 10 + +/* USB2_RX_REG0 */ +#define USB2_RX_SQ_THRESH_SHIFT_MMP3 4 +#define USB2_RX_SQ_THRESH_MASK_MMP3 (0xf << 4) + +#define USB2_RX_SQ_LENGTH_SHIFT_MMP3 10 +#define USB2_RX_SQ_LENGTH_MASK_MMP3 (0x3 << 10) + +/* USB2_ANA_REG1*/ +#define USB2_ANA_PU_ANA_SHIFT_MMP3 14 + +/* USB2_OTG_REG0 */ +#define USB2_OTG_PU_OTG_SHIFT_MMP3 3 + +struct mmp3_usb_phy { + struct phy *phy; + void __iomem *base; +}; + +static unsigned int u2o_get(void __iomem *base, unsigned int offset) +{ + return readl_relaxed(base + offset); +} + +static void u2o_set(void __iomem *base, unsigned int offset, + unsigned int value) +{ + u32 reg; + + reg = readl_relaxed(base + offset); + reg |= value; + writel_relaxed(reg, base + offset); + readl_relaxed(base + offset); +} + +static void u2o_clear(void __iomem *base, unsigned int offset, + unsigned int value) +{ + u32 reg; + + reg = readl_relaxed(base + offset); + reg &= ~value; + writel_relaxed(reg, base + offset); + readl_relaxed(base + offset); +} + +static int mmp3_usb_phy_init(struct phy *phy) +{ + struct mmp3_usb_phy *mmp3_usb_phy = phy_get_drvdata(phy); + void __iomem *base = mmp3_usb_phy->base; + + if (cpu_is_mmp3_a0()) { + u2o_clear(base, USB2_PLL_REG0, (USB2_PLL_FBDIV_MASK_MMP3 + | USB2_PLL_REFDIV_MASK_MMP3)); + u2o_set(base, USB2_PLL_REG0, + 0xd << USB2_PLL_REFDIV_SHIFT_MMP3 + | 0xf0 << USB2_PLL_FBDIV_SHIFT_MMP3); + } else if (cpu_is_mmp3_b0()) { + u2o_clear(base, USB2_PLL_REG0, USB2_PLL_REFDIV_MASK_MMP3_B0 + | USB2_PLL_FBDIV_MASK_MMP3_B0); + u2o_set(base, USB2_PLL_REG0, + 0xd << USB2_PLL_REFDIV_SHIFT_MMP3_B0 + | 0xf0 << USB2_PLL_FBDIV_SHIFT_MMP3_B0); + } else { + dev_err(&phy->dev, "unsupported silicon revision\n"); + return -ENODEV; + } + + u2o_clear(base, USB2_PLL_REG1, USB2_PLL_PU_PLL_MASK + | USB2_PLL_ICP_MASK_MMP3 + | USB2_PLL_KVCO_MASK_MMP3 + | USB2_PLL_CALI12_MASK_MMP3); + u2o_set(base, USB2_PLL_REG1, 1 << USB2_PLL_PU_PLL_SHIFT_MMP3 + | 1 << USB2_PLL_LOCK_BYPASS_SHIFT_MMP3 + | 3 << USB2_PLL_ICP_SHIFT_MMP3 + | 3 << USB2_PLL_KVCO_SHIFT_MMP3 + | 3 << USB2_PLL_CAL12_SHIFT_MMP3); + + u2o_clear(base, USB2_TX_REG0, USB2_TX_IMPCAL_VTH_MASK_MMP3); + u2o_set(base, USB2_TX_REG0, 2 << USB2_TX_IMPCAL_VTH_SHIFT_MMP3); + + u2o_clear(base, USB2_TX_REG1, USB2_TX_VDD12_MASK_MMP3 + | USB2_TX_AMP_MASK_MMP3 + | USB2_TX_CK60_PHSEL_MASK_MMP3); + u2o_set(base, USB2_TX_REG1, 3 << USB2_TX_VDD12_SHIFT_MMP3 + | 4 << USB2_TX_AMP_SHIFT_MMP3 + | 4 << USB2_TX_CK60_PHSEL_SHIFT_MMP3); + + u2o_clear(base, USB2_TX_REG2, 3 << USB2_TX_DRV_SLEWRATE_SHIFT); + u2o_set(base, USB2_TX_REG2, 2 << USB2_TX_DRV_SLEWRATE_SHIFT); + + u2o_clear(base, USB2_RX_REG0, USB2_RX_SQ_THRESH_MASK_MMP3); + u2o_set(base, USB2_RX_REG0, 0xa << USB2_RX_SQ_THRESH_SHIFT_MMP3); + + u2o_set(base, USB2_ANA_REG1, 0x1 << USB2_ANA_PU_ANA_SHIFT_MMP3); + + u2o_set(base, USB2_OTG_REG0, 0x1 << USB2_OTG_PU_OTG_SHIFT_MMP3); + + return 0; +} + +static int mmp3_usb_phy_calibrate(struct phy *phy) +{ + struct mmp3_usb_phy *mmp3_usb_phy = phy_get_drvdata(phy); + void __iomem *base = mmp3_usb_phy->base; + int loops; + + /* + * PLL VCO and TX Impedance Calibration Timing: + * + * _____________________________________ + * PU __________| + * _____________________________ + * VCOCAL START _________| + * ___ + * REG_RCAL_START ________________| |________|_______ + * | 200us | 400us | 40| 400us | USB PHY READY + */ + + udelay(200); + u2o_set(base, USB2_PLL_REG1, 1 << USB2_PLL_VCOCAL_START_SHIFT_MMP3); + udelay(400); + u2o_set(base, USB2_TX_REG0, 1 << USB2_TX_RCAL_START_SHIFT_MMP3); + udelay(40); + u2o_clear(base, USB2_TX_REG0, 1 << USB2_TX_RCAL_START_SHIFT_MMP3); + udelay(400); + + loops = 0; + while ((u2o_get(base, USB2_PLL_REG1) & USB2_PLL_READY_MASK_MMP3) == 0) { + mdelay(1); + loops++; + if (loops > 100) { + dev_err(&phy->dev, "PLL_READY not set after 100mS.\n"); + return -ETIMEDOUT; + } + } + + return 0; +} + +static const struct phy_ops mmp3_usb_phy_ops = { + .init = mmp3_usb_phy_init, + .calibrate = mmp3_usb_phy_calibrate, + .owner = THIS_MODULE, +}; + +static const struct of_device_id mmp3_usb_phy_of_match[] = { + { .compatible = "marvell,mmp3-usb-phy", }, + { }, +}; +MODULE_DEVICE_TABLE(of, mmp3_usb_phy_of_match); + +static int mmp3_usb_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *resource; + struct mmp3_usb_phy *mmp3_usb_phy; + struct phy_provider *provider; + + mmp3_usb_phy = devm_kzalloc(dev, sizeof(*mmp3_usb_phy), GFP_KERNEL); + if (!mmp3_usb_phy) + return -ENOMEM; + + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mmp3_usb_phy->base = devm_ioremap_resource(dev, resource); + if (IS_ERR(mmp3_usb_phy->base)) { + dev_err(dev, "failed to remap PHY regs\n"); + return PTR_ERR(mmp3_usb_phy->base); + } + + mmp3_usb_phy->phy = devm_phy_create(dev, NULL, &mmp3_usb_phy_ops); + if (IS_ERR(mmp3_usb_phy->phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(mmp3_usb_phy->phy); + } + + phy_set_drvdata(mmp3_usb_phy->phy, mmp3_usb_phy); + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(provider)) { + dev_err(dev, "failed to register PHY provider\n"); + return PTR_ERR(provider); + } + + return 0; +} + +static struct platform_driver mmp3_usb_phy_driver = { + .probe = mmp3_usb_phy_probe, + .driver = { + .name = "mmp3-usb-phy", + .of_match_table = mmp3_usb_phy_of_match, + }, +}; +module_platform_driver(mmp3_usb_phy_driver); + +MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); +MODULE_DESCRIPTION("Marvell MMP3 USB PHY Driver"); +MODULE_LICENSE("GPL v2");
This is the USB2 PHY as found on the Marvell MMP3 SoC. Based on Marvell GPL release. While at that, also add a MAINTAINERS entry including the other MMP PHY driver. Signed-off-by: Lubomir Rintel <lkundrak@v3.sk> --- MAINTAINERS | 7 + drivers/phy/marvell/Kconfig | 11 ++ drivers/phy/marvell/Makefile | 1 + drivers/phy/marvell/phy-mmp3-usb.c | 291 +++++++++++++++++++++++++++++ 4 files changed, 310 insertions(+) create mode 100644 drivers/phy/marvell/phy-mmp3-usb.c