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