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