blob: d63af31e78404cb07e567f89544d91993fed1ce2 [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 Didelote0970972017-06-02 17:06:18 -040020#include "global1.h" /* for GLOBAL_STATUS_IRQ_DEVICE */
Vivien Didelotec561272016-09-02 14:45:33 -040021#include "global2.h"
22
Vivien Didelot9fe850f2016-09-29 12:21:54 -040023static int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
24{
25 return mv88e6xxx_read(chip, ADDR_GLOBAL2, reg, val);
26}
27
28static int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
29{
30 return mv88e6xxx_write(chip, ADDR_GLOBAL2, reg, val);
31}
32
33static int mv88e6xxx_g2_update(struct mv88e6xxx_chip *chip, int reg, u16 update)
34{
35 return mv88e6xxx_update(chip, ADDR_GLOBAL2, reg, update);
36}
37
38static int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask)
39{
40 return mv88e6xxx_wait(chip, ADDR_GLOBAL2, reg, mask);
41}
42
Andrew Lunn6e55f692016-12-03 04:45:16 +010043/* Offset 0x02: Management Enable 2x */
44/* Offset 0x03: Management Enable 0x */
45
46int mv88e6095_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip)
47{
48 int err;
49
50 /* Consider the frames with reserved multicast destination
51 * addresses matching 01:80:c2:00:00:2x as MGMT.
52 */
53 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) {
54 err = mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_2X, 0xffff);
55 if (err)
56 return err;
57 }
58
59 /* Consider the frames with reserved multicast destination
60 * addresses matching 01:80:c2:00:00:0x as MGMT.
61 */
62 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X))
63 return mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_0X, 0xffff);
64
65 return 0;
66}
67
Vivien Didelotec561272016-09-02 14:45:33 -040068/* Offset 0x06: Device Mapping Table register */
69
70static int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip,
71 int target, int port)
72{
73 u16 val = (target << 8) | (port & 0xf);
74
Vivien Didelot9fe850f2016-09-29 12:21:54 -040075 return mv88e6xxx_g2_update(chip, GLOBAL2_DEVICE_MAPPING, val);
Vivien Didelotec561272016-09-02 14:45:33 -040076}
77
78static int mv88e6xxx_g2_set_device_mapping(struct mv88e6xxx_chip *chip)
79{
80 int target, port;
81 int err;
82
83 /* Initialize the routing port to the 32 possible target devices */
84 for (target = 0; target < 32; ++target) {
85 port = 0xf;
86
87 if (target < DSA_MAX_SWITCHES) {
88 port = chip->ds->rtable[target];
89 if (port == DSA_RTABLE_NONE)
90 port = 0xf;
91 }
92
93 err = mv88e6xxx_g2_device_mapping_write(chip, target, port);
94 if (err)
95 break;
96 }
97
98 return err;
99}
100
101/* Offset 0x07: Trunk Mask Table register */
102
103static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
104 bool hask, u16 mask)
105{
Vivien Didelot370b4ff2016-09-29 12:21:57 -0400106 const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
Vivien Didelotec561272016-09-02 14:45:33 -0400107 u16 val = (num << 12) | (mask & port_mask);
108
109 if (hask)
110 val |= GLOBAL2_TRUNK_MASK_HASK;
111
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400112 return mv88e6xxx_g2_update(chip, GLOBAL2_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 Didelot9fe850f2016-09-29 12:21:54 -0400123 return mv88e6xxx_g2_update(chip, GLOBAL2_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
152static int mv88e6xxx_g2_clear_irl(struct mv88e6xxx_chip *chip)
153{
154 int port, err;
155
156 /* Init all Ingress Rate Limit resources of all ports */
Vivien Didelot370b4ff2016-09-29 12:21:57 -0400157 for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) {
Vivien Didelotec561272016-09-02 14:45:33 -0400158 /* XXX newer chips (like 88E6390) have different 2-bit ops */
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400159 err = mv88e6xxx_g2_write(chip, GLOBAL2_IRL_CMD,
160 GLOBAL2_IRL_CMD_OP_INIT_ALL |
161 (port << 8));
Vivien Didelotec561272016-09-02 14:45:33 -0400162 if (err)
163 break;
164
165 /* Wait for the operation to complete */
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400166 err = mv88e6xxx_g2_wait(chip, GLOBAL2_IRL_CMD,
167 GLOBAL2_IRL_CMD_BUSY);
Vivien Didelotec561272016-09-02 14:45:33 -0400168 if (err)
169 break;
170 }
171
172 return err;
173}
174
Vivien Didelot17a15942017-03-30 17:37:09 -0400175/* Offset 0x0B: Cross-chip Port VLAN (Addr) Register
176 * Offset 0x0C: Cross-chip Port VLAN Data Register
177 */
178
179static int mv88e6xxx_g2_pvt_op_wait(struct mv88e6xxx_chip *chip)
180{
181 return mv88e6xxx_g2_wait(chip, GLOBAL2_PVT_ADDR, GLOBAL2_PVT_ADDR_BUSY);
182}
183
184static int mv88e6xxx_g2_pvt_op(struct mv88e6xxx_chip *chip, int src_dev,
185 int src_port, u16 op)
186{
187 int err;
188
189 /* 9-bit Cross-chip PVT pointer: with GLOBAL2_MISC_5_BIT_PORT cleared,
190 * source device is 5-bit, source port is 4-bit.
191 */
192 op |= (src_dev & 0x1f) << 4;
193 op |= (src_port & 0xf);
194
195 err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_ADDR, op);
196 if (err)
197 return err;
198
199 return mv88e6xxx_g2_pvt_op_wait(chip);
200}
201
202int mv88e6xxx_g2_pvt_write(struct mv88e6xxx_chip *chip, int src_dev,
203 int src_port, u16 data)
204{
205 int err;
206
207 err = mv88e6xxx_g2_pvt_op_wait(chip);
208 if (err)
209 return err;
210
211 err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_DATA, data);
212 if (err)
213 return err;
214
215 return mv88e6xxx_g2_pvt_op(chip, src_dev, src_port,
216 GLOBAL2_PVT_ADDR_OP_WRITE_PVLAN);
217}
218
Vivien Didelotec561272016-09-02 14:45:33 -0400219/* Offset 0x0D: Switch MAC/WoL/WoF register */
220
221static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip,
222 unsigned int pointer, u8 data)
223{
224 u16 val = (pointer << 8) | data;
225
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400226 return mv88e6xxx_g2_update(chip, GLOBAL2_SWITCH_MAC, val);
Vivien Didelotec561272016-09-02 14:45:33 -0400227}
228
229int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
230{
231 int i, err;
232
233 for (i = 0; i < 6; i++) {
234 err = mv88e6xxx_g2_switch_mac_write(chip, i, addr[i]);
235 if (err)
236 break;
237 }
238
239 return err;
240}
241
242/* Offset 0x0F: Priority Override Table */
243
244static int mv88e6xxx_g2_pot_write(struct mv88e6xxx_chip *chip, int pointer,
245 u8 data)
246{
247 u16 val = (pointer << 8) | (data & 0x7);
248
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400249 return mv88e6xxx_g2_update(chip, GLOBAL2_PRIO_OVERRIDE, val);
Vivien Didelotec561272016-09-02 14:45:33 -0400250}
251
252static int mv88e6xxx_g2_clear_pot(struct mv88e6xxx_chip *chip)
253{
254 int i, err;
255
256 /* Clear all sixteen possible Priority Override entries */
257 for (i = 0; i < 16; i++) {
258 err = mv88e6xxx_g2_pot_write(chip, i, 0);
259 if (err)
260 break;
261 }
262
263 return err;
264}
265
266/* Offset 0x14: EEPROM Command
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500267 * Offset 0x15: EEPROM Data (for 16-bit data access)
268 * Offset 0x15: EEPROM Addr (for 8-bit data access)
Vivien Didelotec561272016-09-02 14:45:33 -0400269 */
270
271static int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip)
272{
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400273 return mv88e6xxx_g2_wait(chip, GLOBAL2_EEPROM_CMD,
274 GLOBAL2_EEPROM_CMD_BUSY |
275 GLOBAL2_EEPROM_CMD_RUNNING);
Vivien Didelotec561272016-09-02 14:45:33 -0400276}
277
278static int mv88e6xxx_g2_eeprom_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
279{
280 int err;
281
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400282 err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_CMD, cmd);
Vivien Didelotec561272016-09-02 14:45:33 -0400283 if (err)
284 return err;
285
286 return mv88e6xxx_g2_eeprom_wait(chip);
287}
288
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500289static int mv88e6xxx_g2_eeprom_read8(struct mv88e6xxx_chip *chip,
290 u16 addr, u8 *data)
291{
292 u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ;
293 int err;
294
295 err = mv88e6xxx_g2_eeprom_wait(chip);
296 if (err)
297 return err;
298
299 err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_ADDR, addr);
300 if (err)
301 return err;
302
303 err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
304 if (err)
305 return err;
306
307 err = mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_CMD, &cmd);
308 if (err)
309 return err;
310
311 *data = cmd & 0xff;
312
313 return 0;
314}
315
316static int mv88e6xxx_g2_eeprom_write8(struct mv88e6xxx_chip *chip,
317 u16 addr, u8 data)
318{
319 u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | GLOBAL2_EEPROM_CMD_WRITE_EN;
320 int err;
321
322 err = mv88e6xxx_g2_eeprom_wait(chip);
323 if (err)
324 return err;
325
326 err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_ADDR, addr);
327 if (err)
328 return err;
329
330 return mv88e6xxx_g2_eeprom_cmd(chip, cmd | data);
331}
332
Vivien Didelotec561272016-09-02 14:45:33 -0400333static int mv88e6xxx_g2_eeprom_read16(struct mv88e6xxx_chip *chip,
334 u8 addr, u16 *data)
335{
336 u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ | addr;
337 int err;
338
339 err = mv88e6xxx_g2_eeprom_wait(chip);
340 if (err)
341 return err;
342
343 err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
344 if (err)
345 return err;
346
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400347 return mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_DATA, data);
Vivien Didelotec561272016-09-02 14:45:33 -0400348}
349
350static int mv88e6xxx_g2_eeprom_write16(struct mv88e6xxx_chip *chip,
351 u8 addr, u16 data)
352{
353 u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | addr;
354 int err;
355
356 err = mv88e6xxx_g2_eeprom_wait(chip);
357 if (err)
358 return err;
359
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400360 err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_DATA, data);
Vivien Didelotec561272016-09-02 14:45:33 -0400361 if (err)
362 return err;
363
364 return mv88e6xxx_g2_eeprom_cmd(chip, cmd);
365}
366
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500367int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip,
368 struct ethtool_eeprom *eeprom, u8 *data)
369{
370 unsigned int offset = eeprom->offset;
371 unsigned int len = eeprom->len;
372 int err;
373
374 eeprom->len = 0;
375
376 while (len) {
377 err = mv88e6xxx_g2_eeprom_read8(chip, offset, data);
378 if (err)
379 return err;
380
381 eeprom->len++;
382 offset++;
383 data++;
384 len--;
385 }
386
387 return 0;
388}
389
390int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip,
391 struct ethtool_eeprom *eeprom, u8 *data)
392{
393 unsigned int offset = eeprom->offset;
394 unsigned int len = eeprom->len;
395 int err;
396
397 eeprom->len = 0;
398
399 while (len) {
400 err = mv88e6xxx_g2_eeprom_write8(chip, offset, *data);
401 if (err)
402 return err;
403
404 eeprom->len++;
405 offset++;
406 data++;
407 len--;
408 }
409
410 return 0;
411}
412
Vivien Didelotec561272016-09-02 14:45:33 -0400413int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip,
414 struct ethtool_eeprom *eeprom, u8 *data)
415{
416 unsigned int offset = eeprom->offset;
417 unsigned int len = eeprom->len;
418 u16 val;
419 int err;
420
421 eeprom->len = 0;
422
423 if (offset & 1) {
424 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
425 if (err)
426 return err;
427
428 *data++ = (val >> 8) & 0xff;
429
430 offset++;
431 len--;
432 eeprom->len++;
433 }
434
435 while (len >= 2) {
436 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
437 if (err)
438 return err;
439
440 *data++ = val & 0xff;
441 *data++ = (val >> 8) & 0xff;
442
443 offset += 2;
444 len -= 2;
445 eeprom->len += 2;
446 }
447
448 if (len) {
449 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
450 if (err)
451 return err;
452
453 *data++ = val & 0xff;
454
455 offset++;
456 len--;
457 eeprom->len++;
458 }
459
460 return 0;
461}
462
463int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip,
464 struct ethtool_eeprom *eeprom, u8 *data)
465{
466 unsigned int offset = eeprom->offset;
467 unsigned int len = eeprom->len;
468 u16 val;
469 int err;
470
471 /* Ensure the RO WriteEn bit is set */
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400472 err = mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_CMD, &val);
Vivien Didelotec561272016-09-02 14:45:33 -0400473 if (err)
474 return err;
475
476 if (!(val & GLOBAL2_EEPROM_CMD_WRITE_EN))
477 return -EROFS;
478
479 eeprom->len = 0;
480
481 if (offset & 1) {
482 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
483 if (err)
484 return err;
485
486 val = (*data++ << 8) | (val & 0xff);
487
488 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
489 if (err)
490 return err;
491
492 offset++;
493 len--;
494 eeprom->len++;
495 }
496
497 while (len >= 2) {
498 val = *data++;
499 val |= *data++ << 8;
500
501 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
502 if (err)
503 return err;
504
505 offset += 2;
506 len -= 2;
507 eeprom->len += 2;
508 }
509
510 if (len) {
511 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
512 if (err)
513 return err;
514
515 val = (val & 0xff00) | *data++;
516
517 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
518 if (err)
519 return err;
520
521 offset++;
522 len--;
523 eeprom->len++;
524 }
525
526 return 0;
527}
528
529/* Offset 0x18: SMI PHY Command Register
530 * Offset 0x19: SMI PHY Data Register
531 */
532
533static int mv88e6xxx_g2_smi_phy_wait(struct mv88e6xxx_chip *chip)
534{
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400535 return mv88e6xxx_g2_wait(chip, GLOBAL2_SMI_PHY_CMD,
536 GLOBAL2_SMI_PHY_CMD_BUSY);
Vivien Didelotec561272016-09-02 14:45:33 -0400537}
538
539static int mv88e6xxx_g2_smi_phy_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
540{
541 int err;
542
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400543 err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_CMD, cmd);
Vivien Didelotec561272016-09-02 14:45:33 -0400544 if (err)
545 return err;
546
547 return mv88e6xxx_g2_smi_phy_wait(chip);
548}
549
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100550static int mv88e6xxx_g2_smi_phy_write_addr(struct mv88e6xxx_chip *chip,
551 int addr, int device, int reg,
552 bool external)
Vivien Didelotec561272016-09-02 14:45:33 -0400553{
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100554 int cmd = SMI_CMD_OP_45_WRITE_ADDR | (addr << 5) | device;
Vivien Didelotec561272016-09-02 14:45:33 -0400555 int err;
556
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100557 if (external)
558 cmd |= GLOBAL2_SMI_PHY_CMD_EXTERNAL;
559
560 err = mv88e6xxx_g2_smi_phy_wait(chip);
561 if (err)
562 return err;
563
564 err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_DATA, reg);
565 if (err)
566 return err;
567
568 return mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
569}
570
Florian Fainelli54a88e42017-04-06 12:42:16 -0700571static int mv88e6xxx_g2_smi_phy_read_c45(struct mv88e6xxx_chip *chip,
572 int addr, int reg_c45, u16 *val,
573 bool external)
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100574{
575 int device = (reg_c45 >> 16) & 0x1f;
576 int reg = reg_c45 & 0xffff;
577 int err;
578 u16 cmd;
579
580 err = mv88e6xxx_g2_smi_phy_write_addr(chip, addr, device, reg,
581 external);
582 if (err)
583 return err;
584
585 cmd = GLOBAL2_SMI_PHY_CMD_OP_45_READ_DATA | (addr << 5) | device;
586
587 if (external)
588 cmd |= GLOBAL2_SMI_PHY_CMD_EXTERNAL;
589
590 err = mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
591 if (err)
592 return err;
593
594 err = mv88e6xxx_g2_read(chip, GLOBAL2_SMI_PHY_DATA, val);
595 if (err)
596 return err;
597
598 err = *val;
599
600 return 0;
601}
602
Florian Fainelli54a88e42017-04-06 12:42:16 -0700603static int mv88e6xxx_g2_smi_phy_read_c22(struct mv88e6xxx_chip *chip,
604 int addr, int reg, u16 *val,
605 bool external)
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100606{
607 u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_READ_DATA | (addr << 5) | reg;
608 int err;
609
610 if (external)
Andrew Lunnc61a6a72017-01-24 14:53:51 +0100611 cmd |= GLOBAL2_SMI_PHY_CMD_EXTERNAL;
612
Vivien Didelotec561272016-09-02 14:45:33 -0400613 err = mv88e6xxx_g2_smi_phy_wait(chip);
614 if (err)
615 return err;
616
617 err = mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
618 if (err)
619 return err;
620
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400621 return mv88e6xxx_g2_read(chip, GLOBAL2_SMI_PHY_DATA, val);
Vivien Didelotec561272016-09-02 14:45:33 -0400622}
623
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100624int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip,
625 struct mii_bus *bus,
626 int addr, int reg, u16 *val)
627{
628 struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
629 bool external = mdio_bus->external;
630
631 if (reg & MII_ADDR_C45)
632 return mv88e6xxx_g2_smi_phy_read_c45(chip, addr, reg, val,
633 external);
634 return mv88e6xxx_g2_smi_phy_read_c22(chip, addr, reg, val, external);
635}
636
Florian Fainelli54a88e42017-04-06 12:42:16 -0700637static int mv88e6xxx_g2_smi_phy_write_c45(struct mv88e6xxx_chip *chip,
638 int addr, int reg_c45, u16 val,
639 bool external)
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100640{
641 int device = (reg_c45 >> 16) & 0x1f;
642 int reg = reg_c45 & 0xffff;
643 int err;
644 u16 cmd;
645
646 err = mv88e6xxx_g2_smi_phy_write_addr(chip, addr, device, reg,
647 external);
648 if (err)
649 return err;
650
651 cmd = GLOBAL2_SMI_PHY_CMD_OP_45_WRITE_DATA | (addr << 5) | device;
652
653 if (external)
654 cmd |= GLOBAL2_SMI_PHY_CMD_EXTERNAL;
655
656 err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_DATA, val);
657 if (err)
658 return err;
659
660 err = mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
661 if (err)
662 return err;
663
664 return 0;
665}
666
Florian Fainelli54a88e42017-04-06 12:42:16 -0700667static int mv88e6xxx_g2_smi_phy_write_c22(struct mv88e6xxx_chip *chip,
668 int addr, int reg, u16 val,
669 bool external)
Vivien Didelotec561272016-09-02 14:45:33 -0400670{
671 u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_WRITE_DATA | (addr << 5) | reg;
672 int err;
673
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100674 if (external)
Andrew Lunnc61a6a72017-01-24 14:53:51 +0100675 cmd |= GLOBAL2_SMI_PHY_CMD_EXTERNAL;
676
Vivien Didelotec561272016-09-02 14:45:33 -0400677 err = mv88e6xxx_g2_smi_phy_wait(chip);
678 if (err)
679 return err;
680
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400681 err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_DATA, val);
Vivien Didelotec561272016-09-02 14:45:33 -0400682 if (err)
683 return err;
684
685 return mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
686}
687
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100688int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip,
689 struct mii_bus *bus,
690 int addr, int reg, u16 val)
691{
692 struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
693 bool external = mdio_bus->external;
694
695 if (reg & MII_ADDR_C45)
696 return mv88e6xxx_g2_smi_phy_write_c45(chip, addr, reg, val,
697 external);
698
699 return mv88e6xxx_g2_smi_phy_write_c22(chip, addr, reg, val, external);
700}
701
Andrew Lunnfcd25162017-02-09 00:03:42 +0100702static int mv88e6097_watchdog_action(struct mv88e6xxx_chip *chip, int irq)
703{
704 u16 reg;
705
706 mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
707
708 dev_info(chip->dev, "Watchdog event: 0x%04x", reg);
709
710 return IRQ_HANDLED;
711}
712
713static void mv88e6097_watchdog_free(struct mv88e6xxx_chip *chip)
714{
715 u16 reg;
716
717 mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
718
719 reg &= ~(GLOBAL2_WDOG_CONTROL_EGRESS_ENABLE |
720 GLOBAL2_WDOG_CONTROL_QC_ENABLE);
721
722 mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, reg);
723}
724
725static int mv88e6097_watchdog_setup(struct mv88e6xxx_chip *chip)
726{
727 return mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL,
728 GLOBAL2_WDOG_CONTROL_EGRESS_ENABLE |
729 GLOBAL2_WDOG_CONTROL_QC_ENABLE |
730 GLOBAL2_WDOG_CONTROL_SWRESET);
731}
732
733const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = {
734 .irq_action = mv88e6097_watchdog_action,
735 .irq_setup = mv88e6097_watchdog_setup,
736 .irq_free = mv88e6097_watchdog_free,
737};
738
Andrew Lunn61303732017-02-09 00:03:43 +0100739static int mv88e6390_watchdog_setup(struct mv88e6xxx_chip *chip)
740{
741 return mv88e6xxx_g2_update(chip, GLOBAL2_WDOG_CONTROL,
742 GLOBAL2_WDOG_INT_ENABLE |
743 GLOBAL2_WDOG_CUT_THROUGH |
744 GLOBAL2_WDOG_QUEUE_CONTROLLER |
745 GLOBAL2_WDOG_EGRESS |
746 GLOBAL2_WDOG_FORCE_IRQ);
747}
748
749static int mv88e6390_watchdog_action(struct mv88e6xxx_chip *chip, int irq)
750{
751 int err;
752 u16 reg;
753
754 mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, GLOBAL2_WDOG_EVENT);
755 err = mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
756
757 dev_info(chip->dev, "Watchdog event: 0x%04x",
758 reg & GLOBAL2_WDOG_DATA_MASK);
759
760 mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, GLOBAL2_WDOG_HISTORY);
761 err = mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
762
763 dev_info(chip->dev, "Watchdog history: 0x%04x",
764 reg & GLOBAL2_WDOG_DATA_MASK);
765
766 /* Trigger a software reset to try to recover the switch */
767 if (chip->info->ops->reset)
768 chip->info->ops->reset(chip);
769
770 mv88e6390_watchdog_setup(chip);
771
772 return IRQ_HANDLED;
773}
774
775static void mv88e6390_watchdog_free(struct mv88e6xxx_chip *chip)
776{
777 mv88e6xxx_g2_update(chip, GLOBAL2_WDOG_CONTROL,
778 GLOBAL2_WDOG_INT_ENABLE);
779}
780
781const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = {
782 .irq_action = mv88e6390_watchdog_action,
783 .irq_setup = mv88e6390_watchdog_setup,
784 .irq_free = mv88e6390_watchdog_free,
785};
786
Andrew Lunnfcd25162017-02-09 00:03:42 +0100787static irqreturn_t mv88e6xxx_g2_watchdog_thread_fn(int irq, void *dev_id)
788{
789 struct mv88e6xxx_chip *chip = dev_id;
790 irqreturn_t ret = IRQ_NONE;
791
792 mutex_lock(&chip->reg_lock);
793 if (chip->info->ops->watchdog_ops->irq_action)
794 ret = chip->info->ops->watchdog_ops->irq_action(chip, irq);
795 mutex_unlock(&chip->reg_lock);
796
797 return ret;
798}
799
800static void mv88e6xxx_g2_watchdog_free(struct mv88e6xxx_chip *chip)
801{
802 mutex_lock(&chip->reg_lock);
803 if (chip->info->ops->watchdog_ops->irq_free)
804 chip->info->ops->watchdog_ops->irq_free(chip);
805 mutex_unlock(&chip->reg_lock);
806
807 free_irq(chip->watchdog_irq, chip);
808 irq_dispose_mapping(chip->watchdog_irq);
809}
810
811static int mv88e6xxx_g2_watchdog_setup(struct mv88e6xxx_chip *chip)
812{
813 int err;
814
815 chip->watchdog_irq = irq_find_mapping(chip->g2_irq.domain,
816 GLOBAL2_INT_SOURCE_WATCHDOG);
817 if (chip->watchdog_irq < 0)
818 return chip->watchdog_irq;
819
820 err = request_threaded_irq(chip->watchdog_irq, NULL,
821 mv88e6xxx_g2_watchdog_thread_fn,
822 IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
823 "mv88e6xxx-watchdog", chip);
824 if (err)
825 return err;
826
827 mutex_lock(&chip->reg_lock);
828 if (chip->info->ops->watchdog_ops->irq_setup)
829 err = chip->info->ops->watchdog_ops->irq_setup(chip);
830 mutex_unlock(&chip->reg_lock);
831
832 return err;
833}
834
Vivien Didelot81228992017-03-30 17:37:08 -0400835/* Offset 0x1D: Misc Register */
836
837static int mv88e6xxx_g2_misc_5_bit_port(struct mv88e6xxx_chip *chip,
838 bool port_5_bit)
839{
840 u16 val;
841 int err;
842
843 err = mv88e6xxx_g2_read(chip, GLOBAL2_MISC, &val);
844 if (err)
845 return err;
846
847 if (port_5_bit)
848 val |= GLOBAL2_MISC_5_BIT_PORT;
849 else
850 val &= ~GLOBAL2_MISC_5_BIT_PORT;
851
852 return mv88e6xxx_g2_write(chip, GLOBAL2_MISC, val);
853}
854
855int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip)
856{
857 return mv88e6xxx_g2_misc_5_bit_port(chip, false);
858}
859
Andrew Lunndc30c352016-10-16 19:56:49 +0200860static void mv88e6xxx_g2_irq_mask(struct irq_data *d)
861{
862 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
863 unsigned int n = d->hwirq;
864
865 chip->g2_irq.masked |= (1 << n);
866}
867
868static void mv88e6xxx_g2_irq_unmask(struct irq_data *d)
869{
870 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
871 unsigned int n = d->hwirq;
872
873 chip->g2_irq.masked &= ~(1 << n);
874}
875
876static irqreturn_t mv88e6xxx_g2_irq_thread_fn(int irq, void *dev_id)
877{
878 struct mv88e6xxx_chip *chip = dev_id;
879 unsigned int nhandled = 0;
880 unsigned int sub_irq;
881 unsigned int n;
882 int err;
883 u16 reg;
884
885 mutex_lock(&chip->reg_lock);
886 err = mv88e6xxx_g2_read(chip, GLOBAL2_INT_SOURCE, &reg);
887 mutex_unlock(&chip->reg_lock);
888 if (err)
889 goto out;
890
891 for (n = 0; n < 16; ++n) {
892 if (reg & (1 << n)) {
893 sub_irq = irq_find_mapping(chip->g2_irq.domain, n);
894 handle_nested_irq(sub_irq);
895 ++nhandled;
896 }
897 }
898out:
899 return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
900}
901
902static void mv88e6xxx_g2_irq_bus_lock(struct irq_data *d)
903{
904 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
905
906 mutex_lock(&chip->reg_lock);
907}
908
909static void mv88e6xxx_g2_irq_bus_sync_unlock(struct irq_data *d)
910{
911 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
912
913 mv88e6xxx_g2_write(chip, GLOBAL2_INT_MASK, ~chip->g2_irq.masked);
914
915 mutex_unlock(&chip->reg_lock);
916}
917
918static struct irq_chip mv88e6xxx_g2_irq_chip = {
919 .name = "mv88e6xxx-g2",
920 .irq_mask = mv88e6xxx_g2_irq_mask,
921 .irq_unmask = mv88e6xxx_g2_irq_unmask,
922 .irq_bus_lock = mv88e6xxx_g2_irq_bus_lock,
923 .irq_bus_sync_unlock = mv88e6xxx_g2_irq_bus_sync_unlock,
924};
925
926static int mv88e6xxx_g2_irq_domain_map(struct irq_domain *d,
927 unsigned int irq,
928 irq_hw_number_t hwirq)
929{
930 struct mv88e6xxx_chip *chip = d->host_data;
931
932 irq_set_chip_data(irq, d->host_data);
933 irq_set_chip_and_handler(irq, &chip->g2_irq.chip, handle_level_irq);
934 irq_set_noprobe(irq);
935
936 return 0;
937}
938
939static const struct irq_domain_ops mv88e6xxx_g2_irq_domain_ops = {
940 .map = mv88e6xxx_g2_irq_domain_map,
941 .xlate = irq_domain_xlate_twocell,
942};
943
944void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip)
945{
946 int irq, virq;
947
Andrew Lunnfcd25162017-02-09 00:03:42 +0100948 mv88e6xxx_g2_watchdog_free(chip);
949
Andrew Lunn8e757eb2016-11-20 20:14:18 +0100950 free_irq(chip->device_irq, chip);
951 irq_dispose_mapping(chip->device_irq);
952
Andrew Lunndc30c352016-10-16 19:56:49 +0200953 for (irq = 0; irq < 16; irq++) {
954 virq = irq_find_mapping(chip->g2_irq.domain, irq);
955 irq_dispose_mapping(virq);
956 }
957
958 irq_domain_remove(chip->g2_irq.domain);
959}
960
961int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip)
962{
Andrew Lunn8e757eb2016-11-20 20:14:18 +0100963 int err, irq, virq;
Andrew Lunndc30c352016-10-16 19:56:49 +0200964
965 if (!chip->dev->of_node)
966 return -EINVAL;
967
968 chip->g2_irq.domain = irq_domain_add_simple(
969 chip->dev->of_node, 16, 0, &mv88e6xxx_g2_irq_domain_ops, chip);
970 if (!chip->g2_irq.domain)
971 return -ENOMEM;
972
973 for (irq = 0; irq < 16; irq++)
974 irq_create_mapping(chip->g2_irq.domain, irq);
975
976 chip->g2_irq.chip = mv88e6xxx_g2_irq_chip;
977 chip->g2_irq.masked = ~0;
978
Andrew Lunn8e757eb2016-11-20 20:14:18 +0100979 chip->device_irq = irq_find_mapping(chip->g1_irq.domain,
980 GLOBAL_STATUS_IRQ_DEVICE);
981 if (chip->device_irq < 0) {
982 err = chip->device_irq;
Andrew Lunndc30c352016-10-16 19:56:49 +0200983 goto out;
984 }
985
Andrew Lunn8e757eb2016-11-20 20:14:18 +0100986 err = request_threaded_irq(chip->device_irq, NULL,
987 mv88e6xxx_g2_irq_thread_fn,
988 IRQF_ONESHOT, "mv88e6xxx-g1", chip);
Andrew Lunndc30c352016-10-16 19:56:49 +0200989 if (err)
990 goto out;
991
Andrew Lunnfcd25162017-02-09 00:03:42 +0100992 return mv88e6xxx_g2_watchdog_setup(chip);
Andrew Lunn8e757eb2016-11-20 20:14:18 +0100993
Andrew Lunndc30c352016-10-16 19:56:49 +0200994out:
Andrew Lunn8e757eb2016-11-20 20:14:18 +0100995 for (irq = 0; irq < 16; irq++) {
996 virq = irq_find_mapping(chip->g2_irq.domain, irq);
997 irq_dispose_mapping(virq);
998 }
999
1000 irq_domain_remove(chip->g2_irq.domain);
Andrew Lunndc30c352016-10-16 19:56:49 +02001001
1002 return err;
1003}
1004
Vivien Didelotec561272016-09-02 14:45:33 -04001005int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip)
1006{
1007 u16 reg;
1008 int err;
1009
Vivien Didelotec561272016-09-02 14:45:33 -04001010 /* Ignore removed tag data on doubly tagged packets, disable
1011 * flow control messages, force flow control priority to the
1012 * highest, and send all special multicast frames to the CPU
1013 * port at the highest priority.
1014 */
1015 reg = GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI | (0x7 << 4);
1016 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X) ||
1017 mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X))
1018 reg |= GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x7;
Vivien Didelot9fe850f2016-09-29 12:21:54 -04001019 err = mv88e6xxx_g2_write(chip, GLOBAL2_SWITCH_MGMT, reg);
Vivien Didelotec561272016-09-02 14:45:33 -04001020 if (err)
1021 return err;
1022
1023 /* Program the DSA routing table. */
1024 err = mv88e6xxx_g2_set_device_mapping(chip);
1025 if (err)
1026 return err;
1027
1028 /* Clear all trunk masks and mapping. */
1029 err = mv88e6xxx_g2_clear_trunk(chip);
1030 if (err)
1031 return err;
1032
1033 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_IRL)) {
1034 /* Disable ingress rate limiting by resetting all per port
1035 * ingress rate limit resources to their initial state.
1036 */
1037 err = mv88e6xxx_g2_clear_irl(chip);
1038 if (err)
1039 return err;
1040 }
1041
Vivien Didelotec561272016-09-02 14:45:33 -04001042 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_POT)) {
1043 /* Clear the priority override table. */
1044 err = mv88e6xxx_g2_clear_pot(chip);
1045 if (err)
1046 return err;
1047 }
1048
1049 return 0;
1050}