blob: 75af86a7fad80feb606fa6bab4cf78720d228eb0 [file] [log] [blame]
Vivien Didelota935c052016-09-29 12:21:53 -04001/*
2 * Marvell 88E6xxx Switch Global (1) Registers support
3 *
4 * Copyright (c) 2008 Marvell Semiconductor
5 *
6 * Copyright (c) 2016 Vivien Didelot <vivien.didelot@savoirfairelinux.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include "mv88e6xxx.h"
15#include "global1.h"
16
17int mv88e6xxx_g1_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
18{
19 int addr = chip->info->global1_addr;
20
21 return mv88e6xxx_read(chip, addr, reg, val);
22}
23
24int mv88e6xxx_g1_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
25{
26 int addr = chip->info->global1_addr;
27
28 return mv88e6xxx_write(chip, addr, reg, val);
29}
30
31int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask)
32{
33 return mv88e6xxx_wait(chip, chip->info->global1_addr, reg, mask);
34}
Andrew Lunna605a0f2016-11-21 23:26:58 +010035
Vivien Didelot17e708b2016-12-05 17:30:27 -050036/* Offset 0x00: Switch Global Status Register */
37
Vivien Didelota199d8b2016-12-05 17:30:28 -050038static int mv88e6185_g1_wait_ppu_disabled(struct mv88e6xxx_chip *chip)
39{
40 u16 state;
41 int i, err;
42
43 for (i = 0; i < 16; i++) {
44 err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &state);
45 if (err)
46 return err;
47
48 /* Check the value of the PPUState bits 15:14 */
49 state &= GLOBAL_STATUS_PPU_STATE_MASK;
50 if (state != GLOBAL_STATUS_PPU_STATE_POLLING)
51 return 0;
52
53 usleep_range(1000, 2000);
54 }
55
56 return -ETIMEDOUT;
57}
58
Vivien Didelot17e708b2016-12-05 17:30:27 -050059static int mv88e6185_g1_wait_ppu_polling(struct mv88e6xxx_chip *chip)
60{
61 u16 state;
62 int i, err;
63
64 for (i = 0; i < 16; ++i) {
65 err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &state);
66 if (err)
67 return err;
68
69 /* Check the value of the PPUState bits 15:14 */
70 state &= GLOBAL_STATUS_PPU_STATE_MASK;
71 if (state == GLOBAL_STATUS_PPU_STATE_POLLING)
72 return 0;
73
74 usleep_range(1000, 2000);
75 }
76
77 return -ETIMEDOUT;
78}
79
80static int mv88e6352_g1_wait_ppu_polling(struct mv88e6xxx_chip *chip)
81{
82 u16 state;
83 int i, err;
84
85 for (i = 0; i < 16; ++i) {
86 err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &state);
87 if (err)
88 return err;
89
90 /* Check the value of the PPUState (or InitState) bit 15 */
91 if (state & GLOBAL_STATUS_PPU_STATE)
92 return 0;
93
94 usleep_range(1000, 2000);
95 }
96
97 return -ETIMEDOUT;
98}
99
100static int mv88e6xxx_g1_wait_init_ready(struct mv88e6xxx_chip *chip)
101{
102 const unsigned long timeout = jiffies + 1 * HZ;
103 u16 val;
104 int err;
105
106 /* Wait up to 1 second for the switch to be ready. The InitReady bit 11
107 * is set to a one when all units inside the device (ATU, VTU, etc.)
108 * have finished their initialization and are ready to accept frames.
109 */
110 while (time_before(jiffies, timeout)) {
111 err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val);
112 if (err)
113 return err;
114
115 if (val & GLOBAL_STATUS_INIT_READY)
116 break;
117
118 usleep_range(1000, 2000);
119 }
120
121 if (time_after(jiffies, timeout))
122 return -ETIMEDOUT;
123
124 return 0;
125}
126
127/* Offset 0x04: Switch Global Control Register */
128
129int mv88e6185_g1_reset(struct mv88e6xxx_chip *chip)
130{
131 u16 val;
132 int err;
133
134 /* Set the SWReset bit 15 along with the PPUEn bit 14, to also restart
135 * the PPU, including re-doing PHY detection and initialization
136 */
137 err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
138 if (err)
139 return err;
140
141 val |= GLOBAL_CONTROL_SW_RESET;
142 val |= GLOBAL_CONTROL_PPU_ENABLE;
143
144 err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val);
145 if (err)
146 return err;
147
148 err = mv88e6xxx_g1_wait_init_ready(chip);
149 if (err)
150 return err;
151
152 return mv88e6185_g1_wait_ppu_polling(chip);
153}
154
155int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip)
156{
157 u16 val;
158 int err;
159
160 /* Set the SWReset bit 15 */
161 err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
162 if (err)
163 return err;
164
165 val |= GLOBAL_CONTROL_SW_RESET;
166
167 err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val);
168 if (err)
169 return err;
170
171 err = mv88e6xxx_g1_wait_init_ready(chip);
172 if (err)
173 return err;
174
175 return mv88e6352_g1_wait_ppu_polling(chip);
176}
177
Vivien Didelota199d8b2016-12-05 17:30:28 -0500178int mv88e6185_g1_ppu_enable(struct mv88e6xxx_chip *chip)
179{
180 u16 val;
181 int err;
182
183 err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
184 if (err)
185 return err;
186
187 val |= GLOBAL_CONTROL_PPU_ENABLE;
188
189 err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val);
190 if (err)
191 return err;
192
193 return mv88e6185_g1_wait_ppu_polling(chip);
194}
195
196int mv88e6185_g1_ppu_disable(struct mv88e6xxx_chip *chip)
197{
198 u16 val;
199 int err;
200
201 err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
202 if (err)
203 return err;
204
205 val &= ~GLOBAL_CONTROL_PPU_ENABLE;
206
207 err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val);
208 if (err)
209 return err;
210
211 return mv88e6185_g1_wait_ppu_disabled(chip);
212}
213
Andrew Lunn33641992016-12-03 04:35:17 +0100214/* Offset 0x1a: Monitor Control */
215/* Offset 0x1a: Monitor & MGMT Control on some devices */
216
217int mv88e6095_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port)
218{
219 u16 reg;
220 int err;
221
222 err = mv88e6xxx_g1_read(chip, GLOBAL_MONITOR_CONTROL, &reg);
223 if (err)
224 return err;
225
226 reg &= ~(GLOBAL_MONITOR_CONTROL_INGRESS_MASK |
227 GLOBAL_MONITOR_CONTROL_EGRESS_MASK);
228
229 reg |= port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
230 port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT;
231
232 return mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg);
233}
234
235/* Older generations also call this the ARP destination. It has been
236 * generalized in more modern devices such that more than ARP can
237 * egress it
238 */
239int mv88e6095_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port)
240{
241 u16 reg;
242 int err;
243
244 err = mv88e6xxx_g1_read(chip, GLOBAL_MONITOR_CONTROL, &reg);
245 if (err)
246 return err;
247
248 reg &= ~GLOBAL_MONITOR_CONTROL_ARP_MASK;
249 reg |= port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT;
250
251 return mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg);
252}
253
254static int mv88e6390_g1_monitor_write(struct mv88e6xxx_chip *chip,
255 u16 pointer, u8 data)
256{
257 u16 reg;
258
259 reg = GLOBAL_MONITOR_CONTROL_UPDATE | pointer | data;
260
261 return mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg);
262}
263
264int mv88e6390_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port)
265{
266 int err;
267
268 err = mv88e6390_g1_monitor_write(chip, GLOBAL_MONITOR_CONTROL_INGRESS,
269 port);
270 if (err)
271 return err;
272
273 return mv88e6390_g1_monitor_write(chip, GLOBAL_MONITOR_CONTROL_EGRESS,
274 port);
275}
276
277int mv88e6390_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port)
278{
279 return mv88e6390_g1_monitor_write(chip, GLOBAL_MONITOR_CONTROL_CPU_DEST,
280 port);
281}
282
Andrew Lunn6e55f692016-12-03 04:45:16 +0100283int mv88e6390_g1_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip)
284{
285 int err;
286
287 /* 01:c2:80:00:00:00:00-01:c2:80:00:00:00:07 are Management */
288 err = mv88e6390_g1_monitor_write(
289 chip, GLOBAL_MONITOR_CONTROL_0180C280000000XLO, 0xff);
290 if (err)
291 return err;
292
293 /* 01:c2:80:00:00:00:08-01:c2:80:00:00:00:0f are Management */
294 err = mv88e6390_g1_monitor_write(
295 chip, GLOBAL_MONITOR_CONTROL_0180C280000000XHI, 0xff);
296 if (err)
297 return err;
298
299 /* 01:c2:80:00:00:00:20-01:c2:80:00:00:00:27 are Management */
300 err = mv88e6390_g1_monitor_write(
301 chip, GLOBAL_MONITOR_CONTROL_0180C280000002XLO, 0xff);
302 if (err)
303 return err;
304
305 /* 01:c2:80:00:00:00:28-01:c2:80:00:00:00:2f are Management */
306 return mv88e6390_g1_monitor_write(
307 chip, GLOBAL_MONITOR_CONTROL_0180C280000002XHI, 0xff);
308}
309
Andrew Lunnde2273872016-11-21 23:27:01 +0100310/* Offset 0x1c: Global Control 2 */
311
312int mv88e6390_g1_stats_set_histogram(struct mv88e6xxx_chip *chip)
313{
314 u16 val;
315 int err;
316
317 err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL_2, &val);
318 if (err)
319 return err;
320
321 val |= GLOBAL_CONTROL_2_HIST_RX_TX;
322
323 err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL_2, val);
324
325 return err;
326}
327
328/* Offset 0x1d: Statistics Operation 2 */
329
Andrew Lunn7f9ef3a2016-11-21 23:27:05 +0100330int mv88e6xxx_g1_stats_wait(struct mv88e6xxx_chip *chip)
Andrew Lunna605a0f2016-11-21 23:26:58 +0100331{
332 return mv88e6xxx_g1_wait(chip, GLOBAL_STATS_OP, GLOBAL_STATS_OP_BUSY);
333}
334
335int mv88e6xxx_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
336{
337 int err;
338
339 /* Snapshot the hardware statistics counters for this port. */
340 err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
341 GLOBAL_STATS_OP_CAPTURE_PORT |
342 GLOBAL_STATS_OP_HIST_RX_TX | port);
343 if (err)
344 return err;
345
346 /* Wait for the snapshotting to complete. */
347 return mv88e6xxx_g1_stats_wait(chip);
348}
349
350int mv88e6320_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
351{
352 port = (port + 1) << 5;
353
354 return mv88e6xxx_g1_stats_snapshot(chip, port);
355}
Andrew Lunn79523472016-11-21 23:27:00 +0100356
357int mv88e6390_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
358{
359 int err;
360
361 port = (port + 1) << 5;
362
363 /* Snapshot the hardware statistics counters for this port. */
364 err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
365 GLOBAL_STATS_OP_CAPTURE_PORT | port);
366 if (err)
367 return err;
368
369 /* Wait for the snapshotting to complete. */
370 return mv88e6xxx_g1_stats_wait(chip);
371}
Andrew Lunn7f9ef3a2016-11-21 23:27:05 +0100372
373void mv88e6xxx_g1_stats_read(struct mv88e6xxx_chip *chip, int stat, u32 *val)
374{
375 u32 value;
376 u16 reg;
377 int err;
378
379 *val = 0;
380
381 err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
382 GLOBAL_STATS_OP_READ_CAPTURED | stat);
383 if (err)
384 return;
385
386 err = mv88e6xxx_g1_stats_wait(chip);
387 if (err)
388 return;
389
390 err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_32, &reg);
391 if (err)
392 return;
393
394 value = reg << 16;
395
396 err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_01, &reg);
397 if (err)
398 return;
399
400 *val = value | reg;
401}