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