xref: /linux/drivers/net/dsa/mxl862xx/mxl862xx-host.c (revision c17ee635fd3a482b2ad2bf5e269755c2eae5f25e)
1*23794becSDaniel Golle // SPDX-License-Identifier: GPL-2.0-or-later
2*23794becSDaniel Golle /*
3*23794becSDaniel Golle  * Based upon the MaxLinear SDK driver
4*23794becSDaniel Golle  *
5*23794becSDaniel Golle  * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org>
6*23794becSDaniel Golle  * Copyright (C) 2025 John Crispin <john@phrozen.org>
7*23794becSDaniel Golle  * Copyright (C) 2024 MaxLinear Inc.
8*23794becSDaniel Golle  */
9*23794becSDaniel Golle 
10*23794becSDaniel Golle #include <linux/bits.h>
11*23794becSDaniel Golle #include <linux/iopoll.h>
12*23794becSDaniel Golle #include <linux/limits.h>
13*23794becSDaniel Golle #include <net/dsa.h>
14*23794becSDaniel Golle #include "mxl862xx.h"
15*23794becSDaniel Golle #include "mxl862xx-host.h"
16*23794becSDaniel Golle 
17*23794becSDaniel Golle #define CTRL_BUSY_MASK			BIT(15)
18*23794becSDaniel Golle 
19*23794becSDaniel Golle #define MXL862XX_MMD_REG_CTRL		0
20*23794becSDaniel Golle #define MXL862XX_MMD_REG_LEN_RET	1
21*23794becSDaniel Golle #define MXL862XX_MMD_REG_DATA_FIRST	2
22*23794becSDaniel Golle #define MXL862XX_MMD_REG_DATA_LAST	95
23*23794becSDaniel Golle #define MXL862XX_MMD_REG_DATA_MAX_SIZE \
24*23794becSDaniel Golle 		(MXL862XX_MMD_REG_DATA_LAST - MXL862XX_MMD_REG_DATA_FIRST + 1)
25*23794becSDaniel Golle 
26*23794becSDaniel Golle #define MMD_API_SET_DATA_0		2
27*23794becSDaniel Golle #define MMD_API_GET_DATA_0		5
28*23794becSDaniel Golle #define MMD_API_RST_DATA		8
29*23794becSDaniel Golle 
30*23794becSDaniel Golle #define MXL862XX_SWITCH_RESET 0x9907
31*23794becSDaniel Golle 
32*23794becSDaniel Golle static int mxl862xx_reg_read(struct mxl862xx_priv *priv, u32 addr)
33*23794becSDaniel Golle {
34*23794becSDaniel Golle 	return __mdiodev_c45_read(priv->mdiodev, MDIO_MMD_VEND1, addr);
35*23794becSDaniel Golle }
36*23794becSDaniel Golle 
37*23794becSDaniel Golle static int mxl862xx_reg_write(struct mxl862xx_priv *priv, u32 addr, u16 data)
38*23794becSDaniel Golle {
39*23794becSDaniel Golle 	return __mdiodev_c45_write(priv->mdiodev, MDIO_MMD_VEND1, addr, data);
40*23794becSDaniel Golle }
41*23794becSDaniel Golle 
42*23794becSDaniel Golle static int mxl862xx_ctrl_read(struct mxl862xx_priv *priv)
43*23794becSDaniel Golle {
44*23794becSDaniel Golle 	return mxl862xx_reg_read(priv, MXL862XX_MMD_REG_CTRL);
45*23794becSDaniel Golle }
46*23794becSDaniel Golle 
47*23794becSDaniel Golle static int mxl862xx_busy_wait(struct mxl862xx_priv *priv)
48*23794becSDaniel Golle {
49*23794becSDaniel Golle 	int val;
50*23794becSDaniel Golle 
51*23794becSDaniel Golle 	return readx_poll_timeout(mxl862xx_ctrl_read, priv, val,
52*23794becSDaniel Golle 				  !(val & CTRL_BUSY_MASK), 15, 500000);
53*23794becSDaniel Golle }
54*23794becSDaniel Golle 
55*23794becSDaniel Golle static int mxl862xx_set_data(struct mxl862xx_priv *priv, u16 words)
56*23794becSDaniel Golle {
57*23794becSDaniel Golle 	int ret;
58*23794becSDaniel Golle 	u16 cmd;
59*23794becSDaniel Golle 
60*23794becSDaniel Golle 	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET,
61*23794becSDaniel Golle 				 MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16));
62*23794becSDaniel Golle 	if (ret < 0)
63*23794becSDaniel Golle 		return ret;
64*23794becSDaniel Golle 
65*23794becSDaniel Golle 	cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE - 1;
66*23794becSDaniel Golle 	if (!(cmd < 2))
67*23794becSDaniel Golle 		return -EINVAL;
68*23794becSDaniel Golle 
69*23794becSDaniel Golle 	cmd += MMD_API_SET_DATA_0;
70*23794becSDaniel Golle 	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL,
71*23794becSDaniel Golle 				 cmd | CTRL_BUSY_MASK);
72*23794becSDaniel Golle 	if (ret < 0)
73*23794becSDaniel Golle 		return ret;
74*23794becSDaniel Golle 
75*23794becSDaniel Golle 	return mxl862xx_busy_wait(priv);
76*23794becSDaniel Golle }
77*23794becSDaniel Golle 
78*23794becSDaniel Golle static int mxl862xx_get_data(struct mxl862xx_priv *priv, u16 words)
79*23794becSDaniel Golle {
80*23794becSDaniel Golle 	int ret;
81*23794becSDaniel Golle 	u16 cmd;
82*23794becSDaniel Golle 
83*23794becSDaniel Golle 	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET,
84*23794becSDaniel Golle 				 MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16));
85*23794becSDaniel Golle 	if (ret < 0)
86*23794becSDaniel Golle 		return ret;
87*23794becSDaniel Golle 
88*23794becSDaniel Golle 	cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE;
89*23794becSDaniel Golle 	if (!(cmd > 0 && cmd < 3))
90*23794becSDaniel Golle 		return -EINVAL;
91*23794becSDaniel Golle 
92*23794becSDaniel Golle 	cmd += MMD_API_GET_DATA_0;
93*23794becSDaniel Golle 	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL,
94*23794becSDaniel Golle 				 cmd | CTRL_BUSY_MASK);
95*23794becSDaniel Golle 	if (ret < 0)
96*23794becSDaniel Golle 		return ret;
97*23794becSDaniel Golle 
98*23794becSDaniel Golle 	return mxl862xx_busy_wait(priv);
99*23794becSDaniel Golle }
100*23794becSDaniel Golle 
101*23794becSDaniel Golle static int mxl862xx_firmware_return(int ret)
102*23794becSDaniel Golle {
103*23794becSDaniel Golle 	/* Only 16-bit values are valid. */
104*23794becSDaniel Golle 	if (WARN_ON(ret & GENMASK(31, 16)))
105*23794becSDaniel Golle 		return -EINVAL;
106*23794becSDaniel Golle 
107*23794becSDaniel Golle 	/* Interpret value as signed 16-bit integer. */
108*23794becSDaniel Golle 	return (s16)ret;
109*23794becSDaniel Golle }
110*23794becSDaniel Golle 
111*23794becSDaniel Golle static int mxl862xx_send_cmd(struct mxl862xx_priv *priv, u16 cmd, u16 size,
112*23794becSDaniel Golle 			     bool quiet)
113*23794becSDaniel Golle {
114*23794becSDaniel Golle 	int ret;
115*23794becSDaniel Golle 
116*23794becSDaniel Golle 	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, size);
117*23794becSDaniel Golle 	if (ret)
118*23794becSDaniel Golle 		return ret;
119*23794becSDaniel Golle 
120*23794becSDaniel Golle 	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL,
121*23794becSDaniel Golle 				 cmd | CTRL_BUSY_MASK);
122*23794becSDaniel Golle 	if (ret)
123*23794becSDaniel Golle 		return ret;
124*23794becSDaniel Golle 
125*23794becSDaniel Golle 	ret = mxl862xx_busy_wait(priv);
126*23794becSDaniel Golle 	if (ret)
127*23794becSDaniel Golle 		return ret;
128*23794becSDaniel Golle 
129*23794becSDaniel Golle 	ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_LEN_RET);
130*23794becSDaniel Golle 	if (ret < 0)
131*23794becSDaniel Golle 		return ret;
132*23794becSDaniel Golle 
133*23794becSDaniel Golle 	/* handle errors returned by the firmware as -EIO
134*23794becSDaniel Golle 	 * The firmware is based on Zephyr OS and uses the errors as
135*23794becSDaniel Golle 	 * defined in errno.h of Zephyr OS. See
136*23794becSDaniel Golle 	 * https://github.com/zephyrproject-rtos/zephyr/blob/v3.7.0/lib/libc/minimal/include/errno.h
137*23794becSDaniel Golle 	 */
138*23794becSDaniel Golle 	ret = mxl862xx_firmware_return(ret);
139*23794becSDaniel Golle 	if (ret < 0) {
140*23794becSDaniel Golle 		if (!quiet)
141*23794becSDaniel Golle 			dev_err(&priv->mdiodev->dev,
142*23794becSDaniel Golle 				"CMD %04x returned error %d\n", cmd, ret);
143*23794becSDaniel Golle 		return -EIO;
144*23794becSDaniel Golle 	}
145*23794becSDaniel Golle 
146*23794becSDaniel Golle 	return ret;
147*23794becSDaniel Golle }
148*23794becSDaniel Golle 
149*23794becSDaniel Golle int mxl862xx_api_wrap(struct mxl862xx_priv *priv, u16 cmd, void *_data,
150*23794becSDaniel Golle 		      u16 size, bool read, bool quiet)
151*23794becSDaniel Golle {
152*23794becSDaniel Golle 	__le16 *data = _data;
153*23794becSDaniel Golle 	int ret, cmd_ret;
154*23794becSDaniel Golle 	u16 max, i;
155*23794becSDaniel Golle 
156*23794becSDaniel Golle 	dev_dbg(&priv->mdiodev->dev, "CMD %04x DATA %*ph\n", cmd, size, data);
157*23794becSDaniel Golle 
158*23794becSDaniel Golle 	mutex_lock_nested(&priv->mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED);
159*23794becSDaniel Golle 
160*23794becSDaniel Golle 	max = (size + 1) / 2;
161*23794becSDaniel Golle 
162*23794becSDaniel Golle 	ret = mxl862xx_busy_wait(priv);
163*23794becSDaniel Golle 	if (ret < 0)
164*23794becSDaniel Golle 		goto out;
165*23794becSDaniel Golle 
166*23794becSDaniel Golle 	for (i = 0; i < max; i++) {
167*23794becSDaniel Golle 		u16 off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE;
168*23794becSDaniel Golle 
169*23794becSDaniel Golle 		if (i && off == 0) {
170*23794becSDaniel Golle 			/* Send command to set data when every
171*23794becSDaniel Golle 			 * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are written.
172*23794becSDaniel Golle 			 */
173*23794becSDaniel Golle 			ret = mxl862xx_set_data(priv, i);
174*23794becSDaniel Golle 			if (ret < 0)
175*23794becSDaniel Golle 				goto out;
176*23794becSDaniel Golle 		}
177*23794becSDaniel Golle 
178*23794becSDaniel Golle 		ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_DATA_FIRST + off,
179*23794becSDaniel Golle 					 le16_to_cpu(data[i]));
180*23794becSDaniel Golle 		if (ret < 0)
181*23794becSDaniel Golle 			goto out;
182*23794becSDaniel Golle 	}
183*23794becSDaniel Golle 
184*23794becSDaniel Golle 	ret = mxl862xx_send_cmd(priv, cmd, size, quiet);
185*23794becSDaniel Golle 	if (ret < 0 || !read)
186*23794becSDaniel Golle 		goto out;
187*23794becSDaniel Golle 
188*23794becSDaniel Golle 	/* store result of mxl862xx_send_cmd() */
189*23794becSDaniel Golle 	cmd_ret = ret;
190*23794becSDaniel Golle 
191*23794becSDaniel Golle 	for (i = 0; i < max; i++) {
192*23794becSDaniel Golle 		u16 off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE;
193*23794becSDaniel Golle 
194*23794becSDaniel Golle 		if (i && off == 0) {
195*23794becSDaniel Golle 			/* Send command to fetch next batch of data when every
196*23794becSDaniel Golle 			 * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are read.
197*23794becSDaniel Golle 			 */
198*23794becSDaniel Golle 			ret = mxl862xx_get_data(priv, i);
199*23794becSDaniel Golle 			if (ret < 0)
200*23794becSDaniel Golle 				goto out;
201*23794becSDaniel Golle 		}
202*23794becSDaniel Golle 
203*23794becSDaniel Golle 		ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_DATA_FIRST + off);
204*23794becSDaniel Golle 		if (ret < 0)
205*23794becSDaniel Golle 			goto out;
206*23794becSDaniel Golle 
207*23794becSDaniel Golle 		if ((i * 2 + 1) == size) {
208*23794becSDaniel Golle 			/* Special handling for last BYTE if it's not WORD
209*23794becSDaniel Golle 			 * aligned to avoid writing beyond the allocated data
210*23794becSDaniel Golle 			 * structure.
211*23794becSDaniel Golle 			 */
212*23794becSDaniel Golle 			*(uint8_t *)&data[i] = ret & 0xff;
213*23794becSDaniel Golle 		} else {
214*23794becSDaniel Golle 			data[i] = cpu_to_le16((u16)ret);
215*23794becSDaniel Golle 		}
216*23794becSDaniel Golle 	}
217*23794becSDaniel Golle 
218*23794becSDaniel Golle 	/* on success return the result of the mxl862xx_send_cmd() */
219*23794becSDaniel Golle 	ret = cmd_ret;
220*23794becSDaniel Golle 
221*23794becSDaniel Golle 	dev_dbg(&priv->mdiodev->dev, "RET %d DATA %*ph\n", ret, size, data);
222*23794becSDaniel Golle 
223*23794becSDaniel Golle out:
224*23794becSDaniel Golle 	mutex_unlock(&priv->mdiodev->bus->mdio_lock);
225*23794becSDaniel Golle 
226*23794becSDaniel Golle 	return ret;
227*23794becSDaniel Golle }
228*23794becSDaniel Golle 
229*23794becSDaniel Golle int mxl862xx_reset(struct mxl862xx_priv *priv)
230*23794becSDaniel Golle {
231*23794becSDaniel Golle 	int ret;
232*23794becSDaniel Golle 
233*23794becSDaniel Golle 	mutex_lock_nested(&priv->mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED);
234*23794becSDaniel Golle 
235*23794becSDaniel Golle 	/* Software reset */
236*23794becSDaniel Golle 	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, 0);
237*23794becSDaniel Golle 	if (ret)
238*23794becSDaniel Golle 		goto out;
239*23794becSDaniel Golle 
240*23794becSDaniel Golle 	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, MXL862XX_SWITCH_RESET);
241*23794becSDaniel Golle out:
242*23794becSDaniel Golle 	mutex_unlock(&priv->mdiodev->bus->mdio_lock);
243*23794becSDaniel Golle 
244*23794becSDaniel Golle 	return ret;
245*23794becSDaniel Golle }
246