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, ®);
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 ®);
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, ®);
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 ®);
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, ®);
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 ®);
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