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