blob: f20a60af35cb8234e014b3daa61391b1dcb2488b [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,
105 bool hask, u16 mask)
106{
Vivien Didelot370b4ff2016-09-29 12:21:57 -0400107 const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
Vivien Didelotec561272016-09-02 14:45:33 -0400108 u16 val = (num << 12) | (mask & port_mask);
109
110 if (hask)
111 val |= GLOBAL2_TRUNK_MASK_HASK;
112
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400113 return mv88e6xxx_g2_update(chip, GLOBAL2_TRUNK_MASK, val);
Vivien Didelotec561272016-09-02 14:45:33 -0400114}
115
116/* Offset 0x08: Trunk Mapping Table register */
117
118static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id,
119 u16 map)
120{
Vivien Didelot370b4ff2016-09-29 12:21:57 -0400121 const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
Vivien Didelotec561272016-09-02 14:45:33 -0400122 u16 val = (id << 11) | (map & port_mask);
123
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400124 return mv88e6xxx_g2_update(chip, GLOBAL2_TRUNK_MAPPING, val);
Vivien Didelotec561272016-09-02 14:45:33 -0400125}
126
127static int mv88e6xxx_g2_clear_trunk(struct mv88e6xxx_chip *chip)
128{
Vivien Didelot370b4ff2016-09-29 12:21:57 -0400129 const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
Vivien Didelotec561272016-09-02 14:45:33 -0400130 int i, err;
131
132 /* Clear all eight possible Trunk Mask vectors */
133 for (i = 0; i < 8; ++i) {
134 err = mv88e6xxx_g2_trunk_mask_write(chip, i, false, port_mask);
135 if (err)
136 return err;
137 }
138
139 /* Clear all sixteen possible Trunk ID routing vectors */
140 for (i = 0; i < 16; ++i) {
141 err = mv88e6xxx_g2_trunk_mapping_write(chip, i, 0);
142 if (err)
143 return err;
144 }
145
146 return 0;
147}
148
149/* Offset 0x09: Ingress Rate Command register
150 * Offset 0x0A: Ingress Rate Data register
151 */
152
Vivien Didelotcd8da8b2017-06-19 10:55:36 -0400153static int mv88e6xxx_g2_irl_wait(struct mv88e6xxx_chip *chip)
Vivien Didelotec561272016-09-02 14:45:33 -0400154{
Vivien Didelotcd8da8b2017-06-19 10:55:36 -0400155 return mv88e6xxx_g2_wait(chip, MV88E6XXX_G2_IRL_CMD,
156 MV88E6XXX_G2_IRL_CMD_BUSY);
157}
Vivien Didelotec561272016-09-02 14:45:33 -0400158
Vivien Didelotcd8da8b2017-06-19 10:55:36 -0400159static int mv88e6xxx_g2_irl_op(struct mv88e6xxx_chip *chip, u16 op, int port,
160 int res, int reg)
161{
162 int err;
Vivien Didelotec561272016-09-02 14:45:33 -0400163
Vivien Didelotcd8da8b2017-06-19 10:55:36 -0400164 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_IRL_CMD,
165 MV88E6XXX_G2_IRL_CMD_BUSY | op | (port << 8) |
166 (res << 5) | reg);
167 if (err)
168 return err;
Vivien Didelotec561272016-09-02 14:45:33 -0400169
Vivien Didelotcd8da8b2017-06-19 10:55:36 -0400170 return mv88e6xxx_g2_irl_wait(chip);
171}
172
173int mv88e6352_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port)
174{
175 return mv88e6xxx_g2_irl_op(chip, MV88E6352_G2_IRL_CMD_OP_INIT_ALL, port,
176 0, 0);
177}
178
179int mv88e6390_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port)
180{
181 return mv88e6xxx_g2_irl_op(chip, MV88E6390_G2_IRL_CMD_OP_INIT_ALL, port,
182 0, 0);
Vivien Didelotec561272016-09-02 14:45:33 -0400183}
184
Vivien Didelot17a15942017-03-30 17:37:09 -0400185/* Offset 0x0B: Cross-chip Port VLAN (Addr) Register
186 * Offset 0x0C: Cross-chip Port VLAN Data Register
187 */
188
189static int mv88e6xxx_g2_pvt_op_wait(struct mv88e6xxx_chip *chip)
190{
191 return mv88e6xxx_g2_wait(chip, GLOBAL2_PVT_ADDR, GLOBAL2_PVT_ADDR_BUSY);
192}
193
194static int mv88e6xxx_g2_pvt_op(struct mv88e6xxx_chip *chip, int src_dev,
195 int src_port, u16 op)
196{
197 int err;
198
199 /* 9-bit Cross-chip PVT pointer: with GLOBAL2_MISC_5_BIT_PORT cleared,
200 * source device is 5-bit, source port is 4-bit.
201 */
202 op |= (src_dev & 0x1f) << 4;
203 op |= (src_port & 0xf);
204
205 err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_ADDR, op);
206 if (err)
207 return err;
208
209 return mv88e6xxx_g2_pvt_op_wait(chip);
210}
211
212int mv88e6xxx_g2_pvt_write(struct mv88e6xxx_chip *chip, int src_dev,
213 int src_port, u16 data)
214{
215 int err;
216
217 err = mv88e6xxx_g2_pvt_op_wait(chip);
218 if (err)
219 return err;
220
221 err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_DATA, data);
222 if (err)
223 return err;
224
225 return mv88e6xxx_g2_pvt_op(chip, src_dev, src_port,
226 GLOBAL2_PVT_ADDR_OP_WRITE_PVLAN);
227}
228
Vivien Didelotec561272016-09-02 14:45:33 -0400229/* Offset 0x0D: Switch MAC/WoL/WoF register */
230
231static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip,
232 unsigned int pointer, u8 data)
233{
234 u16 val = (pointer << 8) | data;
235
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400236 return mv88e6xxx_g2_update(chip, GLOBAL2_SWITCH_MAC, val);
Vivien Didelotec561272016-09-02 14:45:33 -0400237}
238
239int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
240{
241 int i, err;
242
243 for (i = 0; i < 6; i++) {
244 err = mv88e6xxx_g2_switch_mac_write(chip, i, addr[i]);
245 if (err)
246 break;
247 }
248
249 return err;
250}
251
252/* Offset 0x0F: Priority Override Table */
253
254static int mv88e6xxx_g2_pot_write(struct mv88e6xxx_chip *chip, int pointer,
255 u8 data)
256{
257 u16 val = (pointer << 8) | (data & 0x7);
258
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400259 return mv88e6xxx_g2_update(chip, GLOBAL2_PRIO_OVERRIDE, val);
Vivien Didelotec561272016-09-02 14:45:33 -0400260}
261
262static int mv88e6xxx_g2_clear_pot(struct mv88e6xxx_chip *chip)
263{
264 int i, err;
265
266 /* Clear all sixteen possible Priority Override entries */
267 for (i = 0; i < 16; i++) {
268 err = mv88e6xxx_g2_pot_write(chip, i, 0);
269 if (err)
270 break;
271 }
272
273 return err;
274}
275
276/* Offset 0x14: EEPROM Command
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500277 * Offset 0x15: EEPROM Data (for 16-bit data access)
278 * Offset 0x15: EEPROM Addr (for 8-bit data access)
Vivien Didelotec561272016-09-02 14:45:33 -0400279 */
280
281static int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip)
282{
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400283 return mv88e6xxx_g2_wait(chip, GLOBAL2_EEPROM_CMD,
284 GLOBAL2_EEPROM_CMD_BUSY |
285 GLOBAL2_EEPROM_CMD_RUNNING);
Vivien Didelotec561272016-09-02 14:45:33 -0400286}
287
288static int mv88e6xxx_g2_eeprom_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
289{
290 int err;
291
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400292 err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_CMD, cmd);
Vivien Didelotec561272016-09-02 14:45:33 -0400293 if (err)
294 return err;
295
296 return mv88e6xxx_g2_eeprom_wait(chip);
297}
298
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500299static int mv88e6xxx_g2_eeprom_read8(struct mv88e6xxx_chip *chip,
300 u16 addr, u8 *data)
301{
302 u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ;
303 int err;
304
305 err = mv88e6xxx_g2_eeprom_wait(chip);
306 if (err)
307 return err;
308
309 err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_ADDR, addr);
310 if (err)
311 return err;
312
313 err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
314 if (err)
315 return err;
316
317 err = mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_CMD, &cmd);
318 if (err)
319 return err;
320
321 *data = cmd & 0xff;
322
323 return 0;
324}
325
326static int mv88e6xxx_g2_eeprom_write8(struct mv88e6xxx_chip *chip,
327 u16 addr, u8 data)
328{
329 u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | GLOBAL2_EEPROM_CMD_WRITE_EN;
330 int err;
331
332 err = mv88e6xxx_g2_eeprom_wait(chip);
333 if (err)
334 return err;
335
336 err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_ADDR, addr);
337 if (err)
338 return err;
339
340 return mv88e6xxx_g2_eeprom_cmd(chip, cmd | data);
341}
342
Vivien Didelotec561272016-09-02 14:45:33 -0400343static int mv88e6xxx_g2_eeprom_read16(struct mv88e6xxx_chip *chip,
344 u8 addr, u16 *data)
345{
346 u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ | addr;
347 int err;
348
349 err = mv88e6xxx_g2_eeprom_wait(chip);
350 if (err)
351 return err;
352
353 err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
354 if (err)
355 return err;
356
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400357 return mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_DATA, data);
Vivien Didelotec561272016-09-02 14:45:33 -0400358}
359
360static int mv88e6xxx_g2_eeprom_write16(struct mv88e6xxx_chip *chip,
361 u8 addr, u16 data)
362{
363 u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | addr;
364 int err;
365
366 err = mv88e6xxx_g2_eeprom_wait(chip);
367 if (err)
368 return err;
369
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400370 err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_DATA, data);
Vivien Didelotec561272016-09-02 14:45:33 -0400371 if (err)
372 return err;
373
374 return mv88e6xxx_g2_eeprom_cmd(chip, cmd);
375}
376
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500377int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip,
378 struct ethtool_eeprom *eeprom, u8 *data)
379{
380 unsigned int offset = eeprom->offset;
381 unsigned int len = eeprom->len;
382 int err;
383
384 eeprom->len = 0;
385
386 while (len) {
387 err = mv88e6xxx_g2_eeprom_read8(chip, offset, data);
388 if (err)
389 return err;
390
391 eeprom->len++;
392 offset++;
393 data++;
394 len--;
395 }
396
397 return 0;
398}
399
400int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip,
401 struct ethtool_eeprom *eeprom, u8 *data)
402{
403 unsigned int offset = eeprom->offset;
404 unsigned int len = eeprom->len;
405 int err;
406
407 eeprom->len = 0;
408
409 while (len) {
410 err = mv88e6xxx_g2_eeprom_write8(chip, offset, *data);
411 if (err)
412 return err;
413
414 eeprom->len++;
415 offset++;
416 data++;
417 len--;
418 }
419
420 return 0;
421}
422
Vivien Didelotec561272016-09-02 14:45:33 -0400423int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip,
424 struct ethtool_eeprom *eeprom, u8 *data)
425{
426 unsigned int offset = eeprom->offset;
427 unsigned int len = eeprom->len;
428 u16 val;
429 int err;
430
431 eeprom->len = 0;
432
433 if (offset & 1) {
434 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
435 if (err)
436 return err;
437
438 *data++ = (val >> 8) & 0xff;
439
440 offset++;
441 len--;
442 eeprom->len++;
443 }
444
445 while (len >= 2) {
446 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
447 if (err)
448 return err;
449
450 *data++ = val & 0xff;
451 *data++ = (val >> 8) & 0xff;
452
453 offset += 2;
454 len -= 2;
455 eeprom->len += 2;
456 }
457
458 if (len) {
459 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
460 if (err)
461 return err;
462
463 *data++ = val & 0xff;
464
465 offset++;
466 len--;
467 eeprom->len++;
468 }
469
470 return 0;
471}
472
473int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip,
474 struct ethtool_eeprom *eeprom, u8 *data)
475{
476 unsigned int offset = eeprom->offset;
477 unsigned int len = eeprom->len;
478 u16 val;
479 int err;
480
481 /* Ensure the RO WriteEn bit is set */
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400482 err = mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_CMD, &val);
Vivien Didelotec561272016-09-02 14:45:33 -0400483 if (err)
484 return err;
485
486 if (!(val & GLOBAL2_EEPROM_CMD_WRITE_EN))
487 return -EROFS;
488
489 eeprom->len = 0;
490
491 if (offset & 1) {
492 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
493 if (err)
494 return err;
495
496 val = (*data++ << 8) | (val & 0xff);
497
498 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
499 if (err)
500 return err;
501
502 offset++;
503 len--;
504 eeprom->len++;
505 }
506
507 while (len >= 2) {
508 val = *data++;
509 val |= *data++ << 8;
510
511 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
512 if (err)
513 return err;
514
515 offset += 2;
516 len -= 2;
517 eeprom->len += 2;
518 }
519
520 if (len) {
521 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
522 if (err)
523 return err;
524
525 val = (val & 0xff00) | *data++;
526
527 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
528 if (err)
529 return err;
530
531 offset++;
532 len--;
533 eeprom->len++;
534 }
535
536 return 0;
537}
538
539/* Offset 0x18: SMI PHY Command Register
540 * Offset 0x19: SMI PHY Data Register
541 */
542
543static int mv88e6xxx_g2_smi_phy_wait(struct mv88e6xxx_chip *chip)
544{
Vivien Didelote289ef02017-06-19 10:55:37 -0400545 return mv88e6xxx_g2_wait(chip, MV88E6XXX_G2_SMI_PHY_CMD,
546 MV88E6XXX_G2_SMI_PHY_CMD_BUSY);
Vivien Didelotec561272016-09-02 14:45:33 -0400547}
548
549static int mv88e6xxx_g2_smi_phy_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
550{
551 int err;
552
Vivien Didelote289ef02017-06-19 10:55:37 -0400553 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_CMD,
554 MV88E6XXX_G2_SMI_PHY_CMD_BUSY | cmd);
Vivien Didelotec561272016-09-02 14:45:33 -0400555 if (err)
556 return err;
557
558 return mv88e6xxx_g2_smi_phy_wait(chip);
559}
560
Vivien Didelote289ef02017-06-19 10:55:37 -0400561static int mv88e6xxx_g2_smi_phy_access(struct mv88e6xxx_chip *chip,
562 bool external, bool c45, u16 op, int dev,
563 int reg)
Vivien Didelotec561272016-09-02 14:45:33 -0400564{
Vivien Didelote289ef02017-06-19 10:55:37 -0400565 u16 cmd = op;
Vivien Didelotec561272016-09-02 14:45:33 -0400566
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100567 if (external)
Vivien Didelote289ef02017-06-19 10:55:37 -0400568 cmd |= MV88E6390_G2_SMI_PHY_CMD_FUNC_EXTERNAL;
569 else
570 cmd |= MV88E6390_G2_SMI_PHY_CMD_FUNC_INTERNAL; /* empty mask */
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100571
Vivien Didelote289ef02017-06-19 10:55:37 -0400572 if (c45)
573 cmd |= MV88E6XXX_G2_SMI_PHY_CMD_MODE_45; /* empty mask */
574 else
575 cmd |= MV88E6XXX_G2_SMI_PHY_CMD_MODE_22;
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100576
Vivien Didelote289ef02017-06-19 10:55:37 -0400577 dev <<= __bf_shf(MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK);
578 cmd |= dev & MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK;
579 cmd |= reg & MV88E6XXX_G2_SMI_PHY_CMD_REG_ADDR_MASK;
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100580
581 return mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
582}
583
Vivien Didelote289ef02017-06-19 10:55:37 -0400584static int mv88e6xxx_g2_smi_phy_access_c22(struct mv88e6xxx_chip *chip,
585 bool external, u16 op, int dev,
586 int reg)
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100587{
Vivien Didelote289ef02017-06-19 10:55:37 -0400588 return mv88e6xxx_g2_smi_phy_access(chip, external, false, op, dev, reg);
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100589}
590
Vivien Didelote289ef02017-06-19 10:55:37 -0400591/* IEEE 802.3 Clause 22 Read Data Register */
592static int mv88e6xxx_g2_smi_phy_read_data_c22(struct mv88e6xxx_chip *chip,
593 bool external, int dev, int reg,
594 u16 *data)
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100595{
Vivien Didelote289ef02017-06-19 10:55:37 -0400596 u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_22_READ_DATA;
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100597 int err;
598
Vivien Didelotec561272016-09-02 14:45:33 -0400599 err = mv88e6xxx_g2_smi_phy_wait(chip);
600 if (err)
601 return err;
602
Vivien Didelote289ef02017-06-19 10:55:37 -0400603 err = mv88e6xxx_g2_smi_phy_access_c22(chip, external, op, dev, reg);
Vivien Didelotec561272016-09-02 14:45:33 -0400604 if (err)
605 return err;
606
Vivien Didelote289ef02017-06-19 10:55:37 -0400607 return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
Vivien Didelotec561272016-09-02 14:45:33 -0400608}
609
Vivien Didelote289ef02017-06-19 10:55:37 -0400610/* IEEE 802.3 Clause 22 Write Data Register */
611static int mv88e6xxx_g2_smi_phy_write_data_c22(struct mv88e6xxx_chip *chip,
612 bool external, int dev, int reg,
613 u16 data)
614{
615 u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_22_WRITE_DATA;
616 int err;
617
618 err = mv88e6xxx_g2_smi_phy_wait(chip);
619 if (err)
620 return err;
621
622 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
623 if (err)
624 return err;
625
626 return mv88e6xxx_g2_smi_phy_access_c22(chip, external, op, dev, reg);
627}
628
629static int mv88e6xxx_g2_smi_phy_access_c45(struct mv88e6xxx_chip *chip,
630 bool external, u16 op, int port,
631 int dev)
632{
633 return mv88e6xxx_g2_smi_phy_access(chip, external, true, op, port, dev);
634}
635
636/* IEEE 802.3 Clause 45 Write Address Register */
637static int mv88e6xxx_g2_smi_phy_write_addr_c45(struct mv88e6xxx_chip *chip,
638 bool external, int port, int dev,
639 int addr)
640{
641 u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_ADDR;
642 int err;
643
644 err = mv88e6xxx_g2_smi_phy_wait(chip);
645 if (err)
646 return err;
647
648 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, addr);
649 if (err)
650 return err;
651
652 return mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev);
653}
654
655/* IEEE 802.3 Clause 45 Read Data Register */
656static int mv88e6xxx_g2_smi_phy_read_data_c45(struct mv88e6xxx_chip *chip,
657 bool external, int port, int dev,
658 u16 *data)
659{
660 u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_READ_DATA;
661 int err;
662
663 err = mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev);
664 if (err)
665 return err;
666
667 return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
668}
669
670static int mv88e6xxx_g2_smi_phy_read_c45(struct mv88e6xxx_chip *chip,
671 bool external, int port, int reg,
672 u16 *data)
673{
674 int dev = (reg >> 16) & 0x1f;
675 int addr = reg & 0xffff;
676 int err;
677
678 err = mv88e6xxx_g2_smi_phy_write_addr_c45(chip, external, port, dev,
679 addr);
680 if (err)
681 return err;
682
683 return mv88e6xxx_g2_smi_phy_read_data_c45(chip, external, port, dev,
684 data);
685}
686
687/* IEEE 802.3 Clause 45 Write Data Register */
688static int mv88e6xxx_g2_smi_phy_write_data_c45(struct mv88e6xxx_chip *chip,
689 bool external, int port, int dev,
690 u16 data)
691{
692 u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_DATA;
693 int err;
694
695 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
696 if (err)
697 return err;
698
699 return mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev);
700}
701
702static int mv88e6xxx_g2_smi_phy_write_c45(struct mv88e6xxx_chip *chip,
703 bool external, int port, int reg,
704 u16 data)
705{
706 int dev = (reg >> 16) & 0x1f;
707 int addr = reg & 0xffff;
708 int err;
709
710 err = mv88e6xxx_g2_smi_phy_write_addr_c45(chip, external, port, dev,
711 addr);
712 if (err)
713 return err;
714
715 return mv88e6xxx_g2_smi_phy_write_data_c45(chip, external, port, dev,
716 data);
717}
718
719int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100720 int addr, int reg, u16 *val)
721{
722 struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
723 bool external = mdio_bus->external;
724
725 if (reg & MII_ADDR_C45)
Vivien Didelote289ef02017-06-19 10:55:37 -0400726 return mv88e6xxx_g2_smi_phy_read_c45(chip, external, addr, reg,
727 val);
728
729 return mv88e6xxx_g2_smi_phy_read_data_c22(chip, external, addr, reg,
730 val);
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100731}
732
Vivien Didelote289ef02017-06-19 10:55:37 -0400733int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100734 int addr, int reg, u16 val)
735{
736 struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
737 bool external = mdio_bus->external;
738
739 if (reg & MII_ADDR_C45)
Vivien Didelote289ef02017-06-19 10:55:37 -0400740 return mv88e6xxx_g2_smi_phy_write_c45(chip, external, addr, reg,
741 val);
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100742
Vivien Didelote289ef02017-06-19 10:55:37 -0400743 return mv88e6xxx_g2_smi_phy_write_data_c22(chip, external, addr, reg,
744 val);
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100745}
746
Andrew Lunnfcd25162017-02-09 00:03:42 +0100747static int mv88e6097_watchdog_action(struct mv88e6xxx_chip *chip, int irq)
748{
749 u16 reg;
750
751 mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
752
753 dev_info(chip->dev, "Watchdog event: 0x%04x", reg);
754
755 return IRQ_HANDLED;
756}
757
758static void mv88e6097_watchdog_free(struct mv88e6xxx_chip *chip)
759{
760 u16 reg;
761
762 mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
763
764 reg &= ~(GLOBAL2_WDOG_CONTROL_EGRESS_ENABLE |
765 GLOBAL2_WDOG_CONTROL_QC_ENABLE);
766
767 mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, reg);
768}
769
770static int mv88e6097_watchdog_setup(struct mv88e6xxx_chip *chip)
771{
772 return mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL,
773 GLOBAL2_WDOG_CONTROL_EGRESS_ENABLE |
774 GLOBAL2_WDOG_CONTROL_QC_ENABLE |
775 GLOBAL2_WDOG_CONTROL_SWRESET);
776}
777
778const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = {
779 .irq_action = mv88e6097_watchdog_action,
780 .irq_setup = mv88e6097_watchdog_setup,
781 .irq_free = mv88e6097_watchdog_free,
782};
783
Andrew Lunn61303732017-02-09 00:03:43 +0100784static int mv88e6390_watchdog_setup(struct mv88e6xxx_chip *chip)
785{
786 return mv88e6xxx_g2_update(chip, GLOBAL2_WDOG_CONTROL,
787 GLOBAL2_WDOG_INT_ENABLE |
788 GLOBAL2_WDOG_CUT_THROUGH |
789 GLOBAL2_WDOG_QUEUE_CONTROLLER |
790 GLOBAL2_WDOG_EGRESS |
791 GLOBAL2_WDOG_FORCE_IRQ);
792}
793
794static int mv88e6390_watchdog_action(struct mv88e6xxx_chip *chip, int irq)
795{
796 int err;
797 u16 reg;
798
799 mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, GLOBAL2_WDOG_EVENT);
800 err = mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
801
802 dev_info(chip->dev, "Watchdog event: 0x%04x",
803 reg & GLOBAL2_WDOG_DATA_MASK);
804
805 mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, GLOBAL2_WDOG_HISTORY);
806 err = mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
807
808 dev_info(chip->dev, "Watchdog history: 0x%04x",
809 reg & GLOBAL2_WDOG_DATA_MASK);
810
811 /* Trigger a software reset to try to recover the switch */
812 if (chip->info->ops->reset)
813 chip->info->ops->reset(chip);
814
815 mv88e6390_watchdog_setup(chip);
816
817 return IRQ_HANDLED;
818}
819
820static void mv88e6390_watchdog_free(struct mv88e6xxx_chip *chip)
821{
822 mv88e6xxx_g2_update(chip, GLOBAL2_WDOG_CONTROL,
823 GLOBAL2_WDOG_INT_ENABLE);
824}
825
826const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = {
827 .irq_action = mv88e6390_watchdog_action,
828 .irq_setup = mv88e6390_watchdog_setup,
829 .irq_free = mv88e6390_watchdog_free,
830};
831
Andrew Lunnfcd25162017-02-09 00:03:42 +0100832static irqreturn_t mv88e6xxx_g2_watchdog_thread_fn(int irq, void *dev_id)
833{
834 struct mv88e6xxx_chip *chip = dev_id;
835 irqreturn_t ret = IRQ_NONE;
836
837 mutex_lock(&chip->reg_lock);
838 if (chip->info->ops->watchdog_ops->irq_action)
839 ret = chip->info->ops->watchdog_ops->irq_action(chip, irq);
840 mutex_unlock(&chip->reg_lock);
841
842 return ret;
843}
844
845static void mv88e6xxx_g2_watchdog_free(struct mv88e6xxx_chip *chip)
846{
847 mutex_lock(&chip->reg_lock);
848 if (chip->info->ops->watchdog_ops->irq_free)
849 chip->info->ops->watchdog_ops->irq_free(chip);
850 mutex_unlock(&chip->reg_lock);
851
852 free_irq(chip->watchdog_irq, chip);
853 irq_dispose_mapping(chip->watchdog_irq);
854}
855
856static int mv88e6xxx_g2_watchdog_setup(struct mv88e6xxx_chip *chip)
857{
858 int err;
859
860 chip->watchdog_irq = irq_find_mapping(chip->g2_irq.domain,
861 GLOBAL2_INT_SOURCE_WATCHDOG);
862 if (chip->watchdog_irq < 0)
863 return chip->watchdog_irq;
864
865 err = request_threaded_irq(chip->watchdog_irq, NULL,
866 mv88e6xxx_g2_watchdog_thread_fn,
867 IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
868 "mv88e6xxx-watchdog", chip);
869 if (err)
870 return err;
871
872 mutex_lock(&chip->reg_lock);
873 if (chip->info->ops->watchdog_ops->irq_setup)
874 err = chip->info->ops->watchdog_ops->irq_setup(chip);
875 mutex_unlock(&chip->reg_lock);
876
877 return err;
878}
879
Vivien Didelot81228992017-03-30 17:37:08 -0400880/* Offset 0x1D: Misc Register */
881
882static int mv88e6xxx_g2_misc_5_bit_port(struct mv88e6xxx_chip *chip,
883 bool port_5_bit)
884{
885 u16 val;
886 int err;
887
888 err = mv88e6xxx_g2_read(chip, GLOBAL2_MISC, &val);
889 if (err)
890 return err;
891
892 if (port_5_bit)
893 val |= GLOBAL2_MISC_5_BIT_PORT;
894 else
895 val &= ~GLOBAL2_MISC_5_BIT_PORT;
896
897 return mv88e6xxx_g2_write(chip, GLOBAL2_MISC, val);
898}
899
900int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip)
901{
902 return mv88e6xxx_g2_misc_5_bit_port(chip, false);
903}
904
Andrew Lunndc30c352016-10-16 19:56:49 +0200905static void mv88e6xxx_g2_irq_mask(struct irq_data *d)
906{
907 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
908 unsigned int n = d->hwirq;
909
910 chip->g2_irq.masked |= (1 << n);
911}
912
913static void mv88e6xxx_g2_irq_unmask(struct irq_data *d)
914{
915 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
916 unsigned int n = d->hwirq;
917
918 chip->g2_irq.masked &= ~(1 << n);
919}
920
921static irqreturn_t mv88e6xxx_g2_irq_thread_fn(int irq, void *dev_id)
922{
923 struct mv88e6xxx_chip *chip = dev_id;
924 unsigned int nhandled = 0;
925 unsigned int sub_irq;
926 unsigned int n;
927 int err;
928 u16 reg;
929
930 mutex_lock(&chip->reg_lock);
931 err = mv88e6xxx_g2_read(chip, GLOBAL2_INT_SOURCE, &reg);
932 mutex_unlock(&chip->reg_lock);
933 if (err)
934 goto out;
935
936 for (n = 0; n < 16; ++n) {
937 if (reg & (1 << n)) {
938 sub_irq = irq_find_mapping(chip->g2_irq.domain, n);
939 handle_nested_irq(sub_irq);
940 ++nhandled;
941 }
942 }
943out:
944 return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
945}
946
947static void mv88e6xxx_g2_irq_bus_lock(struct irq_data *d)
948{
949 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
950
951 mutex_lock(&chip->reg_lock);
952}
953
954static void mv88e6xxx_g2_irq_bus_sync_unlock(struct irq_data *d)
955{
956 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
957
958 mv88e6xxx_g2_write(chip, GLOBAL2_INT_MASK, ~chip->g2_irq.masked);
959
960 mutex_unlock(&chip->reg_lock);
961}
962
963static struct irq_chip mv88e6xxx_g2_irq_chip = {
964 .name = "mv88e6xxx-g2",
965 .irq_mask = mv88e6xxx_g2_irq_mask,
966 .irq_unmask = mv88e6xxx_g2_irq_unmask,
967 .irq_bus_lock = mv88e6xxx_g2_irq_bus_lock,
968 .irq_bus_sync_unlock = mv88e6xxx_g2_irq_bus_sync_unlock,
969};
970
971static int mv88e6xxx_g2_irq_domain_map(struct irq_domain *d,
972 unsigned int irq,
973 irq_hw_number_t hwirq)
974{
975 struct mv88e6xxx_chip *chip = d->host_data;
976
977 irq_set_chip_data(irq, d->host_data);
978 irq_set_chip_and_handler(irq, &chip->g2_irq.chip, handle_level_irq);
979 irq_set_noprobe(irq);
980
981 return 0;
982}
983
984static const struct irq_domain_ops mv88e6xxx_g2_irq_domain_ops = {
985 .map = mv88e6xxx_g2_irq_domain_map,
986 .xlate = irq_domain_xlate_twocell,
987};
988
989void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip)
990{
991 int irq, virq;
992
Andrew Lunnfcd25162017-02-09 00:03:42 +0100993 mv88e6xxx_g2_watchdog_free(chip);
994
Andrew Lunn8e757eb2016-11-20 20:14:18 +0100995 free_irq(chip->device_irq, chip);
996 irq_dispose_mapping(chip->device_irq);
997
Andrew Lunndc30c352016-10-16 19:56:49 +0200998 for (irq = 0; irq < 16; irq++) {
999 virq = irq_find_mapping(chip->g2_irq.domain, irq);
1000 irq_dispose_mapping(virq);
1001 }
1002
1003 irq_domain_remove(chip->g2_irq.domain);
1004}
1005
1006int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip)
1007{
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001008 int err, irq, virq;
Andrew Lunndc30c352016-10-16 19:56:49 +02001009
1010 if (!chip->dev->of_node)
1011 return -EINVAL;
1012
1013 chip->g2_irq.domain = irq_domain_add_simple(
1014 chip->dev->of_node, 16, 0, &mv88e6xxx_g2_irq_domain_ops, chip);
1015 if (!chip->g2_irq.domain)
1016 return -ENOMEM;
1017
1018 for (irq = 0; irq < 16; irq++)
1019 irq_create_mapping(chip->g2_irq.domain, irq);
1020
1021 chip->g2_irq.chip = mv88e6xxx_g2_irq_chip;
1022 chip->g2_irq.masked = ~0;
1023
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001024 chip->device_irq = irq_find_mapping(chip->g1_irq.domain,
Vivien Didelot82466922017-06-15 12:13:59 -04001025 MV88E6XXX_G1_STS_IRQ_DEVICE);
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001026 if (chip->device_irq < 0) {
1027 err = chip->device_irq;
Andrew Lunndc30c352016-10-16 19:56:49 +02001028 goto out;
1029 }
1030
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001031 err = request_threaded_irq(chip->device_irq, NULL,
1032 mv88e6xxx_g2_irq_thread_fn,
1033 IRQF_ONESHOT, "mv88e6xxx-g1", chip);
Andrew Lunndc30c352016-10-16 19:56:49 +02001034 if (err)
1035 goto out;
1036
Andrew Lunnfcd25162017-02-09 00:03:42 +01001037 return mv88e6xxx_g2_watchdog_setup(chip);
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001038
Andrew Lunndc30c352016-10-16 19:56:49 +02001039out:
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001040 for (irq = 0; irq < 16; irq++) {
1041 virq = irq_find_mapping(chip->g2_irq.domain, irq);
1042 irq_dispose_mapping(virq);
1043 }
1044
1045 irq_domain_remove(chip->g2_irq.domain);
Andrew Lunndc30c352016-10-16 19:56:49 +02001046
1047 return err;
1048}
1049
Vivien Didelotec561272016-09-02 14:45:33 -04001050int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip)
1051{
1052 u16 reg;
1053 int err;
1054
Vivien Didelotec561272016-09-02 14:45:33 -04001055 /* Ignore removed tag data on doubly tagged packets, disable
1056 * flow control messages, force flow control priority to the
1057 * highest, and send all special multicast frames to the CPU
1058 * port at the highest priority.
1059 */
1060 reg = GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI | (0x7 << 4);
1061 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X) ||
1062 mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X))
1063 reg |= GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x7;
Vivien Didelot9fe850f2016-09-29 12:21:54 -04001064 err = mv88e6xxx_g2_write(chip, GLOBAL2_SWITCH_MGMT, reg);
Vivien Didelotec561272016-09-02 14:45:33 -04001065 if (err)
1066 return err;
1067
1068 /* Program the DSA routing table. */
1069 err = mv88e6xxx_g2_set_device_mapping(chip);
1070 if (err)
1071 return err;
1072
1073 /* Clear all trunk masks and mapping. */
1074 err = mv88e6xxx_g2_clear_trunk(chip);
1075 if (err)
1076 return err;
1077
Vivien Didelotec561272016-09-02 14:45:33 -04001078 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_POT)) {
1079 /* Clear the priority override table. */
1080 err = mv88e6xxx_g2_clear_pot(chip);
1081 if (err)
1082 return err;
1083 }
1084
1085 return 0;
1086}