blob: 7269a6afe1b036480175b58e24fa9c4b969889c3 [file] [log] [blame]
Valentin Longchamp2ebd1502012-08-16 01:17:26 +00001/*
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
49static 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
73static 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
99static 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
128static 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
158static 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
188int 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
220int 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
250int 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
265int 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
281int 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
313U_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 );