blob: 56060762d85c16025656f89237bfbc8056cc3fea [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>
Simon Glassdbd79542020-05-10 11:40:11 -060011#include <linux/delay.h>
Masahiro Yamada56a931c2016-09-21 11:28:55 +090012#include <linux/errno.h>
Valentin Longchamp2ebd1502012-08-16 01:17:26 +000013#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
35static 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
59static 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
85static 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
114static 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, &reg);
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 &reg);
136 if ((reg & 0xc000) == 0xc000)
137 return 0;
138 udelay(1000);
139 }
140
141 return -ETIMEDOUT;
142}
143
144static 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, &reg);
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 &reg);
166 if ((reg & 0xc000) != 0xc000)
167 return 0;
168 udelay(1000);
169 }
170
171 return -ETIMEDOUT;
172}
173
174int 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
206int 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, &reg);
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 &reg);
228 if ((reg & 0xc800) != 0xc800)
229 return 0;
230 udelay(1000);
231 }
232
233 return -ETIMEDOUT;
234}
235
Simon Glassed38aef2020-05-10 11:40:03 -0600236int do_mvsw_reg_read(const char *name, int argc, char *const argv[])
Valentin Longchamp2ebd1502012-08-16 01:17:26 +0000237{
238 u16 value = 0, phyaddr, reg, port;
239 int ret;
240
Simon Glassff9b9032021-07-24 09:03:30 -0600241 phyaddr = dectoul(argv[1], NULL);
242 port = dectoul(argv[2], NULL);
243 reg = dectoul(argv[3], NULL);
Valentin Longchamp2ebd1502012-08-16 01:17:26 +0000244
245 ret = sw_reg_read(name, phyaddr, port, reg, &value);
246 printf("%#x\n", value);
247
248 return ret;
249}
250
Simon Glassed38aef2020-05-10 11:40:03 -0600251int do_mvsw_reg_write(const char *name, int argc, char *const argv[])
Valentin Longchamp2ebd1502012-08-16 01:17:26 +0000252{
253 u16 value = 0, phyaddr, reg, port;
254 int ret;
255
Simon Glassff9b9032021-07-24 09:03:30 -0600256 phyaddr = dectoul(argv[1], NULL);
257 port = dectoul(argv[2], NULL);
258 reg = dectoul(argv[3], NULL);
Simon Glass3ff49ec2021-07-24 09:03:29 -0600259 value = hextoul(argv[4], NULL);
Valentin Longchamp2ebd1502012-08-16 01:17:26 +0000260
261 ret = sw_reg_write(name, phyaddr, port, reg, value);
262
263 return ret;
264}
265
266
Simon Glassed38aef2020-05-10 11:40:03 -0600267int do_mvsw_reg(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Valentin Longchamp2ebd1502012-08-16 01:17:26 +0000268{
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
299U_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 );