1*9bf5c16aSIoana Ciornei // SPDX-License-Identifier: GPL-2.0 2*9bf5c16aSIoana Ciornei /* Copyright 2026 NXP */ 3*9bf5c16aSIoana Ciornei 4*9bf5c16aSIoana Ciornei #include <linux/bitfield.h> 5*9bf5c16aSIoana Ciornei #include <linux/bits.h> 6*9bf5c16aSIoana Ciornei #include <linux/i2c.h> 7*9bf5c16aSIoana Ciornei #include <linux/iopoll.h> 8*9bf5c16aSIoana Ciornei #include <linux/kernel.h> 9*9bf5c16aSIoana Ciornei #include <linux/module.h> 10*9bf5c16aSIoana Ciornei #include <linux/phy.h> 11*9bf5c16aSIoana Ciornei #include <linux/phy/phy.h> 12*9bf5c16aSIoana Ciornei #include <linux/slab.h> 13*9bf5c16aSIoana Ciornei 14*9bf5c16aSIoana Ciornei #define DS125DF111_NUM_CH 2 15*9bf5c16aSIoana Ciornei #define DS125DF111_NUM_VCO_GROUP_REG 5 16*9bf5c16aSIoana Ciornei 17*9bf5c16aSIoana Ciornei #define DS125DF111_CH_SELECT 0xff 18*9bf5c16aSIoana Ciornei #define DS125DF111_CH_SELECT_TARGET_MASK GENMASK(3, 0) 19*9bf5c16aSIoana Ciornei #define DS125DF111_CH_SELECT_EN BIT(2) 20*9bf5c16aSIoana Ciornei 21*9bf5c16aSIoana Ciornei #define DS125DF111_CH_CTRL 0x00 22*9bf5c16aSIoana Ciornei #define DS125DF111_CH_CTRL_RESET BIT(2) /* self clearing */ 23*9bf5c16aSIoana Ciornei 24*9bf5c16aSIoana Ciornei #define DS125DF111_CH_RST_SLEEP_US 10 25*9bf5c16aSIoana Ciornei #define DS125DF111_CH_RST_TIMEOUT_US 10000 26*9bf5c16aSIoana Ciornei 27*9bf5c16aSIoana Ciornei #define DS125DF111_VCO_GROUP_BASE 0x60 28*9bf5c16aSIoana Ciornei 29*9bf5c16aSIoana Ciornei #define DS125DF111_RATIOS 0x2f 30*9bf5c16aSIoana Ciornei #define DS125DF111_RATIOS_RATE_MASK GENMASK(7, 6) 31*9bf5c16aSIoana Ciornei #define DS125DF111_RATIOS_SUBRATE_MASK GENMASK(5, 4) 32*9bf5c16aSIoana Ciornei #define DS125DF111_RATIOS_MASK GENMASK(7, 4) 33*9bf5c16aSIoana Ciornei 34*9bf5c16aSIoana Ciornei struct ds125df111_ch { 35*9bf5c16aSIoana Ciornei struct phy *phy; 36*9bf5c16aSIoana Ciornei struct ds125df111_priv *priv; 37*9bf5c16aSIoana Ciornei int idx; 38*9bf5c16aSIoana Ciornei }; 39*9bf5c16aSIoana Ciornei 40*9bf5c16aSIoana Ciornei struct ds125df111_priv { 41*9bf5c16aSIoana Ciornei struct ds125df111_ch ch[DS125DF111_NUM_CH]; 42*9bf5c16aSIoana Ciornei struct i2c_client *client; 43*9bf5c16aSIoana Ciornei struct mutex mutex; /* protects access to shared registers */ 44*9bf5c16aSIoana Ciornei }; 45*9bf5c16aSIoana Ciornei 46*9bf5c16aSIoana Ciornei enum ds125df111_mode { 47*9bf5c16aSIoana Ciornei FREQ_1G, 48*9bf5c16aSIoana Ciornei FREQ_10G, 49*9bf5c16aSIoana Ciornei }; 50*9bf5c16aSIoana Ciornei 51*9bf5c16aSIoana Ciornei static const struct ds125df111_config { 52*9bf5c16aSIoana Ciornei u8 vco_group[DS125DF111_NUM_VCO_GROUP_REG]; 53*9bf5c16aSIoana Ciornei u8 rate; 54*9bf5c16aSIoana Ciornei u8 subrate; 55*9bf5c16aSIoana Ciornei } ds125df111_cfg[] = { 56*9bf5c16aSIoana Ciornei [FREQ_1G] = { 57*9bf5c16aSIoana Ciornei /* VCO group #0 = 10GHz, VCO group #1 = 10GHz */ 58*9bf5c16aSIoana Ciornei .vco_group = {0x00, 0xB2, 0x00, 0xB2, 0xCC}, 59*9bf5c16aSIoana Ciornei /* By using the following combination of rate and subrate we 60*9bf5c16aSIoana Ciornei * select divide ratios of 1, 2, 4, 8 on both groups 61*9bf5c16aSIoana Ciornei */ 62*9bf5c16aSIoana Ciornei .rate = 0x1, 63*9bf5c16aSIoana Ciornei .subrate = 0x2, 64*9bf5c16aSIoana Ciornei }, 65*9bf5c16aSIoana Ciornei 66*9bf5c16aSIoana Ciornei [FREQ_10G] = { 67*9bf5c16aSIoana Ciornei /* VCO group #0 = 10.3125GHz, VCO group #1 = 10.3125GHz */ 68*9bf5c16aSIoana Ciornei .vco_group = {0x90, 0xB3, 0x90, 0xB3, 0xCD}, 69*9bf5c16aSIoana Ciornei /* By using the following combination of rate and subrate we 70*9bf5c16aSIoana Ciornei * select divide ratios of 1 on both groups 71*9bf5c16aSIoana Ciornei */ 72*9bf5c16aSIoana Ciornei .rate = 0x1, 73*9bf5c16aSIoana Ciornei .subrate = 0x3, 74*9bf5c16aSIoana Ciornei }, 75*9bf5c16aSIoana Ciornei }; 76*9bf5c16aSIoana Ciornei 77*9bf5c16aSIoana Ciornei static int ds125df111_rmw(struct ds125df111_priv *priv, u8 reg, u8 clr, u8 set) 78*9bf5c16aSIoana Ciornei { 79*9bf5c16aSIoana Ciornei struct i2c_client *i2c = priv->client; 80*9bf5c16aSIoana Ciornei int err; 81*9bf5c16aSIoana Ciornei u8 val; 82*9bf5c16aSIoana Ciornei 83*9bf5c16aSIoana Ciornei err = i2c_smbus_read_byte_data(i2c, reg); 84*9bf5c16aSIoana Ciornei if (err < 0) 85*9bf5c16aSIoana Ciornei return err; 86*9bf5c16aSIoana Ciornei 87*9bf5c16aSIoana Ciornei val = (u8)err; 88*9bf5c16aSIoana Ciornei val &= ~clr; 89*9bf5c16aSIoana Ciornei val |= set; 90*9bf5c16aSIoana Ciornei 91*9bf5c16aSIoana Ciornei err = i2c_smbus_write_byte_data(i2c, reg, val); 92*9bf5c16aSIoana Ciornei if (err < 0) 93*9bf5c16aSIoana Ciornei return err; 94*9bf5c16aSIoana Ciornei 95*9bf5c16aSIoana Ciornei return 0; 96*9bf5c16aSIoana Ciornei } 97*9bf5c16aSIoana Ciornei 98*9bf5c16aSIoana Ciornei static int ds125df111_configure(struct phy *phy, 99*9bf5c16aSIoana Ciornei const struct ds125df111_config *cfg) 100*9bf5c16aSIoana Ciornei { 101*9bf5c16aSIoana Ciornei struct ds125df111_ch *ch = phy_get_drvdata(phy); 102*9bf5c16aSIoana Ciornei struct ds125df111_priv *priv = ch->priv; 103*9bf5c16aSIoana Ciornei struct i2c_client *i2c = priv->client; 104*9bf5c16aSIoana Ciornei struct device *dev = &phy->dev; 105*9bf5c16aSIoana Ciornei u8 ratios_val; 106*9bf5c16aSIoana Ciornei int err, i; 107*9bf5c16aSIoana Ciornei int val; 108*9bf5c16aSIoana Ciornei 109*9bf5c16aSIoana Ciornei mutex_lock(&priv->mutex); 110*9bf5c16aSIoana Ciornei 111*9bf5c16aSIoana Ciornei /* Make sure that any subsequent read/write operation will be directed 112*9bf5c16aSIoana Ciornei * only to the registers of the selected channel 113*9bf5c16aSIoana Ciornei */ 114*9bf5c16aSIoana Ciornei err = ds125df111_rmw(priv, DS125DF111_CH_SELECT, 115*9bf5c16aSIoana Ciornei DS125DF111_CH_SELECT_TARGET_MASK, 116*9bf5c16aSIoana Ciornei DS125DF111_CH_SELECT_EN | ch->idx); 117*9bf5c16aSIoana Ciornei if (err < 0) { 118*9bf5c16aSIoana Ciornei dev_err(dev, "Unable to select channel: %pe\n", ERR_PTR(err)); 119*9bf5c16aSIoana Ciornei goto out; 120*9bf5c16aSIoana Ciornei } 121*9bf5c16aSIoana Ciornei 122*9bf5c16aSIoana Ciornei /* Reset channel registers and wait until the bit was cleared */ 123*9bf5c16aSIoana Ciornei err = ds125df111_rmw(priv, DS125DF111_CH_CTRL, 0, 124*9bf5c16aSIoana Ciornei DS125DF111_CH_CTRL_RESET); 125*9bf5c16aSIoana Ciornei if (err < 0) { 126*9bf5c16aSIoana Ciornei dev_err(dev, "Error resetting channel configuration: %pe\n", 127*9bf5c16aSIoana Ciornei ERR_PTR(err)); 128*9bf5c16aSIoana Ciornei goto out; 129*9bf5c16aSIoana Ciornei } 130*9bf5c16aSIoana Ciornei 131*9bf5c16aSIoana Ciornei err = read_poll_timeout(i2c_smbus_read_byte_data, val, 132*9bf5c16aSIoana Ciornei val < 0 || !(val & DS125DF111_CH_CTRL_RESET), 133*9bf5c16aSIoana Ciornei DS125DF111_CH_RST_SLEEP_US, 134*9bf5c16aSIoana Ciornei DS125DF111_CH_RST_TIMEOUT_US, false, i2c, 135*9bf5c16aSIoana Ciornei DS125DF111_CH_CTRL); 136*9bf5c16aSIoana Ciornei if (err) { 137*9bf5c16aSIoana Ciornei dev_err(dev, "Timed out waiting for channel reset: %pe\n", 138*9bf5c16aSIoana Ciornei ERR_PTR(err)); 139*9bf5c16aSIoana Ciornei goto out; 140*9bf5c16aSIoana Ciornei } 141*9bf5c16aSIoana Ciornei 142*9bf5c16aSIoana Ciornei if (val < 0) { 143*9bf5c16aSIoana Ciornei dev_err(dev, "Error reading reset status: %pe\n", ERR_PTR(val)); 144*9bf5c16aSIoana Ciornei err = val; 145*9bf5c16aSIoana Ciornei goto out; 146*9bf5c16aSIoana Ciornei } 147*9bf5c16aSIoana Ciornei 148*9bf5c16aSIoana Ciornei /* Program the VCO group frequencies */ 149*9bf5c16aSIoana Ciornei for (i = 0; i < DS125DF111_NUM_VCO_GROUP_REG; i++) { 150*9bf5c16aSIoana Ciornei err = i2c_smbus_write_byte_data(i2c, 151*9bf5c16aSIoana Ciornei DS125DF111_VCO_GROUP_BASE + i, 152*9bf5c16aSIoana Ciornei cfg->vco_group[i]); 153*9bf5c16aSIoana Ciornei if (err < 0) { 154*9bf5c16aSIoana Ciornei dev_err(dev, "Error programming VCO group: %pe\n", 155*9bf5c16aSIoana Ciornei ERR_PTR(err)); 156*9bf5c16aSIoana Ciornei goto out; 157*9bf5c16aSIoana Ciornei } 158*9bf5c16aSIoana Ciornei } 159*9bf5c16aSIoana Ciornei 160*9bf5c16aSIoana Ciornei /* Set the divide ratios for the VCO groups */ 161*9bf5c16aSIoana Ciornei ratios_val = FIELD_PREP(DS125DF111_RATIOS_RATE_MASK, cfg->rate) | 162*9bf5c16aSIoana Ciornei FIELD_PREP(DS125DF111_RATIOS_SUBRATE_MASK, cfg->subrate); 163*9bf5c16aSIoana Ciornei err = ds125df111_rmw(priv, DS125DF111_RATIOS, DS125DF111_RATIOS_MASK, 164*9bf5c16aSIoana Ciornei ratios_val); 165*9bf5c16aSIoana Ciornei if (err < 0) { 166*9bf5c16aSIoana Ciornei dev_err(dev, "Error programming the divide ratios: %pe\n", 167*9bf5c16aSIoana Ciornei ERR_PTR(err)); 168*9bf5c16aSIoana Ciornei goto out; 169*9bf5c16aSIoana Ciornei } 170*9bf5c16aSIoana Ciornei 171*9bf5c16aSIoana Ciornei out: 172*9bf5c16aSIoana Ciornei mutex_unlock(&priv->mutex); 173*9bf5c16aSIoana Ciornei 174*9bf5c16aSIoana Ciornei return err; 175*9bf5c16aSIoana Ciornei } 176*9bf5c16aSIoana Ciornei 177*9bf5c16aSIoana Ciornei static int ds125df111_set_mode(struct phy *phy, enum phy_mode mode, int submode) 178*9bf5c16aSIoana Ciornei { 179*9bf5c16aSIoana Ciornei const struct ds125df111_config *cfg; 180*9bf5c16aSIoana Ciornei 181*9bf5c16aSIoana Ciornei if (mode != PHY_MODE_ETHERNET) 182*9bf5c16aSIoana Ciornei return -EINVAL; 183*9bf5c16aSIoana Ciornei 184*9bf5c16aSIoana Ciornei switch (submode) { 185*9bf5c16aSIoana Ciornei case PHY_INTERFACE_MODE_10GBASER: 186*9bf5c16aSIoana Ciornei cfg = &ds125df111_cfg[FREQ_10G]; 187*9bf5c16aSIoana Ciornei break; 188*9bf5c16aSIoana Ciornei case PHY_INTERFACE_MODE_1000BASEX: 189*9bf5c16aSIoana Ciornei case PHY_INTERFACE_MODE_SGMII: 190*9bf5c16aSIoana Ciornei cfg = &ds125df111_cfg[FREQ_1G]; 191*9bf5c16aSIoana Ciornei break; 192*9bf5c16aSIoana Ciornei default: 193*9bf5c16aSIoana Ciornei return -EINVAL; 194*9bf5c16aSIoana Ciornei } 195*9bf5c16aSIoana Ciornei 196*9bf5c16aSIoana Ciornei return ds125df111_configure(phy, cfg); 197*9bf5c16aSIoana Ciornei } 198*9bf5c16aSIoana Ciornei 199*9bf5c16aSIoana Ciornei static int ds125df111_validate(struct phy *phy, enum phy_mode mode, int submode, 200*9bf5c16aSIoana Ciornei union phy_configure_opts *opts __always_unused) 201*9bf5c16aSIoana Ciornei { 202*9bf5c16aSIoana Ciornei if (mode != PHY_MODE_ETHERNET) 203*9bf5c16aSIoana Ciornei return -EINVAL; 204*9bf5c16aSIoana Ciornei 205*9bf5c16aSIoana Ciornei switch (submode) { 206*9bf5c16aSIoana Ciornei case PHY_INTERFACE_MODE_10GBASER: 207*9bf5c16aSIoana Ciornei case PHY_INTERFACE_MODE_1000BASEX: 208*9bf5c16aSIoana Ciornei case PHY_INTERFACE_MODE_SGMII: 209*9bf5c16aSIoana Ciornei return 0; 210*9bf5c16aSIoana Ciornei default: 211*9bf5c16aSIoana Ciornei return -EINVAL; 212*9bf5c16aSIoana Ciornei } 213*9bf5c16aSIoana Ciornei } 214*9bf5c16aSIoana Ciornei 215*9bf5c16aSIoana Ciornei static const struct phy_ops ds125df111_ops = { 216*9bf5c16aSIoana Ciornei .validate = ds125df111_validate, 217*9bf5c16aSIoana Ciornei .set_mode = ds125df111_set_mode, 218*9bf5c16aSIoana Ciornei .owner = THIS_MODULE, 219*9bf5c16aSIoana Ciornei }; 220*9bf5c16aSIoana Ciornei 221*9bf5c16aSIoana Ciornei static struct phy *ds125df111_xlate(struct device *dev, 222*9bf5c16aSIoana Ciornei const struct of_phandle_args *args) 223*9bf5c16aSIoana Ciornei { 224*9bf5c16aSIoana Ciornei struct ds125df111_priv *priv = dev_get_drvdata(dev); 225*9bf5c16aSIoana Ciornei u32 idx; 226*9bf5c16aSIoana Ciornei 227*9bf5c16aSIoana Ciornei if (args->args_count != 1) 228*9bf5c16aSIoana Ciornei return ERR_PTR(-EINVAL); 229*9bf5c16aSIoana Ciornei 230*9bf5c16aSIoana Ciornei idx = args->args[0]; 231*9bf5c16aSIoana Ciornei if (idx >= DS125DF111_NUM_CH) { 232*9bf5c16aSIoana Ciornei dev_err(dev, "Maximum number of channels is %d\n", 233*9bf5c16aSIoana Ciornei DS125DF111_NUM_CH); 234*9bf5c16aSIoana Ciornei return ERR_PTR(-EINVAL); 235*9bf5c16aSIoana Ciornei } 236*9bf5c16aSIoana Ciornei 237*9bf5c16aSIoana Ciornei return priv->ch[idx].phy; 238*9bf5c16aSIoana Ciornei } 239*9bf5c16aSIoana Ciornei 240*9bf5c16aSIoana Ciornei static int ds125df111_probe(struct i2c_client *client) 241*9bf5c16aSIoana Ciornei { 242*9bf5c16aSIoana Ciornei struct device *dev = &client->dev; 243*9bf5c16aSIoana Ciornei struct phy_provider *provider; 244*9bf5c16aSIoana Ciornei struct ds125df111_priv *priv; 245*9bf5c16aSIoana Ciornei int i, err; 246*9bf5c16aSIoana Ciornei 247*9bf5c16aSIoana Ciornei priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 248*9bf5c16aSIoana Ciornei if (!priv) 249*9bf5c16aSIoana Ciornei return -ENOMEM; 250*9bf5c16aSIoana Ciornei priv->client = client; 251*9bf5c16aSIoana Ciornei err = devm_mutex_init(dev, &priv->mutex); 252*9bf5c16aSIoana Ciornei if (err) 253*9bf5c16aSIoana Ciornei return err; 254*9bf5c16aSIoana Ciornei 255*9bf5c16aSIoana Ciornei i2c_set_clientdata(client, priv); 256*9bf5c16aSIoana Ciornei 257*9bf5c16aSIoana Ciornei for (i = 0; i < DS125DF111_NUM_CH; i++) { 258*9bf5c16aSIoana Ciornei struct ds125df111_ch *ch = &priv->ch[i]; 259*9bf5c16aSIoana Ciornei struct phy *phy; 260*9bf5c16aSIoana Ciornei 261*9bf5c16aSIoana Ciornei phy = devm_phy_create(dev, NULL, &ds125df111_ops); 262*9bf5c16aSIoana Ciornei if (IS_ERR(phy)) 263*9bf5c16aSIoana Ciornei return PTR_ERR(phy); 264*9bf5c16aSIoana Ciornei 265*9bf5c16aSIoana Ciornei ch->idx = i; 266*9bf5c16aSIoana Ciornei ch->priv = priv; 267*9bf5c16aSIoana Ciornei ch->phy = phy; 268*9bf5c16aSIoana Ciornei 269*9bf5c16aSIoana Ciornei phy_set_drvdata(phy, ch); 270*9bf5c16aSIoana Ciornei } 271*9bf5c16aSIoana Ciornei 272*9bf5c16aSIoana Ciornei provider = devm_of_phy_provider_register(dev, ds125df111_xlate); 273*9bf5c16aSIoana Ciornei 274*9bf5c16aSIoana Ciornei return PTR_ERR_OR_ZERO(provider); 275*9bf5c16aSIoana Ciornei } 276*9bf5c16aSIoana Ciornei 277*9bf5c16aSIoana Ciornei static const struct of_device_id ds125df111_dt_ids[] = { 278*9bf5c16aSIoana Ciornei { .compatible = "ti,ds125df111", }, 279*9bf5c16aSIoana Ciornei {} 280*9bf5c16aSIoana Ciornei }; 281*9bf5c16aSIoana Ciornei MODULE_DEVICE_TABLE(of, ds125df111_dt_ids); 282*9bf5c16aSIoana Ciornei 283*9bf5c16aSIoana Ciornei static struct i2c_driver ds125df111_driver = { 284*9bf5c16aSIoana Ciornei .driver = { 285*9bf5c16aSIoana Ciornei .name = "ds125df111", 286*9bf5c16aSIoana Ciornei .of_match_table = ds125df111_dt_ids, 287*9bf5c16aSIoana Ciornei }, 288*9bf5c16aSIoana Ciornei .probe = ds125df111_probe, 289*9bf5c16aSIoana Ciornei }; 290*9bf5c16aSIoana Ciornei module_i2c_driver(ds125df111_driver); 291*9bf5c16aSIoana Ciornei 292*9bf5c16aSIoana Ciornei MODULE_AUTHOR("Ioana Ciornei <ioana.ciornei@nxp.com>"); 293*9bf5c16aSIoana Ciornei MODULE_DESCRIPTION("TI DS125DF111 Retimer driver"); 294*9bf5c16aSIoana Ciornei MODULE_LICENSE("GPL"); 295