blob: 0defce71e3811a01d8e06aea47767573516899e3 [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
Florian Westphal282ccf62017-03-29 17:17:31 +020016#include <linux/interrupt.h>
Andrew Lunndc30c352016-10-16 19:56:49 +020017#include <linux/irqdomain.h>
Vivien Didelot4d5f2ba72017-06-02 17:06:15 -040018
19#include "chip.h"
Vivien Didelotec561272016-09-02 14:45:33 -040020#include "global2.h"
21
Vivien Didelot9fe850f2016-09-29 12:21:54 -040022#define ADDR_GLOBAL2 0x1c
23
24static 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
153static int mv88e6xxx_g2_clear_irl(struct mv88e6xxx_chip *chip)
154{
155 int port, err;
156
157 /* Init all Ingress Rate Limit resources of all ports */
Vivien Didelot370b4ff2016-09-29 12:21:57 -0400158 for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) {
Vivien Didelotec561272016-09-02 14:45:33 -0400159 /* XXX newer chips (like 88E6390) have different 2-bit ops */
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400160 err = mv88e6xxx_g2_write(chip, GLOBAL2_IRL_CMD,
161 GLOBAL2_IRL_CMD_OP_INIT_ALL |
162 (port << 8));
Vivien Didelotec561272016-09-02 14:45:33 -0400163 if (err)
164 break;
165
166 /* Wait for the operation to complete */
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400167 err = mv88e6xxx_g2_wait(chip, GLOBAL2_IRL_CMD,
168 GLOBAL2_IRL_CMD_BUSY);
Vivien Didelotec561272016-09-02 14:45:33 -0400169 if (err)
170 break;
171 }
172
173 return err;
174}
175
Vivien Didelot17a15942017-03-30 17:37:09 -0400176/* Offset 0x0B: Cross-chip Port VLAN (Addr) Register
177 * Offset 0x0C: Cross-chip Port VLAN Data Register
178 */
179
180static int mv88e6xxx_g2_pvt_op_wait(struct mv88e6xxx_chip *chip)
181{
182 return mv88e6xxx_g2_wait(chip, GLOBAL2_PVT_ADDR, GLOBAL2_PVT_ADDR_BUSY);
183}
184
185static int mv88e6xxx_g2_pvt_op(struct mv88e6xxx_chip *chip, int src_dev,
186 int src_port, u16 op)
187{
188 int err;
189
190 /* 9-bit Cross-chip PVT pointer: with GLOBAL2_MISC_5_BIT_PORT cleared,
191 * source device is 5-bit, source port is 4-bit.
192 */
193 op |= (src_dev & 0x1f) << 4;
194 op |= (src_port & 0xf);
195
196 err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_ADDR, op);
197 if (err)
198 return err;
199
200 return mv88e6xxx_g2_pvt_op_wait(chip);
201}
202
203int mv88e6xxx_g2_pvt_write(struct mv88e6xxx_chip *chip, int src_dev,
204 int src_port, u16 data)
205{
206 int err;
207
208 err = mv88e6xxx_g2_pvt_op_wait(chip);
209 if (err)
210 return err;
211
212 err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_DATA, data);
213 if (err)
214 return err;
215
216 return mv88e6xxx_g2_pvt_op(chip, src_dev, src_port,
217 GLOBAL2_PVT_ADDR_OP_WRITE_PVLAN);
218}
219
Vivien Didelotec561272016-09-02 14:45:33 -0400220/* Offset 0x0D: Switch MAC/WoL/WoF register */
221
222static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip,
223 unsigned int pointer, u8 data)
224{
225 u16 val = (pointer << 8) | data;
226
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400227 return mv88e6xxx_g2_update(chip, GLOBAL2_SWITCH_MAC, val);
Vivien Didelotec561272016-09-02 14:45:33 -0400228}
229
230int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
231{
232 int i, err;
233
234 for (i = 0; i < 6; i++) {
235 err = mv88e6xxx_g2_switch_mac_write(chip, i, addr[i]);
236 if (err)
237 break;
238 }
239
240 return err;
241}
242
243/* Offset 0x0F: Priority Override Table */
244
245static int mv88e6xxx_g2_pot_write(struct mv88e6xxx_chip *chip, int pointer,
246 u8 data)
247{
248 u16 val = (pointer << 8) | (data & 0x7);
249
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400250 return mv88e6xxx_g2_update(chip, GLOBAL2_PRIO_OVERRIDE, val);
Vivien Didelotec561272016-09-02 14:45:33 -0400251}
252
253static int mv88e6xxx_g2_clear_pot(struct mv88e6xxx_chip *chip)
254{
255 int i, err;
256
257 /* Clear all sixteen possible Priority Override entries */
258 for (i = 0; i < 16; i++) {
259 err = mv88e6xxx_g2_pot_write(chip, i, 0);
260 if (err)
261 break;
262 }
263
264 return err;
265}
266
267/* Offset 0x14: EEPROM Command
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500268 * Offset 0x15: EEPROM Data (for 16-bit data access)
269 * Offset 0x15: EEPROM Addr (for 8-bit data access)
Vivien Didelotec561272016-09-02 14:45:33 -0400270 */
271
272static int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip)
273{
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400274 return mv88e6xxx_g2_wait(chip, GLOBAL2_EEPROM_CMD,
275 GLOBAL2_EEPROM_CMD_BUSY |
276 GLOBAL2_EEPROM_CMD_RUNNING);
Vivien Didelotec561272016-09-02 14:45:33 -0400277}
278
279static int mv88e6xxx_g2_eeprom_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
280{
281 int err;
282
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400283 err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_CMD, cmd);
Vivien Didelotec561272016-09-02 14:45:33 -0400284 if (err)
285 return err;
286
287 return mv88e6xxx_g2_eeprom_wait(chip);
288}
289
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500290static int mv88e6xxx_g2_eeprom_read8(struct mv88e6xxx_chip *chip,
291 u16 addr, u8 *data)
292{
293 u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ;
294 int err;
295
296 err = mv88e6xxx_g2_eeprom_wait(chip);
297 if (err)
298 return err;
299
300 err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_ADDR, addr);
301 if (err)
302 return err;
303
304 err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
305 if (err)
306 return err;
307
308 err = mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_CMD, &cmd);
309 if (err)
310 return err;
311
312 *data = cmd & 0xff;
313
314 return 0;
315}
316
317static int mv88e6xxx_g2_eeprom_write8(struct mv88e6xxx_chip *chip,
318 u16 addr, u8 data)
319{
320 u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | GLOBAL2_EEPROM_CMD_WRITE_EN;
321 int err;
322
323 err = mv88e6xxx_g2_eeprom_wait(chip);
324 if (err)
325 return err;
326
327 err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_ADDR, addr);
328 if (err)
329 return err;
330
331 return mv88e6xxx_g2_eeprom_cmd(chip, cmd | data);
332}
333
Vivien Didelotec561272016-09-02 14:45:33 -0400334static int mv88e6xxx_g2_eeprom_read16(struct mv88e6xxx_chip *chip,
335 u8 addr, u16 *data)
336{
337 u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ | addr;
338 int err;
339
340 err = mv88e6xxx_g2_eeprom_wait(chip);
341 if (err)
342 return err;
343
344 err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
345 if (err)
346 return err;
347
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400348 return mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_DATA, data);
Vivien Didelotec561272016-09-02 14:45:33 -0400349}
350
351static int mv88e6xxx_g2_eeprom_write16(struct mv88e6xxx_chip *chip,
352 u8 addr, u16 data)
353{
354 u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | addr;
355 int err;
356
357 err = mv88e6xxx_g2_eeprom_wait(chip);
358 if (err)
359 return err;
360
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400361 err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_DATA, data);
Vivien Didelotec561272016-09-02 14:45:33 -0400362 if (err)
363 return err;
364
365 return mv88e6xxx_g2_eeprom_cmd(chip, cmd);
366}
367
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500368int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip,
369 struct ethtool_eeprom *eeprom, u8 *data)
370{
371 unsigned int offset = eeprom->offset;
372 unsigned int len = eeprom->len;
373 int err;
374
375 eeprom->len = 0;
376
377 while (len) {
378 err = mv88e6xxx_g2_eeprom_read8(chip, offset, data);
379 if (err)
380 return err;
381
382 eeprom->len++;
383 offset++;
384 data++;
385 len--;
386 }
387
388 return 0;
389}
390
391int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip,
392 struct ethtool_eeprom *eeprom, u8 *data)
393{
394 unsigned int offset = eeprom->offset;
395 unsigned int len = eeprom->len;
396 int err;
397
398 eeprom->len = 0;
399
400 while (len) {
401 err = mv88e6xxx_g2_eeprom_write8(chip, offset, *data);
402 if (err)
403 return err;
404
405 eeprom->len++;
406 offset++;
407 data++;
408 len--;
409 }
410
411 return 0;
412}
413
Vivien Didelotec561272016-09-02 14:45:33 -0400414int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip,
415 struct ethtool_eeprom *eeprom, u8 *data)
416{
417 unsigned int offset = eeprom->offset;
418 unsigned int len = eeprom->len;
419 u16 val;
420 int err;
421
422 eeprom->len = 0;
423
424 if (offset & 1) {
425 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
426 if (err)
427 return err;
428
429 *data++ = (val >> 8) & 0xff;
430
431 offset++;
432 len--;
433 eeprom->len++;
434 }
435
436 while (len >= 2) {
437 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
438 if (err)
439 return err;
440
441 *data++ = val & 0xff;
442 *data++ = (val >> 8) & 0xff;
443
444 offset += 2;
445 len -= 2;
446 eeprom->len += 2;
447 }
448
449 if (len) {
450 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
451 if (err)
452 return err;
453
454 *data++ = val & 0xff;
455
456 offset++;
457 len--;
458 eeprom->len++;
459 }
460
461 return 0;
462}
463
464int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip,
465 struct ethtool_eeprom *eeprom, u8 *data)
466{
467 unsigned int offset = eeprom->offset;
468 unsigned int len = eeprom->len;
469 u16 val;
470 int err;
471
472 /* Ensure the RO WriteEn bit is set */
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400473 err = mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_CMD, &val);
Vivien Didelotec561272016-09-02 14:45:33 -0400474 if (err)
475 return err;
476
477 if (!(val & GLOBAL2_EEPROM_CMD_WRITE_EN))
478 return -EROFS;
479
480 eeprom->len = 0;
481
482 if (offset & 1) {
483 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
484 if (err)
485 return err;
486
487 val = (*data++ << 8) | (val & 0xff);
488
489 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
490 if (err)
491 return err;
492
493 offset++;
494 len--;
495 eeprom->len++;
496 }
497
498 while (len >= 2) {
499 val = *data++;
500 val |= *data++ << 8;
501
502 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
503 if (err)
504 return err;
505
506 offset += 2;
507 len -= 2;
508 eeprom->len += 2;
509 }
510
511 if (len) {
512 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
513 if (err)
514 return err;
515
516 val = (val & 0xff00) | *data++;
517
518 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
519 if (err)
520 return err;
521
522 offset++;
523 len--;
524 eeprom->len++;
525 }
526
527 return 0;
528}
529
530/* Offset 0x18: SMI PHY Command Register
531 * Offset 0x19: SMI PHY Data Register
532 */
533
534static int mv88e6xxx_g2_smi_phy_wait(struct mv88e6xxx_chip *chip)
535{
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400536 return mv88e6xxx_g2_wait(chip, GLOBAL2_SMI_PHY_CMD,
537 GLOBAL2_SMI_PHY_CMD_BUSY);
Vivien Didelotec561272016-09-02 14:45:33 -0400538}
539
540static int mv88e6xxx_g2_smi_phy_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
541{
542 int err;
543
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400544 err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_CMD, cmd);
Vivien Didelotec561272016-09-02 14:45:33 -0400545 if (err)
546 return err;
547
548 return mv88e6xxx_g2_smi_phy_wait(chip);
549}
550
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100551static int mv88e6xxx_g2_smi_phy_write_addr(struct mv88e6xxx_chip *chip,
552 int addr, int device, int reg,
553 bool external)
Vivien Didelotec561272016-09-02 14:45:33 -0400554{
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100555 int cmd = SMI_CMD_OP_45_WRITE_ADDR | (addr << 5) | device;
Vivien Didelotec561272016-09-02 14:45:33 -0400556 int err;
557
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100558 if (external)
559 cmd |= GLOBAL2_SMI_PHY_CMD_EXTERNAL;
560
561 err = mv88e6xxx_g2_smi_phy_wait(chip);
562 if (err)
563 return err;
564
565 err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_DATA, reg);
566 if (err)
567 return err;
568
569 return mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
570}
571
Florian Fainelli54a88e42017-04-06 12:42:16 -0700572static int mv88e6xxx_g2_smi_phy_read_c45(struct mv88e6xxx_chip *chip,
573 int addr, int reg_c45, u16 *val,
574 bool external)
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100575{
576 int device = (reg_c45 >> 16) & 0x1f;
577 int reg = reg_c45 & 0xffff;
578 int err;
579 u16 cmd;
580
581 err = mv88e6xxx_g2_smi_phy_write_addr(chip, addr, device, reg,
582 external);
583 if (err)
584 return err;
585
586 cmd = GLOBAL2_SMI_PHY_CMD_OP_45_READ_DATA | (addr << 5) | device;
587
588 if (external)
589 cmd |= GLOBAL2_SMI_PHY_CMD_EXTERNAL;
590
591 err = mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
592 if (err)
593 return err;
594
595 err = mv88e6xxx_g2_read(chip, GLOBAL2_SMI_PHY_DATA, val);
596 if (err)
597 return err;
598
599 err = *val;
600
601 return 0;
602}
603
Florian Fainelli54a88e42017-04-06 12:42:16 -0700604static int mv88e6xxx_g2_smi_phy_read_c22(struct mv88e6xxx_chip *chip,
605 int addr, int reg, u16 *val,
606 bool external)
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100607{
608 u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_READ_DATA | (addr << 5) | reg;
609 int err;
610
611 if (external)
Andrew Lunnc61a6a72017-01-24 14:53:51 +0100612 cmd |= GLOBAL2_SMI_PHY_CMD_EXTERNAL;
613
Vivien Didelotec561272016-09-02 14:45:33 -0400614 err = mv88e6xxx_g2_smi_phy_wait(chip);
615 if (err)
616 return err;
617
618 err = mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
619 if (err)
620 return err;
621
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400622 return mv88e6xxx_g2_read(chip, GLOBAL2_SMI_PHY_DATA, val);
Vivien Didelotec561272016-09-02 14:45:33 -0400623}
624
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100625int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip,
626 struct mii_bus *bus,
627 int addr, int reg, u16 *val)
628{
629 struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
630 bool external = mdio_bus->external;
631
632 if (reg & MII_ADDR_C45)
633 return mv88e6xxx_g2_smi_phy_read_c45(chip, addr, reg, val,
634 external);
635 return mv88e6xxx_g2_smi_phy_read_c22(chip, addr, reg, val, external);
636}
637
Florian Fainelli54a88e42017-04-06 12:42:16 -0700638static int mv88e6xxx_g2_smi_phy_write_c45(struct mv88e6xxx_chip *chip,
639 int addr, int reg_c45, u16 val,
640 bool external)
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100641{
642 int device = (reg_c45 >> 16) & 0x1f;
643 int reg = reg_c45 & 0xffff;
644 int err;
645 u16 cmd;
646
647 err = mv88e6xxx_g2_smi_phy_write_addr(chip, addr, device, reg,
648 external);
649 if (err)
650 return err;
651
652 cmd = GLOBAL2_SMI_PHY_CMD_OP_45_WRITE_DATA | (addr << 5) | device;
653
654 if (external)
655 cmd |= GLOBAL2_SMI_PHY_CMD_EXTERNAL;
656
657 err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_DATA, val);
658 if (err)
659 return err;
660
661 err = mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
662 if (err)
663 return err;
664
665 return 0;
666}
667
Florian Fainelli54a88e42017-04-06 12:42:16 -0700668static int mv88e6xxx_g2_smi_phy_write_c22(struct mv88e6xxx_chip *chip,
669 int addr, int reg, u16 val,
670 bool external)
Vivien Didelotec561272016-09-02 14:45:33 -0400671{
672 u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_WRITE_DATA | (addr << 5) | reg;
673 int err;
674
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100675 if (external)
Andrew Lunnc61a6a72017-01-24 14:53:51 +0100676 cmd |= GLOBAL2_SMI_PHY_CMD_EXTERNAL;
677
Vivien Didelotec561272016-09-02 14:45:33 -0400678 err = mv88e6xxx_g2_smi_phy_wait(chip);
679 if (err)
680 return err;
681
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400682 err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_DATA, val);
Vivien Didelotec561272016-09-02 14:45:33 -0400683 if (err)
684 return err;
685
686 return mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
687}
688
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100689int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip,
690 struct mii_bus *bus,
691 int addr, int reg, u16 val)
692{
693 struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
694 bool external = mdio_bus->external;
695
696 if (reg & MII_ADDR_C45)
697 return mv88e6xxx_g2_smi_phy_write_c45(chip, addr, reg, val,
698 external);
699
700 return mv88e6xxx_g2_smi_phy_write_c22(chip, addr, reg, val, external);
701}
702
Andrew Lunnfcd25162017-02-09 00:03:42 +0100703static int mv88e6097_watchdog_action(struct mv88e6xxx_chip *chip, int irq)
704{
705 u16 reg;
706
707 mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
708
709 dev_info(chip->dev, "Watchdog event: 0x%04x", reg);
710
711 return IRQ_HANDLED;
712}
713
714static void mv88e6097_watchdog_free(struct mv88e6xxx_chip *chip)
715{
716 u16 reg;
717
718 mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
719
720 reg &= ~(GLOBAL2_WDOG_CONTROL_EGRESS_ENABLE |
721 GLOBAL2_WDOG_CONTROL_QC_ENABLE);
722
723 mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, reg);
724}
725
726static int mv88e6097_watchdog_setup(struct mv88e6xxx_chip *chip)
727{
728 return mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL,
729 GLOBAL2_WDOG_CONTROL_EGRESS_ENABLE |
730 GLOBAL2_WDOG_CONTROL_QC_ENABLE |
731 GLOBAL2_WDOG_CONTROL_SWRESET);
732}
733
734const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = {
735 .irq_action = mv88e6097_watchdog_action,
736 .irq_setup = mv88e6097_watchdog_setup,
737 .irq_free = mv88e6097_watchdog_free,
738};
739
Andrew Lunn61303732017-02-09 00:03:43 +0100740static int mv88e6390_watchdog_setup(struct mv88e6xxx_chip *chip)
741{
742 return mv88e6xxx_g2_update(chip, GLOBAL2_WDOG_CONTROL,
743 GLOBAL2_WDOG_INT_ENABLE |
744 GLOBAL2_WDOG_CUT_THROUGH |
745 GLOBAL2_WDOG_QUEUE_CONTROLLER |
746 GLOBAL2_WDOG_EGRESS |
747 GLOBAL2_WDOG_FORCE_IRQ);
748}
749
750static int mv88e6390_watchdog_action(struct mv88e6xxx_chip *chip, int irq)
751{
752 int err;
753 u16 reg;
754
755 mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, GLOBAL2_WDOG_EVENT);
756 err = mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
757
758 dev_info(chip->dev, "Watchdog event: 0x%04x",
759 reg & GLOBAL2_WDOG_DATA_MASK);
760
761 mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, GLOBAL2_WDOG_HISTORY);
762 err = mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
763
764 dev_info(chip->dev, "Watchdog history: 0x%04x",
765 reg & GLOBAL2_WDOG_DATA_MASK);
766
767 /* Trigger a software reset to try to recover the switch */
768 if (chip->info->ops->reset)
769 chip->info->ops->reset(chip);
770
771 mv88e6390_watchdog_setup(chip);
772
773 return IRQ_HANDLED;
774}
775
776static void mv88e6390_watchdog_free(struct mv88e6xxx_chip *chip)
777{
778 mv88e6xxx_g2_update(chip, GLOBAL2_WDOG_CONTROL,
779 GLOBAL2_WDOG_INT_ENABLE);
780}
781
782const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = {
783 .irq_action = mv88e6390_watchdog_action,
784 .irq_setup = mv88e6390_watchdog_setup,
785 .irq_free = mv88e6390_watchdog_free,
786};
787
Andrew Lunnfcd25162017-02-09 00:03:42 +0100788static irqreturn_t mv88e6xxx_g2_watchdog_thread_fn(int irq, void *dev_id)
789{
790 struct mv88e6xxx_chip *chip = dev_id;
791 irqreturn_t ret = IRQ_NONE;
792
793 mutex_lock(&chip->reg_lock);
794 if (chip->info->ops->watchdog_ops->irq_action)
795 ret = chip->info->ops->watchdog_ops->irq_action(chip, irq);
796 mutex_unlock(&chip->reg_lock);
797
798 return ret;
799}
800
801static void mv88e6xxx_g2_watchdog_free(struct mv88e6xxx_chip *chip)
802{
803 mutex_lock(&chip->reg_lock);
804 if (chip->info->ops->watchdog_ops->irq_free)
805 chip->info->ops->watchdog_ops->irq_free(chip);
806 mutex_unlock(&chip->reg_lock);
807
808 free_irq(chip->watchdog_irq, chip);
809 irq_dispose_mapping(chip->watchdog_irq);
810}
811
812static int mv88e6xxx_g2_watchdog_setup(struct mv88e6xxx_chip *chip)
813{
814 int err;
815
816 chip->watchdog_irq = irq_find_mapping(chip->g2_irq.domain,
817 GLOBAL2_INT_SOURCE_WATCHDOG);
818 if (chip->watchdog_irq < 0)
819 return chip->watchdog_irq;
820
821 err = request_threaded_irq(chip->watchdog_irq, NULL,
822 mv88e6xxx_g2_watchdog_thread_fn,
823 IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
824 "mv88e6xxx-watchdog", chip);
825 if (err)
826 return err;
827
828 mutex_lock(&chip->reg_lock);
829 if (chip->info->ops->watchdog_ops->irq_setup)
830 err = chip->info->ops->watchdog_ops->irq_setup(chip);
831 mutex_unlock(&chip->reg_lock);
832
833 return err;
834}
835
Vivien Didelot81228992017-03-30 17:37:08 -0400836/* Offset 0x1D: Misc Register */
837
838static int mv88e6xxx_g2_misc_5_bit_port(struct mv88e6xxx_chip *chip,
839 bool port_5_bit)
840{
841 u16 val;
842 int err;
843
844 err = mv88e6xxx_g2_read(chip, GLOBAL2_MISC, &val);
845 if (err)
846 return err;
847
848 if (port_5_bit)
849 val |= GLOBAL2_MISC_5_BIT_PORT;
850 else
851 val &= ~GLOBAL2_MISC_5_BIT_PORT;
852
853 return mv88e6xxx_g2_write(chip, GLOBAL2_MISC, val);
854}
855
856int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip)
857{
858 return mv88e6xxx_g2_misc_5_bit_port(chip, false);
859}
860
Andrew Lunndc30c352016-10-16 19:56:49 +0200861static void mv88e6xxx_g2_irq_mask(struct irq_data *d)
862{
863 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
864 unsigned int n = d->hwirq;
865
866 chip->g2_irq.masked |= (1 << n);
867}
868
869static void mv88e6xxx_g2_irq_unmask(struct irq_data *d)
870{
871 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
872 unsigned int n = d->hwirq;
873
874 chip->g2_irq.masked &= ~(1 << n);
875}
876
877static irqreturn_t mv88e6xxx_g2_irq_thread_fn(int irq, void *dev_id)
878{
879 struct mv88e6xxx_chip *chip = dev_id;
880 unsigned int nhandled = 0;
881 unsigned int sub_irq;
882 unsigned int n;
883 int err;
884 u16 reg;
885
886 mutex_lock(&chip->reg_lock);
887 err = mv88e6xxx_g2_read(chip, GLOBAL2_INT_SOURCE, &reg);
888 mutex_unlock(&chip->reg_lock);
889 if (err)
890 goto out;
891
892 for (n = 0; n < 16; ++n) {
893 if (reg & (1 << n)) {
894 sub_irq = irq_find_mapping(chip->g2_irq.domain, n);
895 handle_nested_irq(sub_irq);
896 ++nhandled;
897 }
898 }
899out:
900 return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
901}
902
903static void mv88e6xxx_g2_irq_bus_lock(struct irq_data *d)
904{
905 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
906
907 mutex_lock(&chip->reg_lock);
908}
909
910static void mv88e6xxx_g2_irq_bus_sync_unlock(struct irq_data *d)
911{
912 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
913
914 mv88e6xxx_g2_write(chip, GLOBAL2_INT_MASK, ~chip->g2_irq.masked);
915
916 mutex_unlock(&chip->reg_lock);
917}
918
919static struct irq_chip mv88e6xxx_g2_irq_chip = {
920 .name = "mv88e6xxx-g2",
921 .irq_mask = mv88e6xxx_g2_irq_mask,
922 .irq_unmask = mv88e6xxx_g2_irq_unmask,
923 .irq_bus_lock = mv88e6xxx_g2_irq_bus_lock,
924 .irq_bus_sync_unlock = mv88e6xxx_g2_irq_bus_sync_unlock,
925};
926
927static int mv88e6xxx_g2_irq_domain_map(struct irq_domain *d,
928 unsigned int irq,
929 irq_hw_number_t hwirq)
930{
931 struct mv88e6xxx_chip *chip = d->host_data;
932
933 irq_set_chip_data(irq, d->host_data);
934 irq_set_chip_and_handler(irq, &chip->g2_irq.chip, handle_level_irq);
935 irq_set_noprobe(irq);
936
937 return 0;
938}
939
940static const struct irq_domain_ops mv88e6xxx_g2_irq_domain_ops = {
941 .map = mv88e6xxx_g2_irq_domain_map,
942 .xlate = irq_domain_xlate_twocell,
943};
944
945void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip)
946{
947 int irq, virq;
948
Andrew Lunnfcd25162017-02-09 00:03:42 +0100949 mv88e6xxx_g2_watchdog_free(chip);
950
Andrew Lunn8e757eb2016-11-20 20:14:18 +0100951 free_irq(chip->device_irq, chip);
952 irq_dispose_mapping(chip->device_irq);
953
Andrew Lunndc30c352016-10-16 19:56:49 +0200954 for (irq = 0; irq < 16; irq++) {
955 virq = irq_find_mapping(chip->g2_irq.domain, irq);
956 irq_dispose_mapping(virq);
957 }
958
959 irq_domain_remove(chip->g2_irq.domain);
960}
961
962int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip)
963{
Andrew Lunn8e757eb2016-11-20 20:14:18 +0100964 int err, irq, virq;
Andrew Lunndc30c352016-10-16 19:56:49 +0200965
966 if (!chip->dev->of_node)
967 return -EINVAL;
968
969 chip->g2_irq.domain = irq_domain_add_simple(
970 chip->dev->of_node, 16, 0, &mv88e6xxx_g2_irq_domain_ops, chip);
971 if (!chip->g2_irq.domain)
972 return -ENOMEM;
973
974 for (irq = 0; irq < 16; irq++)
975 irq_create_mapping(chip->g2_irq.domain, irq);
976
977 chip->g2_irq.chip = mv88e6xxx_g2_irq_chip;
978 chip->g2_irq.masked = ~0;
979
Andrew Lunn8e757eb2016-11-20 20:14:18 +0100980 chip->device_irq = irq_find_mapping(chip->g1_irq.domain,
981 GLOBAL_STATUS_IRQ_DEVICE);
982 if (chip->device_irq < 0) {
983 err = chip->device_irq;
Andrew Lunndc30c352016-10-16 19:56:49 +0200984 goto out;
985 }
986
Andrew Lunn8e757eb2016-11-20 20:14:18 +0100987 err = request_threaded_irq(chip->device_irq, NULL,
988 mv88e6xxx_g2_irq_thread_fn,
989 IRQF_ONESHOT, "mv88e6xxx-g1", chip);
Andrew Lunndc30c352016-10-16 19:56:49 +0200990 if (err)
991 goto out;
992
Andrew Lunnfcd25162017-02-09 00:03:42 +0100993 return mv88e6xxx_g2_watchdog_setup(chip);
Andrew Lunn8e757eb2016-11-20 20:14:18 +0100994
Andrew Lunndc30c352016-10-16 19:56:49 +0200995out:
Andrew Lunn8e757eb2016-11-20 20:14:18 +0100996 for (irq = 0; irq < 16; irq++) {
997 virq = irq_find_mapping(chip->g2_irq.domain, irq);
998 irq_dispose_mapping(virq);
999 }
1000
1001 irq_domain_remove(chip->g2_irq.domain);
Andrew Lunndc30c352016-10-16 19:56:49 +02001002
1003 return err;
1004}
1005
Vivien Didelotec561272016-09-02 14:45:33 -04001006int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip)
1007{
1008 u16 reg;
1009 int err;
1010
Vivien Didelotec561272016-09-02 14:45:33 -04001011 /* Ignore removed tag data on doubly tagged packets, disable
1012 * flow control messages, force flow control priority to the
1013 * highest, and send all special multicast frames to the CPU
1014 * port at the highest priority.
1015 */
1016 reg = GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI | (0x7 << 4);
1017 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X) ||
1018 mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X))
1019 reg |= GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x7;
Vivien Didelot9fe850f2016-09-29 12:21:54 -04001020 err = mv88e6xxx_g2_write(chip, GLOBAL2_SWITCH_MGMT, reg);
Vivien Didelotec561272016-09-02 14:45:33 -04001021 if (err)
1022 return err;
1023
1024 /* Program the DSA routing table. */
1025 err = mv88e6xxx_g2_set_device_mapping(chip);
1026 if (err)
1027 return err;
1028
1029 /* Clear all trunk masks and mapping. */
1030 err = mv88e6xxx_g2_clear_trunk(chip);
1031 if (err)
1032 return err;
1033
1034 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_IRL)) {
1035 /* Disable ingress rate limiting by resetting all per port
1036 * ingress rate limit resources to their initial state.
1037 */
1038 err = mv88e6xxx_g2_clear_irl(chip);
1039 if (err)
1040 return err;
1041 }
1042
Vivien Didelotec561272016-09-02 14:45:33 -04001043 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_POT)) {
1044 /* Clear the priority override table. */
1045 err = mv88e6xxx_g2_clear_pot(chip);
1046 if (err)
1047 return err;
1048 }
1049
1050 return 0;
1051}