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