Merge remote-tracking branch 'mmc-uh/next'
[deliverable/linux.git] / drivers / net / dsa / mv88e6xxx / global2.c
1 /*
2 * Marvell 88E6xxx Switch Global 2 Registers support (device address 0x1C)
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 "global2.h"
16
17 /* Offset 0x06: Device Mapping Table register */
18
19 static int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip,
20 int target, int port)
21 {
22 u16 val = (target << 8) | (port & 0xf);
23
24 return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_DEVICE_MAPPING, val);
25 }
26
27 static int mv88e6xxx_g2_set_device_mapping(struct mv88e6xxx_chip *chip)
28 {
29 int target, port;
30 int err;
31
32 /* Initialize the routing port to the 32 possible target devices */
33 for (target = 0; target < 32; ++target) {
34 port = 0xf;
35
36 if (target < DSA_MAX_SWITCHES) {
37 port = chip->ds->rtable[target];
38 if (port == DSA_RTABLE_NONE)
39 port = 0xf;
40 }
41
42 err = mv88e6xxx_g2_device_mapping_write(chip, target, port);
43 if (err)
44 break;
45 }
46
47 return err;
48 }
49
50 /* Offset 0x07: Trunk Mask Table register */
51
52 static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
53 bool hask, u16 mask)
54 {
55 const u16 port_mask = BIT(chip->info->num_ports) - 1;
56 u16 val = (num << 12) | (mask & port_mask);
57
58 if (hask)
59 val |= GLOBAL2_TRUNK_MASK_HASK;
60
61 return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_TRUNK_MASK, val);
62 }
63
64 /* Offset 0x08: Trunk Mapping Table register */
65
66 static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id,
67 u16 map)
68 {
69 const u16 port_mask = BIT(chip->info->num_ports) - 1;
70 u16 val = (id << 11) | (map & port_mask);
71
72 return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_TRUNK_MAPPING, val);
73 }
74
75 static int mv88e6xxx_g2_clear_trunk(struct mv88e6xxx_chip *chip)
76 {
77 const u16 port_mask = BIT(chip->info->num_ports) - 1;
78 int i, err;
79
80 /* Clear all eight possible Trunk Mask vectors */
81 for (i = 0; i < 8; ++i) {
82 err = mv88e6xxx_g2_trunk_mask_write(chip, i, false, port_mask);
83 if (err)
84 return err;
85 }
86
87 /* Clear all sixteen possible Trunk ID routing vectors */
88 for (i = 0; i < 16; ++i) {
89 err = mv88e6xxx_g2_trunk_mapping_write(chip, i, 0);
90 if (err)
91 return err;
92 }
93
94 return 0;
95 }
96
97 /* Offset 0x09: Ingress Rate Command register
98 * Offset 0x0A: Ingress Rate Data register
99 */
100
101 static int mv88e6xxx_g2_clear_irl(struct mv88e6xxx_chip *chip)
102 {
103 int port, err;
104
105 /* Init all Ingress Rate Limit resources of all ports */
106 for (port = 0; port < chip->info->num_ports; ++port) {
107 /* XXX newer chips (like 88E6390) have different 2-bit ops */
108 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_IRL_CMD,
109 GLOBAL2_IRL_CMD_OP_INIT_ALL |
110 (port << 8));
111 if (err)
112 break;
113
114 /* Wait for the operation to complete */
115 err = mv88e6xxx_wait(chip, REG_GLOBAL2, GLOBAL2_IRL_CMD,
116 GLOBAL2_IRL_CMD_BUSY);
117 if (err)
118 break;
119 }
120
121 return err;
122 }
123
124 /* Offset 0x0D: Switch MAC/WoL/WoF register */
125
126 static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip,
127 unsigned int pointer, u8 data)
128 {
129 u16 val = (pointer << 8) | data;
130
131 return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_SWITCH_MAC, val);
132 }
133
134 int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
135 {
136 int i, err;
137
138 for (i = 0; i < 6; i++) {
139 err = mv88e6xxx_g2_switch_mac_write(chip, i, addr[i]);
140 if (err)
141 break;
142 }
143
144 return err;
145 }
146
147 /* Offset 0x0F: Priority Override Table */
148
149 static int mv88e6xxx_g2_pot_write(struct mv88e6xxx_chip *chip, int pointer,
150 u8 data)
151 {
152 u16 val = (pointer << 8) | (data & 0x7);
153
154 return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_PRIO_OVERRIDE, val);
155 }
156
157 static int mv88e6xxx_g2_clear_pot(struct mv88e6xxx_chip *chip)
158 {
159 int i, err;
160
161 /* Clear all sixteen possible Priority Override entries */
162 for (i = 0; i < 16; i++) {
163 err = mv88e6xxx_g2_pot_write(chip, i, 0);
164 if (err)
165 break;
166 }
167
168 return err;
169 }
170
171 /* Offset 0x14: EEPROM Command
172 * Offset 0x15: EEPROM Data
173 */
174
175 static int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip)
176 {
177 return mv88e6xxx_wait(chip, REG_GLOBAL2, GLOBAL2_EEPROM_CMD,
178 GLOBAL2_EEPROM_CMD_BUSY |
179 GLOBAL2_EEPROM_CMD_RUNNING);
180 }
181
182 static int mv88e6xxx_g2_eeprom_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
183 {
184 int err;
185
186 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_EEPROM_CMD, cmd);
187 if (err)
188 return err;
189
190 return mv88e6xxx_g2_eeprom_wait(chip);
191 }
192
193 static int mv88e6xxx_g2_eeprom_read16(struct mv88e6xxx_chip *chip,
194 u8 addr, u16 *data)
195 {
196 u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ | addr;
197 int err;
198
199 err = mv88e6xxx_g2_eeprom_wait(chip);
200 if (err)
201 return err;
202
203 err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
204 if (err)
205 return err;
206
207 return mv88e6xxx_read(chip, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
208 }
209
210 static int mv88e6xxx_g2_eeprom_write16(struct mv88e6xxx_chip *chip,
211 u8 addr, u16 data)
212 {
213 u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | addr;
214 int err;
215
216 err = mv88e6xxx_g2_eeprom_wait(chip);
217 if (err)
218 return err;
219
220 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
221 if (err)
222 return err;
223
224 return mv88e6xxx_g2_eeprom_cmd(chip, cmd);
225 }
226
227 int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip,
228 struct ethtool_eeprom *eeprom, u8 *data)
229 {
230 unsigned int offset = eeprom->offset;
231 unsigned int len = eeprom->len;
232 u16 val;
233 int err;
234
235 eeprom->len = 0;
236
237 if (offset & 1) {
238 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
239 if (err)
240 return err;
241
242 *data++ = (val >> 8) & 0xff;
243
244 offset++;
245 len--;
246 eeprom->len++;
247 }
248
249 while (len >= 2) {
250 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
251 if (err)
252 return err;
253
254 *data++ = val & 0xff;
255 *data++ = (val >> 8) & 0xff;
256
257 offset += 2;
258 len -= 2;
259 eeprom->len += 2;
260 }
261
262 if (len) {
263 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
264 if (err)
265 return err;
266
267 *data++ = val & 0xff;
268
269 offset++;
270 len--;
271 eeprom->len++;
272 }
273
274 return 0;
275 }
276
277 int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip,
278 struct ethtool_eeprom *eeprom, u8 *data)
279 {
280 unsigned int offset = eeprom->offset;
281 unsigned int len = eeprom->len;
282 u16 val;
283 int err;
284
285 /* Ensure the RO WriteEn bit is set */
286 err = mv88e6xxx_read(chip, REG_GLOBAL2, GLOBAL2_EEPROM_CMD, &val);
287 if (err)
288 return err;
289
290 if (!(val & GLOBAL2_EEPROM_CMD_WRITE_EN))
291 return -EROFS;
292
293 eeprom->len = 0;
294
295 if (offset & 1) {
296 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
297 if (err)
298 return err;
299
300 val = (*data++ << 8) | (val & 0xff);
301
302 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
303 if (err)
304 return err;
305
306 offset++;
307 len--;
308 eeprom->len++;
309 }
310
311 while (len >= 2) {
312 val = *data++;
313 val |= *data++ << 8;
314
315 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
316 if (err)
317 return err;
318
319 offset += 2;
320 len -= 2;
321 eeprom->len += 2;
322 }
323
324 if (len) {
325 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
326 if (err)
327 return err;
328
329 val = (val & 0xff00) | *data++;
330
331 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
332 if (err)
333 return err;
334
335 offset++;
336 len--;
337 eeprom->len++;
338 }
339
340 return 0;
341 }
342
343 /* Offset 0x18: SMI PHY Command Register
344 * Offset 0x19: SMI PHY Data Register
345 */
346
347 static int mv88e6xxx_g2_smi_phy_wait(struct mv88e6xxx_chip *chip)
348 {
349 return mv88e6xxx_wait(chip, REG_GLOBAL2, GLOBAL2_SMI_PHY_CMD,
350 GLOBAL2_SMI_PHY_CMD_BUSY);
351 }
352
353 static int mv88e6xxx_g2_smi_phy_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
354 {
355 int err;
356
357 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_SMI_PHY_CMD, cmd);
358 if (err)
359 return err;
360
361 return mv88e6xxx_g2_smi_phy_wait(chip);
362 }
363
364 int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, int addr, int reg,
365 u16 *val)
366 {
367 u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_READ_DATA | (addr << 5) | reg;
368 int err;
369
370 err = mv88e6xxx_g2_smi_phy_wait(chip);
371 if (err)
372 return err;
373
374 err = mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
375 if (err)
376 return err;
377
378 return mv88e6xxx_read(chip, REG_GLOBAL2, GLOBAL2_SMI_PHY_DATA, val);
379 }
380
381 int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, int addr, int reg,
382 u16 val)
383 {
384 u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_WRITE_DATA | (addr << 5) | reg;
385 int err;
386
387 err = mv88e6xxx_g2_smi_phy_wait(chip);
388 if (err)
389 return err;
390
391 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_SMI_PHY_DATA, val);
392 if (err)
393 return err;
394
395 return mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
396 }
397
398 int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip)
399 {
400 u16 reg;
401 int err;
402
403 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) {
404 /* Consider the frames with reserved multicast destination
405 * addresses matching 01:80:c2:00:00:2x as MGMT.
406 */
407 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_MGMT_EN_2X,
408 0xffff);
409 if (err)
410 return err;
411 }
412
413 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X)) {
414 /* Consider the frames with reserved multicast destination
415 * addresses matching 01:80:c2:00:00:0x as MGMT.
416 */
417 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X,
418 0xffff);
419 if (err)
420 return err;
421 }
422
423 /* Ignore removed tag data on doubly tagged packets, disable
424 * flow control messages, force flow control priority to the
425 * highest, and send all special multicast frames to the CPU
426 * port at the highest priority.
427 */
428 reg = GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI | (0x7 << 4);
429 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X) ||
430 mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X))
431 reg |= GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x7;
432 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_SWITCH_MGMT, reg);
433 if (err)
434 return err;
435
436 /* Program the DSA routing table. */
437 err = mv88e6xxx_g2_set_device_mapping(chip);
438 if (err)
439 return err;
440
441 /* Clear all trunk masks and mapping. */
442 err = mv88e6xxx_g2_clear_trunk(chip);
443 if (err)
444 return err;
445
446 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_IRL)) {
447 /* Disable ingress rate limiting by resetting all per port
448 * ingress rate limit resources to their initial state.
449 */
450 err = mv88e6xxx_g2_clear_irl(chip);
451 if (err)
452 return err;
453 }
454
455 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_PVT)) {
456 /* Initialize Cross-chip Port VLAN Table to reset defaults */
457 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_PVT_ADDR,
458 GLOBAL2_PVT_ADDR_OP_INIT_ONES);
459 if (err)
460 return err;
461 }
462
463 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_POT)) {
464 /* Clear the priority override table. */
465 err = mv88e6xxx_g2_clear_pot(chip);
466 if (err)
467 return err;
468 }
469
470 return 0;
471 }
This page took 0.045076 seconds and 5 git commands to generate.