phy: lynx-28g: move data structures to core

The goal is to avoid duplicating the core data structures when
introducing the new lynx-10g driver.

We move the following to phy-fsl-lynx-core:
- struct lynx_28g_pll -> struct lynx_pll. This has some
  hardware-specific register fields which need to become hardware
  agnostic (the PLL register layout is different for Lynx 10G), So:
  - PLLnRSTCTL_DIS(pll->rstctl) becomes !pll->enabled
  - PLLnRSTCTL_LOCK(pll->rstctl) becomes pll->locked
  - FIELD_GET(PLLnCR1_FRATE_SEL, pll->cr1) becomes pll->frate_sel
  - FIELD_GET(PLLnCR0_REFCLK_SEL, pll->cr0) becomes pll->refclk_sel
- struct lynx_28g_lane -> struct lynx_lane
- struct lynx_28g_priv -> struct lynx_priv
  - field lane[LYNX_28G_NUM_LANE] has to be dynamically allocated. Not
    all Lynx 10G SerDes blocks have 8 lanes.
- LYNX_28G_NUM_PLL -> LYNX_NUM_PLL. This is an architectural constant
  which is the same for Lynx 10G as well.

To avoid major noise in the lynx-28g driver, we keep compatibility shims
(for now) where the old lynx_28g names are preserved, but translate to
the common data structures.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Link: https://patch.msgid.link/20260610151952.2141019-5-vladimir.oltean@nxp.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
Vladimir Oltean 2026-06-10 18:19:40 +03:00 committed by Vinod Koul
parent ac0ffe7cf1
commit b7021a4d31
3 changed files with 129 additions and 93 deletions

View File

@ -12,7 +12,7 @@
#include "phy-fsl-lynx-core.h"
#define LYNX_28G_NUM_LANE 8
#define LYNX_28G_NUM_PLL 2
#define LYNX_28G_NUM_PLL LYNX_NUM_PLL
/* SoC IP wrapper for protocol converters */
#define PCC8 0x10a0
@ -43,8 +43,8 @@
/* Per PLL registers */
#define PLLnRSTCTL(pll) (0x400 + (pll) * 0x100 + 0x0)
#define PLLnRSTCTL_DIS(rstctl) (((rstctl) & BIT(24)) >> 24)
#define PLLnRSTCTL_LOCK(rstctl) (((rstctl) & BIT(23)) >> 23)
#define PLLnRSTCTL_DIS BIT(24)
#define PLLnRSTCTL_LOCK BIT(23)
#define PLLnCR0(pll) (0x400 + (pll) * 0x100 + 0x4)
#define PLLnCR0_REFCLK_SEL GENMASK(20, 16)
@ -269,6 +269,17 @@
#define LYNX_28G_LANE_STOP_SLEEP_US 100
#define LYNX_28G_LANE_STOP_TIMEOUT_US 1000000
#define lynx_28g_read lynx_read
#define lynx_28g_write lynx_write
#define lynx_28g_lane_rmw lynx_lane_rmw
#define lynx_28g_lane_read lynx_lane_read
#define lynx_28g_lane_write lynx_lane_write
#define lynx_28g_pll_read lynx_pll_read
#define lynx_28g_priv lynx_priv
#define lynx_28g_lane lynx_lane
#define lynx_28g_pll lynx_pll
enum lynx_28g_eq_type {
EQ_TYPE_NO_EQ = 0,
EQ_TYPE_2TAP = 1,
@ -456,86 +467,6 @@ static const struct lynx_28g_proto_conf lynx_28g_proto_conf[LANE_MODE_MAX] = {
},
};
struct lynx_28g_priv;
struct lynx_28g_pll {
struct lynx_28g_priv *priv;
u32 rstctl, cr0, cr1;
int id;
DECLARE_BITMAP(supported, LANE_MODE_MAX);
};
struct lynx_28g_lane {
struct lynx_28g_priv *priv;
struct phy *phy;
bool powered_up;
bool init;
unsigned int id;
enum lynx_lane_mode mode;
};
struct lynx_28g_priv {
void __iomem *base;
struct device *dev;
const struct lynx_info *info;
/* Serialize concurrent access to registers shared between lanes,
* like PCCn
*/
spinlock_t pcc_lock;
struct lynx_28g_pll pll[LYNX_28G_NUM_PLL];
struct lynx_28g_lane lane[LYNX_28G_NUM_LANE];
struct delayed_work cdr_check;
};
static void lynx_28g_rmw(struct lynx_28g_priv *priv, unsigned long off,
u32 val, u32 mask)
{
void __iomem *reg = priv->base + off;
u32 orig, tmp;
orig = ioread32(reg);
tmp = orig & ~mask;
tmp |= val;
iowrite32(tmp, reg);
}
#define lynx_28g_read(priv, off) \
ioread32((priv)->base + (off))
#define lynx_28g_write(priv, off, val) \
iowrite32(val, (priv)->base + (off))
#define lynx_28g_lane_rmw(lane, reg, val, mask) \
lynx_28g_rmw((lane)->priv, reg(lane->id), val, mask)
#define lynx_28g_lane_read(lane, reg) \
ioread32((lane)->priv->base + reg((lane)->id))
#define lynx_28g_lane_write(lane, reg, val) \
iowrite32(val, (lane)->priv->base + reg((lane)->id))
#define lynx_28g_pll_read(pll, reg) \
ioread32((pll)->priv->base + reg((pll)->id))
/* A lane mode is supported if we have a PLL that can provide its required
* clock net, and if there is a protocol converter for that mode on that lane.
*/
static bool lynx_28g_supports_lane_mode(struct lynx_28g_lane *lane,
enum lynx_lane_mode mode)
{
struct lynx_28g_priv *priv = lane->priv;
int i;
if (!priv->info->lane_supports_mode(lane->id, mode))
return false;
for (i = 0; i < LYNX_28G_NUM_PLL; i++) {
if (PLLnRSTCTL_DIS(priv->pll[i].rstctl))
continue;
if (test_bit(mode, priv->pll[i].supported))
return true;
}
return false;
}
static struct lynx_28g_pll *lynx_28g_pll_get(struct lynx_28g_priv *priv,
enum lynx_lane_mode mode)
{
@ -545,7 +476,7 @@ static struct lynx_28g_pll *lynx_28g_pll_get(struct lynx_28g_priv *priv,
for (i = 0; i < LYNX_28G_NUM_PLL; i++) {
pll = &priv->pll[i];
if (PLLnRSTCTL_DIS(pll->rstctl))
if (!pll->enabled)
continue;
if (test_bit(mode, pll->supported))
@ -553,7 +484,7 @@ static struct lynx_28g_pll *lynx_28g_pll_get(struct lynx_28g_priv *priv,
}
/* no pll supports requested mode, either caller forgot to check
* lynx_28g_supports_lane_mode, or this is a bug.
* lynx_lane_supports_mode(), or this is a bug.
*/
dev_WARN_ONCE(priv->dev, 1, "no pll for lane mode %s\n",
lynx_lane_mode_str(mode));
@ -564,7 +495,7 @@ static void lynx_28g_lane_set_nrate(struct lynx_28g_lane *lane,
struct lynx_28g_pll *pll,
enum lynx_lane_mode lane_mode)
{
switch (FIELD_GET(PLLnCR1_FRATE_SEL, pll->cr1)) {
switch (pll->frate_sel) {
case PLLnCR1_FRATE_5G_10GVCO:
case PLLnCR1_FRATE_5G_25GVCO:
switch (lane_mode) {
@ -879,27 +810,33 @@ static bool lynx_28g_compat_lane_supports_mode(int lane,
static const struct lynx_info lynx_info_compat = {
.lane_supports_mode = lynx_28g_compat_lane_supports_mode,
.num_lanes = LYNX_28G_NUM_LANE,
};
static const struct lynx_info lynx_info_lx2160a_serdes1 = {
.lane_supports_mode = lx2160a_serdes1_lane_supports_mode,
.num_lanes = LYNX_28G_NUM_LANE,
};
static const struct lynx_info lynx_info_lx2160a_serdes2 = {
.lane_supports_mode = lx2160a_serdes2_lane_supports_mode,
.num_lanes = LYNX_28G_NUM_LANE,
};
static const struct lynx_info lynx_info_lx2160a_serdes3 = {
.lane_supports_mode = lx2160a_serdes3_lane_supports_mode,
.num_lanes = LYNX_28G_NUM_LANE,
};
static const struct lynx_info lynx_info_lx2162a_serdes1 = {
.lane_supports_mode = lx2162a_serdes1_lane_supports_mode,
.first_lane = 4,
.num_lanes = LYNX_28G_NUM_LANE,
};
static const struct lynx_info lynx_info_lx2162a_serdes2 = {
.lane_supports_mode = lx2162a_serdes2_lane_supports_mode,
.num_lanes = LYNX_28G_NUM_LANE,
};
static int lynx_pccr_read(struct lynx_28g_lane *lane, enum lynx_lane_mode mode,
@ -1168,7 +1105,7 @@ static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode)
return -EOPNOTSUPP;
lane_mode = phy_interface_to_lane_mode(submode);
if (!lynx_28g_supports_lane_mode(lane, lane_mode))
if (!lynx_lane_supports_mode(lane, lane_mode))
return -EOPNOTSUPP;
if (lane_mode == lane->mode)
@ -1210,7 +1147,7 @@ static int lynx_28g_validate(struct phy *phy, enum phy_mode mode, int submode,
return -EOPNOTSUPP;
lane_mode = phy_interface_to_lane_mode(submode);
if (!lynx_28g_supports_lane_mode(lane, lane_mode))
if (!lynx_lane_supports_mode(lane, lane_mode))
return -EOPNOTSUPP;
return 0;
@ -1262,6 +1199,7 @@ static const struct phy_ops lynx_28g_ops = {
static void lynx_28g_pll_read_configuration(struct lynx_28g_priv *priv)
{
struct lynx_28g_pll *pll;
u32 val;
int i;
for (i = 0; i < LYNX_28G_NUM_PLL; i++) {
@ -1269,14 +1207,20 @@ static void lynx_28g_pll_read_configuration(struct lynx_28g_priv *priv)
pll->priv = priv;
pll->id = i;
pll->rstctl = lynx_28g_pll_read(pll, PLLnRSTCTL);
pll->cr0 = lynx_28g_pll_read(pll, PLLnCR0);
pll->cr1 = lynx_28g_pll_read(pll, PLLnCR1);
val = lynx_28g_pll_read(pll, PLLnRSTCTL);
pll->enabled = !(val & PLLnRSTCTL_DIS);
pll->locked = !!(val & PLLnRSTCTL_LOCK);
if (PLLnRSTCTL_DIS(pll->rstctl))
val = lynx_28g_pll_read(pll, PLLnCR0);
pll->refclk_sel = FIELD_GET(PLLnCR0_REFCLK_SEL, val);
val = lynx_28g_pll_read(pll, PLLnCR1);
pll->frate_sel = FIELD_GET(PLLnCR1_FRATE_SEL, val);
if (!pll->enabled)
continue;
switch (FIELD_GET(PLLnCR1_FRATE_SEL, pll->cr1)) {
switch (pll->frate_sel) {
case PLLnCR1_FRATE_5G_10GVCO:
case PLLnCR1_FRATE_5G_25GVCO:
/* 5GHz clock net */
@ -1440,6 +1384,11 @@ static int lynx_28g_probe(struct platform_device *pdev)
if (priv->info == &lynx_info_compat)
dev_warn(dev, "Please update device tree to use per-device compatible strings\n");
priv->lane = devm_kcalloc(dev, priv->info->num_lanes,
sizeof(*priv->lane), GFP_KERNEL);
if (!priv->lane)
return -ENOMEM;
priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);

View File

@ -40,5 +40,28 @@ enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf)
}
EXPORT_SYMBOL_NS_GPL(phy_interface_to_lane_mode, "PHY_FSL_LYNX");
/* A lane mode is supported if we have a PLL that can provide its required
* clock net, and if there is a protocol converter for that mode on that lane.
*/
bool lynx_lane_supports_mode(struct lynx_lane *lane, enum lynx_lane_mode mode)
{
struct lynx_priv *priv = lane->priv;
int i;
if (!priv->info->lane_supports_mode(lane->id, mode))
return false;
for (i = 0; i < LYNX_NUM_PLL; i++) {
if (!priv->pll[i].enabled)
continue;
if (test_bit(mode, priv->pll[i].supported))
return true;
}
return false;
}
EXPORT_SYMBOL_NS_GPL(lynx_lane_supports_mode, "PHY_FSL_LYNX");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Freescale Lynx SerDes core functionality");

View File

@ -7,18 +7,82 @@
#include <linux/phy.h>
#include <soc/fsl/phy-fsl-lynx.h>
#define LYNX_NUM_PLL 2
struct lynx_pccr {
int offset;
int width;
int shift;
};
struct lynx_priv;
struct lynx_pll {
struct lynx_priv *priv;
int id;
int refclk_sel;
int frate_sel;
bool enabled;
bool locked;
DECLARE_BITMAP(supported, LANE_MODE_MAX);
};
struct lynx_lane {
struct lynx_priv *priv;
struct phy *phy;
bool powered_up;
bool init;
unsigned int id;
enum lynx_lane_mode mode;
};
struct lynx_info {
bool (*lane_supports_mode)(int lane, enum lynx_lane_mode mode);
int first_lane;
int num_lanes;
};
struct lynx_priv {
void __iomem *base;
struct device *dev;
const struct lynx_info *info;
/* Serialize concurrent access to registers shared between lanes,
* like PCCn
*/
spinlock_t pcc_lock;
struct lynx_pll pll[LYNX_NUM_PLL];
struct lynx_lane *lane;
struct delayed_work cdr_check;
};
static inline void lynx_rmw(struct lynx_priv *priv, unsigned long off, u32 val,
u32 mask)
{
void __iomem *reg = priv->base + off;
u32 orig, tmp;
orig = ioread32(reg);
tmp = orig & ~mask;
tmp |= val;
iowrite32(tmp, reg);
}
#define lynx_read(priv, off) \
ioread32((priv)->base + (off))
#define lynx_write(priv, off, val) \
iowrite32(val, (priv)->base + (off))
#define lynx_lane_rmw(lane, reg, val, mask) \
lynx_rmw((lane)->priv, reg(lane->id), val, mask)
#define lynx_lane_read(lane, reg) \
ioread32((lane)->priv->base + reg((lane)->id))
#define lynx_lane_write(lane, reg, val) \
iowrite32(val, (lane)->priv->base + reg((lane)->id))
#define lynx_pll_read(pll, reg) \
ioread32((pll)->priv->base + reg((pll)->id))
const char *lynx_lane_mode_str(enum lynx_lane_mode lane_mode);
enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf);
bool lynx_lane_supports_mode(struct lynx_lane *lane, enum lynx_lane_mode mode);
#endif