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

Quelle  phy-uniphier-pcie.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * phy-uniphier-pcie.c - PHY driver for UniPhier PCIe controller
 * Copyright 2018, Socionext Inc.
 * Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
 */


#include <linux/bitops.h>
#include <linux/bitfield.h>
#include <linux/clk.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>
#include <linux/reset.h>
#include <linux/resource.h>

/* PHY */
#define PCL_PHY_CLKCTRL  0x0000
#define PORT_SEL_MASK  GENMASK(11, 9)
#define PORT_SEL_1  FIELD_PREP(PORT_SEL_MASK, 1)

#define PCL_PHY_TEST_I  0x2000
#define TESTI_DAT_MASK  GENMASK(13, 6)
#define TESTI_ADR_MASK  GENMASK(5, 1)
#define TESTI_WR_EN  BIT(0)
#define TESTIO_PHY_SHIFT 16

#define PCL_PHY_TEST_O  0x2004
#define TESTO_DAT_MASK  GENMASK(7, 0)

#define PCL_PHY_RESET  0x200c
#define PCL_PHY_RESET_N_MNMODE BIT(8) /* =1:manual */
#define PCL_PHY_RESET_N  BIT(0) /* =1:deasssert */

/* SG */
#define SG_USBPCIESEL  0x590
#define SG_USBPCIESEL_PCIE BIT(0)

/* SC */
#define SC_US3SRCSEL  0x2244
#define SC_US3SRCSEL_2LANE GENMASK(9, 8)

#define PCL_PHY_R00  0
#define   RX_EQ_ADJ_EN  BIT(3)  /* enable for EQ adjustment */
#define PCL_PHY_R06  6
#define   RX_EQ_ADJ  GENMASK(5, 0) /* EQ adjustment value */
#define   RX_EQ_ADJ_VAL  0
#define PCL_PHY_R26  26
#define   VCO_CTRL  GENMASK(7, 4) /* Tx VCO adjustment value */
#define   VCO_CTRL_INIT_VAL 5
#define PCL_PHY_R28  28
#define   VCOPLL_CLMP  GENMASK(3, 2) /* Tx VCOPLL clamp mode */
#define   VCOPLL_CLMP_VAL 0

struct uniphier_pciephy_priv {
 void __iomem *base;
 struct device *dev;
 struct clk *clk, *clk_gio;
 struct reset_control *rst, *rst_gio;
 const struct uniphier_pciephy_soc_data *data;
};

struct uniphier_pciephy_soc_data {
 bool is_legacy;
 bool is_dual_phy;
 void (*set_phymode)(struct regmap *regmap);
};

static void uniphier_pciephy_testio_write(struct uniphier_pciephy_priv *priv,
       int id, u32 data)
{
 if (id)
  data <<= TESTIO_PHY_SHIFT;

 /* need to read TESTO twice after accessing TESTI */
 writel(data, priv->base + PCL_PHY_TEST_I);
 readl(priv->base + PCL_PHY_TEST_O);
 readl(priv->base + PCL_PHY_TEST_O);
}

static u32 uniphier_pciephy_testio_read(struct uniphier_pciephy_priv *priv, int id)
{
 u32 val = readl(priv->base + PCL_PHY_TEST_O);

 if (id)
  val >>= TESTIO_PHY_SHIFT;

 return val & TESTO_DAT_MASK;
}

static void uniphier_pciephy_set_param(struct uniphier_pciephy_priv *priv,
           int id, u32 reg, u32 mask, u32 param)
{
 u32 val;

 /* read previous data */
 val  = FIELD_PREP(TESTI_DAT_MASK, 1);
 val |= FIELD_PREP(TESTI_ADR_MASK, reg);
 uniphier_pciephy_testio_write(priv, id, val);
 val = uniphier_pciephy_testio_read(priv, id);

 /* update value */
 val &= ~mask;
 val |= mask & param;
 val = FIELD_PREP(TESTI_DAT_MASK, val);
 val |= FIELD_PREP(TESTI_ADR_MASK, reg);
 uniphier_pciephy_testio_write(priv, id, val);
 uniphier_pciephy_testio_write(priv, id, val | TESTI_WR_EN);
 uniphier_pciephy_testio_write(priv, id, val);

 /* read current data as dummy */
 val  = FIELD_PREP(TESTI_DAT_MASK, 1);
 val |= FIELD_PREP(TESTI_ADR_MASK, reg);
 uniphier_pciephy_testio_write(priv, id, val);
 uniphier_pciephy_testio_read(priv, id);
}

static void uniphier_pciephy_assert(struct uniphier_pciephy_priv *priv)
{
 u32 val;

 val = readl(priv->base + PCL_PHY_RESET);
 val &= ~PCL_PHY_RESET_N;
 val |= PCL_PHY_RESET_N_MNMODE;
 writel(val, priv->base + PCL_PHY_RESET);
}

static void uniphier_pciephy_deassert(struct uniphier_pciephy_priv *priv)
{
 u32 val;

 val = readl(priv->base + PCL_PHY_RESET);
 val |= PCL_PHY_RESET_N_MNMODE | PCL_PHY_RESET_N;
 writel(val, priv->base + PCL_PHY_RESET);
}

static int uniphier_pciephy_init(struct phy *phy)
{
 struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy);
 u32 val;
 int ret, id;

 ret = clk_prepare_enable(priv->clk);
 if (ret)
  return ret;

 ret = clk_prepare_enable(priv->clk_gio);
 if (ret)
  goto out_clk_disable;

 ret = reset_control_deassert(priv->rst);
 if (ret)
  goto out_clk_gio_disable;

 ret = reset_control_deassert(priv->rst_gio);
 if (ret)
  goto out_rst_assert;

 /* support only 1 port */
 val = readl(priv->base + PCL_PHY_CLKCTRL);
 val &= ~PORT_SEL_MASK;
 val |= PORT_SEL_1;
 writel(val, priv->base + PCL_PHY_CLKCTRL);

 /* legacy controller doesn't have phy_reset and parameters */
 if (priv->data->is_legacy)
  return 0;

 for (id = 0; id < (priv->data->is_dual_phy ? 2 : 1); id++) {
  uniphier_pciephy_set_param(priv, id, PCL_PHY_R00,
       RX_EQ_ADJ_EN, RX_EQ_ADJ_EN);
  uniphier_pciephy_set_param(priv, id, PCL_PHY_R06, RX_EQ_ADJ,
       FIELD_PREP(RX_EQ_ADJ, RX_EQ_ADJ_VAL));
  uniphier_pciephy_set_param(priv, id, PCL_PHY_R26, VCO_CTRL,
       FIELD_PREP(VCO_CTRL, VCO_CTRL_INIT_VAL));
  uniphier_pciephy_set_param(priv, id, PCL_PHY_R28, VCOPLL_CLMP,
       FIELD_PREP(VCOPLL_CLMP, VCOPLL_CLMP_VAL));
 }
 usleep_range(1, 10);

 uniphier_pciephy_deassert(priv);
 usleep_range(1, 10);

 return 0;

out_rst_assert:
 reset_control_assert(priv->rst);
out_clk_gio_disable:
 clk_disable_unprepare(priv->clk_gio);
out_clk_disable:
 clk_disable_unprepare(priv->clk);

 return ret;
}

static int uniphier_pciephy_exit(struct phy *phy)
{
 struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy);

 if (!priv->data->is_legacy)
  uniphier_pciephy_assert(priv);
 reset_control_assert(priv->rst_gio);
 reset_control_assert(priv->rst);
 clk_disable_unprepare(priv->clk_gio);
 clk_disable_unprepare(priv->clk);

 return 0;
}

static const struct phy_ops uniphier_pciephy_ops = {
 .init  = uniphier_pciephy_init,
 .exit  = uniphier_pciephy_exit,
 .owner = THIS_MODULE,
};

static int uniphier_pciephy_probe(struct platform_device *pdev)
{
 struct uniphier_pciephy_priv *priv;
 struct phy_provider *phy_provider;
 struct device *dev = &pdev->dev;
 struct regmap *regmap;
 struct phy *phy;

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

 priv->data = of_device_get_match_data(dev);
 if (WARN_ON(!priv->data))
  return -EINVAL;

 priv->dev = dev;

 priv->base = devm_platform_ioremap_resource(pdev, 0);
 if (IS_ERR(priv->base))
  return PTR_ERR(priv->base);

 if (priv->data->is_legacy) {
  priv->clk_gio = devm_clk_get(dev, "gio");
  if (IS_ERR(priv->clk_gio))
   return PTR_ERR(priv->clk_gio);

  priv->rst_gio =
   devm_reset_control_get_shared(dev, "gio");
  if (IS_ERR(priv->rst_gio))
   return PTR_ERR(priv->rst_gio);

  priv->clk = devm_clk_get(dev, "link");
  if (IS_ERR(priv->clk))
   return PTR_ERR(priv->clk);

  priv->rst = devm_reset_control_get_shared(dev, "link");
  if (IS_ERR(priv->rst))
   return PTR_ERR(priv->rst);
 } else {
  priv->clk = devm_clk_get(dev, NULL);
  if (IS_ERR(priv->clk))
   return PTR_ERR(priv->clk);

  priv->rst = devm_reset_control_get_shared(dev, NULL);
  if (IS_ERR(priv->rst))
   return PTR_ERR(priv->rst);
 }

 phy = devm_phy_create(dev, dev->of_node, &uniphier_pciephy_ops);
 if (IS_ERR(phy))
  return PTR_ERR(phy);

 regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
       "socionext,syscon");
 if (!IS_ERR(regmap) && priv->data->set_phymode)
  priv->data->set_phymode(regmap);

 phy_set_drvdata(phy, priv);
 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);

 return PTR_ERR_OR_ZERO(phy_provider);
}

static void uniphier_pciephy_ld20_setmode(struct regmap *regmap)
{
 regmap_update_bits(regmap, SG_USBPCIESEL,
      SG_USBPCIESEL_PCIE, SG_USBPCIESEL_PCIE);
}

static void uniphier_pciephy_nx1_setmode(struct regmap *regmap)
{
 regmap_update_bits(regmap, SC_US3SRCSEL,
      SC_US3SRCSEL_2LANE, SC_US3SRCSEL_2LANE);
}

static const struct uniphier_pciephy_soc_data uniphier_pro5_data = {
 .is_legacy = true,
};

static const struct uniphier_pciephy_soc_data uniphier_ld20_data = {
 .is_legacy = false,
 .is_dual_phy = false,
 .set_phymode = uniphier_pciephy_ld20_setmode,
};

static const struct uniphier_pciephy_soc_data uniphier_pxs3_data = {
 .is_legacy = false,
 .is_dual_phy = false,
};

static const struct uniphier_pciephy_soc_data uniphier_nx1_data = {
 .is_legacy = false,
 .is_dual_phy = true,
 .set_phymode = uniphier_pciephy_nx1_setmode,
};

static const struct of_device_id uniphier_pciephy_match[] = {
 {
  .compatible = "socionext,uniphier-pro5-pcie-phy",
  .data = &uniphier_pro5_data,
 },
 {
  .compatible = "socionext,uniphier-ld20-pcie-phy",
  .data = &uniphier_ld20_data,
 },
 {
  .compatible = "socionext,uniphier-pxs3-pcie-phy",
  .data = &uniphier_pxs3_data,
 },
 {
  .compatible = "socionext,uniphier-nx1-pcie-phy",
  .data = &uniphier_nx1_data,
 },
 { /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, uniphier_pciephy_match);

static struct platform_driver uniphier_pciephy_driver = {
 .probe = uniphier_pciephy_probe,
 .driver = {
  .name = "uniphier-pcie-phy",
  .of_match_table = uniphier_pciephy_match,
 },
};
module_platform_driver(uniphier_pciephy_driver);

MODULE_AUTHOR("Kunihiko Hayashi ");
MODULE_DESCRIPTION("UniPhier PHY driver for PCIe controller");
MODULE_LICENSE("GPL v2");

Messung V0.5
C=96 H=92 G=93

¤ Dauer der Verarbeitung: 0.4 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.