blob: d821b9591a8f8193d5bf65c80bd60770a6a88a7a [file] [log] [blame]
Vivien Didelotec561272016-09-02 14:45:33 -04001/*
Andrew Lunndc30c352016-10-16 19:56:49 +02002 * Marvell 88E6xxx Switch Global 2 Registers support (device address
3 * 0x1C)
Vivien Didelotec561272016-09-02 14:45:33 -04004 *
5 * Copyright (c) 2008 Marvell Semiconductor
6 *
Vivien Didelot4333d612017-03-28 15:10:36 -04007 * Copyright (c) 2016-2017 Savoir-faire Linux Inc.
8 * Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Vivien Didelotec561272016-09-02 14:45:33 -04009 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 */
15
Vivien Didelote289ef02017-06-19 10:55:37 -040016#include <linux/bitfield.h>
Florian Westphal282ccf62017-03-29 17:17:31 +020017#include <linux/interrupt.h>
Andrew Lunndc30c352016-10-16 19:56:49 +020018#include <linux/irqdomain.h>
Vivien Didelot4d5f2ba72017-06-02 17:06:15 -040019
20#include "chip.h"
Vivien Didelot82466922017-06-15 12:13:59 -040021#include "global1.h" /* for MV88E6XXX_G1_STS_IRQ_DEVICE */
Vivien Didelotec561272016-09-02 14:45:33 -040022#include "global2.h"
23
Vivien Didelot9fe850f2016-09-29 12:21:54 -040024static int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
25{
26 return mv88e6xxx_read(chip, ADDR_GLOBAL2, reg, val);
27}
28
29static int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
30{
31 return mv88e6xxx_write(chip, ADDR_GLOBAL2, reg, val);
32}
33
34static int mv88e6xxx_g2_update(struct mv88e6xxx_chip *chip, int reg, u16 update)
35{
36 return mv88e6xxx_update(chip, ADDR_GLOBAL2, reg, update);
37}
38
39static int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask)
40{
41 return mv88e6xxx_wait(chip, ADDR_GLOBAL2, reg, mask);
42}
43
Andrew Lunn6e55f692016-12-03 04:45:16 +010044/* Offset 0x02: Management Enable 2x */
45/* Offset 0x03: Management Enable 0x */
46
47int mv88e6095_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip)
48{
49 int err;
50
51 /* Consider the frames with reserved multicast destination
52 * addresses matching 01:80:c2:00:00:2x as MGMT.
53 */
54 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) {
55 err = mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_2X, 0xffff);
56 if (err)
57 return err;
58 }
59
60 /* Consider the frames with reserved multicast destination
61 * addresses matching 01:80:c2:00:00:0x as MGMT.
62 */
63 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X))
64 return mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_0X, 0xffff);
65
66 return 0;
67}
68
Vivien Didelotec561272016-09-02 14:45:33 -040069/* Offset 0x06: Device Mapping Table register */
70
71static int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip,
72 int target, int port)
73{
74 u16 val = (target << 8) | (port & 0xf);
75
Vivien Didelot9fe850f2016-09-29 12:21:54 -040076 return mv88e6xxx_g2_update(chip, GLOBAL2_DEVICE_MAPPING, val);
Vivien Didelotec561272016-09-02 14:45:33 -040077}
78
79static int mv88e6xxx_g2_set_device_mapping(struct mv88e6xxx_chip *chip)
80{
81 int target, port;
82 int err;
83
84 /* Initialize the routing port to the 32 possible target devices */
85 for (target = 0; target < 32; ++target) {
86 port = 0xf;
87
88 if (target < DSA_MAX_SWITCHES) {
89 port = chip->ds->rtable[target];
90 if (port == DSA_RTABLE_NONE)
91 port = 0xf;
92 }
93
94 err = mv88e6xxx_g2_device_mapping_write(chip, target, port);
95 if (err)
96 break;
97 }
98
99 return err;
100}
101
102/* Offset 0x07: Trunk Mask Table register */
103
104static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
Vivien Didelot56dc7342017-06-19 10:55:38 -0400105 bool hash, u16 mask)
Vivien Didelotec561272016-09-02 14:45:33 -0400106{
Vivien Didelot56dc7342017-06-19 10:55:38 -0400107 u16 val = (num << 12) | (mask & mv88e6xxx_port_mask(chip));
Vivien Didelotec561272016-09-02 14:45:33 -0400108
Vivien Didelot56dc7342017-06-19 10:55:38 -0400109 if (hash)
110 val |= MV88E6XXX_G2_TRUNK_MASK_HASH;
Vivien Didelotec561272016-09-02 14:45:33 -0400111
Vivien Didelot56dc7342017-06-19 10:55:38 -0400112 return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_TRUNK_MASK, val);
Vivien Didelotec561272016-09-02 14:45:33 -0400113}
114
115/* Offset 0x08: Trunk Mapping Table register */
116
117static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id,
118 u16 map)
119{
Vivien Didelot370b4ff2016-09-29 12:21:57 -0400120 const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
Vivien Didelotec561272016-09-02 14:45:33 -0400121 u16 val = (id << 11) | (map & port_mask);
122
Vivien Didelot56dc7342017-06-19 10:55:38 -0400123 return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_TRUNK_MAPPING, val);
Vivien Didelotec561272016-09-02 14:45:33 -0400124}
125
126static int mv88e6xxx_g2_clear_trunk(struct mv88e6xxx_chip *chip)
127{
Vivien Didelot370b4ff2016-09-29 12:21:57 -0400128 const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
Vivien Didelotec561272016-09-02 14:45:33 -0400129 int i, err;
130
131 /* Clear all eight possible Trunk Mask vectors */
132 for (i = 0; i < 8; ++i) {
133 err = mv88e6xxx_g2_trunk_mask_write(chip, i, false, port_mask);
134 if (err)
135 return err;
136 }
137
138 /* Clear all sixteen possible Trunk ID routing vectors */
139 for (i = 0; i < 16; ++i) {
140 err = mv88e6xxx_g2_trunk_mapping_write(chip, i, 0);
141 if (err)
142 return err;
143 }
144
145 return 0;
146}
147
148/* Offset 0x09: Ingress Rate Command register
149 * Offset 0x0A: Ingress Rate Data register
150 */
151
Vivien Didelotcd8da8b2017-06-19 10:55:36 -0400152static int mv88e6xxx_g2_irl_wait(struct mv88e6xxx_chip *chip)
Vivien Didelotec561272016-09-02 14:45:33 -0400153{
Vivien Didelotcd8da8b2017-06-19 10:55:36 -0400154 return mv88e6xxx_g2_wait(chip, MV88E6XXX_G2_IRL_CMD,
155 MV88E6XXX_G2_IRL_CMD_BUSY);
156}
Vivien Didelotec561272016-09-02 14:45:33 -0400157
Vivien Didelotcd8da8b2017-06-19 10:55:36 -0400158static int mv88e6xxx_g2_irl_op(struct mv88e6xxx_chip *chip, u16 op, int port,
159 int res, int reg)
160{
161 int err;
Vivien Didelotec561272016-09-02 14:45:33 -0400162
Vivien Didelotcd8da8b2017-06-19 10:55:36 -0400163 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_IRL_CMD,
164 MV88E6XXX_G2_IRL_CMD_BUSY | op | (port << 8) |
165 (res << 5) | reg);
166 if (err)
167 return err;
Vivien Didelotec561272016-09-02 14:45:33 -0400168
Vivien Didelotcd8da8b2017-06-19 10:55:36 -0400169 return mv88e6xxx_g2_irl_wait(chip);
170}
171
172int mv88e6352_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port)
173{
174 return mv88e6xxx_g2_irl_op(chip, MV88E6352_G2_IRL_CMD_OP_INIT_ALL, port,
175 0, 0);
176}
177
178int mv88e6390_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port)
179{
180 return mv88e6xxx_g2_irl_op(chip, MV88E6390_G2_IRL_CMD_OP_INIT_ALL, port,
181 0, 0);
Vivien Didelotec561272016-09-02 14:45:33 -0400182}
183
Vivien Didelot17a15942017-03-30 17:37:09 -0400184/* Offset 0x0B: Cross-chip Port VLAN (Addr) Register
185 * Offset 0x0C: Cross-chip Port VLAN Data Register
186 */
187
188static int mv88e6xxx_g2_pvt_op_wait(struct mv88e6xxx_chip *chip)
189{
190 return mv88e6xxx_g2_wait(chip, GLOBAL2_PVT_ADDR, GLOBAL2_PVT_ADDR_BUSY);
191}
192
193static int mv88e6xxx_g2_pvt_op(struct mv88e6xxx_chip *chip, int src_dev,
194 int src_port, u16 op)
195{
196 int err;
197
198 /* 9-bit Cross-chip PVT pointer: with GLOBAL2_MISC_5_BIT_PORT cleared,
199 * source device is 5-bit, source port is 4-bit.
200 */
201 op |= (src_dev & 0x1f) << 4;
202 op |= (src_port & 0xf);
203
204 err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_ADDR, op);
205 if (err)
206 return err;
207
208 return mv88e6xxx_g2_pvt_op_wait(chip);
209}
210
211int mv88e6xxx_g2_pvt_write(struct mv88e6xxx_chip *chip, int src_dev,
212 int src_port, u16 data)
213{
214 int err;
215
216 err = mv88e6xxx_g2_pvt_op_wait(chip);
217 if (err)
218 return err;
219
220 err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_DATA, data);
221 if (err)
222 return err;
223
224 return mv88e6xxx_g2_pvt_op(chip, src_dev, src_port,
225 GLOBAL2_PVT_ADDR_OP_WRITE_PVLAN);
226}
227
Vivien Didelotec561272016-09-02 14:45:33 -0400228/* Offset 0x0D: Switch MAC/WoL/WoF register */
229
230static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip,
231 unsigned int pointer, u8 data)
232{
233 u16 val = (pointer << 8) | data;
234
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400235 return mv88e6xxx_g2_update(chip, GLOBAL2_SWITCH_MAC, val);
Vivien Didelotec561272016-09-02 14:45:33 -0400236}
237
238int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
239{
240 int i, err;
241
242 for (i = 0; i < 6; i++) {
243 err = mv88e6xxx_g2_switch_mac_write(chip, i, addr[i]);
244 if (err)
245 break;
246 }
247
248 return err;
249}
250
251/* Offset 0x0F: Priority Override Table */
252
253static int mv88e6xxx_g2_pot_write(struct mv88e6xxx_chip *chip, int pointer,
254 u8 data)
255{
256 u16 val = (pointer << 8) | (data & 0x7);
257
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400258 return mv88e6xxx_g2_update(chip, GLOBAL2_PRIO_OVERRIDE, val);
Vivien Didelotec561272016-09-02 14:45:33 -0400259}
260
261static int mv88e6xxx_g2_clear_pot(struct mv88e6xxx_chip *chip)
262{
263 int i, err;
264
265 /* Clear all sixteen possible Priority Override entries */
266 for (i = 0; i < 16; i++) {
267 err = mv88e6xxx_g2_pot_write(chip, i, 0);
268 if (err)
269 break;
270 }
271
272 return err;
273}
274
275/* Offset 0x14: EEPROM Command
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500276 * Offset 0x15: EEPROM Data (for 16-bit data access)
277 * Offset 0x15: EEPROM Addr (for 8-bit data access)
Vivien Didelotec561272016-09-02 14:45:33 -0400278 */
279
280static int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip)
281{
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400282 return mv88e6xxx_g2_wait(chip, GLOBAL2_EEPROM_CMD,
283 GLOBAL2_EEPROM_CMD_BUSY |
284 GLOBAL2_EEPROM_CMD_RUNNING);
Vivien Didelotec561272016-09-02 14:45:33 -0400285}
286
287static int mv88e6xxx_g2_eeprom_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
288{
289 int err;
290
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400291 err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_CMD, cmd);
Vivien Didelotec561272016-09-02 14:45:33 -0400292 if (err)
293 return err;
294
295 return mv88e6xxx_g2_eeprom_wait(chip);
296}
297
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500298static int mv88e6xxx_g2_eeprom_read8(struct mv88e6xxx_chip *chip,
299 u16 addr, u8 *data)
300{
301 u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ;
302 int err;
303
304 err = mv88e6xxx_g2_eeprom_wait(chip);
305 if (err)
306 return err;
307
308 err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_ADDR, addr);
309 if (err)
310 return err;
311
312 err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
313 if (err)
314 return err;
315
316 err = mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_CMD, &cmd);
317 if (err)
318 return err;
319
320 *data = cmd & 0xff;
321
322 return 0;
323}
324
325static int mv88e6xxx_g2_eeprom_write8(struct mv88e6xxx_chip *chip,
326 u16 addr, u8 data)
327{
328 u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | GLOBAL2_EEPROM_CMD_WRITE_EN;
329 int err;
330
331 err = mv88e6xxx_g2_eeprom_wait(chip);
332 if (err)
333 return err;
334
335 err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_ADDR, addr);
336 if (err)
337 return err;
338
339 return mv88e6xxx_g2_eeprom_cmd(chip, cmd | data);
340}
341
Vivien Didelotec561272016-09-02 14:45:33 -0400342static int mv88e6xxx_g2_eeprom_read16(struct mv88e6xxx_chip *chip,
343 u8 addr, u16 *data)
344{
345 u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ | addr;
346 int err;
347
348 err = mv88e6xxx_g2_eeprom_wait(chip);
349 if (err)
350 return err;
351
352 err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
353 if (err)
354 return err;
355
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400356 return mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_DATA, data);
Vivien Didelotec561272016-09-02 14:45:33 -0400357}
358
359static int mv88e6xxx_g2_eeprom_write16(struct mv88e6xxx_chip *chip,
360 u8 addr, u16 data)
361{
362 u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | addr;
363 int err;
364
365 err = mv88e6xxx_g2_eeprom_wait(chip);
366 if (err)
367 return err;
368
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400369 err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_DATA, data);
Vivien Didelotec561272016-09-02 14:45:33 -0400370 if (err)
371 return err;
372
373 return mv88e6xxx_g2_eeprom_cmd(chip, cmd);
374}
375
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500376int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip,
377 struct ethtool_eeprom *eeprom, u8 *data)
378{
379 unsigned int offset = eeprom->offset;
380 unsigned int len = eeprom->len;
381 int err;
382
383 eeprom->len = 0;
384
385 while (len) {
386 err = mv88e6xxx_g2_eeprom_read8(chip, offset, data);
387 if (err)
388 return err;
389
390 eeprom->len++;
391 offset++;
392 data++;
393 len--;
394 }
395
396 return 0;
397}
398
399int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip,
400 struct ethtool_eeprom *eeprom, u8 *data)
401{
402 unsigned int offset = eeprom->offset;
403 unsigned int len = eeprom->len;
404 int err;
405
406 eeprom->len = 0;
407
408 while (len) {
409 err = mv88e6xxx_g2_eeprom_write8(chip, offset, *data);
410 if (err)
411 return err;
412
413 eeprom->len++;
414 offset++;
415 data++;
416 len--;
417 }
418
419 return 0;
420}
421
Vivien Didelotec561272016-09-02 14:45:33 -0400422int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip,
423 struct ethtool_eeprom *eeprom, u8 *data)
424{
425 unsigned int offset = eeprom->offset;
426 unsigned int len = eeprom->len;
427 u16 val;
428 int err;
429
430 eeprom->len = 0;
431
432 if (offset & 1) {
433 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
434 if (err)
435 return err;
436
437 *data++ = (val >> 8) & 0xff;
438
439 offset++;
440 len--;
441 eeprom->len++;
442 }
443
444 while (len >= 2) {
445 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
446 if (err)
447 return err;
448
449 *data++ = val & 0xff;
450 *data++ = (val >> 8) & 0xff;
451
452 offset += 2;
453 len -= 2;
454 eeprom->len += 2;
455 }
456
457 if (len) {
458 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
459 if (err)
460 return err;
461
462 *data++ = val & 0xff;
463
464 offset++;
465 len--;
466 eeprom->len++;
467 }
468
469 return 0;
470}
471
472int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip,
473 struct ethtool_eeprom *eeprom, u8 *data)
474{
475 unsigned int offset = eeprom->offset;
476 unsigned int len = eeprom->len;
477 u16 val;
478 int err;
479
480 /* Ensure the RO WriteEn bit is set */
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400481 err = mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_CMD, &val);
Vivien Didelotec561272016-09-02 14:45:33 -0400482 if (err)
483 return err;
484
485 if (!(val & GLOBAL2_EEPROM_CMD_WRITE_EN))
486 return -EROFS;
487
488 eeprom->len = 0;
489
490 if (offset & 1) {
491 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
492 if (err)
493 return err;
494
495 val = (*data++ << 8) | (val & 0xff);
496
497 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
498 if (err)
499 return err;
500
501 offset++;
502 len--;
503 eeprom->len++;
504 }
505
506 while (len >= 2) {
507 val = *data++;
508 val |= *data++ << 8;
509
510 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
511 if (err)
512 return err;
513
514 offset += 2;
515 len -= 2;
516 eeprom->len += 2;
517 }
518
519 if (len) {
520 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
521 if (err)
522 return err;
523
524 val = (val & 0xff00) | *data++;
525
526 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
527 if (err)
528 return err;
529
530 offset++;
531 len--;
532 eeprom->len++;
533 }
534
535 return 0;
536}
537
538/* Offset 0x18: SMI PHY Command Register
539 * Offset 0x19: SMI PHY Data Register
540 */
541
542static int mv88e6xxx_g2_smi_phy_wait(struct mv88e6xxx_chip *chip)
543{
Vivien Didelote289ef02017-06-19 10:55:37 -0400544 return mv88e6xxx_g2_wait(chip, MV88E6XXX_G2_SMI_PHY_CMD,
545 MV88E6XXX_G2_SMI_PHY_CMD_BUSY);
Vivien Didelotec561272016-09-02 14:45:33 -0400546}
547
548static int mv88e6xxx_g2_smi_phy_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
549{
550 int err;
551
Vivien Didelote289ef02017-06-19 10:55:37 -0400552 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_CMD,
553 MV88E6XXX_G2_SMI_PHY_CMD_BUSY | cmd);
Vivien Didelotec561272016-09-02 14:45:33 -0400554 if (err)
555 return err;
556
557 return mv88e6xxx_g2_smi_phy_wait(chip);
558}
559
Vivien Didelote289ef02017-06-19 10:55:37 -0400560static int mv88e6xxx_g2_smi_phy_access(struct mv88e6xxx_chip *chip,
561 bool external, bool c45, u16 op, int dev,
562 int reg)
Vivien Didelotec561272016-09-02 14:45:33 -0400563{
Vivien Didelote289ef02017-06-19 10:55:37 -0400564 u16 cmd = op;
Vivien Didelotec561272016-09-02 14:45:33 -0400565
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100566 if (external)
Vivien Didelote289ef02017-06-19 10:55:37 -0400567 cmd |= MV88E6390_G2_SMI_PHY_CMD_FUNC_EXTERNAL;
568 else
569 cmd |= MV88E6390_G2_SMI_PHY_CMD_FUNC_INTERNAL; /* empty mask */
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100570
Vivien Didelote289ef02017-06-19 10:55:37 -0400571 if (c45)
572 cmd |= MV88E6XXX_G2_SMI_PHY_CMD_MODE_45; /* empty mask */
573 else
574 cmd |= MV88E6XXX_G2_SMI_PHY_CMD_MODE_22;
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100575
Vivien Didelote289ef02017-06-19 10:55:37 -0400576 dev <<= __bf_shf(MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK);
577 cmd |= dev & MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK;
578 cmd |= reg & MV88E6XXX_G2_SMI_PHY_CMD_REG_ADDR_MASK;
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100579
580 return mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
581}
582
Vivien Didelote289ef02017-06-19 10:55:37 -0400583static int mv88e6xxx_g2_smi_phy_access_c22(struct mv88e6xxx_chip *chip,
584 bool external, u16 op, int dev,
585 int reg)
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100586{
Vivien Didelote289ef02017-06-19 10:55:37 -0400587 return mv88e6xxx_g2_smi_phy_access(chip, external, false, op, dev, reg);
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100588}
589
Vivien Didelote289ef02017-06-19 10:55:37 -0400590/* IEEE 802.3 Clause 22 Read Data Register */
591static int mv88e6xxx_g2_smi_phy_read_data_c22(struct mv88e6xxx_chip *chip,
592 bool external, int dev, int reg,
593 u16 *data)
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100594{
Vivien Didelote289ef02017-06-19 10:55:37 -0400595 u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_22_READ_DATA;
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100596 int err;
597
Vivien Didelotec561272016-09-02 14:45:33 -0400598 err = mv88e6xxx_g2_smi_phy_wait(chip);
599 if (err)
600 return err;
601
Vivien Didelote289ef02017-06-19 10:55:37 -0400602 err = mv88e6xxx_g2_smi_phy_access_c22(chip, external, op, dev, reg);
Vivien Didelotec561272016-09-02 14:45:33 -0400603 if (err)
604 return err;
605
Vivien Didelote289ef02017-06-19 10:55:37 -0400606 return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
Vivien Didelotec561272016-09-02 14:45:33 -0400607}
608
Vivien Didelote289ef02017-06-19 10:55:37 -0400609/* IEEE 802.3 Clause 22 Write Data Register */
610static int mv88e6xxx_g2_smi_phy_write_data_c22(struct mv88e6xxx_chip *chip,
611 bool external, int dev, int reg,
612 u16 data)
613{
614 u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_22_WRITE_DATA;
615 int err;
616
617 err = mv88e6xxx_g2_smi_phy_wait(chip);
618 if (err)
619 return err;
620
621 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
622 if (err)
623 return err;
624
625 return mv88e6xxx_g2_smi_phy_access_c22(chip, external, op, dev, reg);
626}
627
628static int mv88e6xxx_g2_smi_phy_access_c45(struct mv88e6xxx_chip *chip,
629 bool external, u16 op, int port,
630 int dev)
631{
632 return mv88e6xxx_g2_smi_phy_access(chip, external, true, op, port, dev);
633}
634
635/* IEEE 802.3 Clause 45 Write Address Register */
636static int mv88e6xxx_g2_smi_phy_write_addr_c45(struct mv88e6xxx_chip *chip,
637 bool external, int port, int dev,
638 int addr)
639{
640 u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_ADDR;
641 int err;
642
643 err = mv88e6xxx_g2_smi_phy_wait(chip);
644 if (err)
645 return err;
646
647 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, addr);
648 if (err)
649 return err;
650
651 return mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev);
652}
653
654/* IEEE 802.3 Clause 45 Read Data Register */
655static int mv88e6xxx_g2_smi_phy_read_data_c45(struct mv88e6xxx_chip *chip,
656 bool external, int port, int dev,
657 u16 *data)
658{
659 u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_READ_DATA;
660 int err;
661
662 err = mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev);
663 if (err)
664 return err;
665
666 return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
667}
668
669static int mv88e6xxx_g2_smi_phy_read_c45(struct mv88e6xxx_chip *chip,
670 bool external, int port, int reg,
671 u16 *data)
672{
673 int dev = (reg >> 16) & 0x1f;
674 int addr = reg & 0xffff;
675 int err;
676
677 err = mv88e6xxx_g2_smi_phy_write_addr_c45(chip, external, port, dev,
678 addr);
679 if (err)
680 return err;
681
682 return mv88e6xxx_g2_smi_phy_read_data_c45(chip, external, port, dev,
683 data);
684}
685
686/* IEEE 802.3 Clause 45 Write Data Register */
687static int mv88e6xxx_g2_smi_phy_write_data_c45(struct mv88e6xxx_chip *chip,
688 bool external, int port, int dev,
689 u16 data)
690{
691 u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_DATA;
692 int err;
693
694 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
695 if (err)
696 return err;
697
698 return mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev);
699}
700
701static int mv88e6xxx_g2_smi_phy_write_c45(struct mv88e6xxx_chip *chip,
702 bool external, int port, int reg,
703 u16 data)
704{
705 int dev = (reg >> 16) & 0x1f;
706 int addr = reg & 0xffff;
707 int err;
708
709 err = mv88e6xxx_g2_smi_phy_write_addr_c45(chip, external, port, dev,
710 addr);
711 if (err)
712 return err;
713
714 return mv88e6xxx_g2_smi_phy_write_data_c45(chip, external, port, dev,
715 data);
716}
717
718int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100719 int addr, int reg, u16 *val)
720{
721 struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
722 bool external = mdio_bus->external;
723
724 if (reg & MII_ADDR_C45)
Vivien Didelote289ef02017-06-19 10:55:37 -0400725 return mv88e6xxx_g2_smi_phy_read_c45(chip, external, addr, reg,
726 val);
727
728 return mv88e6xxx_g2_smi_phy_read_data_c22(chip, external, addr, reg,
729 val);
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100730}
731
Vivien Didelote289ef02017-06-19 10:55:37 -0400732int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100733 int addr, int reg, u16 val)
734{
735 struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
736 bool external = mdio_bus->external;
737
738 if (reg & MII_ADDR_C45)
Vivien Didelote289ef02017-06-19 10:55:37 -0400739 return mv88e6xxx_g2_smi_phy_write_c45(chip, external, addr, reg,
740 val);
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100741
Vivien Didelote289ef02017-06-19 10:55:37 -0400742 return mv88e6xxx_g2_smi_phy_write_data_c22(chip, external, addr, reg,
743 val);
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100744}
745
Andrew Lunnfcd25162017-02-09 00:03:42 +0100746static int mv88e6097_watchdog_action(struct mv88e6xxx_chip *chip, int irq)
747{
748 u16 reg;
749
750 mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
751
752 dev_info(chip->dev, "Watchdog event: 0x%04x", reg);
753
754 return IRQ_HANDLED;
755}
756
757static void mv88e6097_watchdog_free(struct mv88e6xxx_chip *chip)
758{
759 u16 reg;
760
761 mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
762
763 reg &= ~(GLOBAL2_WDOG_CONTROL_EGRESS_ENABLE |
764 GLOBAL2_WDOG_CONTROL_QC_ENABLE);
765
766 mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, reg);
767}
768
769static int mv88e6097_watchdog_setup(struct mv88e6xxx_chip *chip)
770{
771 return mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL,
772 GLOBAL2_WDOG_CONTROL_EGRESS_ENABLE |
773 GLOBAL2_WDOG_CONTROL_QC_ENABLE |
774 GLOBAL2_WDOG_CONTROL_SWRESET);
775}
776
777const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = {
778 .irq_action = mv88e6097_watchdog_action,
779 .irq_setup = mv88e6097_watchdog_setup,
780 .irq_free = mv88e6097_watchdog_free,
781};
782
Andrew Lunn61303732017-02-09 00:03:43 +0100783static int mv88e6390_watchdog_setup(struct mv88e6xxx_chip *chip)
784{
785 return mv88e6xxx_g2_update(chip, GLOBAL2_WDOG_CONTROL,
786 GLOBAL2_WDOG_INT_ENABLE |
787 GLOBAL2_WDOG_CUT_THROUGH |
788 GLOBAL2_WDOG_QUEUE_CONTROLLER |
789 GLOBAL2_WDOG_EGRESS |
790 GLOBAL2_WDOG_FORCE_IRQ);
791}
792
793static int mv88e6390_watchdog_action(struct mv88e6xxx_chip *chip, int irq)
794{
795 int err;
796 u16 reg;
797
798 mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, GLOBAL2_WDOG_EVENT);
799 err = mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
800
801 dev_info(chip->dev, "Watchdog event: 0x%04x",
802 reg & GLOBAL2_WDOG_DATA_MASK);
803
804 mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, GLOBAL2_WDOG_HISTORY);
805 err = mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
806
807 dev_info(chip->dev, "Watchdog history: 0x%04x",
808 reg & GLOBAL2_WDOG_DATA_MASK);
809
810 /* Trigger a software reset to try to recover the switch */
811 if (chip->info->ops->reset)
812 chip->info->ops->reset(chip);
813
814 mv88e6390_watchdog_setup(chip);
815
816 return IRQ_HANDLED;
817}
818
819static void mv88e6390_watchdog_free(struct mv88e6xxx_chip *chip)
820{
821 mv88e6xxx_g2_update(chip, GLOBAL2_WDOG_CONTROL,
822 GLOBAL2_WDOG_INT_ENABLE);
823}
824
825const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = {
826 .irq_action = mv88e6390_watchdog_action,
827 .irq_setup = mv88e6390_watchdog_setup,
828 .irq_free = mv88e6390_watchdog_free,
829};
830
Andrew Lunnfcd25162017-02-09 00:03:42 +0100831static irqreturn_t mv88e6xxx_g2_watchdog_thread_fn(int irq, void *dev_id)
832{
833 struct mv88e6xxx_chip *chip = dev_id;
834 irqreturn_t ret = IRQ_NONE;
835
836 mutex_lock(&chip->reg_lock);
837 if (chip->info->ops->watchdog_ops->irq_action)
838 ret = chip->info->ops->watchdog_ops->irq_action(chip, irq);
839 mutex_unlock(&chip->reg_lock);
840
841 return ret;
842}
843
844static void mv88e6xxx_g2_watchdog_free(struct mv88e6xxx_chip *chip)
845{
846 mutex_lock(&chip->reg_lock);
847 if (chip->info->ops->watchdog_ops->irq_free)
848 chip->info->ops->watchdog_ops->irq_free(chip);
849 mutex_unlock(&chip->reg_lock);
850
851 free_irq(chip->watchdog_irq, chip);
852 irq_dispose_mapping(chip->watchdog_irq);
853}
854
855static int mv88e6xxx_g2_watchdog_setup(struct mv88e6xxx_chip *chip)
856{
857 int err;
858
859 chip->watchdog_irq = irq_find_mapping(chip->g2_irq.domain,
860 GLOBAL2_INT_SOURCE_WATCHDOG);
861 if (chip->watchdog_irq < 0)
862 return chip->watchdog_irq;
863
864 err = request_threaded_irq(chip->watchdog_irq, NULL,
865 mv88e6xxx_g2_watchdog_thread_fn,
866 IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
867 "mv88e6xxx-watchdog", chip);
868 if (err)
869 return err;
870
871 mutex_lock(&chip->reg_lock);
872 if (chip->info->ops->watchdog_ops->irq_setup)
873 err = chip->info->ops->watchdog_ops->irq_setup(chip);
874 mutex_unlock(&chip->reg_lock);
875
876 return err;
877}
878
Vivien Didelot81228992017-03-30 17:37:08 -0400879/* Offset 0x1D: Misc Register */
880
881static int mv88e6xxx_g2_misc_5_bit_port(struct mv88e6xxx_chip *chip,
882 bool port_5_bit)
883{
884 u16 val;
885 int err;
886
887 err = mv88e6xxx_g2_read(chip, GLOBAL2_MISC, &val);
888 if (err)
889 return err;
890
891 if (port_5_bit)
892 val |= GLOBAL2_MISC_5_BIT_PORT;
893 else
894 val &= ~GLOBAL2_MISC_5_BIT_PORT;
895
896 return mv88e6xxx_g2_write(chip, GLOBAL2_MISC, val);
897}
898
899int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip)
900{
901 return mv88e6xxx_g2_misc_5_bit_port(chip, false);
902}
903
Andrew Lunndc30c352016-10-16 19:56:49 +0200904static void mv88e6xxx_g2_irq_mask(struct irq_data *d)
905{
906 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
907 unsigned int n = d->hwirq;
908
909 chip->g2_irq.masked |= (1 << n);
910}
911
912static void mv88e6xxx_g2_irq_unmask(struct irq_data *d)
913{
914 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
915 unsigned int n = d->hwirq;
916
917 chip->g2_irq.masked &= ~(1 << n);
918}
919
920static irqreturn_t mv88e6xxx_g2_irq_thread_fn(int irq, void *dev_id)
921{
922 struct mv88e6xxx_chip *chip = dev_id;
923 unsigned int nhandled = 0;
924 unsigned int sub_irq;
925 unsigned int n;
926 int err;
927 u16 reg;
928
929 mutex_lock(&chip->reg_lock);
930 err = mv88e6xxx_g2_read(chip, GLOBAL2_INT_SOURCE, &reg);
931 mutex_unlock(&chip->reg_lock);
932 if (err)
933 goto out;
934
935 for (n = 0; n < 16; ++n) {
936 if (reg & (1 << n)) {
937 sub_irq = irq_find_mapping(chip->g2_irq.domain, n);
938 handle_nested_irq(sub_irq);
939 ++nhandled;
940 }
941 }
942out:
943 return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
944}
945
946static void mv88e6xxx_g2_irq_bus_lock(struct irq_data *d)
947{
948 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
949
950 mutex_lock(&chip->reg_lock);
951}
952
953static void mv88e6xxx_g2_irq_bus_sync_unlock(struct irq_data *d)
954{
955 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
956
957 mv88e6xxx_g2_write(chip, GLOBAL2_INT_MASK, ~chip->g2_irq.masked);
958
959 mutex_unlock(&chip->reg_lock);
960}
961
962static struct irq_chip mv88e6xxx_g2_irq_chip = {
963 .name = "mv88e6xxx-g2",
964 .irq_mask = mv88e6xxx_g2_irq_mask,
965 .irq_unmask = mv88e6xxx_g2_irq_unmask,
966 .irq_bus_lock = mv88e6xxx_g2_irq_bus_lock,
967 .irq_bus_sync_unlock = mv88e6xxx_g2_irq_bus_sync_unlock,
968};
969
970static int mv88e6xxx_g2_irq_domain_map(struct irq_domain *d,
971 unsigned int irq,
972 irq_hw_number_t hwirq)
973{
974 struct mv88e6xxx_chip *chip = d->host_data;
975
976 irq_set_chip_data(irq, d->host_data);
977 irq_set_chip_and_handler(irq, &chip->g2_irq.chip, handle_level_irq);
978 irq_set_noprobe(irq);
979
980 return 0;
981}
982
983static const struct irq_domain_ops mv88e6xxx_g2_irq_domain_ops = {
984 .map = mv88e6xxx_g2_irq_domain_map,
985 .xlate = irq_domain_xlate_twocell,
986};
987
988void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip)
989{
990 int irq, virq;
991
Andrew Lunnfcd25162017-02-09 00:03:42 +0100992 mv88e6xxx_g2_watchdog_free(chip);
993
Andrew Lunn8e757eb2016-11-20 20:14:18 +0100994 free_irq(chip->device_irq, chip);
995 irq_dispose_mapping(chip->device_irq);
996
Andrew Lunndc30c352016-10-16 19:56:49 +0200997 for (irq = 0; irq < 16; irq++) {
998 virq = irq_find_mapping(chip->g2_irq.domain, irq);
999 irq_dispose_mapping(virq);
1000 }
1001
1002 irq_domain_remove(chip->g2_irq.domain);
1003}
1004
1005int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip)
1006{
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001007 int err, irq, virq;
Andrew Lunndc30c352016-10-16 19:56:49 +02001008
1009 if (!chip->dev->of_node)
1010 return -EINVAL;
1011
1012 chip->g2_irq.domain = irq_domain_add_simple(
1013 chip->dev->of_node, 16, 0, &mv88e6xxx_g2_irq_domain_ops, chip);
1014 if (!chip->g2_irq.domain)
1015 return -ENOMEM;
1016
1017 for (irq = 0; irq < 16; irq++)
1018 irq_create_mapping(chip->g2_irq.domain, irq);
1019
1020 chip->g2_irq.chip = mv88e6xxx_g2_irq_chip;
1021 chip->g2_irq.masked = ~0;
1022
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001023 chip->device_irq = irq_find_mapping(chip->g1_irq.domain,
Vivien Didelot82466922017-06-15 12:13:59 -04001024 MV88E6XXX_G1_STS_IRQ_DEVICE);
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001025 if (chip->device_irq < 0) {
1026 err = chip->device_irq;
Andrew Lunndc30c352016-10-16 19:56:49 +02001027 goto out;
1028 }
1029
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001030 err = request_threaded_irq(chip->device_irq, NULL,
1031 mv88e6xxx_g2_irq_thread_fn,
1032 IRQF_ONESHOT, "mv88e6xxx-g1", chip);
Andrew Lunndc30c352016-10-16 19:56:49 +02001033 if (err)
1034 goto out;
1035
Andrew Lunnfcd25162017-02-09 00:03:42 +01001036 return mv88e6xxx_g2_watchdog_setup(chip);
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001037
Andrew Lunndc30c352016-10-16 19:56:49 +02001038out:
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001039 for (irq = 0; irq < 16; irq++) {
1040 virq = irq_find_mapping(chip->g2_irq.domain, irq);
1041 irq_dispose_mapping(virq);
1042 }
1043
1044 irq_domain_remove(chip->g2_irq.domain);
Andrew Lunndc30c352016-10-16 19:56:49 +02001045
1046 return err;
1047}
1048
Vivien Didelotec561272016-09-02 14:45:33 -04001049int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip)
1050{
1051 u16 reg;
1052 int err;
1053
Vivien Didelotec561272016-09-02 14:45:33 -04001054 /* Ignore removed tag data on doubly tagged packets, disable
1055 * flow control messages, force flow control priority to the
1056 * highest, and send all special multicast frames to the CPU
1057 * port at the highest priority.
1058 */
1059 reg = GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI | (0x7 << 4);
1060 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X) ||
1061 mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X))
1062 reg |= GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x7;
Vivien Didelot9fe850f2016-09-29 12:21:54 -04001063 err = mv88e6xxx_g2_write(chip, GLOBAL2_SWITCH_MGMT, reg);
Vivien Didelotec561272016-09-02 14:45:33 -04001064 if (err)
1065 return err;
1066
1067 /* Program the DSA routing table. */
1068 err = mv88e6xxx_g2_set_device_mapping(chip);
1069 if (err)
1070 return err;
1071
1072 /* Clear all trunk masks and mapping. */
1073 err = mv88e6xxx_g2_clear_trunk(chip);
1074 if (err)
1075 return err;
1076
Vivien Didelotec561272016-09-02 14:45:33 -04001077 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_POT)) {
1078 /* Clear the priority override table. */
1079 err = mv88e6xxx_g2_clear_pot(chip);
1080 if (err)
1081 return err;
1082 }
1083
1084 return 0;
1085}