551 lines · c
1// SPDX-License-Identifier: (GPL-2.0 OR MIT)2/*3 * SerDes PHY driver for Microsemi Ocelot4 *5 * Copyright (c) 2018 Microsemi6 *7 */8 9#include <linux/err.h>10#include <linux/mfd/syscon.h>11#include <linux/module.h>12#include <linux/of.h>13#include <linux/of_platform.h>14#include <linux/phy.h>15#include <linux/phy/phy.h>16#include <linux/platform_device.h>17#include <linux/regmap.h>18#include <soc/mscc/ocelot_hsio.h>19#include <dt-bindings/phy/phy-ocelot-serdes.h>20 21struct serdes_ctrl {22 struct regmap *regs;23 struct device *dev;24 struct phy *phys[SERDES_MAX];25};26 27struct serdes_macro {28 u8 idx;29 /* Not used when in QSGMII or PCIe mode */30 int port;31 struct serdes_ctrl *ctrl;32};33 34#define MCB_S6G_CFG_TIMEOUT 5035 36static int __serdes_write_mcb_s6g(struct regmap *regmap, u8 macro, u32 op)37{38 unsigned int regval = 0;39 40 regmap_write(regmap, HSIO_MCB_S6G_ADDR_CFG, op |41 HSIO_MCB_S6G_ADDR_CFG_SERDES6G_ADDR(BIT(macro)));42 43 return regmap_read_poll_timeout(regmap, HSIO_MCB_S6G_ADDR_CFG, regval,44 (regval & op) != op, 100,45 MCB_S6G_CFG_TIMEOUT * 1000);46}47 48static int serdes_commit_mcb_s6g(struct regmap *regmap, u8 macro)49{50 return __serdes_write_mcb_s6g(regmap, macro,51 HSIO_MCB_S6G_ADDR_CFG_SERDES6G_WR_ONE_SHOT);52}53 54static int serdes_update_mcb_s6g(struct regmap *regmap, u8 macro)55{56 return __serdes_write_mcb_s6g(regmap, macro,57 HSIO_MCB_S6G_ADDR_CFG_SERDES6G_RD_ONE_SHOT);58}59 60static int serdes_init_s6g(struct regmap *regmap, u8 serdes, int mode)61{62 u32 pll_fsm_ctrl_data;63 u32 ob_ena1v_mode;64 u32 des_bw_ana;65 u32 ob_ena_cas;66 u32 if_mode;67 u32 ob_lev;68 u32 qrate;69 int ret;70 71 if (mode == PHY_INTERFACE_MODE_QSGMII) {72 pll_fsm_ctrl_data = 120;73 ob_ena1v_mode = 0;74 ob_ena_cas = 0;75 des_bw_ana = 5;76 ob_lev = 24;77 if_mode = 3;78 qrate = 0;79 } else {80 pll_fsm_ctrl_data = 60;81 ob_ena1v_mode = 1;82 ob_ena_cas = 2;83 des_bw_ana = 3;84 ob_lev = 48;85 if_mode = 1;86 qrate = 1;87 }88 89 ret = serdes_update_mcb_s6g(regmap, serdes);90 if (ret)91 return ret;92 93 /* Test pattern */94 95 regmap_update_bits(regmap, HSIO_S6G_COMMON_CFG,96 HSIO_S6G_COMMON_CFG_SYS_RST, 0);97 98 regmap_update_bits(regmap, HSIO_S6G_PLL_CFG,99 HSIO_S6G_PLL_CFG_PLL_FSM_ENA, 0);100 101 regmap_update_bits(regmap, HSIO_S6G_IB_CFG,102 HSIO_S6G_IB_CFG_IB_SIG_DET_ENA |103 HSIO_S6G_IB_CFG_IB_REG_ENA |104 HSIO_S6G_IB_CFG_IB_SAM_ENA |105 HSIO_S6G_IB_CFG_IB_EQZ_ENA |106 HSIO_S6G_IB_CFG_IB_CONCUR |107 HSIO_S6G_IB_CFG_IB_CAL_ENA,108 HSIO_S6G_IB_CFG_IB_SIG_DET_ENA |109 HSIO_S6G_IB_CFG_IB_REG_ENA |110 HSIO_S6G_IB_CFG_IB_SAM_ENA |111 HSIO_S6G_IB_CFG_IB_EQZ_ENA |112 HSIO_S6G_IB_CFG_IB_CONCUR);113 114 regmap_update_bits(regmap, HSIO_S6G_IB_CFG1,115 HSIO_S6G_IB_CFG1_IB_FRC_OFFSET |116 HSIO_S6G_IB_CFG1_IB_FRC_LP |117 HSIO_S6G_IB_CFG1_IB_FRC_MID |118 HSIO_S6G_IB_CFG1_IB_FRC_HP |119 HSIO_S6G_IB_CFG1_IB_FILT_OFFSET |120 HSIO_S6G_IB_CFG1_IB_FILT_LP |121 HSIO_S6G_IB_CFG1_IB_FILT_MID |122 HSIO_S6G_IB_CFG1_IB_FILT_HP,123 HSIO_S6G_IB_CFG1_IB_FILT_OFFSET |124 HSIO_S6G_IB_CFG1_IB_FILT_HP |125 HSIO_S6G_IB_CFG1_IB_FILT_LP |126 HSIO_S6G_IB_CFG1_IB_FILT_MID);127 128 regmap_update_bits(regmap, HSIO_S6G_IB_CFG2,129 HSIO_S6G_IB_CFG2_IB_UREG_M,130 HSIO_S6G_IB_CFG2_IB_UREG(4));131 132 regmap_update_bits(regmap, HSIO_S6G_IB_CFG3,133 HSIO_S6G_IB_CFG3_IB_INI_OFFSET_M |134 HSIO_S6G_IB_CFG3_IB_INI_LP_M |135 HSIO_S6G_IB_CFG3_IB_INI_MID_M |136 HSIO_S6G_IB_CFG3_IB_INI_HP_M,137 HSIO_S6G_IB_CFG3_IB_INI_OFFSET(31) |138 HSIO_S6G_IB_CFG3_IB_INI_LP(1) |139 HSIO_S6G_IB_CFG3_IB_INI_MID(31) |140 HSIO_S6G_IB_CFG3_IB_INI_HP(0));141 142 regmap_update_bits(regmap, HSIO_S6G_MISC_CFG,143 HSIO_S6G_MISC_CFG_LANE_RST,144 HSIO_S6G_MISC_CFG_LANE_RST);145 146 ret = serdes_commit_mcb_s6g(regmap, serdes);147 if (ret)148 return ret;149 150 /* OB + DES + IB + SER CFG */151 regmap_update_bits(regmap, HSIO_S6G_OB_CFG,152 HSIO_S6G_OB_CFG_OB_IDLE |153 HSIO_S6G_OB_CFG_OB_ENA1V_MODE |154 HSIO_S6G_OB_CFG_OB_POST0_M |155 HSIO_S6G_OB_CFG_OB_PREC_M,156 (ob_ena1v_mode ? HSIO_S6G_OB_CFG_OB_ENA1V_MODE : 0) |157 HSIO_S6G_OB_CFG_OB_POST0(0) |158 HSIO_S6G_OB_CFG_OB_PREC(0));159 160 regmap_update_bits(regmap, HSIO_S6G_OB_CFG1,161 HSIO_S6G_OB_CFG1_OB_ENA_CAS_M |162 HSIO_S6G_OB_CFG1_OB_LEV_M,163 HSIO_S6G_OB_CFG1_OB_LEV(ob_lev) |164 HSIO_S6G_OB_CFG1_OB_ENA_CAS(ob_ena_cas));165 166 regmap_update_bits(regmap, HSIO_S6G_DES_CFG,167 HSIO_S6G_DES_CFG_DES_PHS_CTRL_M |168 HSIO_S6G_DES_CFG_DES_CPMD_SEL_M |169 HSIO_S6G_DES_CFG_DES_BW_ANA_M,170 HSIO_S6G_DES_CFG_DES_PHS_CTRL(2) |171 HSIO_S6G_DES_CFG_DES_CPMD_SEL(0) |172 HSIO_S6G_DES_CFG_DES_BW_ANA(des_bw_ana));173 174 regmap_update_bits(regmap, HSIO_S6G_IB_CFG,175 HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL_M |176 HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET_M,177 HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET(0) |178 HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL(0));179 180 regmap_update_bits(regmap, HSIO_S6G_IB_CFG1,181 HSIO_S6G_IB_CFG1_IB_TSDET_M,182 HSIO_S6G_IB_CFG1_IB_TSDET(16));183 184 regmap_update_bits(regmap, HSIO_S6G_SER_CFG,185 HSIO_S6G_SER_CFG_SER_ALISEL_M |186 HSIO_S6G_SER_CFG_SER_ENALI,187 HSIO_S6G_SER_CFG_SER_ALISEL(0));188 189 regmap_update_bits(regmap, HSIO_S6G_PLL_CFG,190 HSIO_S6G_PLL_CFG_PLL_DIV4 |191 HSIO_S6G_PLL_CFG_PLL_ENA_ROT |192 HSIO_S6G_PLL_CFG_PLL_FSM_CTRL_DATA_M |193 HSIO_S6G_PLL_CFG_PLL_ROT_DIR |194 HSIO_S6G_PLL_CFG_PLL_ROT_FRQ,195 HSIO_S6G_PLL_CFG_PLL_FSM_CTRL_DATA196 (pll_fsm_ctrl_data));197 198 regmap_update_bits(regmap, HSIO_S6G_COMMON_CFG,199 HSIO_S6G_COMMON_CFG_SYS_RST |200 HSIO_S6G_COMMON_CFG_ENA_LANE |201 HSIO_S6G_COMMON_CFG_PWD_RX |202 HSIO_S6G_COMMON_CFG_PWD_TX |203 HSIO_S6G_COMMON_CFG_HRATE |204 HSIO_S6G_COMMON_CFG_QRATE |205 HSIO_S6G_COMMON_CFG_ENA_ELOOP |206 HSIO_S6G_COMMON_CFG_ENA_FLOOP |207 HSIO_S6G_COMMON_CFG_IF_MODE_M,208 HSIO_S6G_COMMON_CFG_SYS_RST |209 HSIO_S6G_COMMON_CFG_ENA_LANE |210 (qrate ? HSIO_S6G_COMMON_CFG_QRATE : 0) |211 HSIO_S6G_COMMON_CFG_IF_MODE(if_mode));212 213 regmap_update_bits(regmap, HSIO_S6G_MISC_CFG,214 HSIO_S6G_MISC_CFG_LANE_RST |215 HSIO_S6G_MISC_CFG_DES_100FX_CPMD_ENA |216 HSIO_S6G_MISC_CFG_RX_LPI_MODE_ENA |217 HSIO_S6G_MISC_CFG_TX_LPI_MODE_ENA,218 HSIO_S6G_MISC_CFG_LANE_RST |219 HSIO_S6G_MISC_CFG_RX_LPI_MODE_ENA);220 221 222 ret = serdes_commit_mcb_s6g(regmap, serdes);223 if (ret)224 return ret;225 226 regmap_update_bits(regmap, HSIO_S6G_PLL_CFG,227 HSIO_S6G_PLL_CFG_PLL_FSM_ENA,228 HSIO_S6G_PLL_CFG_PLL_FSM_ENA);229 230 ret = serdes_commit_mcb_s6g(regmap, serdes);231 if (ret)232 return ret;233 234 /* Wait for PLL bringup */235 msleep(20);236 237 regmap_update_bits(regmap, HSIO_S6G_IB_CFG,238 HSIO_S6G_IB_CFG_IB_CAL_ENA,239 HSIO_S6G_IB_CFG_IB_CAL_ENA);240 241 regmap_update_bits(regmap, HSIO_S6G_MISC_CFG,242 HSIO_S6G_MISC_CFG_LANE_RST, 0);243 244 ret = serdes_commit_mcb_s6g(regmap, serdes);245 if (ret)246 return ret;247 248 /* Wait for calibration */249 msleep(60);250 251 regmap_update_bits(regmap, HSIO_S6G_IB_CFG,252 HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET_M |253 HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL_M,254 HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET(0) |255 HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL(7));256 257 regmap_update_bits(regmap, HSIO_S6G_IB_CFG1,258 HSIO_S6G_IB_CFG1_IB_TSDET_M,259 HSIO_S6G_IB_CFG1_IB_TSDET(3));260 261 /* IB CFG */262 263 return 0;264}265 266#define MCB_S1G_CFG_TIMEOUT 50267 268static int __serdes_write_mcb_s1g(struct regmap *regmap, u8 macro, u32 op)269{270 unsigned int regval;271 272 regmap_write(regmap, HSIO_MCB_S1G_ADDR_CFG, op |273 HSIO_MCB_S1G_ADDR_CFG_SERDES1G_ADDR(BIT(macro)));274 275 return regmap_read_poll_timeout(regmap, HSIO_MCB_S1G_ADDR_CFG, regval,276 (regval & op) != op, 100,277 MCB_S1G_CFG_TIMEOUT * 1000);278}279 280static int serdes_commit_mcb_s1g(struct regmap *regmap, u8 macro)281{282 return __serdes_write_mcb_s1g(regmap, macro,283 HSIO_MCB_S1G_ADDR_CFG_SERDES1G_WR_ONE_SHOT);284}285 286static int serdes_update_mcb_s1g(struct regmap *regmap, u8 macro)287{288 return __serdes_write_mcb_s1g(regmap, macro,289 HSIO_MCB_S1G_ADDR_CFG_SERDES1G_RD_ONE_SHOT);290}291 292static int serdes_init_s1g(struct regmap *regmap, u8 serdes)293{294 int ret;295 296 ret = serdes_update_mcb_s1g(regmap, serdes);297 if (ret)298 return ret;299 300 regmap_update_bits(regmap, HSIO_S1G_COMMON_CFG,301 HSIO_S1G_COMMON_CFG_SYS_RST |302 HSIO_S1G_COMMON_CFG_ENA_LANE |303 HSIO_S1G_COMMON_CFG_ENA_ELOOP |304 HSIO_S1G_COMMON_CFG_ENA_FLOOP,305 HSIO_S1G_COMMON_CFG_ENA_LANE);306 307 regmap_update_bits(regmap, HSIO_S1G_PLL_CFG,308 HSIO_S1G_PLL_CFG_PLL_FSM_ENA |309 HSIO_S1G_PLL_CFG_PLL_FSM_CTRL_DATA_M,310 HSIO_S1G_PLL_CFG_PLL_FSM_CTRL_DATA(200) |311 HSIO_S1G_PLL_CFG_PLL_FSM_ENA);312 313 regmap_update_bits(regmap, HSIO_S1G_MISC_CFG,314 HSIO_S1G_MISC_CFG_DES_100FX_CPMD_ENA |315 HSIO_S1G_MISC_CFG_LANE_RST,316 HSIO_S1G_MISC_CFG_LANE_RST);317 318 ret = serdes_commit_mcb_s1g(regmap, serdes);319 if (ret)320 return ret;321 322 regmap_update_bits(regmap, HSIO_S1G_COMMON_CFG,323 HSIO_S1G_COMMON_CFG_SYS_RST,324 HSIO_S1G_COMMON_CFG_SYS_RST);325 326 regmap_update_bits(regmap, HSIO_S1G_MISC_CFG,327 HSIO_S1G_MISC_CFG_LANE_RST, 0);328 329 ret = serdes_commit_mcb_s1g(regmap, serdes);330 if (ret)331 return ret;332 333 return 0;334}335 336struct serdes_mux {337 u8 idx;338 u8 port;339 enum phy_mode mode;340 int submode;341 u32 mask;342 u32 mux;343};344 345#define SERDES_MUX(_idx, _port, _mode, _submode, _mask, _mux) { \346 .idx = _idx, \347 .port = _port, \348 .mode = _mode, \349 .submode = _submode, \350 .mask = _mask, \351 .mux = _mux, \352}353 354#define SERDES_MUX_SGMII(i, p, m, c) \355 SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_SGMII, m, c)356#define SERDES_MUX_QSGMII(i, p, m, c) \357 SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_QSGMII, m, c)358 359static const struct serdes_mux ocelot_serdes_muxes[] = {360 SERDES_MUX_SGMII(SERDES1G(0), 0, 0, 0),361 SERDES_MUX_SGMII(SERDES1G(1), 1, HSIO_HW_CFG_DEV1G_5_MODE, 0),362 SERDES_MUX_SGMII(SERDES1G(1), 5, HSIO_HW_CFG_QSGMII_ENA |363 HSIO_HW_CFG_DEV1G_5_MODE, HSIO_HW_CFG_DEV1G_5_MODE),364 SERDES_MUX_SGMII(SERDES1G(2), 2, HSIO_HW_CFG_DEV1G_4_MODE, 0),365 SERDES_MUX_SGMII(SERDES1G(2), 4, HSIO_HW_CFG_QSGMII_ENA |366 HSIO_HW_CFG_DEV1G_4_MODE, HSIO_HW_CFG_DEV1G_4_MODE),367 SERDES_MUX_SGMII(SERDES1G(3), 3, HSIO_HW_CFG_DEV1G_6_MODE, 0),368 SERDES_MUX_SGMII(SERDES1G(3), 6, HSIO_HW_CFG_QSGMII_ENA |369 HSIO_HW_CFG_DEV1G_6_MODE, HSIO_HW_CFG_DEV1G_6_MODE),370 SERDES_MUX_SGMII(SERDES1G(4), 4, HSIO_HW_CFG_QSGMII_ENA |371 HSIO_HW_CFG_DEV1G_4_MODE | HSIO_HW_CFG_DEV1G_9_MODE,372 0),373 SERDES_MUX_SGMII(SERDES1G(4), 9, HSIO_HW_CFG_DEV1G_4_MODE |374 HSIO_HW_CFG_DEV1G_9_MODE, HSIO_HW_CFG_DEV1G_4_MODE |375 HSIO_HW_CFG_DEV1G_9_MODE),376 SERDES_MUX_SGMII(SERDES1G(5), 5, HSIO_HW_CFG_QSGMII_ENA |377 HSIO_HW_CFG_DEV1G_5_MODE | HSIO_HW_CFG_DEV2G5_10_MODE,378 0),379 SERDES_MUX_SGMII(SERDES1G(5), 10, HSIO_HW_CFG_PCIE_ENA |380 HSIO_HW_CFG_DEV1G_5_MODE | HSIO_HW_CFG_DEV2G5_10_MODE,381 HSIO_HW_CFG_DEV1G_5_MODE | HSIO_HW_CFG_DEV2G5_10_MODE),382 SERDES_MUX_QSGMII(SERDES6G(0), 4, HSIO_HW_CFG_QSGMII_ENA,383 HSIO_HW_CFG_QSGMII_ENA),384 SERDES_MUX_QSGMII(SERDES6G(0), 5, HSIO_HW_CFG_QSGMII_ENA,385 HSIO_HW_CFG_QSGMII_ENA),386 SERDES_MUX_QSGMII(SERDES6G(0), 6, HSIO_HW_CFG_QSGMII_ENA,387 HSIO_HW_CFG_QSGMII_ENA),388 SERDES_MUX_SGMII(SERDES6G(0), 7, HSIO_HW_CFG_QSGMII_ENA, 0),389 SERDES_MUX_QSGMII(SERDES6G(0), 7, HSIO_HW_CFG_QSGMII_ENA,390 HSIO_HW_CFG_QSGMII_ENA),391 SERDES_MUX_SGMII(SERDES6G(1), 8, 0, 0),392 SERDES_MUX_SGMII(SERDES6G(2), 10, HSIO_HW_CFG_PCIE_ENA |393 HSIO_HW_CFG_DEV2G5_10_MODE, 0),394 SERDES_MUX(SERDES6G(2), 10, PHY_MODE_PCIE, 0, HSIO_HW_CFG_PCIE_ENA,395 HSIO_HW_CFG_PCIE_ENA),396};397 398static int serdes_set_mode(struct phy *phy, enum phy_mode mode, int submode)399{400 struct serdes_macro *macro = phy_get_drvdata(phy);401 unsigned int i;402 int ret;403 404 /* As of now only PHY_MODE_ETHERNET is supported */405 if (mode != PHY_MODE_ETHERNET)406 return -EOPNOTSUPP;407 408 for (i = 0; i < ARRAY_SIZE(ocelot_serdes_muxes); i++) {409 if (macro->idx != ocelot_serdes_muxes[i].idx ||410 mode != ocelot_serdes_muxes[i].mode ||411 submode != ocelot_serdes_muxes[i].submode)412 continue;413 414 if (submode != PHY_INTERFACE_MODE_QSGMII &&415 macro->port != ocelot_serdes_muxes[i].port)416 continue;417 418 ret = regmap_update_bits(macro->ctrl->regs, HSIO_HW_CFG,419 ocelot_serdes_muxes[i].mask,420 ocelot_serdes_muxes[i].mux);421 if (ret)422 return ret;423 424 if (macro->idx <= SERDES1G_MAX)425 return serdes_init_s1g(macro->ctrl->regs, macro->idx);426 else if (macro->idx <= SERDES6G_MAX)427 return serdes_init_s6g(macro->ctrl->regs,428 macro->idx - (SERDES1G_MAX + 1),429 ocelot_serdes_muxes[i].submode);430 431 /* PCIe not supported yet */432 return -EOPNOTSUPP;433 }434 435 return -EINVAL;436}437 438static const struct phy_ops serdes_ops = {439 .set_mode = serdes_set_mode,440 .owner = THIS_MODULE,441};442 443static struct phy *serdes_simple_xlate(struct device *dev,444 const struct of_phandle_args *args)445{446 struct serdes_ctrl *ctrl = dev_get_drvdata(dev);447 unsigned int port, idx, i;448 449 if (args->args_count != 2)450 return ERR_PTR(-EINVAL);451 452 port = args->args[0];453 idx = args->args[1];454 455 for (i = 0; i < SERDES_MAX; i++) {456 struct serdes_macro *macro = phy_get_drvdata(ctrl->phys[i]);457 458 if (idx != macro->idx)459 continue;460 461 /* SERDES6G(0) is the only SerDes capable of QSGMII */462 if (idx != SERDES6G(0) && macro->port >= 0)463 return ERR_PTR(-EBUSY);464 465 macro->port = port;466 return ctrl->phys[i];467 }468 469 return ERR_PTR(-ENODEV);470}471 472static int serdes_phy_create(struct serdes_ctrl *ctrl, u8 idx, struct phy **phy)473{474 struct serdes_macro *macro;475 476 *phy = devm_phy_create(ctrl->dev, NULL, &serdes_ops);477 if (IS_ERR(*phy))478 return PTR_ERR(*phy);479 480 macro = devm_kzalloc(ctrl->dev, sizeof(*macro), GFP_KERNEL);481 if (!macro)482 return -ENOMEM;483 484 macro->idx = idx;485 macro->ctrl = ctrl;486 macro->port = -1;487 488 phy_set_drvdata(*phy, macro);489 490 return 0;491}492 493static int serdes_probe(struct platform_device *pdev)494{495 struct phy_provider *provider;496 struct serdes_ctrl *ctrl;497 struct resource *res;498 unsigned int i;499 int ret;500 501 ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL);502 if (!ctrl)503 return -ENOMEM;504 505 ctrl->dev = &pdev->dev;506 ctrl->regs = syscon_node_to_regmap(pdev->dev.parent->of_node);507 if (IS_ERR(ctrl->regs)) {508 /* Fall back to using IORESOURCE_REG, if possible */509 res = platform_get_resource(pdev, IORESOURCE_REG, 0);510 if (res)511 ctrl->regs = dev_get_regmap(ctrl->dev->parent,512 res->name);513 }514 515 if (IS_ERR(ctrl->regs))516 return PTR_ERR(ctrl->regs);517 518 for (i = 0; i < SERDES_MAX; i++) {519 ret = serdes_phy_create(ctrl, i, &ctrl->phys[i]);520 if (ret)521 return ret;522 }523 524 dev_set_drvdata(&pdev->dev, ctrl);525 526 provider = devm_of_phy_provider_register(ctrl->dev,527 serdes_simple_xlate);528 529 return PTR_ERR_OR_ZERO(provider);530}531 532static const struct of_device_id serdes_ids[] = {533 { .compatible = "mscc,vsc7514-serdes", },534 {},535};536MODULE_DEVICE_TABLE(of, serdes_ids);537 538static struct platform_driver mscc_ocelot_serdes = {539 .probe = serdes_probe,540 .driver = {541 .name = "mscc,ocelot-serdes",542 .of_match_table = of_match_ptr(serdes_ids),543 },544};545 546module_platform_driver(mscc_ocelot_serdes);547 548MODULE_AUTHOR("Quentin Schulz <quentin.schulz@bootlin.com>");549MODULE_DESCRIPTION("SerDes driver for Microsemi Ocelot");550MODULE_LICENSE("Dual MIT/GPL");551