1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Marvell 88E6xxx Switch Global 2 Registers support |
4 | * |
5 | * Copyright (c) 2008 Marvell Semiconductor |
6 | * |
7 | * Copyright (c) 2016-2017 Savoir-faire Linux Inc. |
8 | * Vivien Didelot <vivien.didelot@savoirfairelinux.com> |
9 | */ |
10 | |
11 | #include <linux/bitfield.h> |
12 | #include <linux/interrupt.h> |
13 | #include <linux/irqdomain.h> |
14 | |
15 | #include "chip.h" |
16 | #include "global1.h" /* for MV88E6XXX_G1_STS_IRQ_DEVICE */ |
17 | #include "global2.h" |
18 | |
19 | int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val) |
20 | { |
21 | return mv88e6xxx_read(chip, addr: chip->info->global2_addr, reg, val); |
22 | } |
23 | |
24 | int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val) |
25 | { |
26 | return mv88e6xxx_write(chip, addr: chip->info->global2_addr, reg, val); |
27 | } |
28 | |
29 | int mv88e6xxx_g2_wait_bit(struct mv88e6xxx_chip *chip, int reg, int |
30 | bit, int val) |
31 | { |
32 | return mv88e6xxx_wait_bit(chip, addr: chip->info->global2_addr, reg, |
33 | bit, val); |
34 | } |
35 | |
36 | /* Offset 0x00: Interrupt Source Register */ |
37 | |
38 | static int mv88e6xxx_g2_int_source(struct mv88e6xxx_chip *chip, u16 *src) |
39 | { |
40 | /* Read (and clear most of) the Interrupt Source bits */ |
41 | return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_INT_SRC, val: src); |
42 | } |
43 | |
44 | /* Offset 0x01: Interrupt Mask Register */ |
45 | |
46 | static int mv88e6xxx_g2_int_mask(struct mv88e6xxx_chip *chip, u16 mask) |
47 | { |
48 | return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_INT_MASK, val: mask); |
49 | } |
50 | |
51 | /* Offset 0x02: Management Enable 2x */ |
52 | |
53 | static int mv88e6xxx_g2_mgmt_enable_2x(struct mv88e6xxx_chip *chip, u16 en2x) |
54 | { |
55 | return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_MGMT_EN_2X, val: en2x); |
56 | } |
57 | |
58 | /* Offset 0x03: Management Enable 0x */ |
59 | |
60 | static int mv88e6xxx_g2_mgmt_enable_0x(struct mv88e6xxx_chip *chip, u16 en0x) |
61 | { |
62 | return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_MGMT_EN_0X, val: en0x); |
63 | } |
64 | |
65 | /* Offset 0x05: Switch Management Register */ |
66 | |
67 | static int mv88e6xxx_g2_switch_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip, |
68 | bool enable) |
69 | { |
70 | u16 val; |
71 | int err; |
72 | |
73 | err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SWITCH_MGMT, val: &val); |
74 | if (err) |
75 | return err; |
76 | |
77 | if (enable) |
78 | val |= MV88E6XXX_G2_SWITCH_MGMT_RSVD2CPU; |
79 | else |
80 | val &= ~MV88E6XXX_G2_SWITCH_MGMT_RSVD2CPU; |
81 | |
82 | return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SWITCH_MGMT, val); |
83 | } |
84 | |
85 | int mv88e6185_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) |
86 | { |
87 | int err; |
88 | |
89 | /* Consider the frames with reserved multicast destination |
90 | * addresses matching 01:80:c2:00:00:0x as MGMT. |
91 | */ |
92 | err = mv88e6xxx_g2_mgmt_enable_0x(chip, en0x: 0xffff); |
93 | if (err) |
94 | return err; |
95 | |
96 | return mv88e6xxx_g2_switch_mgmt_rsvd2cpu(chip, enable: true); |
97 | } |
98 | |
99 | int mv88e6352_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) |
100 | { |
101 | int err; |
102 | |
103 | /* Consider the frames with reserved multicast destination |
104 | * addresses matching 01:80:c2:00:00:2x as MGMT. |
105 | */ |
106 | err = mv88e6xxx_g2_mgmt_enable_2x(chip, en2x: 0xffff); |
107 | if (err) |
108 | return err; |
109 | |
110 | return mv88e6185_g2_mgmt_rsvd2cpu(chip); |
111 | } |
112 | |
113 | /* Offset 0x06: Device Mapping Table register */ |
114 | |
115 | int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, int target, |
116 | int port) |
117 | { |
118 | u16 val = (target << 8) | (port & 0x1f); |
119 | /* Modern chips use 5 bits to define a device mapping port, |
120 | * but bit 4 is reserved on older chips, so it is safe to use. |
121 | */ |
122 | |
123 | return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_DEVICE_MAPPING, |
124 | MV88E6XXX_G2_DEVICE_MAPPING_UPDATE | val); |
125 | } |
126 | |
127 | /* Offset 0x07: Trunk Mask Table register */ |
128 | |
129 | int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num, |
130 | bool hash, u16 mask) |
131 | { |
132 | u16 val = (num << 12) | (mask & mv88e6xxx_port_mask(chip)); |
133 | |
134 | if (hash) |
135 | val |= MV88E6XXX_G2_TRUNK_MASK_HASH; |
136 | |
137 | return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_TRUNK_MASK, |
138 | MV88E6XXX_G2_TRUNK_MASK_UPDATE | val); |
139 | } |
140 | |
141 | /* Offset 0x08: Trunk Mapping Table register */ |
142 | |
143 | int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id, |
144 | u16 map) |
145 | { |
146 | const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1; |
147 | u16 val = (id << 11) | (map & port_mask); |
148 | |
149 | return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_TRUNK_MAPPING, |
150 | MV88E6XXX_G2_TRUNK_MAPPING_UPDATE | val); |
151 | } |
152 | |
153 | int mv88e6xxx_g2_trunk_clear(struct mv88e6xxx_chip *chip) |
154 | { |
155 | const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1; |
156 | int i, err; |
157 | |
158 | /* Clear all eight possible Trunk Mask vectors */ |
159 | for (i = 0; i < 8; ++i) { |
160 | err = mv88e6xxx_g2_trunk_mask_write(chip, num: i, hash: false, mask: port_mask); |
161 | if (err) |
162 | return err; |
163 | } |
164 | |
165 | /* Clear all sixteen possible Trunk ID routing vectors */ |
166 | for (i = 0; i < 16; ++i) { |
167 | err = mv88e6xxx_g2_trunk_mapping_write(chip, id: i, map: 0); |
168 | if (err) |
169 | return err; |
170 | } |
171 | |
172 | return 0; |
173 | } |
174 | |
175 | /* Offset 0x09: Ingress Rate Command register |
176 | * Offset 0x0A: Ingress Rate Data register |
177 | */ |
178 | |
179 | static int mv88e6xxx_g2_irl_wait(struct mv88e6xxx_chip *chip) |
180 | { |
181 | int bit = __bf_shf(MV88E6XXX_G2_IRL_CMD_BUSY); |
182 | |
183 | return mv88e6xxx_g2_wait_bit(chip, MV88E6XXX_G2_IRL_CMD, bit, val: 0); |
184 | } |
185 | |
186 | static int mv88e6xxx_g2_irl_op(struct mv88e6xxx_chip *chip, u16 op, int port, |
187 | int res, int reg) |
188 | { |
189 | int err; |
190 | |
191 | err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_IRL_CMD, |
192 | MV88E6XXX_G2_IRL_CMD_BUSY | op | (port << 8) | |
193 | (res << 5) | reg); |
194 | if (err) |
195 | return err; |
196 | |
197 | return mv88e6xxx_g2_irl_wait(chip); |
198 | } |
199 | |
200 | int mv88e6352_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port) |
201 | { |
202 | return mv88e6xxx_g2_irl_op(chip, MV88E6352_G2_IRL_CMD_OP_INIT_ALL, port, |
203 | res: 0, reg: 0); |
204 | } |
205 | |
206 | int mv88e6390_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port) |
207 | { |
208 | return mv88e6xxx_g2_irl_op(chip, MV88E6390_G2_IRL_CMD_OP_INIT_ALL, port, |
209 | res: 0, reg: 0); |
210 | } |
211 | |
212 | /* Offset 0x0B: Cross-chip Port VLAN (Addr) Register |
213 | * Offset 0x0C: Cross-chip Port VLAN Data Register |
214 | */ |
215 | |
216 | static int mv88e6xxx_g2_pvt_op_wait(struct mv88e6xxx_chip *chip) |
217 | { |
218 | int bit = __bf_shf(MV88E6XXX_G2_PVT_ADDR_BUSY); |
219 | |
220 | return mv88e6xxx_g2_wait_bit(chip, MV88E6XXX_G2_PVT_ADDR, bit, val: 0); |
221 | } |
222 | |
223 | static int mv88e6xxx_g2_pvt_op(struct mv88e6xxx_chip *chip, int src_dev, |
224 | int src_port, u16 op) |
225 | { |
226 | int err; |
227 | |
228 | /* 9-bit Cross-chip PVT pointer: with MV88E6XXX_G2_MISC_5_BIT_PORT |
229 | * cleared, source device is 5-bit, source port is 4-bit. |
230 | */ |
231 | op |= MV88E6XXX_G2_PVT_ADDR_BUSY; |
232 | op |= (src_dev & 0x1f) << 4; |
233 | op |= (src_port & 0xf); |
234 | |
235 | err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_PVT_ADDR, val: op); |
236 | if (err) |
237 | return err; |
238 | |
239 | return mv88e6xxx_g2_pvt_op_wait(chip); |
240 | } |
241 | |
242 | int mv88e6xxx_g2_pvt_read(struct mv88e6xxx_chip *chip, int src_dev, |
243 | int src_port, u16 *data) |
244 | { |
245 | int err; |
246 | |
247 | err = mv88e6xxx_g2_pvt_op_wait(chip); |
248 | if (err) |
249 | return err; |
250 | |
251 | err = mv88e6xxx_g2_pvt_op(chip, src_dev, src_port, |
252 | MV88E6XXX_G2_PVT_ADDR_OP_READ); |
253 | if (err) |
254 | return err; |
255 | |
256 | return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_PVT_DATA, val: data); |
257 | } |
258 | |
259 | int mv88e6xxx_g2_pvt_write(struct mv88e6xxx_chip *chip, int src_dev, |
260 | int src_port, u16 data) |
261 | { |
262 | int err; |
263 | |
264 | err = mv88e6xxx_g2_pvt_op_wait(chip); |
265 | if (err) |
266 | return err; |
267 | |
268 | err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_PVT_DATA, val: data); |
269 | if (err) |
270 | return err; |
271 | |
272 | return mv88e6xxx_g2_pvt_op(chip, src_dev, src_port, |
273 | MV88E6XXX_G2_PVT_ADDR_OP_WRITE_PVLAN); |
274 | } |
275 | |
276 | /* Offset 0x0D: Switch MAC/WoL/WoF register */ |
277 | |
278 | static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip, |
279 | unsigned int pointer, u8 data) |
280 | { |
281 | u16 val = (pointer << 8) | data; |
282 | |
283 | return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SWITCH_MAC, |
284 | MV88E6XXX_G2_SWITCH_MAC_UPDATE | val); |
285 | } |
286 | |
287 | int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr) |
288 | { |
289 | int i, err; |
290 | |
291 | for (i = 0; i < 6; i++) { |
292 | err = mv88e6xxx_g2_switch_mac_write(chip, pointer: i, data: addr[i]); |
293 | if (err) |
294 | break; |
295 | } |
296 | |
297 | return err; |
298 | } |
299 | |
300 | /* Offset 0x0E: ATU Statistics */ |
301 | |
302 | int mv88e6xxx_g2_atu_stats_set(struct mv88e6xxx_chip *chip, u16 kind, u16 bin) |
303 | { |
304 | return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_ATU_STATS, |
305 | val: kind | bin); |
306 | } |
307 | |
308 | int mv88e6xxx_g2_atu_stats_get(struct mv88e6xxx_chip *chip, u16 *stats) |
309 | { |
310 | return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_ATU_STATS, val: stats); |
311 | } |
312 | |
313 | /* Offset 0x0F: Priority Override Table */ |
314 | |
315 | static int mv88e6xxx_g2_pot_write(struct mv88e6xxx_chip *chip, int pointer, |
316 | u8 data) |
317 | { |
318 | u16 val = (pointer << 8) | (data & 0x7); |
319 | |
320 | return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_PRIO_OVERRIDE, |
321 | MV88E6XXX_G2_PRIO_OVERRIDE_UPDATE | val); |
322 | } |
323 | |
324 | int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip) |
325 | { |
326 | int i, err; |
327 | |
328 | /* Clear all sixteen possible Priority Override entries */ |
329 | for (i = 0; i < 16; i++) { |
330 | err = mv88e6xxx_g2_pot_write(chip, pointer: i, data: 0); |
331 | if (err) |
332 | break; |
333 | } |
334 | |
335 | return err; |
336 | } |
337 | |
338 | /* Offset 0x14: EEPROM Command |
339 | * Offset 0x15: EEPROM Data (for 16-bit data access) |
340 | * Offset 0x15: EEPROM Addr (for 8-bit data access) |
341 | */ |
342 | |
343 | int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip) |
344 | { |
345 | int bit = __bf_shf(MV88E6XXX_G2_EEPROM_CMD_BUSY); |
346 | int err; |
347 | |
348 | err = mv88e6xxx_g2_wait_bit(chip, MV88E6XXX_G2_EEPROM_CMD, bit, val: 0); |
349 | if (err) |
350 | return err; |
351 | |
352 | bit = __bf_shf(MV88E6XXX_G2_EEPROM_CMD_RUNNING); |
353 | |
354 | return mv88e6xxx_g2_wait_bit(chip, MV88E6XXX_G2_EEPROM_CMD, bit, val: 0); |
355 | } |
356 | |
357 | static int mv88e6xxx_g2_eeprom_cmd(struct mv88e6xxx_chip *chip, u16 cmd) |
358 | { |
359 | int err; |
360 | |
361 | err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_EEPROM_CMD, |
362 | MV88E6XXX_G2_EEPROM_CMD_BUSY | cmd); |
363 | if (err) |
364 | return err; |
365 | |
366 | return mv88e6xxx_g2_eeprom_wait(chip); |
367 | } |
368 | |
369 | static int mv88e6xxx_g2_eeprom_read8(struct mv88e6xxx_chip *chip, |
370 | u16 addr, u8 *data) |
371 | { |
372 | u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_READ; |
373 | int err; |
374 | |
375 | err = mv88e6xxx_g2_eeprom_wait(chip); |
376 | if (err) |
377 | return err; |
378 | |
379 | err = mv88e6xxx_g2_write(chip, MV88E6390_G2_EEPROM_ADDR, val: addr); |
380 | if (err) |
381 | return err; |
382 | |
383 | err = mv88e6xxx_g2_eeprom_cmd(chip, cmd); |
384 | if (err) |
385 | return err; |
386 | |
387 | err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_EEPROM_CMD, val: &cmd); |
388 | if (err) |
389 | return err; |
390 | |
391 | *data = cmd & 0xff; |
392 | |
393 | return 0; |
394 | } |
395 | |
396 | static int mv88e6xxx_g2_eeprom_write8(struct mv88e6xxx_chip *chip, |
397 | u16 addr, u8 data) |
398 | { |
399 | u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_WRITE | |
400 | MV88E6XXX_G2_EEPROM_CMD_WRITE_EN; |
401 | int err; |
402 | |
403 | err = mv88e6xxx_g2_eeprom_wait(chip); |
404 | if (err) |
405 | return err; |
406 | |
407 | err = mv88e6xxx_g2_write(chip, MV88E6390_G2_EEPROM_ADDR, val: addr); |
408 | if (err) |
409 | return err; |
410 | |
411 | return mv88e6xxx_g2_eeprom_cmd(chip, cmd: cmd | data); |
412 | } |
413 | |
414 | static int mv88e6xxx_g2_eeprom_read16(struct mv88e6xxx_chip *chip, |
415 | u8 addr, u16 *data) |
416 | { |
417 | u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_READ | addr; |
418 | int err; |
419 | |
420 | err = mv88e6xxx_g2_eeprom_wait(chip); |
421 | if (err) |
422 | return err; |
423 | |
424 | err = mv88e6xxx_g2_eeprom_cmd(chip, cmd); |
425 | if (err) |
426 | return err; |
427 | |
428 | return mv88e6xxx_g2_read(chip, MV88E6352_G2_EEPROM_DATA, val: data); |
429 | } |
430 | |
431 | static int mv88e6xxx_g2_eeprom_write16(struct mv88e6xxx_chip *chip, |
432 | u8 addr, u16 data) |
433 | { |
434 | u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_WRITE | addr; |
435 | int err; |
436 | |
437 | err = mv88e6xxx_g2_eeprom_wait(chip); |
438 | if (err) |
439 | return err; |
440 | |
441 | err = mv88e6xxx_g2_write(chip, MV88E6352_G2_EEPROM_DATA, val: data); |
442 | if (err) |
443 | return err; |
444 | |
445 | return mv88e6xxx_g2_eeprom_cmd(chip, cmd); |
446 | } |
447 | |
448 | int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip, |
449 | struct ethtool_eeprom *eeprom, u8 *data) |
450 | { |
451 | unsigned int offset = eeprom->offset; |
452 | unsigned int len = eeprom->len; |
453 | int err; |
454 | |
455 | eeprom->len = 0; |
456 | |
457 | while (len) { |
458 | err = mv88e6xxx_g2_eeprom_read8(chip, addr: offset, data); |
459 | if (err) |
460 | return err; |
461 | |
462 | eeprom->len++; |
463 | offset++; |
464 | data++; |
465 | len--; |
466 | } |
467 | |
468 | return 0; |
469 | } |
470 | |
471 | int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip, |
472 | struct ethtool_eeprom *eeprom, u8 *data) |
473 | { |
474 | unsigned int offset = eeprom->offset; |
475 | unsigned int len = eeprom->len; |
476 | int err; |
477 | |
478 | eeprom->len = 0; |
479 | |
480 | while (len) { |
481 | err = mv88e6xxx_g2_eeprom_write8(chip, addr: offset, data: *data); |
482 | if (err) |
483 | return err; |
484 | |
485 | eeprom->len++; |
486 | offset++; |
487 | data++; |
488 | len--; |
489 | } |
490 | |
491 | return 0; |
492 | } |
493 | |
494 | int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip, |
495 | struct ethtool_eeprom *eeprom, u8 *data) |
496 | { |
497 | unsigned int offset = eeprom->offset; |
498 | unsigned int len = eeprom->len; |
499 | u16 val; |
500 | int err; |
501 | |
502 | eeprom->len = 0; |
503 | |
504 | if (offset & 1) { |
505 | err = mv88e6xxx_g2_eeprom_read16(chip, addr: offset >> 1, data: &val); |
506 | if (err) |
507 | return err; |
508 | |
509 | *data++ = (val >> 8) & 0xff; |
510 | |
511 | offset++; |
512 | len--; |
513 | eeprom->len++; |
514 | } |
515 | |
516 | while (len >= 2) { |
517 | err = mv88e6xxx_g2_eeprom_read16(chip, addr: offset >> 1, data: &val); |
518 | if (err) |
519 | return err; |
520 | |
521 | *data++ = val & 0xff; |
522 | *data++ = (val >> 8) & 0xff; |
523 | |
524 | offset += 2; |
525 | len -= 2; |
526 | eeprom->len += 2; |
527 | } |
528 | |
529 | if (len) { |
530 | err = mv88e6xxx_g2_eeprom_read16(chip, addr: offset >> 1, data: &val); |
531 | if (err) |
532 | return err; |
533 | |
534 | *data++ = val & 0xff; |
535 | |
536 | offset++; |
537 | len--; |
538 | eeprom->len++; |
539 | } |
540 | |
541 | return 0; |
542 | } |
543 | |
544 | int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip, |
545 | struct ethtool_eeprom *eeprom, u8 *data) |
546 | { |
547 | unsigned int offset = eeprom->offset; |
548 | unsigned int len = eeprom->len; |
549 | u16 val; |
550 | int err; |
551 | |
552 | /* Ensure the RO WriteEn bit is set */ |
553 | err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_EEPROM_CMD, val: &val); |
554 | if (err) |
555 | return err; |
556 | |
557 | if (!(val & MV88E6XXX_G2_EEPROM_CMD_WRITE_EN)) |
558 | return -EROFS; |
559 | |
560 | eeprom->len = 0; |
561 | |
562 | if (offset & 1) { |
563 | err = mv88e6xxx_g2_eeprom_read16(chip, addr: offset >> 1, data: &val); |
564 | if (err) |
565 | return err; |
566 | |
567 | val = (*data++ << 8) | (val & 0xff); |
568 | |
569 | err = mv88e6xxx_g2_eeprom_write16(chip, addr: offset >> 1, data: val); |
570 | if (err) |
571 | return err; |
572 | |
573 | offset++; |
574 | len--; |
575 | eeprom->len++; |
576 | } |
577 | |
578 | while (len >= 2) { |
579 | val = *data++; |
580 | val |= *data++ << 8; |
581 | |
582 | err = mv88e6xxx_g2_eeprom_write16(chip, addr: offset >> 1, data: val); |
583 | if (err) |
584 | return err; |
585 | |
586 | offset += 2; |
587 | len -= 2; |
588 | eeprom->len += 2; |
589 | } |
590 | |
591 | if (len) { |
592 | err = mv88e6xxx_g2_eeprom_read16(chip, addr: offset >> 1, data: &val); |
593 | if (err) |
594 | return err; |
595 | |
596 | val = (val & 0xff00) | *data++; |
597 | |
598 | err = mv88e6xxx_g2_eeprom_write16(chip, addr: offset >> 1, data: val); |
599 | if (err) |
600 | return err; |
601 | |
602 | offset++; |
603 | len--; |
604 | eeprom->len++; |
605 | } |
606 | |
607 | return 0; |
608 | } |
609 | |
610 | /* Offset 0x18: SMI PHY Command Register |
611 | * Offset 0x19: SMI PHY Data Register |
612 | */ |
613 | |
614 | static int mv88e6xxx_g2_smi_phy_wait(struct mv88e6xxx_chip *chip) |
615 | { |
616 | int bit = __bf_shf(MV88E6XXX_G2_SMI_PHY_CMD_BUSY); |
617 | |
618 | return mv88e6xxx_g2_wait_bit(chip, MV88E6XXX_G2_SMI_PHY_CMD, bit, val: 0); |
619 | } |
620 | |
621 | static int mv88e6xxx_g2_smi_phy_cmd(struct mv88e6xxx_chip *chip, u16 cmd) |
622 | { |
623 | int err; |
624 | |
625 | err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_CMD, |
626 | MV88E6XXX_G2_SMI_PHY_CMD_BUSY | cmd); |
627 | if (err) |
628 | return err; |
629 | |
630 | return mv88e6xxx_g2_smi_phy_wait(chip); |
631 | } |
632 | |
633 | static int mv88e6xxx_g2_smi_phy_access(struct mv88e6xxx_chip *chip, |
634 | bool external, bool c45, u16 op, int dev, |
635 | int reg) |
636 | { |
637 | u16 cmd = op; |
638 | |
639 | if (external) |
640 | cmd |= MV88E6390_G2_SMI_PHY_CMD_FUNC_EXTERNAL; |
641 | else |
642 | cmd |= MV88E6390_G2_SMI_PHY_CMD_FUNC_INTERNAL; /* empty mask */ |
643 | |
644 | if (c45) |
645 | cmd |= MV88E6XXX_G2_SMI_PHY_CMD_MODE_45; /* empty mask */ |
646 | else |
647 | cmd |= MV88E6XXX_G2_SMI_PHY_CMD_MODE_22; |
648 | |
649 | dev <<= __bf_shf(MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK); |
650 | cmd |= dev & MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK; |
651 | cmd |= reg & MV88E6XXX_G2_SMI_PHY_CMD_REG_ADDR_MASK; |
652 | |
653 | return mv88e6xxx_g2_smi_phy_cmd(chip, cmd); |
654 | } |
655 | |
656 | static int mv88e6xxx_g2_smi_phy_access_c22(struct mv88e6xxx_chip *chip, |
657 | bool external, u16 op, int dev, |
658 | int reg) |
659 | { |
660 | return mv88e6xxx_g2_smi_phy_access(chip, external, c45: false, op, dev, reg); |
661 | } |
662 | |
663 | /* IEEE 802.3 Clause 22 Read Data Register */ |
664 | static int mv88e6xxx_g2_smi_phy_read_data_c22(struct mv88e6xxx_chip *chip, |
665 | bool external, int dev, int reg, |
666 | u16 *data) |
667 | { |
668 | u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_22_READ_DATA; |
669 | int err; |
670 | |
671 | err = mv88e6xxx_g2_smi_phy_wait(chip); |
672 | if (err) |
673 | return err; |
674 | |
675 | err = mv88e6xxx_g2_smi_phy_access_c22(chip, external, op, dev, reg); |
676 | if (err) |
677 | return err; |
678 | |
679 | return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SMI_PHY_DATA, val: data); |
680 | } |
681 | |
682 | /* IEEE 802.3 Clause 22 Write Data Register */ |
683 | static int mv88e6xxx_g2_smi_phy_write_data_c22(struct mv88e6xxx_chip *chip, |
684 | bool external, int dev, int reg, |
685 | u16 data) |
686 | { |
687 | u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_22_WRITE_DATA; |
688 | int err; |
689 | |
690 | err = mv88e6xxx_g2_smi_phy_wait(chip); |
691 | if (err) |
692 | return err; |
693 | |
694 | err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, val: data); |
695 | if (err) |
696 | return err; |
697 | |
698 | return mv88e6xxx_g2_smi_phy_access_c22(chip, external, op, dev, reg); |
699 | } |
700 | |
701 | static int mv88e6xxx_g2_smi_phy_access_c45(struct mv88e6xxx_chip *chip, |
702 | bool external, u16 op, int port, |
703 | int dev) |
704 | { |
705 | return mv88e6xxx_g2_smi_phy_access(chip, external, c45: true, op, dev: port, reg: dev); |
706 | } |
707 | |
708 | /* IEEE 802.3 Clause 45 Write Address Register */ |
709 | static int mv88e6xxx_g2_smi_phy_write_addr_c45(struct mv88e6xxx_chip *chip, |
710 | bool external, int port, int dev, |
711 | int addr) |
712 | { |
713 | u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_ADDR; |
714 | int err; |
715 | |
716 | err = mv88e6xxx_g2_smi_phy_wait(chip); |
717 | if (err) |
718 | return err; |
719 | |
720 | err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, val: addr); |
721 | if (err) |
722 | return err; |
723 | |
724 | return mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev); |
725 | } |
726 | |
727 | /* IEEE 802.3 Clause 45 Read Data Register */ |
728 | static int mv88e6xxx_g2_smi_phy_read_data_c45(struct mv88e6xxx_chip *chip, |
729 | bool external, int port, int dev, |
730 | u16 *data) |
731 | { |
732 | u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_READ_DATA; |
733 | int err; |
734 | |
735 | err = mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev); |
736 | if (err) |
737 | return err; |
738 | |
739 | return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SMI_PHY_DATA, val: data); |
740 | } |
741 | |
742 | static int _mv88e6xxx_g2_smi_phy_read_c45(struct mv88e6xxx_chip *chip, |
743 | bool external, int port, int devad, |
744 | int reg, u16 *data) |
745 | { |
746 | int err; |
747 | |
748 | err = mv88e6xxx_g2_smi_phy_write_addr_c45(chip, external, port, dev: devad, |
749 | addr: reg); |
750 | if (err) |
751 | return err; |
752 | |
753 | return mv88e6xxx_g2_smi_phy_read_data_c45(chip, external, port, dev: devad, |
754 | data); |
755 | } |
756 | |
757 | /* IEEE 802.3 Clause 45 Write Data Register */ |
758 | static int mv88e6xxx_g2_smi_phy_write_data_c45(struct mv88e6xxx_chip *chip, |
759 | bool external, int port, int dev, |
760 | u16 data) |
761 | { |
762 | u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_DATA; |
763 | int err; |
764 | |
765 | err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, val: data); |
766 | if (err) |
767 | return err; |
768 | |
769 | return mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev); |
770 | } |
771 | |
772 | static int _mv88e6xxx_g2_smi_phy_write_c45(struct mv88e6xxx_chip *chip, |
773 | bool external, int port, int devad, |
774 | int reg, u16 data) |
775 | { |
776 | int err; |
777 | |
778 | err = mv88e6xxx_g2_smi_phy_write_addr_c45(chip, external, port, dev: devad, |
779 | addr: reg); |
780 | if (err) |
781 | return err; |
782 | |
783 | return mv88e6xxx_g2_smi_phy_write_data_c45(chip, external, port, dev: devad, |
784 | data); |
785 | } |
786 | |
787 | int mv88e6xxx_g2_smi_phy_read_c22(struct mv88e6xxx_chip *chip, |
788 | struct mii_bus *bus, |
789 | int addr, int reg, u16 *val) |
790 | { |
791 | struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv; |
792 | bool external = mdio_bus->external; |
793 | |
794 | return mv88e6xxx_g2_smi_phy_read_data_c22(chip, external, dev: addr, reg, |
795 | data: val); |
796 | } |
797 | |
798 | int mv88e6xxx_g2_smi_phy_read_c45(struct mv88e6xxx_chip *chip, |
799 | struct mii_bus *bus, int addr, int devad, |
800 | int reg, u16 *val) |
801 | { |
802 | struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv; |
803 | bool external = mdio_bus->external; |
804 | |
805 | return _mv88e6xxx_g2_smi_phy_read_c45(chip, external, port: addr, devad, reg, |
806 | data: val); |
807 | } |
808 | |
809 | int mv88e6xxx_g2_smi_phy_write_c22(struct mv88e6xxx_chip *chip, |
810 | struct mii_bus *bus, int addr, int reg, |
811 | u16 val) |
812 | { |
813 | struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv; |
814 | bool external = mdio_bus->external; |
815 | |
816 | return mv88e6xxx_g2_smi_phy_write_data_c22(chip, external, dev: addr, reg, |
817 | data: val); |
818 | } |
819 | |
820 | int mv88e6xxx_g2_smi_phy_write_c45(struct mv88e6xxx_chip *chip, |
821 | struct mii_bus *bus, int addr, int devad, |
822 | int reg, u16 val) |
823 | { |
824 | struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv; |
825 | bool external = mdio_bus->external; |
826 | |
827 | return _mv88e6xxx_g2_smi_phy_write_c45(chip, external, port: addr, devad, reg, |
828 | data: val); |
829 | } |
830 | |
831 | /* Offset 0x1B: Watchdog Control */ |
832 | static int mv88e6097_watchdog_action(struct mv88e6xxx_chip *chip, int irq) |
833 | { |
834 | u16 reg; |
835 | |
836 | mv88e6xxx_g2_read(chip, MV88E6352_G2_WDOG_CTL, val: ®); |
837 | |
838 | dev_info(chip->dev, "Watchdog event: 0x%04x" , reg); |
839 | |
840 | return IRQ_HANDLED; |
841 | } |
842 | |
843 | static void mv88e6097_watchdog_free(struct mv88e6xxx_chip *chip) |
844 | { |
845 | u16 reg; |
846 | |
847 | mv88e6xxx_g2_read(chip, MV88E6352_G2_WDOG_CTL, val: ®); |
848 | |
849 | reg &= ~(MV88E6352_G2_WDOG_CTL_EGRESS_ENABLE | |
850 | MV88E6352_G2_WDOG_CTL_QC_ENABLE); |
851 | |
852 | mv88e6xxx_g2_write(chip, MV88E6352_G2_WDOG_CTL, val: reg); |
853 | } |
854 | |
855 | static int mv88e6097_watchdog_setup(struct mv88e6xxx_chip *chip) |
856 | { |
857 | return mv88e6xxx_g2_write(chip, MV88E6352_G2_WDOG_CTL, |
858 | MV88E6352_G2_WDOG_CTL_EGRESS_ENABLE | |
859 | MV88E6352_G2_WDOG_CTL_QC_ENABLE | |
860 | MV88E6352_G2_WDOG_CTL_SWRESET); |
861 | } |
862 | |
863 | const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = { |
864 | .irq_action = mv88e6097_watchdog_action, |
865 | .irq_setup = mv88e6097_watchdog_setup, |
866 | .irq_free = mv88e6097_watchdog_free, |
867 | }; |
868 | |
869 | static void mv88e6250_watchdog_free(struct mv88e6xxx_chip *chip) |
870 | { |
871 | u16 reg; |
872 | |
873 | mv88e6xxx_g2_read(chip, MV88E6250_G2_WDOG_CTL, val: ®); |
874 | |
875 | reg &= ~(MV88E6250_G2_WDOG_CTL_EGRESS_ENABLE | |
876 | MV88E6250_G2_WDOG_CTL_QC_ENABLE); |
877 | |
878 | mv88e6xxx_g2_write(chip, MV88E6250_G2_WDOG_CTL, val: reg); |
879 | } |
880 | |
881 | static int mv88e6250_watchdog_setup(struct mv88e6xxx_chip *chip) |
882 | { |
883 | return mv88e6xxx_g2_write(chip, MV88E6250_G2_WDOG_CTL, |
884 | MV88E6250_G2_WDOG_CTL_EGRESS_ENABLE | |
885 | MV88E6250_G2_WDOG_CTL_QC_ENABLE | |
886 | MV88E6250_G2_WDOG_CTL_SWRESET); |
887 | } |
888 | |
889 | const struct mv88e6xxx_irq_ops mv88e6250_watchdog_ops = { |
890 | .irq_action = mv88e6097_watchdog_action, |
891 | .irq_setup = mv88e6250_watchdog_setup, |
892 | .irq_free = mv88e6250_watchdog_free, |
893 | }; |
894 | |
895 | static int mv88e6390_watchdog_setup(struct mv88e6xxx_chip *chip) |
896 | { |
897 | return mv88e6xxx_g2_write(chip, MV88E6390_G2_WDOG_CTL, |
898 | MV88E6390_G2_WDOG_CTL_UPDATE | |
899 | MV88E6390_G2_WDOG_CTL_PTR_INT_ENABLE | |
900 | MV88E6390_G2_WDOG_CTL_CUT_THROUGH | |
901 | MV88E6390_G2_WDOG_CTL_QUEUE_CONTROLLER | |
902 | MV88E6390_G2_WDOG_CTL_EGRESS | |
903 | MV88E6390_G2_WDOG_CTL_FORCE_IRQ); |
904 | } |
905 | |
906 | static int mv88e6390_watchdog_action(struct mv88e6xxx_chip *chip, int irq) |
907 | { |
908 | u16 reg; |
909 | |
910 | mv88e6xxx_g2_write(chip, MV88E6390_G2_WDOG_CTL, |
911 | MV88E6390_G2_WDOG_CTL_PTR_EVENT); |
912 | mv88e6xxx_g2_read(chip, MV88E6390_G2_WDOG_CTL, val: ®); |
913 | |
914 | dev_info(chip->dev, "Watchdog event: 0x%04x" , |
915 | reg & MV88E6390_G2_WDOG_CTL_DATA_MASK); |
916 | |
917 | mv88e6xxx_g2_write(chip, MV88E6390_G2_WDOG_CTL, |
918 | MV88E6390_G2_WDOG_CTL_PTR_HISTORY); |
919 | mv88e6xxx_g2_read(chip, MV88E6390_G2_WDOG_CTL, val: ®); |
920 | |
921 | dev_info(chip->dev, "Watchdog history: 0x%04x" , |
922 | reg & MV88E6390_G2_WDOG_CTL_DATA_MASK); |
923 | |
924 | /* Trigger a software reset to try to recover the switch */ |
925 | if (chip->info->ops->reset) |
926 | chip->info->ops->reset(chip); |
927 | |
928 | mv88e6390_watchdog_setup(chip); |
929 | |
930 | return IRQ_HANDLED; |
931 | } |
932 | |
933 | static void mv88e6390_watchdog_free(struct mv88e6xxx_chip *chip) |
934 | { |
935 | mv88e6xxx_g2_write(chip, MV88E6390_G2_WDOG_CTL, |
936 | MV88E6390_G2_WDOG_CTL_UPDATE | |
937 | MV88E6390_G2_WDOG_CTL_PTR_INT_ENABLE); |
938 | } |
939 | |
940 | const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = { |
941 | .irq_action = mv88e6390_watchdog_action, |
942 | .irq_setup = mv88e6390_watchdog_setup, |
943 | .irq_free = mv88e6390_watchdog_free, |
944 | }; |
945 | |
946 | static int mv88e6393x_watchdog_action(struct mv88e6xxx_chip *chip, int irq) |
947 | { |
948 | mv88e6390_watchdog_action(chip, irq); |
949 | |
950 | /* Fix for clearing the force WD event bit. |
951 | * Unreleased erratum on mv88e6393x. |
952 | */ |
953 | mv88e6xxx_g2_write(chip, MV88E6390_G2_WDOG_CTL, |
954 | MV88E6390_G2_WDOG_CTL_UPDATE | |
955 | MV88E6390_G2_WDOG_CTL_PTR_EVENT); |
956 | |
957 | return IRQ_HANDLED; |
958 | } |
959 | |
960 | const struct mv88e6xxx_irq_ops mv88e6393x_watchdog_ops = { |
961 | .irq_action = mv88e6393x_watchdog_action, |
962 | .irq_setup = mv88e6390_watchdog_setup, |
963 | .irq_free = mv88e6390_watchdog_free, |
964 | }; |
965 | |
966 | static irqreturn_t mv88e6xxx_g2_watchdog_thread_fn(int irq, void *dev_id) |
967 | { |
968 | struct mv88e6xxx_chip *chip = dev_id; |
969 | irqreturn_t ret = IRQ_NONE; |
970 | |
971 | mv88e6xxx_reg_lock(chip); |
972 | if (chip->info->ops->watchdog_ops->irq_action) |
973 | ret = chip->info->ops->watchdog_ops->irq_action(chip, irq); |
974 | mv88e6xxx_reg_unlock(chip); |
975 | |
976 | return ret; |
977 | } |
978 | |
979 | static void mv88e6xxx_g2_watchdog_free(struct mv88e6xxx_chip *chip) |
980 | { |
981 | mv88e6xxx_reg_lock(chip); |
982 | if (chip->info->ops->watchdog_ops->irq_free) |
983 | chip->info->ops->watchdog_ops->irq_free(chip); |
984 | mv88e6xxx_reg_unlock(chip); |
985 | |
986 | free_irq(chip->watchdog_irq, chip); |
987 | irq_dispose_mapping(virq: chip->watchdog_irq); |
988 | } |
989 | |
990 | static int mv88e6xxx_g2_watchdog_setup(struct mv88e6xxx_chip *chip) |
991 | { |
992 | int err; |
993 | |
994 | chip->watchdog_irq = irq_find_mapping(domain: chip->g2_irq.domain, |
995 | MV88E6XXX_G2_INT_SOURCE_WATCHDOG); |
996 | if (chip->watchdog_irq < 0) |
997 | return chip->watchdog_irq; |
998 | |
999 | snprintf(buf: chip->watchdog_irq_name, size: sizeof(chip->watchdog_irq_name), |
1000 | fmt: "mv88e6xxx-%s-watchdog" , dev_name(dev: chip->dev)); |
1001 | |
1002 | err = request_threaded_irq(irq: chip->watchdog_irq, NULL, |
1003 | thread_fn: mv88e6xxx_g2_watchdog_thread_fn, |
1004 | IRQF_ONESHOT | IRQF_TRIGGER_FALLING, |
1005 | name: chip->watchdog_irq_name, dev: chip); |
1006 | if (err) |
1007 | return err; |
1008 | |
1009 | mv88e6xxx_reg_lock(chip); |
1010 | if (chip->info->ops->watchdog_ops->irq_setup) |
1011 | err = chip->info->ops->watchdog_ops->irq_setup(chip); |
1012 | mv88e6xxx_reg_unlock(chip); |
1013 | |
1014 | return err; |
1015 | } |
1016 | |
1017 | /* Offset 0x1D: Misc Register */ |
1018 | |
1019 | static int mv88e6xxx_g2_misc_5_bit_port(struct mv88e6xxx_chip *chip, |
1020 | bool port_5_bit) |
1021 | { |
1022 | u16 val; |
1023 | int err; |
1024 | |
1025 | err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_MISC, val: &val); |
1026 | if (err) |
1027 | return err; |
1028 | |
1029 | if (port_5_bit) |
1030 | val |= MV88E6XXX_G2_MISC_5_BIT_PORT; |
1031 | else |
1032 | val &= ~MV88E6XXX_G2_MISC_5_BIT_PORT; |
1033 | |
1034 | return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_MISC, val); |
1035 | } |
1036 | |
1037 | int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip) |
1038 | { |
1039 | return mv88e6xxx_g2_misc_5_bit_port(chip, port_5_bit: false); |
1040 | } |
1041 | |
1042 | static void mv88e6xxx_g2_irq_mask(struct irq_data *d) |
1043 | { |
1044 | struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); |
1045 | unsigned int n = d->hwirq; |
1046 | |
1047 | chip->g2_irq.masked |= (1 << n); |
1048 | } |
1049 | |
1050 | static void mv88e6xxx_g2_irq_unmask(struct irq_data *d) |
1051 | { |
1052 | struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); |
1053 | unsigned int n = d->hwirq; |
1054 | |
1055 | chip->g2_irq.masked &= ~(1 << n); |
1056 | } |
1057 | |
1058 | static irqreturn_t mv88e6xxx_g2_irq_thread_fn(int irq, void *dev_id) |
1059 | { |
1060 | struct mv88e6xxx_chip *chip = dev_id; |
1061 | unsigned int nhandled = 0; |
1062 | unsigned int sub_irq; |
1063 | unsigned int n; |
1064 | int err; |
1065 | u16 reg; |
1066 | |
1067 | mv88e6xxx_reg_lock(chip); |
1068 | err = mv88e6xxx_g2_int_source(chip, src: ®); |
1069 | mv88e6xxx_reg_unlock(chip); |
1070 | if (err) |
1071 | goto out; |
1072 | |
1073 | for (n = 0; n < 16; ++n) { |
1074 | if (reg & (1 << n)) { |
1075 | sub_irq = irq_find_mapping(domain: chip->g2_irq.domain, hwirq: n); |
1076 | handle_nested_irq(irq: sub_irq); |
1077 | ++nhandled; |
1078 | } |
1079 | } |
1080 | out: |
1081 | return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); |
1082 | } |
1083 | |
1084 | static void mv88e6xxx_g2_irq_bus_lock(struct irq_data *d) |
1085 | { |
1086 | struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); |
1087 | |
1088 | mv88e6xxx_reg_lock(chip); |
1089 | } |
1090 | |
1091 | static void mv88e6xxx_g2_irq_bus_sync_unlock(struct irq_data *d) |
1092 | { |
1093 | struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); |
1094 | int err; |
1095 | |
1096 | err = mv88e6xxx_g2_int_mask(chip, mask: ~chip->g2_irq.masked); |
1097 | if (err) |
1098 | dev_err(chip->dev, "failed to mask interrupts\n" ); |
1099 | |
1100 | mv88e6xxx_reg_unlock(chip); |
1101 | } |
1102 | |
1103 | static const struct irq_chip mv88e6xxx_g2_irq_chip = { |
1104 | .name = "mv88e6xxx-g2" , |
1105 | .irq_mask = mv88e6xxx_g2_irq_mask, |
1106 | .irq_unmask = mv88e6xxx_g2_irq_unmask, |
1107 | .irq_bus_lock = mv88e6xxx_g2_irq_bus_lock, |
1108 | .irq_bus_sync_unlock = mv88e6xxx_g2_irq_bus_sync_unlock, |
1109 | }; |
1110 | |
1111 | static int mv88e6xxx_g2_irq_domain_map(struct irq_domain *d, |
1112 | unsigned int irq, |
1113 | irq_hw_number_t hwirq) |
1114 | { |
1115 | struct mv88e6xxx_chip *chip = d->host_data; |
1116 | |
1117 | irq_set_chip_data(irq, data: d->host_data); |
1118 | irq_set_chip_and_handler(irq, chip: &chip->g2_irq.chip, handle: handle_level_irq); |
1119 | irq_set_noprobe(irq); |
1120 | |
1121 | return 0; |
1122 | } |
1123 | |
1124 | static const struct irq_domain_ops mv88e6xxx_g2_irq_domain_ops = { |
1125 | .map = mv88e6xxx_g2_irq_domain_map, |
1126 | .xlate = irq_domain_xlate_twocell, |
1127 | }; |
1128 | |
1129 | void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip) |
1130 | { |
1131 | int irq, virq; |
1132 | |
1133 | mv88e6xxx_g2_watchdog_free(chip); |
1134 | |
1135 | free_irq(chip->device_irq, chip); |
1136 | irq_dispose_mapping(virq: chip->device_irq); |
1137 | |
1138 | for (irq = 0; irq < 16; irq++) { |
1139 | virq = irq_find_mapping(domain: chip->g2_irq.domain, hwirq: irq); |
1140 | irq_dispose_mapping(virq); |
1141 | } |
1142 | |
1143 | irq_domain_remove(host: chip->g2_irq.domain); |
1144 | } |
1145 | |
1146 | int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip) |
1147 | { |
1148 | int err, irq, virq; |
1149 | |
1150 | chip->g2_irq.masked = ~0; |
1151 | mv88e6xxx_reg_lock(chip); |
1152 | err = mv88e6xxx_g2_int_mask(chip, mask: ~chip->g2_irq.masked); |
1153 | mv88e6xxx_reg_unlock(chip); |
1154 | if (err) |
1155 | return err; |
1156 | |
1157 | chip->g2_irq.domain = irq_domain_add_simple( |
1158 | of_node: chip->dev->of_node, size: 16, first_irq: 0, ops: &mv88e6xxx_g2_irq_domain_ops, host_data: chip); |
1159 | if (!chip->g2_irq.domain) |
1160 | return -ENOMEM; |
1161 | |
1162 | for (irq = 0; irq < 16; irq++) |
1163 | irq_create_mapping(host: chip->g2_irq.domain, hwirq: irq); |
1164 | |
1165 | chip->g2_irq.chip = mv88e6xxx_g2_irq_chip; |
1166 | |
1167 | chip->device_irq = irq_find_mapping(domain: chip->g1_irq.domain, |
1168 | MV88E6XXX_G1_STS_IRQ_DEVICE); |
1169 | if (chip->device_irq < 0) { |
1170 | err = chip->device_irq; |
1171 | goto out; |
1172 | } |
1173 | |
1174 | snprintf(buf: chip->device_irq_name, size: sizeof(chip->device_irq_name), |
1175 | fmt: "mv88e6xxx-%s-g2" , dev_name(dev: chip->dev)); |
1176 | |
1177 | err = request_threaded_irq(irq: chip->device_irq, NULL, |
1178 | thread_fn: mv88e6xxx_g2_irq_thread_fn, |
1179 | IRQF_ONESHOT, name: chip->device_irq_name, dev: chip); |
1180 | if (err) |
1181 | goto out; |
1182 | |
1183 | return mv88e6xxx_g2_watchdog_setup(chip); |
1184 | |
1185 | out: |
1186 | for (irq = 0; irq < 16; irq++) { |
1187 | virq = irq_find_mapping(domain: chip->g2_irq.domain, hwirq: irq); |
1188 | irq_dispose_mapping(virq); |
1189 | } |
1190 | |
1191 | irq_domain_remove(host: chip->g2_irq.domain); |
1192 | |
1193 | return err; |
1194 | } |
1195 | |
1196 | int mv88e6xxx_g2_irq_mdio_setup(struct mv88e6xxx_chip *chip, |
1197 | struct mii_bus *bus) |
1198 | { |
1199 | int phy_start = chip->info->internal_phys_offset; |
1200 | int phy_end = chip->info->internal_phys_offset + |
1201 | chip->info->num_internal_phys; |
1202 | int phy, irq; |
1203 | |
1204 | for (phy = phy_start; phy < phy_end; phy++) { |
1205 | irq = irq_find_mapping(domain: chip->g2_irq.domain, hwirq: phy); |
1206 | if (irq < 0) |
1207 | return irq; |
1208 | |
1209 | bus->irq[chip->info->phy_base_addr + phy] = irq; |
1210 | } |
1211 | return 0; |
1212 | } |
1213 | |
1214 | void mv88e6xxx_g2_irq_mdio_free(struct mv88e6xxx_chip *chip, |
1215 | struct mii_bus *bus) |
1216 | { |
1217 | } |
1218 | |