Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/phy/marvell/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 7 kB image not shown  

Quelle  phy-mvebu-a3700-utmi.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2018 Marvell
 *
 * Authors:
 *   Igal Liberman <igall@marvell.com>
 *   Miquèl Raynal <miquel.raynal@bootlin.com>
 *
 * Marvell A3700 UTMI PHY driver
 */


#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>

/* Armada 3700 UTMI PHY registers */
#define USB2_PHY_PLL_CTRL_REG0   0x0
#define   PLL_REF_DIV_OFF   0
#define   PLL_REF_DIV_MASK   GENMASK(6, 0)
#define   PLL_REF_DIV_5    5
#define   PLL_FB_DIV_OFF   16
#define   PLL_FB_DIV_MASK   GENMASK(24, 16)
#define   PLL_FB_DIV_96    96
#define   PLL_SEL_LPFR_OFF   28
#define   PLL_SEL_LPFR_MASK   GENMASK(29, 28)
#define   PLL_READY    BIT(31)
#define USB2_PHY_CAL_CTRL   0x8
#define   PHY_PLLCAL_DONE   BIT(31)
#define   PHY_IMPCAL_DONE   BIT(23)
#define USB2_RX_CHAN_CTRL1   0x18
#define   USB2PHY_SQCAL_DONE   BIT(31)
#define USB2_PHY_OTG_CTRL   0x34
#define   PHY_PU_OTG    BIT(4)
#define USB2_PHY_CHRGR_DETECT   0x38
#define   PHY_CDP_EN    BIT(2)
#define   PHY_DCP_EN    BIT(3)
#define   PHY_PD_EN    BIT(4)
#define   PHY_PU_CHRG_DTC   BIT(5)
#define   PHY_CDP_DM_AUTO   BIT(7)
#define   PHY_ENSWITCH_DP   BIT(12)
#define   PHY_ENSWITCH_DM   BIT(13)

/* Armada 3700 USB miscellaneous registers */
#define USB2_PHY_CTRL(usb32)   (usb32 ? 0x20 : 0x4)
#define   RB_USB2PHY_PU    BIT(0)
#define   USB2_DP_PULLDN_DEV_MODE  BIT(5)
#define   USB2_DM_PULLDN_DEV_MODE  BIT(6)
#define   RB_USB2PHY_SUSPM(usb32)  (usb32 ? BIT(14) : BIT(7))

#define PLL_LOCK_DELAY_US   10000
#define PLL_LOCK_TIMEOUT_US   1000000

/**
 * struct mvebu_a3700_utmi_caps - PHY capabilities
 *
 * @usb32: Flag indicating which PHY is in use (impacts the register map):
 *           - The UTMI PHY wired to the USB3/USB2 controller (otg)
 *           - The UTMI PHY wired to the USB2 controller (host only)
 * @ops: PHY operations
 */

struct mvebu_a3700_utmi_caps {
 int usb32;
 const struct phy_ops *ops;
};

/**
 * struct mvebu_a3700_utmi - PHY driver data
 *
 * @regs: PHY registers
 * @usb_misc: Regmap with USB miscellaneous registers including PHY ones
 * @caps: PHY capabilities
 * @phy: PHY handle
 */

struct mvebu_a3700_utmi {
 void __iomem *regs;
 struct regmap *usb_misc;
 const struct mvebu_a3700_utmi_caps *caps;
 struct phy *phy;
};

static int mvebu_a3700_utmi_phy_power_on(struct phy *phy)
{
 struct mvebu_a3700_utmi *utmi = phy_get_drvdata(phy);
 struct device *dev = &phy->dev;
 int usb32 = utmi->caps->usb32;
 int ret = 0;
 u32 reg;

 /*
 * Setup PLL. 40MHz clock used to be the default, being 25MHz now.
 * See "PLL Settings for Typical REFCLK" table.
 */

 reg = readl(utmi->regs + USB2_PHY_PLL_CTRL_REG0);
 reg &= ~(PLL_REF_DIV_MASK | PLL_FB_DIV_MASK | PLL_SEL_LPFR_MASK);
 reg |= (PLL_REF_DIV_5 << PLL_REF_DIV_OFF) |
        (PLL_FB_DIV_96 << PLL_FB_DIV_OFF);
 writel(reg, utmi->regs + USB2_PHY_PLL_CTRL_REG0);

 /* Enable PHY pull up and disable USB2 suspend */
 regmap_update_bits(utmi->usb_misc, USB2_PHY_CTRL(usb32),
      RB_USB2PHY_SUSPM(usb32) | RB_USB2PHY_PU,
      RB_USB2PHY_SUSPM(usb32) | RB_USB2PHY_PU);

 if (usb32) {
  /* Power up OTG module */
  reg = readl(utmi->regs + USB2_PHY_OTG_CTRL);
  reg |= PHY_PU_OTG;
  writel(reg, utmi->regs + USB2_PHY_OTG_CTRL);

  /* Disable PHY charger detection */
  reg = readl(utmi->regs + USB2_PHY_CHRGR_DETECT);
  reg &= ~(PHY_CDP_EN | PHY_DCP_EN | PHY_PD_EN | PHY_PU_CHRG_DTC |
    PHY_CDP_DM_AUTO | PHY_ENSWITCH_DP | PHY_ENSWITCH_DM);
  writel(reg, utmi->regs + USB2_PHY_CHRGR_DETECT);

  /* Disable PHY DP/DM pull-down (used for device mode) */
  regmap_update_bits(utmi->usb_misc, USB2_PHY_CTRL(usb32),
       USB2_DP_PULLDN_DEV_MODE |
       USB2_DM_PULLDN_DEV_MODE, 0);
 }

 /* Wait for PLL calibration */
 ret = readl_poll_timeout(utmi->regs + USB2_PHY_CAL_CTRL, reg,
     reg & PHY_PLLCAL_DONE,
     PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
 if (ret) {
  dev_err(dev, "Failed to end USB2 PLL calibration\n");
  return ret;
 }

 /* Wait for impedance calibration */
 ret = readl_poll_timeout(utmi->regs + USB2_PHY_CAL_CTRL, reg,
     reg & PHY_IMPCAL_DONE,
     PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
 if (ret) {
  dev_err(dev, "Failed to end USB2 impedance calibration\n");
  return ret;
 }

 /* Wait for squelch calibration */
 ret = readl_poll_timeout(utmi->regs + USB2_RX_CHAN_CTRL1, reg,
     reg & USB2PHY_SQCAL_DONE,
     PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
 if (ret) {
  dev_err(dev, "Failed to end USB2 unknown calibration\n");
  return ret;
 }

 /* Wait for PLL to be locked */
 ret = readl_poll_timeout(utmi->regs + USB2_PHY_PLL_CTRL_REG0, reg,
     reg & PLL_READY,
     PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
 if (ret)
  dev_err(dev, "Failed to lock USB2 PLL\n");

 return ret;
}

static int mvebu_a3700_utmi_phy_power_off(struct phy *phy)
{
 struct mvebu_a3700_utmi *utmi = phy_get_drvdata(phy);
 int usb32 = utmi->caps->usb32;
 u32 reg;

 /* Disable PHY pull-up and enable USB2 suspend */
 reg = readl(utmi->regs + USB2_PHY_CTRL(usb32));
 reg &= ~(RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32));
 writel(reg, utmi->regs + USB2_PHY_CTRL(usb32));

 /* Power down OTG module */
 if (usb32) {
  reg = readl(utmi->regs + USB2_PHY_OTG_CTRL);
  reg &= ~PHY_PU_OTG;
  writel(reg, utmi->regs + USB2_PHY_OTG_CTRL);
 }

 return 0;
}

static const struct phy_ops mvebu_a3700_utmi_phy_ops = {
 .power_on = mvebu_a3700_utmi_phy_power_on,
 .power_off = mvebu_a3700_utmi_phy_power_off,
 .owner = THIS_MODULE,
};

static const struct mvebu_a3700_utmi_caps mvebu_a3700_utmi_otg_phy_caps = {
 .usb32 = true,
 .ops = &mvebu_a3700_utmi_phy_ops,
};

static const struct mvebu_a3700_utmi_caps mvebu_a3700_utmi_host_phy_caps = {
 .usb32 = false,
 .ops = &mvebu_a3700_utmi_phy_ops,
};

static const struct of_device_id mvebu_a3700_utmi_of_match[] = {
 {
  .compatible = "marvell,a3700-utmi-otg-phy",
  .data = &mvebu_a3700_utmi_otg_phy_caps,
 },
 {
  .compatible = "marvell,a3700-utmi-host-phy",
  .data = &mvebu_a3700_utmi_host_phy_caps,
 },
 {},
};
MODULE_DEVICE_TABLE(of, mvebu_a3700_utmi_of_match);

static int mvebu_a3700_utmi_phy_probe(struct platform_device *pdev)
{
 struct device *dev = &pdev->dev;
 struct mvebu_a3700_utmi *utmi;
 struct phy_provider *provider;

 utmi = devm_kzalloc(dev, sizeof(*utmi), GFP_KERNEL);
 if (!utmi)
  return -ENOMEM;

 /* Get UTMI memory region */
 utmi->regs = devm_platform_ioremap_resource(pdev, 0);
 if (IS_ERR(utmi->regs))
  return PTR_ERR(utmi->regs);

 /* Get miscellaneous Host/PHY region */
 utmi->usb_misc = syscon_regmap_lookup_by_phandle(dev->of_node,
        "marvell,usb-misc-reg");
 if (IS_ERR(utmi->usb_misc)) {
  dev_err(dev,
   "Missing USB misc purpose system controller\n");
  return PTR_ERR(utmi->usb_misc);
 }

 /* Retrieve PHY capabilities */
 utmi->caps = of_device_get_match_data(dev);

 /* Instantiate the PHY */
 utmi->phy = devm_phy_create(dev, NULL, utmi->caps->ops);
 if (IS_ERR(utmi->phy)) {
  dev_err(dev, "Failed to create the UTMI PHY\n");
  return PTR_ERR(utmi->phy);
 }

 phy_set_drvdata(utmi->phy, utmi);

 /* Ensure the PHY is powered off */
 utmi->caps->ops->power_off(utmi->phy);

 provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);

 return PTR_ERR_OR_ZERO(provider);
}

static struct platform_driver mvebu_a3700_utmi_driver = {
 .probe = mvebu_a3700_utmi_phy_probe,
 .driver = {
  .name  = "mvebu-a3700-utmi-phy",
  .of_match_table = mvebu_a3700_utmi_of_match,
  },
};
module_platform_driver(mvebu_a3700_utmi_driver);

MODULE_AUTHOR("Igal Liberman ");
MODULE_AUTHOR("Miquel Raynal ");
MODULE_DESCRIPTION("Marvell EBU A3700 UTMI PHY driver");
MODULE_LICENSE("GPL v2");

Messung V0.5
C=93 H=99 G=95

¤ Dauer der Verarbeitung: 0.3 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.