blob: dc6c0f5fd6053c8afc58d329500133b4696bb7fd [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)) {
Vivien Didelot6bff47b2017-06-19 10:55:40 -040055 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_MGMT_EN_2X, 0xffff);
Andrew Lunn6e55f692016-12-03 04:45:16 +010056 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))
Vivien Didelot6bff47b2017-06-19 10:55:40 -040064 return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_MGMT_EN_0X,
65 0xffff);
Andrew Lunn6e55f692016-12-03 04:45:16 +010066
67 return 0;
68}
69
Vivien Didelotec561272016-09-02 14:45:33 -040070/* Offset 0x06: Device Mapping Table register */
71
72static int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip,
73 int target, int port)
74{
75 u16 val = (target << 8) | (port & 0xf);
76
Vivien Didelot067e4742017-06-19 10:55:39 -040077 return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_DEVICE_MAPPING, val);
Vivien Didelotec561272016-09-02 14:45:33 -040078}
79
80static int mv88e6xxx_g2_set_device_mapping(struct mv88e6xxx_chip *chip)
81{
82 int target, port;
83 int err;
84
85 /* Initialize the routing port to the 32 possible target devices */
86 for (target = 0; target < 32; ++target) {
87 port = 0xf;
88
89 if (target < DSA_MAX_SWITCHES) {
90 port = chip->ds->rtable[target];
91 if (port == DSA_RTABLE_NONE)
92 port = 0xf;
93 }
94
95 err = mv88e6xxx_g2_device_mapping_write(chip, target, port);
96 if (err)
97 break;
98 }
99
100 return err;
101}
102
103/* Offset 0x07: Trunk Mask Table register */
104
105static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
Vivien Didelot56dc7342017-06-19 10:55:38 -0400106 bool hash, u16 mask)
Vivien Didelotec561272016-09-02 14:45:33 -0400107{
Vivien Didelot56dc7342017-06-19 10:55:38 -0400108 u16 val = (num << 12) | (mask & mv88e6xxx_port_mask(chip));
Vivien Didelotec561272016-09-02 14:45:33 -0400109
Vivien Didelot56dc7342017-06-19 10:55:38 -0400110 if (hash)
111 val |= MV88E6XXX_G2_TRUNK_MASK_HASH;
Vivien Didelotec561272016-09-02 14:45:33 -0400112
Vivien Didelot56dc7342017-06-19 10:55:38 -0400113 return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_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 Didelot56dc7342017-06-19 10:55:38 -0400124 return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_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{
Vivien Didelot67d1ea82017-06-19 10:55:41 -0400191 return mv88e6xxx_g2_wait(chip, MV88E6XXX_G2_PVT_ADDR,
192 MV88E6XXX_G2_PVT_ADDR_BUSY);
Vivien Didelot17a15942017-03-30 17:37:09 -0400193}
194
195static int mv88e6xxx_g2_pvt_op(struct mv88e6xxx_chip *chip, int src_dev,
196 int src_port, u16 op)
197{
198 int err;
199
Vivien Didelot67d1ea82017-06-19 10:55:41 -0400200 /* 9-bit Cross-chip PVT pointer: with MV88E6XXX_G2_MISC_5_BIT_PORT
201 * cleared, source device is 5-bit, source port is 4-bit.
Vivien Didelot17a15942017-03-30 17:37:09 -0400202 */
Vivien Didelot67d1ea82017-06-19 10:55:41 -0400203 op |= MV88E6XXX_G2_PVT_ADDR_BUSY;
Vivien Didelot17a15942017-03-30 17:37:09 -0400204 op |= (src_dev & 0x1f) << 4;
205 op |= (src_port & 0xf);
206
Vivien Didelot67d1ea82017-06-19 10:55:41 -0400207 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_PVT_ADDR, op);
Vivien Didelot17a15942017-03-30 17:37:09 -0400208 if (err)
209 return err;
210
211 return mv88e6xxx_g2_pvt_op_wait(chip);
212}
213
214int mv88e6xxx_g2_pvt_write(struct mv88e6xxx_chip *chip, int src_dev,
215 int src_port, u16 data)
216{
217 int err;
218
219 err = mv88e6xxx_g2_pvt_op_wait(chip);
220 if (err)
221 return err;
222
Vivien Didelot67d1ea82017-06-19 10:55:41 -0400223 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_PVT_DATA, data);
Vivien Didelot17a15942017-03-30 17:37:09 -0400224 if (err)
225 return err;
226
227 return mv88e6xxx_g2_pvt_op(chip, src_dev, src_port,
Vivien Didelot67d1ea82017-06-19 10:55:41 -0400228 MV88E6XXX_G2_PVT_ADDR_OP_WRITE_PVLAN);
Vivien Didelot17a15942017-03-30 17:37:09 -0400229}
230
Vivien Didelotec561272016-09-02 14:45:33 -0400231/* Offset 0x0D: Switch MAC/WoL/WoF register */
232
233static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip,
234 unsigned int pointer, u8 data)
235{
236 u16 val = (pointer << 8) | data;
237
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400238 return mv88e6xxx_g2_update(chip, GLOBAL2_SWITCH_MAC, val);
Vivien Didelotec561272016-09-02 14:45:33 -0400239}
240
241int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
242{
243 int i, err;
244
245 for (i = 0; i < 6; i++) {
246 err = mv88e6xxx_g2_switch_mac_write(chip, i, addr[i]);
247 if (err)
248 break;
249 }
250
251 return err;
252}
253
254/* Offset 0x0F: Priority Override Table */
255
256static int mv88e6xxx_g2_pot_write(struct mv88e6xxx_chip *chip, int pointer,
257 u8 data)
258{
259 u16 val = (pointer << 8) | (data & 0x7);
260
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400261 return mv88e6xxx_g2_update(chip, GLOBAL2_PRIO_OVERRIDE, val);
Vivien Didelotec561272016-09-02 14:45:33 -0400262}
263
264static int mv88e6xxx_g2_clear_pot(struct mv88e6xxx_chip *chip)
265{
266 int i, err;
267
268 /* Clear all sixteen possible Priority Override entries */
269 for (i = 0; i < 16; i++) {
270 err = mv88e6xxx_g2_pot_write(chip, i, 0);
271 if (err)
272 break;
273 }
274
275 return err;
276}
277
278/* Offset 0x14: EEPROM Command
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500279 * Offset 0x15: EEPROM Data (for 16-bit data access)
280 * Offset 0x15: EEPROM Addr (for 8-bit data access)
Vivien Didelotec561272016-09-02 14:45:33 -0400281 */
282
283static int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip)
284{
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400285 return mv88e6xxx_g2_wait(chip, GLOBAL2_EEPROM_CMD,
286 GLOBAL2_EEPROM_CMD_BUSY |
287 GLOBAL2_EEPROM_CMD_RUNNING);
Vivien Didelotec561272016-09-02 14:45:33 -0400288}
289
290static int mv88e6xxx_g2_eeprom_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
291{
292 int err;
293
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400294 err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_CMD, cmd);
Vivien Didelotec561272016-09-02 14:45:33 -0400295 if (err)
296 return err;
297
298 return mv88e6xxx_g2_eeprom_wait(chip);
299}
300
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500301static int mv88e6xxx_g2_eeprom_read8(struct mv88e6xxx_chip *chip,
302 u16 addr, u8 *data)
303{
304 u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ;
305 int err;
306
307 err = mv88e6xxx_g2_eeprom_wait(chip);
308 if (err)
309 return err;
310
311 err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_ADDR, addr);
312 if (err)
313 return err;
314
315 err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
316 if (err)
317 return err;
318
319 err = mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_CMD, &cmd);
320 if (err)
321 return err;
322
323 *data = cmd & 0xff;
324
325 return 0;
326}
327
328static int mv88e6xxx_g2_eeprom_write8(struct mv88e6xxx_chip *chip,
329 u16 addr, u8 data)
330{
331 u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | GLOBAL2_EEPROM_CMD_WRITE_EN;
332 int err;
333
334 err = mv88e6xxx_g2_eeprom_wait(chip);
335 if (err)
336 return err;
337
338 err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_ADDR, addr);
339 if (err)
340 return err;
341
342 return mv88e6xxx_g2_eeprom_cmd(chip, cmd | data);
343}
344
Vivien Didelotec561272016-09-02 14:45:33 -0400345static int mv88e6xxx_g2_eeprom_read16(struct mv88e6xxx_chip *chip,
346 u8 addr, u16 *data)
347{
348 u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ | addr;
349 int err;
350
351 err = mv88e6xxx_g2_eeprom_wait(chip);
352 if (err)
353 return err;
354
355 err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
356 if (err)
357 return err;
358
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400359 return mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_DATA, data);
Vivien Didelotec561272016-09-02 14:45:33 -0400360}
361
362static int mv88e6xxx_g2_eeprom_write16(struct mv88e6xxx_chip *chip,
363 u8 addr, u16 data)
364{
365 u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | addr;
366 int err;
367
368 err = mv88e6xxx_g2_eeprom_wait(chip);
369 if (err)
370 return err;
371
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400372 err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_DATA, data);
Vivien Didelotec561272016-09-02 14:45:33 -0400373 if (err)
374 return err;
375
376 return mv88e6xxx_g2_eeprom_cmd(chip, cmd);
377}
378
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500379int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip,
380 struct ethtool_eeprom *eeprom, u8 *data)
381{
382 unsigned int offset = eeprom->offset;
383 unsigned int len = eeprom->len;
384 int err;
385
386 eeprom->len = 0;
387
388 while (len) {
389 err = mv88e6xxx_g2_eeprom_read8(chip, offset, data);
390 if (err)
391 return err;
392
393 eeprom->len++;
394 offset++;
395 data++;
396 len--;
397 }
398
399 return 0;
400}
401
402int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip,
403 struct ethtool_eeprom *eeprom, u8 *data)
404{
405 unsigned int offset = eeprom->offset;
406 unsigned int len = eeprom->len;
407 int err;
408
409 eeprom->len = 0;
410
411 while (len) {
412 err = mv88e6xxx_g2_eeprom_write8(chip, offset, *data);
413 if (err)
414 return err;
415
416 eeprom->len++;
417 offset++;
418 data++;
419 len--;
420 }
421
422 return 0;
423}
424
Vivien Didelotec561272016-09-02 14:45:33 -0400425int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip,
426 struct ethtool_eeprom *eeprom, u8 *data)
427{
428 unsigned int offset = eeprom->offset;
429 unsigned int len = eeprom->len;
430 u16 val;
431 int err;
432
433 eeprom->len = 0;
434
435 if (offset & 1) {
436 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
437 if (err)
438 return err;
439
440 *data++ = (val >> 8) & 0xff;
441
442 offset++;
443 len--;
444 eeprom->len++;
445 }
446
447 while (len >= 2) {
448 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
449 if (err)
450 return err;
451
452 *data++ = val & 0xff;
453 *data++ = (val >> 8) & 0xff;
454
455 offset += 2;
456 len -= 2;
457 eeprom->len += 2;
458 }
459
460 if (len) {
461 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
462 if (err)
463 return err;
464
465 *data++ = val & 0xff;
466
467 offset++;
468 len--;
469 eeprom->len++;
470 }
471
472 return 0;
473}
474
475int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip,
476 struct ethtool_eeprom *eeprom, u8 *data)
477{
478 unsigned int offset = eeprom->offset;
479 unsigned int len = eeprom->len;
480 u16 val;
481 int err;
482
483 /* Ensure the RO WriteEn bit is set */
Vivien Didelot9fe850f2016-09-29 12:21:54 -0400484 err = mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_CMD, &val);
Vivien Didelotec561272016-09-02 14:45:33 -0400485 if (err)
486 return err;
487
488 if (!(val & GLOBAL2_EEPROM_CMD_WRITE_EN))
489 return -EROFS;
490
491 eeprom->len = 0;
492
493 if (offset & 1) {
494 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
495 if (err)
496 return err;
497
498 val = (*data++ << 8) | (val & 0xff);
499
500 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
501 if (err)
502 return err;
503
504 offset++;
505 len--;
506 eeprom->len++;
507 }
508
509 while (len >= 2) {
510 val = *data++;
511 val |= *data++ << 8;
512
513 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
514 if (err)
515 return err;
516
517 offset += 2;
518 len -= 2;
519 eeprom->len += 2;
520 }
521
522 if (len) {
523 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
524 if (err)
525 return err;
526
527 val = (val & 0xff00) | *data++;
528
529 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
530 if (err)
531 return err;
532
533 offset++;
534 len--;
535 eeprom->len++;
536 }
537
538 return 0;
539}
540
541/* Offset 0x18: SMI PHY Command Register
542 * Offset 0x19: SMI PHY Data Register
543 */
544
545static int mv88e6xxx_g2_smi_phy_wait(struct mv88e6xxx_chip *chip)
546{
Vivien Didelote289ef02017-06-19 10:55:37 -0400547 return mv88e6xxx_g2_wait(chip, MV88E6XXX_G2_SMI_PHY_CMD,
548 MV88E6XXX_G2_SMI_PHY_CMD_BUSY);
Vivien Didelotec561272016-09-02 14:45:33 -0400549}
550
551static int mv88e6xxx_g2_smi_phy_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
552{
553 int err;
554
Vivien Didelote289ef02017-06-19 10:55:37 -0400555 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_CMD,
556 MV88E6XXX_G2_SMI_PHY_CMD_BUSY | cmd);
Vivien Didelotec561272016-09-02 14:45:33 -0400557 if (err)
558 return err;
559
560 return mv88e6xxx_g2_smi_phy_wait(chip);
561}
562
Vivien Didelote289ef02017-06-19 10:55:37 -0400563static int mv88e6xxx_g2_smi_phy_access(struct mv88e6xxx_chip *chip,
564 bool external, bool c45, u16 op, int dev,
565 int reg)
Vivien Didelotec561272016-09-02 14:45:33 -0400566{
Vivien Didelote289ef02017-06-19 10:55:37 -0400567 u16 cmd = op;
Vivien Didelotec561272016-09-02 14:45:33 -0400568
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100569 if (external)
Vivien Didelote289ef02017-06-19 10:55:37 -0400570 cmd |= MV88E6390_G2_SMI_PHY_CMD_FUNC_EXTERNAL;
571 else
572 cmd |= MV88E6390_G2_SMI_PHY_CMD_FUNC_INTERNAL; /* empty mask */
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100573
Vivien Didelote289ef02017-06-19 10:55:37 -0400574 if (c45)
575 cmd |= MV88E6XXX_G2_SMI_PHY_CMD_MODE_45; /* empty mask */
576 else
577 cmd |= MV88E6XXX_G2_SMI_PHY_CMD_MODE_22;
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100578
Vivien Didelote289ef02017-06-19 10:55:37 -0400579 dev <<= __bf_shf(MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK);
580 cmd |= dev & MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK;
581 cmd |= reg & MV88E6XXX_G2_SMI_PHY_CMD_REG_ADDR_MASK;
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100582
583 return mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
584}
585
Vivien Didelote289ef02017-06-19 10:55:37 -0400586static int mv88e6xxx_g2_smi_phy_access_c22(struct mv88e6xxx_chip *chip,
587 bool external, u16 op, int dev,
588 int reg)
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100589{
Vivien Didelote289ef02017-06-19 10:55:37 -0400590 return mv88e6xxx_g2_smi_phy_access(chip, external, false, op, dev, reg);
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100591}
592
Vivien Didelote289ef02017-06-19 10:55:37 -0400593/* IEEE 802.3 Clause 22 Read Data Register */
594static int mv88e6xxx_g2_smi_phy_read_data_c22(struct mv88e6xxx_chip *chip,
595 bool external, int dev, int reg,
596 u16 *data)
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100597{
Vivien Didelote289ef02017-06-19 10:55:37 -0400598 u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_22_READ_DATA;
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100599 int err;
600
Vivien Didelotec561272016-09-02 14:45:33 -0400601 err = mv88e6xxx_g2_smi_phy_wait(chip);
602 if (err)
603 return err;
604
Vivien Didelote289ef02017-06-19 10:55:37 -0400605 err = mv88e6xxx_g2_smi_phy_access_c22(chip, external, op, dev, reg);
Vivien Didelotec561272016-09-02 14:45:33 -0400606 if (err)
607 return err;
608
Vivien Didelote289ef02017-06-19 10:55:37 -0400609 return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
Vivien Didelotec561272016-09-02 14:45:33 -0400610}
611
Vivien Didelote289ef02017-06-19 10:55:37 -0400612/* IEEE 802.3 Clause 22 Write Data Register */
613static int mv88e6xxx_g2_smi_phy_write_data_c22(struct mv88e6xxx_chip *chip,
614 bool external, int dev, int reg,
615 u16 data)
616{
617 u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_22_WRITE_DATA;
618 int err;
619
620 err = mv88e6xxx_g2_smi_phy_wait(chip);
621 if (err)
622 return err;
623
624 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
625 if (err)
626 return err;
627
628 return mv88e6xxx_g2_smi_phy_access_c22(chip, external, op, dev, reg);
629}
630
631static int mv88e6xxx_g2_smi_phy_access_c45(struct mv88e6xxx_chip *chip,
632 bool external, u16 op, int port,
633 int dev)
634{
635 return mv88e6xxx_g2_smi_phy_access(chip, external, true, op, port, dev);
636}
637
638/* IEEE 802.3 Clause 45 Write Address Register */
639static int mv88e6xxx_g2_smi_phy_write_addr_c45(struct mv88e6xxx_chip *chip,
640 bool external, int port, int dev,
641 int addr)
642{
643 u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_ADDR;
644 int err;
645
646 err = mv88e6xxx_g2_smi_phy_wait(chip);
647 if (err)
648 return err;
649
650 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, addr);
651 if (err)
652 return err;
653
654 return mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev);
655}
656
657/* IEEE 802.3 Clause 45 Read Data Register */
658static int mv88e6xxx_g2_smi_phy_read_data_c45(struct mv88e6xxx_chip *chip,
659 bool external, int port, int dev,
660 u16 *data)
661{
662 u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_READ_DATA;
663 int err;
664
665 err = mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev);
666 if (err)
667 return err;
668
669 return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
670}
671
672static int mv88e6xxx_g2_smi_phy_read_c45(struct mv88e6xxx_chip *chip,
673 bool external, int port, int reg,
674 u16 *data)
675{
676 int dev = (reg >> 16) & 0x1f;
677 int addr = reg & 0xffff;
678 int err;
679
680 err = mv88e6xxx_g2_smi_phy_write_addr_c45(chip, external, port, dev,
681 addr);
682 if (err)
683 return err;
684
685 return mv88e6xxx_g2_smi_phy_read_data_c45(chip, external, port, dev,
686 data);
687}
688
689/* IEEE 802.3 Clause 45 Write Data Register */
690static int mv88e6xxx_g2_smi_phy_write_data_c45(struct mv88e6xxx_chip *chip,
691 bool external, int port, int dev,
692 u16 data)
693{
694 u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_DATA;
695 int err;
696
697 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
698 if (err)
699 return err;
700
701 return mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev);
702}
703
704static int mv88e6xxx_g2_smi_phy_write_c45(struct mv88e6xxx_chip *chip,
705 bool external, int port, int reg,
706 u16 data)
707{
708 int dev = (reg >> 16) & 0x1f;
709 int addr = reg & 0xffff;
710 int err;
711
712 err = mv88e6xxx_g2_smi_phy_write_addr_c45(chip, external, port, dev,
713 addr);
714 if (err)
715 return err;
716
717 return mv88e6xxx_g2_smi_phy_write_data_c45(chip, external, port, dev,
718 data);
719}
720
721int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100722 int addr, int reg, u16 *val)
723{
724 struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
725 bool external = mdio_bus->external;
726
727 if (reg & MII_ADDR_C45)
Vivien Didelote289ef02017-06-19 10:55:37 -0400728 return mv88e6xxx_g2_smi_phy_read_c45(chip, external, addr, reg,
729 val);
730
731 return mv88e6xxx_g2_smi_phy_read_data_c22(chip, external, addr, reg,
732 val);
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100733}
734
Vivien Didelote289ef02017-06-19 10:55:37 -0400735int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100736 int addr, int reg, u16 val)
737{
738 struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
739 bool external = mdio_bus->external;
740
741 if (reg & MII_ADDR_C45)
Vivien Didelote289ef02017-06-19 10:55:37 -0400742 return mv88e6xxx_g2_smi_phy_write_c45(chip, external, addr, reg,
743 val);
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100744
Vivien Didelote289ef02017-06-19 10:55:37 -0400745 return mv88e6xxx_g2_smi_phy_write_data_c22(chip, external, addr, reg,
746 val);
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100747}
748
Andrew Lunnfcd25162017-02-09 00:03:42 +0100749static int mv88e6097_watchdog_action(struct mv88e6xxx_chip *chip, int irq)
750{
751 u16 reg;
752
753 mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
754
755 dev_info(chip->dev, "Watchdog event: 0x%04x", reg);
756
757 return IRQ_HANDLED;
758}
759
760static void mv88e6097_watchdog_free(struct mv88e6xxx_chip *chip)
761{
762 u16 reg;
763
764 mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
765
766 reg &= ~(GLOBAL2_WDOG_CONTROL_EGRESS_ENABLE |
767 GLOBAL2_WDOG_CONTROL_QC_ENABLE);
768
769 mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, reg);
770}
771
772static int mv88e6097_watchdog_setup(struct mv88e6xxx_chip *chip)
773{
774 return mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL,
775 GLOBAL2_WDOG_CONTROL_EGRESS_ENABLE |
776 GLOBAL2_WDOG_CONTROL_QC_ENABLE |
777 GLOBAL2_WDOG_CONTROL_SWRESET);
778}
779
780const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = {
781 .irq_action = mv88e6097_watchdog_action,
782 .irq_setup = mv88e6097_watchdog_setup,
783 .irq_free = mv88e6097_watchdog_free,
784};
785
Andrew Lunn61303732017-02-09 00:03:43 +0100786static int mv88e6390_watchdog_setup(struct mv88e6xxx_chip *chip)
787{
788 return mv88e6xxx_g2_update(chip, GLOBAL2_WDOG_CONTROL,
789 GLOBAL2_WDOG_INT_ENABLE |
790 GLOBAL2_WDOG_CUT_THROUGH |
791 GLOBAL2_WDOG_QUEUE_CONTROLLER |
792 GLOBAL2_WDOG_EGRESS |
793 GLOBAL2_WDOG_FORCE_IRQ);
794}
795
796static int mv88e6390_watchdog_action(struct mv88e6xxx_chip *chip, int irq)
797{
798 int err;
799 u16 reg;
800
801 mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, GLOBAL2_WDOG_EVENT);
802 err = mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
803
804 dev_info(chip->dev, "Watchdog event: 0x%04x",
805 reg & GLOBAL2_WDOG_DATA_MASK);
806
807 mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, GLOBAL2_WDOG_HISTORY);
808 err = mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
809
810 dev_info(chip->dev, "Watchdog history: 0x%04x",
811 reg & GLOBAL2_WDOG_DATA_MASK);
812
813 /* Trigger a software reset to try to recover the switch */
814 if (chip->info->ops->reset)
815 chip->info->ops->reset(chip);
816
817 mv88e6390_watchdog_setup(chip);
818
819 return IRQ_HANDLED;
820}
821
822static void mv88e6390_watchdog_free(struct mv88e6xxx_chip *chip)
823{
824 mv88e6xxx_g2_update(chip, GLOBAL2_WDOG_CONTROL,
825 GLOBAL2_WDOG_INT_ENABLE);
826}
827
828const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = {
829 .irq_action = mv88e6390_watchdog_action,
830 .irq_setup = mv88e6390_watchdog_setup,
831 .irq_free = mv88e6390_watchdog_free,
832};
833
Andrew Lunnfcd25162017-02-09 00:03:42 +0100834static irqreturn_t mv88e6xxx_g2_watchdog_thread_fn(int irq, void *dev_id)
835{
836 struct mv88e6xxx_chip *chip = dev_id;
837 irqreturn_t ret = IRQ_NONE;
838
839 mutex_lock(&chip->reg_lock);
840 if (chip->info->ops->watchdog_ops->irq_action)
841 ret = chip->info->ops->watchdog_ops->irq_action(chip, irq);
842 mutex_unlock(&chip->reg_lock);
843
844 return ret;
845}
846
847static void mv88e6xxx_g2_watchdog_free(struct mv88e6xxx_chip *chip)
848{
849 mutex_lock(&chip->reg_lock);
850 if (chip->info->ops->watchdog_ops->irq_free)
851 chip->info->ops->watchdog_ops->irq_free(chip);
852 mutex_unlock(&chip->reg_lock);
853
854 free_irq(chip->watchdog_irq, chip);
855 irq_dispose_mapping(chip->watchdog_irq);
856}
857
858static int mv88e6xxx_g2_watchdog_setup(struct mv88e6xxx_chip *chip)
859{
860 int err;
861
862 chip->watchdog_irq = irq_find_mapping(chip->g2_irq.domain,
863 GLOBAL2_INT_SOURCE_WATCHDOG);
864 if (chip->watchdog_irq < 0)
865 return chip->watchdog_irq;
866
867 err = request_threaded_irq(chip->watchdog_irq, NULL,
868 mv88e6xxx_g2_watchdog_thread_fn,
869 IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
870 "mv88e6xxx-watchdog", chip);
871 if (err)
872 return err;
873
874 mutex_lock(&chip->reg_lock);
875 if (chip->info->ops->watchdog_ops->irq_setup)
876 err = chip->info->ops->watchdog_ops->irq_setup(chip);
877 mutex_unlock(&chip->reg_lock);
878
879 return err;
880}
881
Vivien Didelot81228992017-03-30 17:37:08 -0400882/* Offset 0x1D: Misc Register */
883
884static int mv88e6xxx_g2_misc_5_bit_port(struct mv88e6xxx_chip *chip,
885 bool port_5_bit)
886{
887 u16 val;
888 int err;
889
890 err = mv88e6xxx_g2_read(chip, GLOBAL2_MISC, &val);
891 if (err)
892 return err;
893
894 if (port_5_bit)
895 val |= GLOBAL2_MISC_5_BIT_PORT;
896 else
897 val &= ~GLOBAL2_MISC_5_BIT_PORT;
898
899 return mv88e6xxx_g2_write(chip, GLOBAL2_MISC, val);
900}
901
902int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip)
903{
904 return mv88e6xxx_g2_misc_5_bit_port(chip, false);
905}
906
Andrew Lunndc30c352016-10-16 19:56:49 +0200907static void mv88e6xxx_g2_irq_mask(struct irq_data *d)
908{
909 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
910 unsigned int n = d->hwirq;
911
912 chip->g2_irq.masked |= (1 << n);
913}
914
915static void mv88e6xxx_g2_irq_unmask(struct irq_data *d)
916{
917 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
918 unsigned int n = d->hwirq;
919
920 chip->g2_irq.masked &= ~(1 << n);
921}
922
923static irqreturn_t mv88e6xxx_g2_irq_thread_fn(int irq, void *dev_id)
924{
925 struct mv88e6xxx_chip *chip = dev_id;
926 unsigned int nhandled = 0;
927 unsigned int sub_irq;
928 unsigned int n;
929 int err;
930 u16 reg;
931
932 mutex_lock(&chip->reg_lock);
933 err = mv88e6xxx_g2_read(chip, GLOBAL2_INT_SOURCE, &reg);
934 mutex_unlock(&chip->reg_lock);
935 if (err)
936 goto out;
937
938 for (n = 0; n < 16; ++n) {
939 if (reg & (1 << n)) {
940 sub_irq = irq_find_mapping(chip->g2_irq.domain, n);
941 handle_nested_irq(sub_irq);
942 ++nhandled;
943 }
944 }
945out:
946 return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
947}
948
949static void mv88e6xxx_g2_irq_bus_lock(struct irq_data *d)
950{
951 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
952
953 mutex_lock(&chip->reg_lock);
954}
955
956static void mv88e6xxx_g2_irq_bus_sync_unlock(struct irq_data *d)
957{
958 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
959
960 mv88e6xxx_g2_write(chip, GLOBAL2_INT_MASK, ~chip->g2_irq.masked);
961
962 mutex_unlock(&chip->reg_lock);
963}
964
965static struct irq_chip mv88e6xxx_g2_irq_chip = {
966 .name = "mv88e6xxx-g2",
967 .irq_mask = mv88e6xxx_g2_irq_mask,
968 .irq_unmask = mv88e6xxx_g2_irq_unmask,
969 .irq_bus_lock = mv88e6xxx_g2_irq_bus_lock,
970 .irq_bus_sync_unlock = mv88e6xxx_g2_irq_bus_sync_unlock,
971};
972
973static int mv88e6xxx_g2_irq_domain_map(struct irq_domain *d,
974 unsigned int irq,
975 irq_hw_number_t hwirq)
976{
977 struct mv88e6xxx_chip *chip = d->host_data;
978
979 irq_set_chip_data(irq, d->host_data);
980 irq_set_chip_and_handler(irq, &chip->g2_irq.chip, handle_level_irq);
981 irq_set_noprobe(irq);
982
983 return 0;
984}
985
986static const struct irq_domain_ops mv88e6xxx_g2_irq_domain_ops = {
987 .map = mv88e6xxx_g2_irq_domain_map,
988 .xlate = irq_domain_xlate_twocell,
989};
990
991void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip)
992{
993 int irq, virq;
994
Andrew Lunnfcd25162017-02-09 00:03:42 +0100995 mv88e6xxx_g2_watchdog_free(chip);
996
Andrew Lunn8e757eb2016-11-20 20:14:18 +0100997 free_irq(chip->device_irq, chip);
998 irq_dispose_mapping(chip->device_irq);
999
Andrew Lunndc30c352016-10-16 19:56:49 +02001000 for (irq = 0; irq < 16; irq++) {
1001 virq = irq_find_mapping(chip->g2_irq.domain, irq);
1002 irq_dispose_mapping(virq);
1003 }
1004
1005 irq_domain_remove(chip->g2_irq.domain);
1006}
1007
1008int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip)
1009{
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001010 int err, irq, virq;
Andrew Lunndc30c352016-10-16 19:56:49 +02001011
1012 if (!chip->dev->of_node)
1013 return -EINVAL;
1014
1015 chip->g2_irq.domain = irq_domain_add_simple(
1016 chip->dev->of_node, 16, 0, &mv88e6xxx_g2_irq_domain_ops, chip);
1017 if (!chip->g2_irq.domain)
1018 return -ENOMEM;
1019
1020 for (irq = 0; irq < 16; irq++)
1021 irq_create_mapping(chip->g2_irq.domain, irq);
1022
1023 chip->g2_irq.chip = mv88e6xxx_g2_irq_chip;
1024 chip->g2_irq.masked = ~0;
1025
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001026 chip->device_irq = irq_find_mapping(chip->g1_irq.domain,
Vivien Didelot82466922017-06-15 12:13:59 -04001027 MV88E6XXX_G1_STS_IRQ_DEVICE);
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001028 if (chip->device_irq < 0) {
1029 err = chip->device_irq;
Andrew Lunndc30c352016-10-16 19:56:49 +02001030 goto out;
1031 }
1032
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001033 err = request_threaded_irq(chip->device_irq, NULL,
1034 mv88e6xxx_g2_irq_thread_fn,
1035 IRQF_ONESHOT, "mv88e6xxx-g1", chip);
Andrew Lunndc30c352016-10-16 19:56:49 +02001036 if (err)
1037 goto out;
1038
Andrew Lunnfcd25162017-02-09 00:03:42 +01001039 return mv88e6xxx_g2_watchdog_setup(chip);
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001040
Andrew Lunndc30c352016-10-16 19:56:49 +02001041out:
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001042 for (irq = 0; irq < 16; irq++) {
1043 virq = irq_find_mapping(chip->g2_irq.domain, irq);
1044 irq_dispose_mapping(virq);
1045 }
1046
1047 irq_domain_remove(chip->g2_irq.domain);
Andrew Lunndc30c352016-10-16 19:56:49 +02001048
1049 return err;
1050}
1051
Vivien Didelotec561272016-09-02 14:45:33 -04001052int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip)
1053{
1054 u16 reg;
1055 int err;
1056
Vivien Didelotec561272016-09-02 14:45:33 -04001057 /* Ignore removed tag data on doubly tagged packets, disable
1058 * flow control messages, force flow control priority to the
1059 * highest, and send all special multicast frames to the CPU
1060 * port at the highest priority.
1061 */
Vivien Didelot6bff47b2017-06-19 10:55:40 -04001062 reg = MV88E6XXX_G2_SWITCH_MGMT_FORCE_FLOW_CTL_PRI | (0x7 << 4);
Vivien Didelotec561272016-09-02 14:45:33 -04001063 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X) ||
1064 mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X))
Vivien Didelot6bff47b2017-06-19 10:55:40 -04001065 reg |= MV88E6XXX_G2_SWITCH_MGMT_RSVD2CPU | 0x7;
1066 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SWITCH_MGMT, reg);
Vivien Didelotec561272016-09-02 14:45:33 -04001067 if (err)
1068 return err;
1069
1070 /* Program the DSA routing table. */
1071 err = mv88e6xxx_g2_set_device_mapping(chip);
1072 if (err)
1073 return err;
1074
1075 /* Clear all trunk masks and mapping. */
1076 err = mv88e6xxx_g2_clear_trunk(chip);
1077 if (err)
1078 return err;
1079
Vivien Didelotec561272016-09-02 14:45:33 -04001080 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_POT)) {
1081 /* Clear the priority override table. */
1082 err = mv88e6xxx_g2_clear_pot(chip);
1083 if (err)
1084 return err;
1085 }
1086
1087 return 0;
1088}