xref: /linux/drivers/phy/ti/phy-ds125df111.c (revision 00599d4841790d05401820a96cf7edb193888b00)
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