1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2012
4  * Valentin Lontgchamp, Keymile AG, valentin.longchamp@keymile.com
5  */
6 
7 #include <common.h>
8 #include <command.h>
9 #include <log.h>
10 #include <miiphy.h>
11 #include <linux/delay.h>
12 #include <linux/errno.h>
13 #include <mv88e6352.h>
14 
15 #define SMI_HDR		((0x8 | 0x1) << 12)
16 #define SMI_BUSY_MASK	(0x8000)
17 #define SMIRD_OP	(0x2 << 10)
18 #define SMIWR_OP	(0x1 << 10)
19 #define SMI_MASK	0x1f
20 #define PORT_SHIFT	5
21 
22 #define COMMAND_REG	0
23 #define DATA_REG	1
24 
25 /* global registers */
26 #define GLOBAL		0x1b
27 
28 #define GLOBAL_STATUS	0x00
29 #define PPU_STATE	0x8000
30 
31 #define GLOBAL_CTRL	0x04
32 #define SW_RESET	0x8000
33 #define PPU_ENABLE	0x4000
34 
sw_wait_rdy(const char * devname,u8 phy_addr)35 static int sw_wait_rdy(const char *devname, u8 phy_addr)
36 {
37 	u16 command;
38 	u32 timeout = 100;
39 	int ret;
40 
41 	/* wait till the SMI is not busy */
42 	do {
43 		/* read command register */
44 		ret = miiphy_read(devname, phy_addr, COMMAND_REG, &command);
45 		if (ret < 0) {
46 			printf("%s: Error reading command register\n",
47 				__func__);
48 			return ret;
49 		}
50 		if (timeout-- == 0) {
51 			printf("Err..(%s) SMI busy timeout\n", __func__);
52 			return -EFAULT;
53 		}
54 	} while (command & SMI_BUSY_MASK);
55 
56 	return 0;
57 }
58 
sw_reg_read(const char * devname,u8 phy_addr,u8 port,u8 reg,u16 * data)59 static int sw_reg_read(const char *devname, u8 phy_addr, u8 port,
60 	u8 reg, u16 *data)
61 {
62 	int ret;
63 	u16 command;
64 
65 	ret = sw_wait_rdy(devname, phy_addr);
66 	if (ret)
67 		return ret;
68 
69 	command = SMI_HDR | SMIRD_OP | ((port&SMI_MASK) << PORT_SHIFT) |
70 			(reg & SMI_MASK);
71 	debug("%s: write to command: %#x\n", __func__, command);
72 	ret = miiphy_write(devname, phy_addr, COMMAND_REG, command);
73 	if (ret)
74 		return ret;
75 
76 	ret = sw_wait_rdy(devname, phy_addr);
77 	if (ret)
78 		return ret;
79 
80 	ret = miiphy_read(devname, phy_addr, DATA_REG, data);
81 
82 	return ret;
83 }
84 
sw_reg_write(const char * devname,u8 phy_addr,u8 port,u8 reg,u16 data)85 static int sw_reg_write(const char *devname, u8 phy_addr, u8 port,
86 	u8 reg, u16 data)
87 {
88 	int ret;
89 	u16 value;
90 
91 	ret = sw_wait_rdy(devname, phy_addr);
92 	if (ret)
93 		return ret;
94 
95 	debug("%s: write to data: %#x\n", __func__, data);
96 	ret = miiphy_write(devname, phy_addr, DATA_REG, data);
97 	if (ret)
98 		return ret;
99 
100 	value = SMI_HDR | SMIWR_OP | ((port & SMI_MASK) << PORT_SHIFT) |
101 			(reg & SMI_MASK);
102 	debug("%s: write to command: %#x\n", __func__, value);
103 	ret = miiphy_write(devname, phy_addr, COMMAND_REG, value);
104 	if (ret)
105 		return ret;
106 
107 	ret = sw_wait_rdy(devname, phy_addr);
108 	if (ret)
109 		return ret;
110 
111 	return 0;
112 }
113 
ppu_enable(const char * devname,u8 phy_addr)114 static int ppu_enable(const char *devname, u8 phy_addr)
115 {
116 	int i, ret = 0;
117 	u16 reg;
118 
119 	ret = sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, &reg);
120 	if (ret) {
121 		printf("%s: Error reading global ctrl reg\n", __func__);
122 		return ret;
123 	}
124 
125 	reg |= PPU_ENABLE;
126 
127 	ret = sw_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg);
128 	if (ret) {
129 		printf("%s: Error writing global ctrl reg\n", __func__);
130 		return ret;
131 	}
132 
133 	for (i = 0; i < 1000; i++) {
134 		sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS,
135 			&reg);
136 		if ((reg & 0xc000) == 0xc000)
137 			return 0;
138 		udelay(1000);
139 	}
140 
141 	return -ETIMEDOUT;
142 }
143 
ppu_disable(const char * devname,u8 phy_addr)144 static int ppu_disable(const char *devname, u8 phy_addr)
145 {
146 	int i, ret = 0;
147 	u16 reg;
148 
149 	ret = sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, &reg);
150 	if (ret) {
151 		printf("%s: Error reading global ctrl reg\n", __func__);
152 		return ret;
153 	}
154 
155 	reg &= ~PPU_ENABLE;
156 
157 	ret = sw_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg);
158 	if (ret) {
159 		printf("%s: Error writing global ctrl reg\n", __func__);
160 		return ret;
161 	}
162 
163 	for (i = 0; i < 1000; i++) {
164 		sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS,
165 			&reg);
166 		if ((reg & 0xc000) != 0xc000)
167 			return 0;
168 		udelay(1000);
169 	}
170 
171 	return -ETIMEDOUT;
172 }
173 
mv88e_sw_program(const char * devname,u8 phy_addr,struct mv88e_sw_reg * regs,int regs_nb)174 int mv88e_sw_program(const char *devname, u8 phy_addr,
175 	struct mv88e_sw_reg *regs, int regs_nb)
176 {
177 	int i, ret = 0;
178 
179 	/* first we need to disable the PPU */
180 	ret = ppu_disable(devname, phy_addr);
181 	if (ret) {
182 		printf("%s: Error disabling PPU\n", __func__);
183 		return ret;
184 	}
185 
186 	for (i = 0; i < regs_nb; i++) {
187 		ret = sw_reg_write(devname, phy_addr, regs[i].port,
188 			regs[i].reg, regs[i].value);
189 		if (ret) {
190 			printf("%s: Error configuring switch\n", __func__);
191 			ppu_enable(devname, phy_addr);
192 			return ret;
193 		}
194 	}
195 
196 	/* re-enable the PPU */
197 	ret = ppu_enable(devname, phy_addr);
198 	if (ret) {
199 		printf("%s: Error enabling PPU\n", __func__);
200 		return ret;
201 	}
202 
203 	return 0;
204 }
205 
mv88e_sw_reset(const char * devname,u8 phy_addr)206 int mv88e_sw_reset(const char *devname, u8 phy_addr)
207 {
208 	int i, ret = 0;
209 	u16 reg;
210 
211 	ret = sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, &reg);
212 	if (ret) {
213 		printf("%s: Error reading global ctrl reg\n", __func__);
214 		return ret;
215 	}
216 
217 	reg = SW_RESET | PPU_ENABLE | 0x0400;
218 
219 	ret = sw_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg);
220 	if (ret) {
221 		printf("%s: Error writing global ctrl reg\n", __func__);
222 		return ret;
223 	}
224 
225 	for (i = 0; i < 1000; i++) {
226 		sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS,
227 			&reg);
228 		if ((reg & 0xc800) != 0xc800)
229 			return 0;
230 		udelay(1000);
231 	}
232 
233 	return -ETIMEDOUT;
234 }
235 
do_mvsw_reg_read(const char * name,int argc,char * const argv[])236 int do_mvsw_reg_read(const char *name, int argc, char *const argv[])
237 {
238 	u16 value = 0, phyaddr, reg, port;
239 	int ret;
240 
241 	phyaddr = simple_strtoul(argv[1], NULL, 10);
242 	port = simple_strtoul(argv[2], NULL, 10);
243 	reg = simple_strtoul(argv[3], NULL, 10);
244 
245 	ret = sw_reg_read(name, phyaddr, port, reg, &value);
246 	printf("%#x\n", value);
247 
248 	return ret;
249 }
250 
do_mvsw_reg_write(const char * name,int argc,char * const argv[])251 int do_mvsw_reg_write(const char *name, int argc, char *const argv[])
252 {
253 	u16 value = 0, phyaddr, reg, port;
254 	int ret;
255 
256 	phyaddr = simple_strtoul(argv[1], NULL, 10);
257 	port = simple_strtoul(argv[2], NULL, 10);
258 	reg = simple_strtoul(argv[3], NULL, 10);
259 	value = simple_strtoul(argv[4], NULL, 16);
260 
261 	ret = sw_reg_write(name, phyaddr, port, reg, value);
262 
263 	return ret;
264 }
265 
266 
do_mvsw_reg(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])267 int do_mvsw_reg(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
268 {
269 	int ret;
270 	const char *cmd, *ethname;
271 
272 	if (argc < 2)
273 		return cmd_usage(cmdtp);
274 
275 	cmd = argv[1];
276 	--argc;
277 	++argv;
278 
279 	if (strcmp(cmd, "read") == 0) {
280 		if (argc < 5)
281 			return cmd_usage(cmdtp);
282 		ethname = argv[1];
283 		--argc;
284 		++argv;
285 		ret = do_mvsw_reg_read(ethname, argc, argv);
286 	} else if (strcmp(cmd, "write") == 0) {
287 		if (argc < 6)
288 			return cmd_usage(cmdtp);
289 		ethname = argv[1];
290 		--argc;
291 		++argv;
292 		ret = do_mvsw_reg_write(ethname, argc, argv);
293 	} else
294 		return cmd_usage(cmdtp);
295 
296 	return ret;
297 }
298 
299 U_BOOT_CMD(
300 	mvsw_reg,	7,	1,	do_mvsw_reg,
301 	"marvell 88e6352 switch register access",
302 	"write ethname phyaddr port reg value\n"
303 	"mvsw_reg read  ethname phyaddr port reg\n"
304 	);
305