123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644 |
- // SPDX-License-Identifier: GPL-2.0+
- /* Copyright (c) 2021-2022 NXP. */
- #include <linux/module.h>
- #include <linux/phy.h>
- #include <linux/phy/phy.h>
- #include <linux/platform_device.h>
- #include <linux/workqueue.h>
- #define LYNX_28G_NUM_LANE 8
- #define LYNX_28G_NUM_PLL 2
- /* General registers per SerDes block */
- #define LYNX_28G_PCC8 0x10a0
- #define LYNX_28G_PCC8_SGMII 0x1
- #define LYNX_28G_PCC8_SGMII_DIS 0x0
- #define LYNX_28G_PCCC 0x10b0
- #define LYNX_28G_PCCC_10GBASER 0x9
- #define LYNX_28G_PCCC_USXGMII 0x1
- #define LYNX_28G_PCCC_SXGMII_DIS 0x0
- #define LYNX_28G_LNa_PCC_OFFSET(lane) (4 * (LYNX_28G_NUM_LANE - (lane->id) - 1))
- /* Per PLL registers */
- #define LYNX_28G_PLLnRSTCTL(pll) (0x400 + (pll) * 0x100 + 0x0)
- #define LYNX_28G_PLLnRSTCTL_DIS(rstctl) (((rstctl) & BIT(24)) >> 24)
- #define LYNX_28G_PLLnRSTCTL_LOCK(rstctl) (((rstctl) & BIT(23)) >> 23)
- #define LYNX_28G_PLLnCR0(pll) (0x400 + (pll) * 0x100 + 0x4)
- #define LYNX_28G_PLLnCR0_REFCLK_SEL(cr0) (((cr0) & GENMASK(20, 16)))
- #define LYNX_28G_PLLnCR0_REFCLK_SEL_100MHZ 0x0
- #define LYNX_28G_PLLnCR0_REFCLK_SEL_125MHZ 0x10000
- #define LYNX_28G_PLLnCR0_REFCLK_SEL_156MHZ 0x20000
- #define LYNX_28G_PLLnCR0_REFCLK_SEL_150MHZ 0x30000
- #define LYNX_28G_PLLnCR0_REFCLK_SEL_161MHZ 0x40000
- #define LYNX_28G_PLLnCR1(pll) (0x400 + (pll) * 0x100 + 0x8)
- #define LYNX_28G_PLLnCR1_FRATE_SEL(cr1) (((cr1) & GENMASK(28, 24)))
- #define LYNX_28G_PLLnCR1_FRATE_5G_10GVCO 0x0
- #define LYNX_28G_PLLnCR1_FRATE_5G_25GVCO 0x10000000
- #define LYNX_28G_PLLnCR1_FRATE_10G_20GVCO 0x6000000
- /* Per SerDes lane registers */
- /* Lane a General Control Register */
- #define LYNX_28G_LNaGCR0(lane) (0x800 + (lane) * 0x100 + 0x0)
- #define LYNX_28G_LNaGCR0_PROTO_SEL_MSK GENMASK(7, 3)
- #define LYNX_28G_LNaGCR0_PROTO_SEL_SGMII 0x8
- #define LYNX_28G_LNaGCR0_PROTO_SEL_XFI 0x50
- #define LYNX_28G_LNaGCR0_IF_WIDTH_MSK GENMASK(2, 0)
- #define LYNX_28G_LNaGCR0_IF_WIDTH_10_BIT 0x0
- #define LYNX_28G_LNaGCR0_IF_WIDTH_20_BIT 0x2
- /* Lane a Tx Reset Control Register */
- #define LYNX_28G_LNaTRSTCTL(lane) (0x800 + (lane) * 0x100 + 0x20)
- #define LYNX_28G_LNaTRSTCTL_HLT_REQ BIT(27)
- #define LYNX_28G_LNaTRSTCTL_RST_DONE BIT(30)
- #define LYNX_28G_LNaTRSTCTL_RST_REQ BIT(31)
- /* Lane a Tx General Control Register */
- #define LYNX_28G_LNaTGCR0(lane) (0x800 + (lane) * 0x100 + 0x24)
- #define LYNX_28G_LNaTGCR0_USE_PLLF 0x0
- #define LYNX_28G_LNaTGCR0_USE_PLLS BIT(28)
- #define LYNX_28G_LNaTGCR0_USE_PLL_MSK BIT(28)
- #define LYNX_28G_LNaTGCR0_N_RATE_FULL 0x0
- #define LYNX_28G_LNaTGCR0_N_RATE_HALF 0x1000000
- #define LYNX_28G_LNaTGCR0_N_RATE_QUARTER 0x2000000
- #define LYNX_28G_LNaTGCR0_N_RATE_MSK GENMASK(26, 24)
- #define LYNX_28G_LNaTECR0(lane) (0x800 + (lane) * 0x100 + 0x30)
- /* Lane a Rx Reset Control Register */
- #define LYNX_28G_LNaRRSTCTL(lane) (0x800 + (lane) * 0x100 + 0x40)
- #define LYNX_28G_LNaRRSTCTL_HLT_REQ BIT(27)
- #define LYNX_28G_LNaRRSTCTL_RST_DONE BIT(30)
- #define LYNX_28G_LNaRRSTCTL_RST_REQ BIT(31)
- #define LYNX_28G_LNaRRSTCTL_CDR_LOCK BIT(12)
- /* Lane a Rx General Control Register */
- #define LYNX_28G_LNaRGCR0(lane) (0x800 + (lane) * 0x100 + 0x44)
- #define LYNX_28G_LNaRGCR0_USE_PLLF 0x0
- #define LYNX_28G_LNaRGCR0_USE_PLLS BIT(28)
- #define LYNX_28G_LNaRGCR0_USE_PLL_MSK BIT(28)
- #define LYNX_28G_LNaRGCR0_N_RATE_MSK GENMASK(26, 24)
- #define LYNX_28G_LNaRGCR0_N_RATE_FULL 0x0
- #define LYNX_28G_LNaRGCR0_N_RATE_HALF 0x1000000
- #define LYNX_28G_LNaRGCR0_N_RATE_QUARTER 0x2000000
- #define LYNX_28G_LNaRGCR0_N_RATE_MSK GENMASK(26, 24)
- #define LYNX_28G_LNaRGCR1(lane) (0x800 + (lane) * 0x100 + 0x48)
- #define LYNX_28G_LNaRECR0(lane) (0x800 + (lane) * 0x100 + 0x50)
- #define LYNX_28G_LNaRECR1(lane) (0x800 + (lane) * 0x100 + 0x54)
- #define LYNX_28G_LNaRECR2(lane) (0x800 + (lane) * 0x100 + 0x58)
- #define LYNX_28G_LNaRSCCR0(lane) (0x800 + (lane) * 0x100 + 0x74)
- #define LYNX_28G_LNaPSS(lane) (0x1000 + (lane) * 0x4)
- #define LYNX_28G_LNaPSS_TYPE(pss) (((pss) & GENMASK(30, 24)) >> 24)
- #define LYNX_28G_LNaPSS_TYPE_SGMII 0x4
- #define LYNX_28G_LNaPSS_TYPE_XFI 0x28
- #define LYNX_28G_SGMIIaCR1(lane) (0x1804 + (lane) * 0x10)
- #define LYNX_28G_SGMIIaCR1_SGPCS_EN BIT(11)
- #define LYNX_28G_SGMIIaCR1_SGPCS_DIS 0x0
- #define LYNX_28G_SGMIIaCR1_SGPCS_MSK BIT(11)
- struct lynx_28g_priv;
- struct lynx_28g_pll {
- struct lynx_28g_priv *priv;
- u32 rstctl, cr0, cr1;
- int id;
- DECLARE_PHY_INTERFACE_MASK(supported);
- };
- struct lynx_28g_lane {
- struct lynx_28g_priv *priv;
- struct phy *phy;
- bool powered_up;
- bool init;
- unsigned int id;
- phy_interface_t interface;
- };
- struct lynx_28g_priv {
- void __iomem *base;
- struct device *dev;
- /* 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_lane_rmw(lane, reg, val, mask) \
- lynx_28g_rmw((lane)->priv, LYNX_28G_##reg(lane->id), \
- LYNX_28G_##reg##_##val, LYNX_28G_##reg##_##mask)
- #define lynx_28g_lane_read(lane, reg) \
- ioread32((lane)->priv->base + LYNX_28G_##reg((lane)->id))
- #define lynx_28g_pll_read(pll, reg) \
- ioread32((pll)->priv->base + LYNX_28G_##reg((pll)->id))
- static bool lynx_28g_supports_interface(struct lynx_28g_priv *priv, int intf)
- {
- int i;
- for (i = 0; i < LYNX_28G_NUM_PLL; i++) {
- if (LYNX_28G_PLLnRSTCTL_DIS(priv->pll[i].rstctl))
- continue;
- if (test_bit(intf, priv->pll[i].supported))
- return true;
- }
- return false;
- }
- static struct lynx_28g_pll *lynx_28g_pll_get(struct lynx_28g_priv *priv,
- phy_interface_t intf)
- {
- struct lynx_28g_pll *pll;
- int i;
- for (i = 0; i < LYNX_28G_NUM_PLL; i++) {
- pll = &priv->pll[i];
- if (LYNX_28G_PLLnRSTCTL_DIS(pll->rstctl))
- continue;
- if (test_bit(intf, pll->supported))
- return pll;
- }
- return NULL;
- }
- static void lynx_28g_lane_set_nrate(struct lynx_28g_lane *lane,
- struct lynx_28g_pll *pll,
- phy_interface_t intf)
- {
- switch (LYNX_28G_PLLnCR1_FRATE_SEL(pll->cr1)) {
- case LYNX_28G_PLLnCR1_FRATE_5G_10GVCO:
- case LYNX_28G_PLLnCR1_FRATE_5G_25GVCO:
- switch (intf) {
- case PHY_INTERFACE_MODE_SGMII:
- case PHY_INTERFACE_MODE_1000BASEX:
- lynx_28g_lane_rmw(lane, LNaTGCR0, N_RATE_QUARTER, N_RATE_MSK);
- lynx_28g_lane_rmw(lane, LNaRGCR0, N_RATE_QUARTER, N_RATE_MSK);
- break;
- default:
- break;
- }
- break;
- case LYNX_28G_PLLnCR1_FRATE_10G_20GVCO:
- switch (intf) {
- case PHY_INTERFACE_MODE_10GBASER:
- case PHY_INTERFACE_MODE_USXGMII:
- lynx_28g_lane_rmw(lane, LNaTGCR0, N_RATE_FULL, N_RATE_MSK);
- lynx_28g_lane_rmw(lane, LNaRGCR0, N_RATE_FULL, N_RATE_MSK);
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
- }
- static void lynx_28g_lane_set_pll(struct lynx_28g_lane *lane,
- struct lynx_28g_pll *pll)
- {
- if (pll->id == 0) {
- lynx_28g_lane_rmw(lane, LNaTGCR0, USE_PLLF, USE_PLL_MSK);
- lynx_28g_lane_rmw(lane, LNaRGCR0, USE_PLLF, USE_PLL_MSK);
- } else {
- lynx_28g_lane_rmw(lane, LNaTGCR0, USE_PLLS, USE_PLL_MSK);
- lynx_28g_lane_rmw(lane, LNaRGCR0, USE_PLLS, USE_PLL_MSK);
- }
- }
- static void lynx_28g_cleanup_lane(struct lynx_28g_lane *lane)
- {
- u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane);
- struct lynx_28g_priv *priv = lane->priv;
- /* Cleanup the protocol configuration registers of the current protocol */
- switch (lane->interface) {
- case PHY_INTERFACE_MODE_10GBASER:
- lynx_28g_rmw(priv, LYNX_28G_PCCC,
- LYNX_28G_PCCC_SXGMII_DIS << lane_offset,
- GENMASK(3, 0) << lane_offset);
- break;
- case PHY_INTERFACE_MODE_SGMII:
- case PHY_INTERFACE_MODE_1000BASEX:
- lynx_28g_rmw(priv, LYNX_28G_PCC8,
- LYNX_28G_PCC8_SGMII_DIS << lane_offset,
- GENMASK(3, 0) << lane_offset);
- break;
- default:
- break;
- }
- }
- static void lynx_28g_lane_set_sgmii(struct lynx_28g_lane *lane)
- {
- u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane);
- struct lynx_28g_priv *priv = lane->priv;
- struct lynx_28g_pll *pll;
- lynx_28g_cleanup_lane(lane);
- /* Setup the lane to run in SGMII */
- lynx_28g_rmw(priv, LYNX_28G_PCC8,
- LYNX_28G_PCC8_SGMII << lane_offset,
- GENMASK(3, 0) << lane_offset);
- /* Setup the protocol select and SerDes parallel interface width */
- lynx_28g_lane_rmw(lane, LNaGCR0, PROTO_SEL_SGMII, PROTO_SEL_MSK);
- lynx_28g_lane_rmw(lane, LNaGCR0, IF_WIDTH_10_BIT, IF_WIDTH_MSK);
- /* Switch to the PLL that works with this interface type */
- pll = lynx_28g_pll_get(priv, PHY_INTERFACE_MODE_SGMII);
- lynx_28g_lane_set_pll(lane, pll);
- /* Choose the portion of clock net to be used on this lane */
- lynx_28g_lane_set_nrate(lane, pll, PHY_INTERFACE_MODE_SGMII);
- /* Enable the SGMII PCS */
- lynx_28g_lane_rmw(lane, SGMIIaCR1, SGPCS_EN, SGPCS_MSK);
- /* Configure the appropriate equalization parameters for the protocol */
- iowrite32(0x00808006, priv->base + LYNX_28G_LNaTECR0(lane->id));
- iowrite32(0x04310000, priv->base + LYNX_28G_LNaRGCR1(lane->id));
- iowrite32(0x9f800000, priv->base + LYNX_28G_LNaRECR0(lane->id));
- iowrite32(0x001f0000, priv->base + LYNX_28G_LNaRECR1(lane->id));
- iowrite32(0x00000000, priv->base + LYNX_28G_LNaRECR2(lane->id));
- iowrite32(0x00000000, priv->base + LYNX_28G_LNaRSCCR0(lane->id));
- }
- static void lynx_28g_lane_set_10gbaser(struct lynx_28g_lane *lane)
- {
- u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane);
- struct lynx_28g_priv *priv = lane->priv;
- struct lynx_28g_pll *pll;
- lynx_28g_cleanup_lane(lane);
- /* Enable the SXGMII lane */
- lynx_28g_rmw(priv, LYNX_28G_PCCC,
- LYNX_28G_PCCC_10GBASER << lane_offset,
- GENMASK(3, 0) << lane_offset);
- /* Setup the protocol select and SerDes parallel interface width */
- lynx_28g_lane_rmw(lane, LNaGCR0, PROTO_SEL_XFI, PROTO_SEL_MSK);
- lynx_28g_lane_rmw(lane, LNaGCR0, IF_WIDTH_20_BIT, IF_WIDTH_MSK);
- /* Switch to the PLL that works with this interface type */
- pll = lynx_28g_pll_get(priv, PHY_INTERFACE_MODE_10GBASER);
- lynx_28g_lane_set_pll(lane, pll);
- /* Choose the portion of clock net to be used on this lane */
- lynx_28g_lane_set_nrate(lane, pll, PHY_INTERFACE_MODE_10GBASER);
- /* Disable the SGMII PCS */
- lynx_28g_lane_rmw(lane, SGMIIaCR1, SGPCS_DIS, SGPCS_MSK);
- /* Configure the appropriate equalization parameters for the protocol */
- iowrite32(0x10808307, priv->base + LYNX_28G_LNaTECR0(lane->id));
- iowrite32(0x10000000, priv->base + LYNX_28G_LNaRGCR1(lane->id));
- iowrite32(0x00000000, priv->base + LYNX_28G_LNaRECR0(lane->id));
- iowrite32(0x001f0000, priv->base + LYNX_28G_LNaRECR1(lane->id));
- iowrite32(0x81000020, priv->base + LYNX_28G_LNaRECR2(lane->id));
- iowrite32(0x00002000, priv->base + LYNX_28G_LNaRSCCR0(lane->id));
- }
- static int lynx_28g_power_off(struct phy *phy)
- {
- struct lynx_28g_lane *lane = phy_get_drvdata(phy);
- u32 trstctl, rrstctl;
- if (!lane->powered_up)
- return 0;
- /* Issue a halt request */
- lynx_28g_lane_rmw(lane, LNaTRSTCTL, HLT_REQ, HLT_REQ);
- lynx_28g_lane_rmw(lane, LNaRRSTCTL, HLT_REQ, HLT_REQ);
- /* Wait until the halting process is complete */
- do {
- trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL);
- rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
- } while ((trstctl & LYNX_28G_LNaTRSTCTL_HLT_REQ) ||
- (rrstctl & LYNX_28G_LNaRRSTCTL_HLT_REQ));
- lane->powered_up = false;
- return 0;
- }
- static int lynx_28g_power_on(struct phy *phy)
- {
- struct lynx_28g_lane *lane = phy_get_drvdata(phy);
- u32 trstctl, rrstctl;
- if (lane->powered_up)
- return 0;
- /* Issue a reset request on the lane */
- lynx_28g_lane_rmw(lane, LNaTRSTCTL, RST_REQ, RST_REQ);
- lynx_28g_lane_rmw(lane, LNaRRSTCTL, RST_REQ, RST_REQ);
- /* Wait until the reset sequence is completed */
- do {
- trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL);
- rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
- } while (!(trstctl & LYNX_28G_LNaTRSTCTL_RST_DONE) ||
- !(rrstctl & LYNX_28G_LNaRRSTCTL_RST_DONE));
- lane->powered_up = true;
- return 0;
- }
- static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode)
- {
- struct lynx_28g_lane *lane = phy_get_drvdata(phy);
- struct lynx_28g_priv *priv = lane->priv;
- int powered_up = lane->powered_up;
- int err = 0;
- if (mode != PHY_MODE_ETHERNET)
- return -EOPNOTSUPP;
- if (lane->interface == PHY_INTERFACE_MODE_NA)
- return -EOPNOTSUPP;
- if (!lynx_28g_supports_interface(priv, submode))
- return -EOPNOTSUPP;
- /* If the lane is powered up, put the lane into the halt state while
- * the reconfiguration is being done.
- */
- if (powered_up)
- lynx_28g_power_off(phy);
- spin_lock(&priv->pcc_lock);
- switch (submode) {
- case PHY_INTERFACE_MODE_SGMII:
- case PHY_INTERFACE_MODE_1000BASEX:
- lynx_28g_lane_set_sgmii(lane);
- break;
- case PHY_INTERFACE_MODE_10GBASER:
- lynx_28g_lane_set_10gbaser(lane);
- break;
- default:
- err = -EOPNOTSUPP;
- goto out;
- }
- lane->interface = submode;
- out:
- spin_unlock(&priv->pcc_lock);
- /* Power up the lane if necessary */
- if (powered_up)
- lynx_28g_power_on(phy);
- return err;
- }
- static int lynx_28g_validate(struct phy *phy, enum phy_mode mode, int submode,
- union phy_configure_opts *opts __always_unused)
- {
- struct lynx_28g_lane *lane = phy_get_drvdata(phy);
- struct lynx_28g_priv *priv = lane->priv;
- if (mode != PHY_MODE_ETHERNET)
- return -EOPNOTSUPP;
- if (!lynx_28g_supports_interface(priv, submode))
- return -EOPNOTSUPP;
- return 0;
- }
- static int lynx_28g_init(struct phy *phy)
- {
- struct lynx_28g_lane *lane = phy_get_drvdata(phy);
- /* Mark the fact that the lane was init */
- lane->init = true;
- /* SerDes lanes are powered on at boot time. Any lane that is managed
- * by this driver will get powered down at init time aka at dpaa2-eth
- * probe time.
- */
- lane->powered_up = true;
- lynx_28g_power_off(phy);
- return 0;
- }
- static const struct phy_ops lynx_28g_ops = {
- .init = lynx_28g_init,
- .power_on = lynx_28g_power_on,
- .power_off = lynx_28g_power_off,
- .set_mode = lynx_28g_set_mode,
- .validate = lynx_28g_validate,
- .owner = THIS_MODULE,
- };
- static void lynx_28g_pll_read_configuration(struct lynx_28g_priv *priv)
- {
- struct lynx_28g_pll *pll;
- int i;
- for (i = 0; i < LYNX_28G_NUM_PLL; i++) {
- pll = &priv->pll[i];
- 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);
- if (LYNX_28G_PLLnRSTCTL_DIS(pll->rstctl))
- continue;
- switch (LYNX_28G_PLLnCR1_FRATE_SEL(pll->cr1)) {
- case LYNX_28G_PLLnCR1_FRATE_5G_10GVCO:
- case LYNX_28G_PLLnCR1_FRATE_5G_25GVCO:
- /* 5GHz clock net */
- __set_bit(PHY_INTERFACE_MODE_1000BASEX, pll->supported);
- __set_bit(PHY_INTERFACE_MODE_SGMII, pll->supported);
- break;
- case LYNX_28G_PLLnCR1_FRATE_10G_20GVCO:
- /* 10.3125GHz clock net */
- __set_bit(PHY_INTERFACE_MODE_10GBASER, pll->supported);
- break;
- default:
- /* 6GHz, 12.890625GHz, 8GHz */
- break;
- }
- }
- }
- #define work_to_lynx(w) container_of((w), struct lynx_28g_priv, cdr_check.work)
- static void lynx_28g_cdr_lock_check(struct work_struct *work)
- {
- struct lynx_28g_priv *priv = work_to_lynx(work);
- struct lynx_28g_lane *lane;
- u32 rrstctl;
- int i;
- for (i = 0; i < LYNX_28G_NUM_LANE; i++) {
- lane = &priv->lane[i];
- mutex_lock(&lane->phy->mutex);
- if (!lane->init || !lane->powered_up) {
- mutex_unlock(&lane->phy->mutex);
- continue;
- }
- rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
- if (!(rrstctl & LYNX_28G_LNaRRSTCTL_CDR_LOCK)) {
- lynx_28g_lane_rmw(lane, LNaRRSTCTL, RST_REQ, RST_REQ);
- do {
- rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
- } while (!(rrstctl & LYNX_28G_LNaRRSTCTL_RST_DONE));
- }
- mutex_unlock(&lane->phy->mutex);
- }
- queue_delayed_work(system_power_efficient_wq, &priv->cdr_check,
- msecs_to_jiffies(1000));
- }
- static void lynx_28g_lane_read_configuration(struct lynx_28g_lane *lane)
- {
- u32 pss, protocol;
- pss = lynx_28g_lane_read(lane, LNaPSS);
- protocol = LYNX_28G_LNaPSS_TYPE(pss);
- switch (protocol) {
- case LYNX_28G_LNaPSS_TYPE_SGMII:
- lane->interface = PHY_INTERFACE_MODE_SGMII;
- break;
- case LYNX_28G_LNaPSS_TYPE_XFI:
- lane->interface = PHY_INTERFACE_MODE_10GBASER;
- break;
- default:
- lane->interface = PHY_INTERFACE_MODE_NA;
- }
- }
- static struct phy *lynx_28g_xlate(struct device *dev,
- struct of_phandle_args *args)
- {
- struct lynx_28g_priv *priv = dev_get_drvdata(dev);
- int idx = args->args[0];
- if (WARN_ON(idx >= LYNX_28G_NUM_LANE))
- return ERR_PTR(-EINVAL);
- return priv->lane[idx].phy;
- }
- static int lynx_28g_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- struct phy_provider *provider;
- struct lynx_28g_priv *priv;
- int i;
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
- priv->dev = &pdev->dev;
- priv->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(priv->base))
- return PTR_ERR(priv->base);
- lynx_28g_pll_read_configuration(priv);
- for (i = 0; i < LYNX_28G_NUM_LANE; i++) {
- struct lynx_28g_lane *lane = &priv->lane[i];
- struct phy *phy;
- memset(lane, 0, sizeof(*lane));
- phy = devm_phy_create(&pdev->dev, NULL, &lynx_28g_ops);
- if (IS_ERR(phy))
- return PTR_ERR(phy);
- lane->priv = priv;
- lane->phy = phy;
- lane->id = i;
- phy_set_drvdata(phy, lane);
- lynx_28g_lane_read_configuration(lane);
- }
- dev_set_drvdata(dev, priv);
- spin_lock_init(&priv->pcc_lock);
- INIT_DELAYED_WORK(&priv->cdr_check, lynx_28g_cdr_lock_check);
- queue_delayed_work(system_power_efficient_wq, &priv->cdr_check,
- msecs_to_jiffies(1000));
- dev_set_drvdata(&pdev->dev, priv);
- provider = devm_of_phy_provider_register(&pdev->dev, lynx_28g_xlate);
- return PTR_ERR_OR_ZERO(provider);
- }
- static void lynx_28g_remove(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- struct lynx_28g_priv *priv = dev_get_drvdata(dev);
- cancel_delayed_work_sync(&priv->cdr_check);
- }
- static const struct of_device_id lynx_28g_of_match_table[] = {
- { .compatible = "fsl,lynx-28g" },
- { },
- };
- MODULE_DEVICE_TABLE(of, lynx_28g_of_match_table);
- static struct platform_driver lynx_28g_driver = {
- .probe = lynx_28g_probe,
- .remove_new = lynx_28g_remove,
- .driver = {
- .name = "lynx-28g",
- .of_match_table = lynx_28g_of_match_table,
- },
- };
- module_platform_driver(lynx_28g_driver);
- MODULE_AUTHOR("Ioana Ciornei <[email protected]>");
- MODULE_DESCRIPTION("Lynx 28G SerDes PHY driver for Layerscape SoCs");
- MODULE_LICENSE("GPL v2");
|