1ac0ffe7cSVladimir Oltean // SPDX-License-Identifier: GPL-2.0+ 2ac0ffe7cSVladimir Oltean /* Copyright 2025-2026 NXP */ 3ac0ffe7cSVladimir Oltean 4ac0ffe7cSVladimir Oltean #include <linux/module.h> 5719a2da3SVladimir Oltean #include <linux/platform_device.h> 6ac0ffe7cSVladimir Oltean 7ac0ffe7cSVladimir Oltean #include "phy-fsl-lynx-core.h" 8ac0ffe7cSVladimir Oltean 9ac0ffe7cSVladimir Oltean const char *lynx_lane_mode_str(enum lynx_lane_mode lane_mode) 10ac0ffe7cSVladimir Oltean { 11ac0ffe7cSVladimir Oltean switch (lane_mode) { 12ac0ffe7cSVladimir Oltean case LANE_MODE_1000BASEX_SGMII: 13ac0ffe7cSVladimir Oltean return "1000Base-X/SGMII"; 14*6dc92ba4SVladimir Oltean case LANE_MODE_2500BASEX: 15*6dc92ba4SVladimir Oltean return "2500Base-X"; 16*6dc92ba4SVladimir Oltean case LANE_MODE_QSGMII: 17*6dc92ba4SVladimir Oltean return "QSGMII"; 18*6dc92ba4SVladimir Oltean case LANE_MODE_10G_QXGMII: 19*6dc92ba4SVladimir Oltean return "10G-QXGMII"; 20ac0ffe7cSVladimir Oltean case LANE_MODE_10GBASER: 21ac0ffe7cSVladimir Oltean return "10GBase-R"; 22ac0ffe7cSVladimir Oltean case LANE_MODE_USXGMII: 23ac0ffe7cSVladimir Oltean return "USXGMII"; 24ac0ffe7cSVladimir Oltean case LANE_MODE_25GBASER: 25ac0ffe7cSVladimir Oltean return "25GBase-R"; 26ac0ffe7cSVladimir Oltean default: 27ac0ffe7cSVladimir Oltean return "unknown"; 28ac0ffe7cSVladimir Oltean } 29ac0ffe7cSVladimir Oltean } 30ac0ffe7cSVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_lane_mode_str, "PHY_FSL_LYNX"); 31ac0ffe7cSVladimir Oltean 32ac0ffe7cSVladimir Oltean enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf) 33ac0ffe7cSVladimir Oltean { 34ac0ffe7cSVladimir Oltean switch (intf) { 35ac0ffe7cSVladimir Oltean case PHY_INTERFACE_MODE_SGMII: 36ac0ffe7cSVladimir Oltean case PHY_INTERFACE_MODE_1000BASEX: 37ac0ffe7cSVladimir Oltean return LANE_MODE_1000BASEX_SGMII; 38*6dc92ba4SVladimir Oltean case PHY_INTERFACE_MODE_2500BASEX: 39*6dc92ba4SVladimir Oltean return LANE_MODE_2500BASEX; 40*6dc92ba4SVladimir Oltean case PHY_INTERFACE_MODE_QSGMII: 41*6dc92ba4SVladimir Oltean return LANE_MODE_QSGMII; 42*6dc92ba4SVladimir Oltean case PHY_INTERFACE_MODE_10G_QXGMII: 43*6dc92ba4SVladimir Oltean return LANE_MODE_10G_QXGMII; 44ac0ffe7cSVladimir Oltean case PHY_INTERFACE_MODE_10GBASER: 45ac0ffe7cSVladimir Oltean return LANE_MODE_10GBASER; 46ac0ffe7cSVladimir Oltean case PHY_INTERFACE_MODE_USXGMII: 47ac0ffe7cSVladimir Oltean return LANE_MODE_USXGMII; 48ac0ffe7cSVladimir Oltean case PHY_INTERFACE_MODE_25GBASER: 49ac0ffe7cSVladimir Oltean return LANE_MODE_25GBASER; 50ac0ffe7cSVladimir Oltean default: 51ac0ffe7cSVladimir Oltean return LANE_MODE_UNKNOWN; 52ac0ffe7cSVladimir Oltean } 53ac0ffe7cSVladimir Oltean } 54ac0ffe7cSVladimir Oltean EXPORT_SYMBOL_NS_GPL(phy_interface_to_lane_mode, "PHY_FSL_LYNX"); 55ac0ffe7cSVladimir Oltean 5609697afaSVladimir Oltean /* By default, assume that if we know how to get the PCCR register and 5709697afaSVladimir Oltean * protocol converter for a lane, that protocol is supported. 5809697afaSVladimir Oltean */ 5909697afaSVladimir Oltean static bool lynx_lane_supports_mode_default(struct lynx_lane *lane, 6009697afaSVladimir Oltean enum lynx_lane_mode mode) 6109697afaSVladimir Oltean { 6209697afaSVladimir Oltean struct lynx_priv *priv = lane->priv; 6309697afaSVladimir Oltean struct lynx_pccr pccr; 6409697afaSVladimir Oltean 6509697afaSVladimir Oltean if (!priv->info->get_pccr || !priv->info->get_pcvt_offset) 6609697afaSVladimir Oltean return false; 6709697afaSVladimir Oltean 6809697afaSVladimir Oltean if (priv->info->get_pccr(mode, lane->id, &pccr) < 0) 6909697afaSVladimir Oltean return false; 7009697afaSVladimir Oltean 7109697afaSVladimir Oltean if (priv->info->get_pcvt_offset(lane->id, mode) < 0) 7209697afaSVladimir Oltean return false; 7309697afaSVladimir Oltean 7409697afaSVladimir Oltean return true; 7509697afaSVladimir Oltean } 7609697afaSVladimir Oltean 77b7021a4dSVladimir Oltean /* A lane mode is supported if we have a PLL that can provide its required 78b7021a4dSVladimir Oltean * clock net, and if there is a protocol converter for that mode on that lane. 79b7021a4dSVladimir Oltean */ 80b7021a4dSVladimir Oltean bool lynx_lane_supports_mode(struct lynx_lane *lane, enum lynx_lane_mode mode) 81b7021a4dSVladimir Oltean { 82b7021a4dSVladimir Oltean struct lynx_priv *priv = lane->priv; 83b7021a4dSVladimir Oltean int i; 84b7021a4dSVladimir Oltean 8509697afaSVladimir Oltean if (priv->info->lane_supports_mode) { 86b7021a4dSVladimir Oltean if (!priv->info->lane_supports_mode(lane->id, mode)) 87b7021a4dSVladimir Oltean return false; 8809697afaSVladimir Oltean } else if (!lynx_lane_supports_mode_default(lane, mode)) { 8909697afaSVladimir Oltean return false; 9009697afaSVladimir Oltean } 91b7021a4dSVladimir Oltean 92b7021a4dSVladimir Oltean for (i = 0; i < LYNX_NUM_PLL; i++) { 93b7021a4dSVladimir Oltean if (!priv->pll[i].enabled) 94b7021a4dSVladimir Oltean continue; 95b7021a4dSVladimir Oltean 96b7021a4dSVladimir Oltean if (test_bit(mode, priv->pll[i].supported)) 97b7021a4dSVladimir Oltean return true; 98b7021a4dSVladimir Oltean } 99b7021a4dSVladimir Oltean 100b7021a4dSVladimir Oltean return false; 101b7021a4dSVladimir Oltean } 102b7021a4dSVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_lane_supports_mode, "PHY_FSL_LYNX"); 103b7021a4dSVladimir Oltean 104*6dc92ba4SVladimir Oltean /* The quad protocols are fixed because the lane has multiple consumers, and 105*6dc92ba4SVladimir Oltean * one phy_set_mode_ext() affects the other consumers as well. We have no use 106*6dc92ba4SVladimir Oltean * case for dynamic protocol changing here, so disallow it. 107*6dc92ba4SVladimir Oltean */ 108*6dc92ba4SVladimir Oltean static enum lynx_lane_mode lynx_fixed_protocols[] = { 109*6dc92ba4SVladimir Oltean LANE_MODE_QSGMII, 110*6dc92ba4SVladimir Oltean LANE_MODE_10G_QXGMII, 111*6dc92ba4SVladimir Oltean }; 112*6dc92ba4SVladimir Oltean 113*6dc92ba4SVladimir Oltean static bool lynx_lane_restrict_fixed_mode_change(struct lynx_lane *lane, 114*6dc92ba4SVladimir Oltean enum lynx_lane_mode new) 115*6dc92ba4SVladimir Oltean { 116*6dc92ba4SVladimir Oltean enum lynx_lane_mode curr = lane->mode; 117*6dc92ba4SVladimir Oltean 118*6dc92ba4SVladimir Oltean for (int i = 0; i < ARRAY_SIZE(lynx_fixed_protocols); i++) 119*6dc92ba4SVladimir Oltean if ((curr == lynx_fixed_protocols[i] || 120*6dc92ba4SVladimir Oltean new == lynx_fixed_protocols[i]) && 121*6dc92ba4SVladimir Oltean curr != new) 122*6dc92ba4SVladimir Oltean return true; 123*6dc92ba4SVladimir Oltean 124*6dc92ba4SVladimir Oltean return false; 125*6dc92ba4SVladimir Oltean } 126*6dc92ba4SVladimir Oltean 127f124b54bSVladimir Oltean /* Translate the mode/submode from phy_validate() and phy_set_mode_ext() to a 128f124b54bSVladimir Oltean * lane_mode and return 0 if it is supported and we can transition to it from 129f124b54bSVladimir Oltean * the current lane mode, or return negative error otherwise. 130f124b54bSVladimir Oltean */ 131f124b54bSVladimir Oltean int lynx_phy_mode_to_lane_mode(struct phy *phy, enum phy_mode mode, 132f124b54bSVladimir Oltean int submode, enum lynx_lane_mode *lane_mode) 133f124b54bSVladimir Oltean { 134f124b54bSVladimir Oltean struct lynx_lane *lane = phy_get_drvdata(phy); 135f124b54bSVladimir Oltean enum lynx_lane_mode tmp_lane_mode; 136f124b54bSVladimir Oltean 137f124b54bSVladimir Oltean /* The protocol configuration tables are incomplete for full lane 138f124b54bSVladimir Oltean * reconfiguration from an arbitrary protocol. 139f124b54bSVladimir Oltean */ 140f124b54bSVladimir Oltean if (lane->mode == LANE_MODE_UNKNOWN) 141f124b54bSVladimir Oltean return -EINVAL; 142f124b54bSVladimir Oltean 143f124b54bSVladimir Oltean if (mode != PHY_MODE_ETHERNET) 144f124b54bSVladimir Oltean return -EINVAL; 145f124b54bSVladimir Oltean 146f124b54bSVladimir Oltean tmp_lane_mode = phy_interface_to_lane_mode(submode); 147f124b54bSVladimir Oltean if (!lynx_lane_supports_mode(lane, tmp_lane_mode)) 148f124b54bSVladimir Oltean return -EINVAL; 149f124b54bSVladimir Oltean 150*6dc92ba4SVladimir Oltean if (lynx_lane_restrict_fixed_mode_change(lane, tmp_lane_mode)) 151*6dc92ba4SVladimir Oltean return -EINVAL; 152*6dc92ba4SVladimir Oltean 153f124b54bSVladimir Oltean if (lane_mode) 154f124b54bSVladimir Oltean *lane_mode = tmp_lane_mode; 155f124b54bSVladimir Oltean 156f124b54bSVladimir Oltean return 0; 157f124b54bSVladimir Oltean } 158f124b54bSVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_phy_mode_to_lane_mode, "PHY_FSL_LYNX"); 159f124b54bSVladimir Oltean 160d4550ea4SVladimir Oltean struct lynx_pll *lynx_pll_get(struct lynx_priv *priv, enum lynx_lane_mode mode) 161d4550ea4SVladimir Oltean { 162d4550ea4SVladimir Oltean struct lynx_pll *pll; 163d4550ea4SVladimir Oltean int i; 164d4550ea4SVladimir Oltean 165d4550ea4SVladimir Oltean for (i = 0; i < LYNX_NUM_PLL; i++) { 166d4550ea4SVladimir Oltean pll = &priv->pll[i]; 167d4550ea4SVladimir Oltean 168d4550ea4SVladimir Oltean if (!pll->enabled) 169d4550ea4SVladimir Oltean continue; 170d4550ea4SVladimir Oltean 171d4550ea4SVladimir Oltean if (test_bit(mode, pll->supported)) 172d4550ea4SVladimir Oltean return pll; 173d4550ea4SVladimir Oltean } 174d4550ea4SVladimir Oltean 175d4550ea4SVladimir Oltean /* no pll supports requested mode, either caller forgot to check 176d4550ea4SVladimir Oltean * lynx_lane_supports_mode(), or this is a bug. 177d4550ea4SVladimir Oltean */ 178d4550ea4SVladimir Oltean dev_WARN_ONCE(priv->dev, 1, "no pll for lane mode %s\n", 179d4550ea4SVladimir Oltean lynx_lane_mode_str(mode)); 180d4550ea4SVladimir Oltean return NULL; 181d4550ea4SVladimir Oltean } 182d4550ea4SVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_pll_get, "PHY_FSL_LYNX"); 183d4550ea4SVladimir Oltean 18451c25f4bSVladimir Oltean int lynx_pccr_read(struct lynx_lane *lane, enum lynx_lane_mode mode, u32 *val) 18551c25f4bSVladimir Oltean { 18651c25f4bSVladimir Oltean struct lynx_priv *priv = lane->priv; 18751c25f4bSVladimir Oltean struct lynx_pccr pccr; 18851c25f4bSVladimir Oltean u32 tmp; 18951c25f4bSVladimir Oltean int err; 19051c25f4bSVladimir Oltean 19151c25f4bSVladimir Oltean err = priv->info->get_pccr(mode, lane->id, &pccr); 19251c25f4bSVladimir Oltean if (err) 19351c25f4bSVladimir Oltean return err; 19451c25f4bSVladimir Oltean 19551c25f4bSVladimir Oltean tmp = lynx_read(priv, pccr.offset); 19651c25f4bSVladimir Oltean *val = (tmp >> pccr.shift) & GENMASK(pccr.width - 1, 0); 19751c25f4bSVladimir Oltean 19851c25f4bSVladimir Oltean return 0; 19951c25f4bSVladimir Oltean } 20051c25f4bSVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_pccr_read, "PHY_FSL_LYNX"); 20151c25f4bSVladimir Oltean 20251c25f4bSVladimir Oltean int lynx_pccr_write(struct lynx_lane *lane, enum lynx_lane_mode mode, u32 val) 20351c25f4bSVladimir Oltean { 20451c25f4bSVladimir Oltean struct lynx_priv *priv = lane->priv; 20551c25f4bSVladimir Oltean struct lynx_pccr pccr; 20651c25f4bSVladimir Oltean u32 old, tmp, mask; 20751c25f4bSVladimir Oltean int err; 20851c25f4bSVladimir Oltean 20951c25f4bSVladimir Oltean err = priv->info->get_pccr(mode, lane->id, &pccr); 21051c25f4bSVladimir Oltean if (err) 21151c25f4bSVladimir Oltean return err; 21251c25f4bSVladimir Oltean 21351c25f4bSVladimir Oltean old = lynx_read(priv, pccr.offset); 21451c25f4bSVladimir Oltean mask = GENMASK(pccr.width - 1, 0) << pccr.shift; 21551c25f4bSVladimir Oltean tmp = (old & ~mask) | (val << pccr.shift); 21651c25f4bSVladimir Oltean lynx_write(priv, pccr.offset, tmp); 21751c25f4bSVladimir Oltean 21851c25f4bSVladimir Oltean dev_dbg(&lane->phy->dev, "PCCR@0x%x: 0x%x -> 0x%x\n", 21951c25f4bSVladimir Oltean pccr.offset, old, tmp); 22051c25f4bSVladimir Oltean 22151c25f4bSVladimir Oltean return 0; 22251c25f4bSVladimir Oltean } 22351c25f4bSVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_pccr_write, "PHY_FSL_LYNX"); 22451c25f4bSVladimir Oltean 22551c25f4bSVladimir Oltean int lynx_pcvt_read(struct lynx_lane *lane, enum lynx_lane_mode mode, int cr, 22651c25f4bSVladimir Oltean u32 *val) 22751c25f4bSVladimir Oltean { 22851c25f4bSVladimir Oltean struct lynx_priv *priv = lane->priv; 22951c25f4bSVladimir Oltean int offset; 23051c25f4bSVladimir Oltean 23151c25f4bSVladimir Oltean offset = priv->info->get_pcvt_offset(lane->id, mode); 23251c25f4bSVladimir Oltean if (offset < 0) 23351c25f4bSVladimir Oltean return offset; 23451c25f4bSVladimir Oltean 23551c25f4bSVladimir Oltean *val = lynx_read(priv, offset + cr); 23651c25f4bSVladimir Oltean 23751c25f4bSVladimir Oltean return 0; 23851c25f4bSVladimir Oltean } 23951c25f4bSVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_pcvt_read, "PHY_FSL_LYNX"); 24051c25f4bSVladimir Oltean 24151c25f4bSVladimir Oltean int lynx_pcvt_write(struct lynx_lane *lane, enum lynx_lane_mode mode, int cr, 24251c25f4bSVladimir Oltean u32 val) 24351c25f4bSVladimir Oltean { 24451c25f4bSVladimir Oltean struct lynx_priv *priv = lane->priv; 24551c25f4bSVladimir Oltean int offset; 24651c25f4bSVladimir Oltean 24751c25f4bSVladimir Oltean offset = priv->info->get_pcvt_offset(lane->id, mode); 24851c25f4bSVladimir Oltean if (offset < 0) 24951c25f4bSVladimir Oltean return offset; 25051c25f4bSVladimir Oltean 25151c25f4bSVladimir Oltean lynx_write(priv, offset + cr, val); 25251c25f4bSVladimir Oltean 25351c25f4bSVladimir Oltean return 0; 25451c25f4bSVladimir Oltean } 25551c25f4bSVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_pcvt_write, "PHY_FSL_LYNX"); 25651c25f4bSVladimir Oltean 25751c25f4bSVladimir Oltean int lynx_pcvt_rmw(struct lynx_lane *lane, enum lynx_lane_mode mode, int cr, 25851c25f4bSVladimir Oltean u32 val, u32 mask) 25951c25f4bSVladimir Oltean { 26051c25f4bSVladimir Oltean int err; 26151c25f4bSVladimir Oltean u32 tmp; 26251c25f4bSVladimir Oltean 26351c25f4bSVladimir Oltean err = lynx_pcvt_read(lane, mode, cr, &tmp); 26451c25f4bSVladimir Oltean if (err) 26551c25f4bSVladimir Oltean return err; 26651c25f4bSVladimir Oltean 26751c25f4bSVladimir Oltean tmp &= ~mask; 26851c25f4bSVladimir Oltean tmp |= val; 26951c25f4bSVladimir Oltean 27051c25f4bSVladimir Oltean return lynx_pcvt_write(lane, mode, cr, tmp); 27151c25f4bSVladimir Oltean } 27251c25f4bSVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_pcvt_rmw, "PHY_FSL_LYNX"); 27351c25f4bSVladimir Oltean 274719a2da3SVladimir Oltean #define work_to_lynx(w) container_of((w), struct lynx_priv, cdr_check.work) 275719a2da3SVladimir Oltean 276719a2da3SVladimir Oltean static void lynx_cdr_lock_check(struct work_struct *work) 277719a2da3SVladimir Oltean { 278719a2da3SVladimir Oltean struct lynx_priv *priv = work_to_lynx(work); 279719a2da3SVladimir Oltean struct lynx_lane *lane; 280719a2da3SVladimir Oltean 281719a2da3SVladimir Oltean for (int i = priv->info->first_lane; i < priv->info->num_lanes; i++) { 282719a2da3SVladimir Oltean lane = &priv->lane[i]; 283719a2da3SVladimir Oltean if (!lane->phy) 284719a2da3SVladimir Oltean continue; 285719a2da3SVladimir Oltean 286719a2da3SVladimir Oltean mutex_lock(&lane->phy->mutex); 287719a2da3SVladimir Oltean 288719a2da3SVladimir Oltean if (!lane->init || !lane->powered_up) { 289719a2da3SVladimir Oltean mutex_unlock(&lane->phy->mutex); 290719a2da3SVladimir Oltean continue; 291719a2da3SVladimir Oltean } 292719a2da3SVladimir Oltean 293719a2da3SVladimir Oltean priv->info->cdr_lock_check(lane); 294719a2da3SVladimir Oltean 295719a2da3SVladimir Oltean mutex_unlock(&lane->phy->mutex); 296719a2da3SVladimir Oltean } 297719a2da3SVladimir Oltean 298719a2da3SVladimir Oltean queue_delayed_work(system_power_efficient_wq, &priv->cdr_check, 299719a2da3SVladimir Oltean msecs_to_jiffies(1000)); 300719a2da3SVladimir Oltean } 301719a2da3SVladimir Oltean 302719a2da3SVladimir Oltean static struct phy *lynx_xlate(struct device *dev, 303719a2da3SVladimir Oltean const struct of_phandle_args *args) 304719a2da3SVladimir Oltean { 305719a2da3SVladimir Oltean struct lynx_priv *priv = dev_get_drvdata(dev); 306719a2da3SVladimir Oltean int idx; 307719a2da3SVladimir Oltean 308719a2da3SVladimir Oltean if (args->args_count == 0) 309719a2da3SVladimir Oltean return of_phy_simple_xlate(dev, args); 310719a2da3SVladimir Oltean else if (args->args_count != 1) 311719a2da3SVladimir Oltean return ERR_PTR(-ENODEV); 312719a2da3SVladimir Oltean 313719a2da3SVladimir Oltean idx = args->args[0]; 314719a2da3SVladimir Oltean 315719a2da3SVladimir Oltean if (WARN_ON(idx >= priv->info->num_lanes || 316719a2da3SVladimir Oltean idx < priv->info->first_lane)) 317719a2da3SVladimir Oltean return ERR_PTR(-EINVAL); 318719a2da3SVladimir Oltean 319719a2da3SVladimir Oltean return priv->lane[idx].phy ?: ERR_PTR(-ENODEV); 320719a2da3SVladimir Oltean } 321719a2da3SVladimir Oltean 322719a2da3SVladimir Oltean static int lynx_probe_lane(struct lynx_priv *priv, int id, 323719a2da3SVladimir Oltean struct device_node *dn, 324719a2da3SVladimir Oltean const struct phy_ops *phy_ops) 325719a2da3SVladimir Oltean { 326719a2da3SVladimir Oltean struct lynx_lane *lane = &priv->lane[id]; 327719a2da3SVladimir Oltean struct phy *phy; 328719a2da3SVladimir Oltean 329719a2da3SVladimir Oltean phy = devm_phy_create(priv->dev, dn, phy_ops); 330719a2da3SVladimir Oltean if (IS_ERR(phy)) 331719a2da3SVladimir Oltean return PTR_ERR(phy); 332719a2da3SVladimir Oltean 333719a2da3SVladimir Oltean lane->priv = priv; 334719a2da3SVladimir Oltean lane->phy = phy; 335719a2da3SVladimir Oltean lane->id = id; 336719a2da3SVladimir Oltean phy_set_drvdata(phy, lane); 337719a2da3SVladimir Oltean priv->info->lane_read_configuration(lane); 338719a2da3SVladimir Oltean 339719a2da3SVladimir Oltean return 0; 340719a2da3SVladimir Oltean } 341719a2da3SVladimir Oltean 342719a2da3SVladimir Oltean int lynx_probe(struct platform_device *pdev, const struct lynx_info *info, 343719a2da3SVladimir Oltean const struct phy_ops *phy_ops) 344719a2da3SVladimir Oltean { 345719a2da3SVladimir Oltean struct device *dev = &pdev->dev; 346719a2da3SVladimir Oltean struct phy_provider *provider; 347719a2da3SVladimir Oltean struct device_node *dn; 348719a2da3SVladimir Oltean struct lynx_priv *priv; 349719a2da3SVladimir Oltean int err; 350719a2da3SVladimir Oltean 351719a2da3SVladimir Oltean dn = dev_of_node(dev); 352719a2da3SVladimir Oltean if (!dn) { 353719a2da3SVladimir Oltean dev_err(dev, "Device requires an OF node\n"); 354719a2da3SVladimir Oltean return -EINVAL; 355719a2da3SVladimir Oltean } 356719a2da3SVladimir Oltean 357719a2da3SVladimir Oltean if (!info) 358719a2da3SVladimir Oltean return -ENODEV; 359719a2da3SVladimir Oltean 360719a2da3SVladimir Oltean priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 361719a2da3SVladimir Oltean if (!priv) 362719a2da3SVladimir Oltean return -ENOMEM; 363719a2da3SVladimir Oltean 364719a2da3SVladimir Oltean priv->dev = dev; 365719a2da3SVladimir Oltean priv->info = info; 366c6c1d7dfSVladimir Oltean priv->big_endian = device_property_read_bool(dev, "big-endian"); 367719a2da3SVladimir Oltean dev_set_drvdata(dev, priv); 368719a2da3SVladimir Oltean spin_lock_init(&priv->pcc_lock); 369719a2da3SVladimir Oltean INIT_DELAYED_WORK(&priv->cdr_check, lynx_cdr_lock_check); 370719a2da3SVladimir Oltean 371719a2da3SVladimir Oltean priv->lane = devm_kcalloc(dev, priv->info->num_lanes, 372719a2da3SVladimir Oltean sizeof(*priv->lane), GFP_KERNEL); 373719a2da3SVladimir Oltean if (!priv->lane) 374719a2da3SVladimir Oltean return -ENOMEM; 375719a2da3SVladimir Oltean 376719a2da3SVladimir Oltean priv->base = devm_platform_ioremap_resource(pdev, 0); 377719a2da3SVladimir Oltean if (IS_ERR(priv->base)) 378719a2da3SVladimir Oltean return PTR_ERR(priv->base); 379719a2da3SVladimir Oltean 380719a2da3SVladimir Oltean for (int i = 0; i < LYNX_NUM_PLL; i++) { 381719a2da3SVladimir Oltean struct lynx_pll *pll = &priv->pll[i]; 382719a2da3SVladimir Oltean 383719a2da3SVladimir Oltean pll->priv = priv; 384719a2da3SVladimir Oltean pll->id = i; 385719a2da3SVladimir Oltean priv->info->pll_read_configuration(pll); 386719a2da3SVladimir Oltean } 387719a2da3SVladimir Oltean 388719a2da3SVladimir Oltean if (of_get_child_count(dn)) { 389719a2da3SVladimir Oltean struct device_node *child; 390719a2da3SVladimir Oltean 391719a2da3SVladimir Oltean for_each_available_child_of_node(dn, child) { 392719a2da3SVladimir Oltean u32 reg; 393719a2da3SVladimir Oltean 394719a2da3SVladimir Oltean /* PHY subnode name must be 'phy'. */ 395719a2da3SVladimir Oltean if (!(of_node_name_eq(child, "phy"))) 396719a2da3SVladimir Oltean continue; 397719a2da3SVladimir Oltean 398719a2da3SVladimir Oltean if (of_property_read_u32(child, "reg", ®)) { 399719a2da3SVladimir Oltean dev_err(dev, "No \"reg\" property for %pOF\n", child); 400719a2da3SVladimir Oltean of_node_put(child); 401719a2da3SVladimir Oltean return -EINVAL; 402719a2da3SVladimir Oltean } 403719a2da3SVladimir Oltean 404719a2da3SVladimir Oltean if (reg < priv->info->first_lane || reg >= priv->info->num_lanes) { 405719a2da3SVladimir Oltean dev_err(dev, "\"reg\" property out of range for %pOF\n", child); 406719a2da3SVladimir Oltean of_node_put(child); 407719a2da3SVladimir Oltean return -EINVAL; 408719a2da3SVladimir Oltean } 409719a2da3SVladimir Oltean 410719a2da3SVladimir Oltean err = lynx_probe_lane(priv, reg, child, phy_ops); 411719a2da3SVladimir Oltean if (err) { 412719a2da3SVladimir Oltean of_node_put(child); 413719a2da3SVladimir Oltean return err; 414719a2da3SVladimir Oltean } 415719a2da3SVladimir Oltean } 416719a2da3SVladimir Oltean } else { 417719a2da3SVladimir Oltean for (int i = priv->info->first_lane; i < priv->info->num_lanes; i++) { 418719a2da3SVladimir Oltean err = lynx_probe_lane(priv, i, NULL, phy_ops); 419719a2da3SVladimir Oltean if (err) 420719a2da3SVladimir Oltean return err; 421719a2da3SVladimir Oltean } 422719a2da3SVladimir Oltean } 423719a2da3SVladimir Oltean 424719a2da3SVladimir Oltean provider = devm_of_phy_provider_register(dev, lynx_xlate); 425719a2da3SVladimir Oltean if (IS_ERR(provider)) 426719a2da3SVladimir Oltean return PTR_ERR(provider); 427719a2da3SVladimir Oltean 428719a2da3SVladimir Oltean queue_delayed_work(system_power_efficient_wq, &priv->cdr_check, 429719a2da3SVladimir Oltean msecs_to_jiffies(1000)); 430719a2da3SVladimir Oltean 431719a2da3SVladimir Oltean return 0; 432719a2da3SVladimir Oltean } 433719a2da3SVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_probe, "PHY_FSL_LYNX"); 434719a2da3SVladimir Oltean 435719a2da3SVladimir Oltean void lynx_remove(struct platform_device *pdev) 436719a2da3SVladimir Oltean { 437719a2da3SVladimir Oltean struct device *dev = &pdev->dev; 438719a2da3SVladimir Oltean struct lynx_priv *priv = dev_get_drvdata(dev); 439719a2da3SVladimir Oltean 440719a2da3SVladimir Oltean cancel_delayed_work_sync(&priv->cdr_check); 441719a2da3SVladimir Oltean } 442719a2da3SVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_remove, "PHY_FSL_LYNX"); 443719a2da3SVladimir Oltean 444ac0ffe7cSVladimir Oltean MODULE_LICENSE("GPL"); 445ac0ffe7cSVladimir Oltean MODULE_DESCRIPTION("Freescale Lynx SerDes core functionality"); 446