blob: 14954d92c56433dac07f70c170ade7483b0d79e4 [file] [log] [blame]
Thomas Gleixner2874c5f2019-05-27 08:55:01 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Vivien Didelotec561272016-09-02 14:45:33 -04002/*
Vivien Didelot1d900162017-06-19 10:55:45 -04003 * Marvell 88E6xxx Switch Global 2 Registers support
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
Vivien Didelote289ef02017-06-19 10:55:37 -040011#include <linux/bitfield.h>
Florian Westphal282ccf62017-03-29 17:17:31 +020012#include <linux/interrupt.h>
Andrew Lunndc30c352016-10-16 19:56:49 +020013#include <linux/irqdomain.h>
Vivien Didelot4d5f2ba72017-06-02 17:06:15 -040014
15#include "chip.h"
Vivien Didelot82466922017-06-15 12:13:59 -040016#include "global1.h" /* for MV88E6XXX_G1_STS_IRQ_DEVICE */
Vivien Didelotec561272016-09-02 14:45:33 -040017#include "global2.h"
18
Brandon Streiffb000be92018-02-14 01:07:43 +010019int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
Vivien Didelot9fe850f2016-09-29 12:21:54 -040020{
Vivien Didelot9069c132017-07-17 13:03:44 -040021 return mv88e6xxx_read(chip, chip->info->global2_addr, reg, val);
Vivien Didelot9fe850f2016-09-29 12:21:54 -040022}
23
Brandon Streiffb000be92018-02-14 01:07:43 +010024int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
Vivien Didelot9fe850f2016-09-29 12:21:54 -040025{
Vivien Didelot9069c132017-07-17 13:03:44 -040026 return mv88e6xxx_write(chip, chip->info->global2_addr, reg, val);
Vivien Didelot9fe850f2016-09-29 12:21:54 -040027}
28
Vivien Didelot19fb7f62019-08-09 18:47:55 -040029int mv88e6xxx_g2_wait_bit(struct mv88e6xxx_chip *chip, int reg, int
30 bit, int val)
31{
32 return mv88e6xxx_wait_bit(chip, chip->info->global2_addr, reg,
33 bit, val);
34}
35
Vivien Didelotd6c5e6a2017-07-17 13:03:40 -040036/* Offset 0x00: Interrupt Source Register */
37
38static 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, src);
42}
43
44/* Offset 0x01: Interrupt Mask Register */
45
46static int mv88e6xxx_g2_int_mask(struct mv88e6xxx_chip *chip, u16 mask)
47{
48 return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_INT_MASK, mask);
49}
50
Andrew Lunn6e55f692016-12-03 04:45:16 +010051/* Offset 0x02: Management Enable 2x */
Vivien Didelot51c901a2017-07-17 13:03:41 -040052
53static int mv88e6xxx_g2_mgmt_enable_2x(struct mv88e6xxx_chip *chip, u16 en2x)
54{
55 return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_MGMT_EN_2X, en2x);
56}
57
Andrew Lunn6e55f692016-12-03 04:45:16 +010058/* Offset 0x03: Management Enable 0x */
59
Vivien Didelot51c901a2017-07-17 13:03:41 -040060static int mv88e6xxx_g2_mgmt_enable_0x(struct mv88e6xxx_chip *chip, u16 en0x)
61{
62 return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_MGMT_EN_0X, en0x);
63}
64
65/* Offset 0x05: Switch Management Register */
66
67static 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);
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
85int 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, 0xffff);
93 if (err)
94 return err;
95
96 return mv88e6xxx_g2_switch_mgmt_rsvd2cpu(chip, true);
97}
98
99int mv88e6352_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip)
Andrew Lunn6e55f692016-12-03 04:45:16 +0100100{
101 int err;
102
103 /* Consider the frames with reserved multicast destination
104 * addresses matching 01:80:c2:00:00:2x as MGMT.
105 */
Vivien Didelot51c901a2017-07-17 13:03:41 -0400106 err = mv88e6xxx_g2_mgmt_enable_2x(chip, 0xffff);
107 if (err)
108 return err;
Andrew Lunn6e55f692016-12-03 04:45:16 +0100109
Vivien Didelot51c901a2017-07-17 13:03:41 -0400110 return mv88e6185_g2_mgmt_rsvd2cpu(chip);
Andrew Lunn6e55f692016-12-03 04:45:16 +0100111}
112
Vivien Didelotec561272016-09-02 14:45:33 -0400113/* Offset 0x06: Device Mapping Table register */
114
Vivien Didelotc7f047b2018-04-26 21:56:45 -0400115int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, int target,
116 int port)
Vivien Didelotec561272016-09-02 14:45:33 -0400117{
Vivien Didelotc7f047b2018-04-26 21:56:45 -0400118 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 */
Vivien Didelotec561272016-09-02 14:45:33 -0400122
Vivien Didelot2ad4da72019-08-09 18:47:57 -0400123 return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_DEVICE_MAPPING,
124 MV88E6XXX_G2_DEVICE_MAPPING_UPDATE | val);
Vivien Didelotec561272016-09-02 14:45:33 -0400125}
126
Vivien Didelotec561272016-09-02 14:45:33 -0400127/* Offset 0x07: Trunk Mask Table register */
128
129static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
Vivien Didelot56dc7342017-06-19 10:55:38 -0400130 bool hash, u16 mask)
Vivien Didelotec561272016-09-02 14:45:33 -0400131{
Vivien Didelot56dc7342017-06-19 10:55:38 -0400132 u16 val = (num << 12) | (mask & mv88e6xxx_port_mask(chip));
Vivien Didelotec561272016-09-02 14:45:33 -0400133
Vivien Didelot56dc7342017-06-19 10:55:38 -0400134 if (hash)
135 val |= MV88E6XXX_G2_TRUNK_MASK_HASH;
Vivien Didelotec561272016-09-02 14:45:33 -0400136
Vivien Didelot2ad4da72019-08-09 18:47:57 -0400137 return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_TRUNK_MASK,
138 MV88E6XXX_G2_TRUNK_MASK_UPDATE | val);
Vivien Didelotec561272016-09-02 14:45:33 -0400139}
140
141/* Offset 0x08: Trunk Mapping Table register */
142
143static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id,
144 u16 map)
145{
Vivien Didelot370b4ff2016-09-29 12:21:57 -0400146 const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
Vivien Didelotec561272016-09-02 14:45:33 -0400147 u16 val = (id << 11) | (map & port_mask);
148
Vivien Didelot2ad4da72019-08-09 18:47:57 -0400149 return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_TRUNK_MAPPING,
150 MV88E6XXX_G2_TRUNK_MAPPING_UPDATE | val);
Vivien Didelotec561272016-09-02 14:45:33 -0400151}
152
Vivien Didelotb28f8722018-04-26 21:56:44 -0400153int mv88e6xxx_g2_trunk_clear(struct mv88e6xxx_chip *chip)
Vivien Didelotec561272016-09-02 14:45:33 -0400154{
Vivien Didelot370b4ff2016-09-29 12:21:57 -0400155 const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
Vivien Didelotec561272016-09-02 14:45:33 -0400156 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, i, false, 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, i, 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
Vivien Didelotcd8da8b2017-06-19 10:55:36 -0400179static int mv88e6xxx_g2_irl_wait(struct mv88e6xxx_chip *chip)
Vivien Didelotec561272016-09-02 14:45:33 -0400180{
Vivien Didelot19fb7f62019-08-09 18:47:55 -0400181 int bit = __bf_shf(MV88E6XXX_G2_IRL_CMD_BUSY);
182
183 return mv88e6xxx_g2_wait_bit(chip, MV88E6XXX_G2_IRL_CMD, bit, 0);
Vivien Didelotcd8da8b2017-06-19 10:55:36 -0400184}
Vivien Didelotec561272016-09-02 14:45:33 -0400185
Vivien Didelotcd8da8b2017-06-19 10:55:36 -0400186static int mv88e6xxx_g2_irl_op(struct mv88e6xxx_chip *chip, u16 op, int port,
187 int res, int reg)
188{
189 int err;
Vivien Didelotec561272016-09-02 14:45:33 -0400190
Vivien Didelotcd8da8b2017-06-19 10:55:36 -0400191 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;
Vivien Didelotec561272016-09-02 14:45:33 -0400196
Vivien Didelotcd8da8b2017-06-19 10:55:36 -0400197 return mv88e6xxx_g2_irl_wait(chip);
198}
199
200int 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 0, 0);
204}
205
206int 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 0, 0);
Vivien Didelotec561272016-09-02 14:45:33 -0400210}
211
Vivien Didelot17a15942017-03-30 17:37:09 -0400212/* Offset 0x0B: Cross-chip Port VLAN (Addr) Register
213 * Offset 0x0C: Cross-chip Port VLAN Data Register
214 */
215
216static int mv88e6xxx_g2_pvt_op_wait(struct mv88e6xxx_chip *chip)
217{
Vivien Didelot19fb7f62019-08-09 18:47:55 -0400218 int bit = __bf_shf(MV88E6XXX_G2_PVT_ADDR_BUSY);
219
220 return mv88e6xxx_g2_wait_bit(chip, MV88E6XXX_G2_PVT_ADDR, bit, 0);
Vivien Didelot17a15942017-03-30 17:37:09 -0400221}
222
223static int mv88e6xxx_g2_pvt_op(struct mv88e6xxx_chip *chip, int src_dev,
224 int src_port, u16 op)
225{
226 int err;
227
Vivien Didelot67d1ea82017-06-19 10:55:41 -0400228 /* 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.
Vivien Didelot17a15942017-03-30 17:37:09 -0400230 */
Vivien Didelot67d1ea82017-06-19 10:55:41 -0400231 op |= MV88E6XXX_G2_PVT_ADDR_BUSY;
Vivien Didelot17a15942017-03-30 17:37:09 -0400232 op |= (src_dev & 0x1f) << 4;
233 op |= (src_port & 0xf);
234
Vivien Didelot67d1ea82017-06-19 10:55:41 -0400235 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_PVT_ADDR, op);
Vivien Didelot17a15942017-03-30 17:37:09 -0400236 if (err)
237 return err;
238
239 return mv88e6xxx_g2_pvt_op_wait(chip);
240}
241
242int mv88e6xxx_g2_pvt_write(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
Vivien Didelot67d1ea82017-06-19 10:55:41 -0400251 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_PVT_DATA, data);
Vivien Didelot17a15942017-03-30 17:37:09 -0400252 if (err)
253 return err;
254
255 return mv88e6xxx_g2_pvt_op(chip, src_dev, src_port,
Vivien Didelot67d1ea82017-06-19 10:55:41 -0400256 MV88E6XXX_G2_PVT_ADDR_OP_WRITE_PVLAN);
Vivien Didelot17a15942017-03-30 17:37:09 -0400257}
258
Vivien Didelotec561272016-09-02 14:45:33 -0400259/* Offset 0x0D: Switch MAC/WoL/WoF register */
260
261static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip,
262 unsigned int pointer, u8 data)
263{
264 u16 val = (pointer << 8) | data;
265
Vivien Didelot2ad4da72019-08-09 18:47:57 -0400266 return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SWITCH_MAC,
267 MV88E6XXX_G2_SWITCH_MAC_UPDATE | val);
Vivien Didelotec561272016-09-02 14:45:33 -0400268}
269
270int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
271{
272 int i, err;
273
274 for (i = 0; i < 6; i++) {
275 err = mv88e6xxx_g2_switch_mac_write(chip, i, addr[i]);
276 if (err)
277 break;
278 }
279
280 return err;
281}
282
Andrew Lunn6239a382019-11-05 01:12:59 +0100283/* Offset 0x0E: ATU Statistics */
284
285int mv88e6xxx_g2_atu_stats_set(struct mv88e6xxx_chip *chip, u16 kind, u16 bin)
286{
287 return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_ATU_STATS,
288 kind | bin);
289}
290
291int mv88e6xxx_g2_atu_stats_get(struct mv88e6xxx_chip *chip)
292{
293 int err;
294 u16 val;
295
296 err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_ATU_STATS, &val);
297 if (err)
298 return err;
299
300 return val & MV88E6XXX_G2_ATU_STATS_MASK;
301}
302
Vivien Didelotec561272016-09-02 14:45:33 -0400303/* Offset 0x0F: Priority Override Table */
304
305static int mv88e6xxx_g2_pot_write(struct mv88e6xxx_chip *chip, int pointer,
306 u8 data)
307{
308 u16 val = (pointer << 8) | (data & 0x7);
309
Vivien Didelot2ad4da72019-08-09 18:47:57 -0400310 return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_PRIO_OVERRIDE,
311 MV88E6XXX_G2_PRIO_OVERRIDE_UPDATE | val);
Vivien Didelotec561272016-09-02 14:45:33 -0400312}
313
Vivien Didelot9e907d72017-07-17 13:03:43 -0400314int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip)
Vivien Didelotec561272016-09-02 14:45:33 -0400315{
316 int i, err;
317
318 /* Clear all sixteen possible Priority Override entries */
319 for (i = 0; i < 16; i++) {
320 err = mv88e6xxx_g2_pot_write(chip, i, 0);
321 if (err)
322 break;
323 }
324
325 return err;
326}
327
328/* Offset 0x14: EEPROM Command
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500329 * Offset 0x15: EEPROM Data (for 16-bit data access)
330 * Offset 0x15: EEPROM Addr (for 8-bit data access)
Vivien Didelotec561272016-09-02 14:45:33 -0400331 */
332
333static int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip)
334{
Vivien Didelot19fb7f62019-08-09 18:47:55 -0400335 int bit = __bf_shf(MV88E6XXX_G2_EEPROM_CMD_BUSY);
336 int err;
337
338 err = mv88e6xxx_g2_wait_bit(chip, MV88E6XXX_G2_EEPROM_CMD, bit, 0);
339 if (err)
340 return err;
341
342 bit = __bf_shf(MV88E6XXX_G2_EEPROM_CMD_RUNNING);
343
344 return mv88e6xxx_g2_wait_bit(chip, MV88E6XXX_G2_EEPROM_CMD, bit, 0);
Vivien Didelotec561272016-09-02 14:45:33 -0400345}
346
347static int mv88e6xxx_g2_eeprom_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
348{
349 int err;
350
Vivien Didelot7fc8c9d2017-06-19 10:55:42 -0400351 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_EEPROM_CMD,
352 MV88E6XXX_G2_EEPROM_CMD_BUSY | cmd);
Vivien Didelotec561272016-09-02 14:45:33 -0400353 if (err)
354 return err;
355
356 return mv88e6xxx_g2_eeprom_wait(chip);
357}
358
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500359static int mv88e6xxx_g2_eeprom_read8(struct mv88e6xxx_chip *chip,
360 u16 addr, u8 *data)
361{
Vivien Didelot7fc8c9d2017-06-19 10:55:42 -0400362 u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_READ;
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500363 int err;
364
365 err = mv88e6xxx_g2_eeprom_wait(chip);
366 if (err)
367 return err;
368
Vivien Didelot7fc8c9d2017-06-19 10:55:42 -0400369 err = mv88e6xxx_g2_write(chip, MV88E6390_G2_EEPROM_ADDR, addr);
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500370 if (err)
371 return err;
372
373 err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
374 if (err)
375 return err;
376
Vivien Didelot7fc8c9d2017-06-19 10:55:42 -0400377 err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_EEPROM_CMD, &cmd);
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500378 if (err)
379 return err;
380
381 *data = cmd & 0xff;
382
383 return 0;
384}
385
386static int mv88e6xxx_g2_eeprom_write8(struct mv88e6xxx_chip *chip,
387 u16 addr, u8 data)
388{
Vivien Didelot7fc8c9d2017-06-19 10:55:42 -0400389 u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_WRITE |
390 MV88E6XXX_G2_EEPROM_CMD_WRITE_EN;
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500391 int err;
392
393 err = mv88e6xxx_g2_eeprom_wait(chip);
394 if (err)
395 return err;
396
Vivien Didelot7fc8c9d2017-06-19 10:55:42 -0400397 err = mv88e6xxx_g2_write(chip, MV88E6390_G2_EEPROM_ADDR, addr);
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500398 if (err)
399 return err;
400
401 return mv88e6xxx_g2_eeprom_cmd(chip, cmd | data);
402}
403
Vivien Didelotec561272016-09-02 14:45:33 -0400404static int mv88e6xxx_g2_eeprom_read16(struct mv88e6xxx_chip *chip,
405 u8 addr, u16 *data)
406{
Vivien Didelot7fc8c9d2017-06-19 10:55:42 -0400407 u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_READ | addr;
Vivien Didelotec561272016-09-02 14:45:33 -0400408 int err;
409
410 err = mv88e6xxx_g2_eeprom_wait(chip);
411 if (err)
412 return err;
413
414 err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
415 if (err)
416 return err;
417
Vivien Didelot7fc8c9d2017-06-19 10:55:42 -0400418 return mv88e6xxx_g2_read(chip, MV88E6352_G2_EEPROM_DATA, data);
Vivien Didelotec561272016-09-02 14:45:33 -0400419}
420
421static int mv88e6xxx_g2_eeprom_write16(struct mv88e6xxx_chip *chip,
422 u8 addr, u16 data)
423{
Vivien Didelot7fc8c9d2017-06-19 10:55:42 -0400424 u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_WRITE | addr;
Vivien Didelotec561272016-09-02 14:45:33 -0400425 int err;
426
427 err = mv88e6xxx_g2_eeprom_wait(chip);
428 if (err)
429 return err;
430
Vivien Didelot7fc8c9d2017-06-19 10:55:42 -0400431 err = mv88e6xxx_g2_write(chip, MV88E6352_G2_EEPROM_DATA, data);
Vivien Didelotec561272016-09-02 14:45:33 -0400432 if (err)
433 return err;
434
435 return mv88e6xxx_g2_eeprom_cmd(chip, cmd);
436}
437
Vivien Didelot98fc3c62017-01-12 18:07:16 -0500438int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip,
439 struct ethtool_eeprom *eeprom, u8 *data)
440{
441 unsigned int offset = eeprom->offset;
442 unsigned int len = eeprom->len;
443 int err;
444
445 eeprom->len = 0;
446
447 while (len) {
448 err = mv88e6xxx_g2_eeprom_read8(chip, offset, data);
449 if (err)
450 return err;
451
452 eeprom->len++;
453 offset++;
454 data++;
455 len--;
456 }
457
458 return 0;
459}
460
461int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip,
462 struct ethtool_eeprom *eeprom, u8 *data)
463{
464 unsigned int offset = eeprom->offset;
465 unsigned int len = eeprom->len;
466 int err;
467
468 eeprom->len = 0;
469
470 while (len) {
471 err = mv88e6xxx_g2_eeprom_write8(chip, offset, *data);
472 if (err)
473 return err;
474
475 eeprom->len++;
476 offset++;
477 data++;
478 len--;
479 }
480
481 return 0;
482}
483
Vivien Didelotec561272016-09-02 14:45:33 -0400484int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip,
485 struct ethtool_eeprom *eeprom, u8 *data)
486{
487 unsigned int offset = eeprom->offset;
488 unsigned int len = eeprom->len;
489 u16 val;
490 int err;
491
492 eeprom->len = 0;
493
494 if (offset & 1) {
495 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
496 if (err)
497 return err;
498
499 *data++ = (val >> 8) & 0xff;
500
501 offset++;
502 len--;
503 eeprom->len++;
504 }
505
506 while (len >= 2) {
507 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
508 if (err)
509 return err;
510
511 *data++ = val & 0xff;
512 *data++ = (val >> 8) & 0xff;
513
514 offset += 2;
515 len -= 2;
516 eeprom->len += 2;
517 }
518
519 if (len) {
520 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
521 if (err)
522 return err;
523
524 *data++ = val & 0xff;
525
526 offset++;
527 len--;
528 eeprom->len++;
529 }
530
531 return 0;
532}
533
534int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip,
535 struct ethtool_eeprom *eeprom, u8 *data)
536{
537 unsigned int offset = eeprom->offset;
538 unsigned int len = eeprom->len;
539 u16 val;
540 int err;
541
542 /* Ensure the RO WriteEn bit is set */
Vivien Didelot7fc8c9d2017-06-19 10:55:42 -0400543 err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_EEPROM_CMD, &val);
Vivien Didelotec561272016-09-02 14:45:33 -0400544 if (err)
545 return err;
546
Vivien Didelot7fc8c9d2017-06-19 10:55:42 -0400547 if (!(val & MV88E6XXX_G2_EEPROM_CMD_WRITE_EN))
Vivien Didelotec561272016-09-02 14:45:33 -0400548 return -EROFS;
549
550 eeprom->len = 0;
551
552 if (offset & 1) {
553 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
554 if (err)
555 return err;
556
557 val = (*data++ << 8) | (val & 0xff);
558
559 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
560 if (err)
561 return err;
562
563 offset++;
564 len--;
565 eeprom->len++;
566 }
567
568 while (len >= 2) {
569 val = *data++;
570 val |= *data++ << 8;
571
572 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
573 if (err)
574 return err;
575
576 offset += 2;
577 len -= 2;
578 eeprom->len += 2;
579 }
580
581 if (len) {
582 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
583 if (err)
584 return err;
585
586 val = (val & 0xff00) | *data++;
587
588 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
589 if (err)
590 return err;
591
592 offset++;
593 len--;
594 eeprom->len++;
595 }
596
597 return 0;
598}
599
600/* Offset 0x18: SMI PHY Command Register
601 * Offset 0x19: SMI PHY Data Register
602 */
603
604static int mv88e6xxx_g2_smi_phy_wait(struct mv88e6xxx_chip *chip)
605{
Vivien Didelot19fb7f62019-08-09 18:47:55 -0400606 int bit = __bf_shf(MV88E6XXX_G2_SMI_PHY_CMD_BUSY);
607
608 return mv88e6xxx_g2_wait_bit(chip, MV88E6XXX_G2_SMI_PHY_CMD, bit, 0);
Vivien Didelotec561272016-09-02 14:45:33 -0400609}
610
611static int mv88e6xxx_g2_smi_phy_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
612{
613 int err;
614
Vivien Didelote289ef02017-06-19 10:55:37 -0400615 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_CMD,
616 MV88E6XXX_G2_SMI_PHY_CMD_BUSY | cmd);
Vivien Didelotec561272016-09-02 14:45:33 -0400617 if (err)
618 return err;
619
620 return mv88e6xxx_g2_smi_phy_wait(chip);
621}
622
Vivien Didelote289ef02017-06-19 10:55:37 -0400623static int mv88e6xxx_g2_smi_phy_access(struct mv88e6xxx_chip *chip,
624 bool external, bool c45, u16 op, int dev,
625 int reg)
Vivien Didelotec561272016-09-02 14:45:33 -0400626{
Vivien Didelote289ef02017-06-19 10:55:37 -0400627 u16 cmd = op;
Vivien Didelotec561272016-09-02 14:45:33 -0400628
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100629 if (external)
Vivien Didelote289ef02017-06-19 10:55:37 -0400630 cmd |= MV88E6390_G2_SMI_PHY_CMD_FUNC_EXTERNAL;
631 else
632 cmd |= MV88E6390_G2_SMI_PHY_CMD_FUNC_INTERNAL; /* empty mask */
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100633
Vivien Didelote289ef02017-06-19 10:55:37 -0400634 if (c45)
635 cmd |= MV88E6XXX_G2_SMI_PHY_CMD_MODE_45; /* empty mask */
636 else
637 cmd |= MV88E6XXX_G2_SMI_PHY_CMD_MODE_22;
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100638
Vivien Didelote289ef02017-06-19 10:55:37 -0400639 dev <<= __bf_shf(MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK);
640 cmd |= dev & MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK;
641 cmd |= reg & MV88E6XXX_G2_SMI_PHY_CMD_REG_ADDR_MASK;
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100642
643 return mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
644}
645
Vivien Didelote289ef02017-06-19 10:55:37 -0400646static int mv88e6xxx_g2_smi_phy_access_c22(struct mv88e6xxx_chip *chip,
647 bool external, u16 op, int dev,
648 int reg)
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100649{
Vivien Didelote289ef02017-06-19 10:55:37 -0400650 return mv88e6xxx_g2_smi_phy_access(chip, external, false, op, dev, reg);
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100651}
652
Vivien Didelote289ef02017-06-19 10:55:37 -0400653/* IEEE 802.3 Clause 22 Read Data Register */
654static int mv88e6xxx_g2_smi_phy_read_data_c22(struct mv88e6xxx_chip *chip,
655 bool external, int dev, int reg,
656 u16 *data)
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100657{
Vivien Didelote289ef02017-06-19 10:55:37 -0400658 u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_22_READ_DATA;
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100659 int err;
660
Vivien Didelotec561272016-09-02 14:45:33 -0400661 err = mv88e6xxx_g2_smi_phy_wait(chip);
662 if (err)
663 return err;
664
Vivien Didelote289ef02017-06-19 10:55:37 -0400665 err = mv88e6xxx_g2_smi_phy_access_c22(chip, external, op, dev, reg);
Vivien Didelotec561272016-09-02 14:45:33 -0400666 if (err)
667 return err;
668
Vivien Didelote289ef02017-06-19 10:55:37 -0400669 return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
Vivien Didelotec561272016-09-02 14:45:33 -0400670}
671
Vivien Didelote289ef02017-06-19 10:55:37 -0400672/* IEEE 802.3 Clause 22 Write Data Register */
673static int mv88e6xxx_g2_smi_phy_write_data_c22(struct mv88e6xxx_chip *chip,
674 bool external, int dev, int reg,
675 u16 data)
676{
677 u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_22_WRITE_DATA;
678 int err;
679
680 err = mv88e6xxx_g2_smi_phy_wait(chip);
681 if (err)
682 return err;
683
684 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
685 if (err)
686 return err;
687
688 return mv88e6xxx_g2_smi_phy_access_c22(chip, external, op, dev, reg);
689}
690
691static int mv88e6xxx_g2_smi_phy_access_c45(struct mv88e6xxx_chip *chip,
692 bool external, u16 op, int port,
693 int dev)
694{
695 return mv88e6xxx_g2_smi_phy_access(chip, external, true, op, port, dev);
696}
697
698/* IEEE 802.3 Clause 45 Write Address Register */
699static int mv88e6xxx_g2_smi_phy_write_addr_c45(struct mv88e6xxx_chip *chip,
700 bool external, int port, int dev,
701 int addr)
702{
703 u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_ADDR;
704 int err;
705
706 err = mv88e6xxx_g2_smi_phy_wait(chip);
707 if (err)
708 return err;
709
710 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, addr);
711 if (err)
712 return err;
713
714 return mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev);
715}
716
717/* IEEE 802.3 Clause 45 Read Data Register */
718static int mv88e6xxx_g2_smi_phy_read_data_c45(struct mv88e6xxx_chip *chip,
719 bool external, int port, int dev,
720 u16 *data)
721{
722 u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_READ_DATA;
723 int err;
724
725 err = mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev);
726 if (err)
727 return err;
728
729 return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
730}
731
732static int mv88e6xxx_g2_smi_phy_read_c45(struct mv88e6xxx_chip *chip,
733 bool external, int port, int reg,
734 u16 *data)
735{
736 int dev = (reg >> 16) & 0x1f;
737 int addr = reg & 0xffff;
738 int err;
739
740 err = mv88e6xxx_g2_smi_phy_write_addr_c45(chip, external, port, dev,
741 addr);
742 if (err)
743 return err;
744
745 return mv88e6xxx_g2_smi_phy_read_data_c45(chip, external, port, dev,
746 data);
747}
748
749/* IEEE 802.3 Clause 45 Write Data Register */
750static int mv88e6xxx_g2_smi_phy_write_data_c45(struct mv88e6xxx_chip *chip,
751 bool external, int port, int dev,
752 u16 data)
753{
754 u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_DATA;
755 int err;
756
757 err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
758 if (err)
759 return err;
760
761 return mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev);
762}
763
764static int mv88e6xxx_g2_smi_phy_write_c45(struct mv88e6xxx_chip *chip,
765 bool external, int port, int reg,
766 u16 data)
767{
768 int dev = (reg >> 16) & 0x1f;
769 int addr = reg & 0xffff;
770 int err;
771
772 err = mv88e6xxx_g2_smi_phy_write_addr_c45(chip, external, port, dev,
773 addr);
774 if (err)
775 return err;
776
777 return mv88e6xxx_g2_smi_phy_write_data_c45(chip, external, port, dev,
778 data);
779}
780
781int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100782 int addr, int reg, u16 *val)
783{
784 struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
785 bool external = mdio_bus->external;
786
787 if (reg & MII_ADDR_C45)
Vivien Didelote289ef02017-06-19 10:55:37 -0400788 return mv88e6xxx_g2_smi_phy_read_c45(chip, external, addr, reg,
789 val);
790
791 return mv88e6xxx_g2_smi_phy_read_data_c22(chip, external, addr, reg,
792 val);
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100793}
794
Vivien Didelote289ef02017-06-19 10:55:37 -0400795int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100796 int addr, int reg, u16 val)
797{
798 struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
799 bool external = mdio_bus->external;
800
801 if (reg & MII_ADDR_C45)
Vivien Didelote289ef02017-06-19 10:55:37 -0400802 return mv88e6xxx_g2_smi_phy_write_c45(chip, external, addr, reg,
803 val);
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100804
Vivien Didelote289ef02017-06-19 10:55:37 -0400805 return mv88e6xxx_g2_smi_phy_write_data_c22(chip, external, addr, reg,
806 val);
Andrew Lunncf3e80d2017-02-04 20:12:24 +0100807}
808
Brandon Streiffa73ccd62018-02-14 01:07:46 +0100809/* Offset 0x1B: Watchdog Control */
Andrew Lunnfcd25162017-02-09 00:03:42 +0100810static int mv88e6097_watchdog_action(struct mv88e6xxx_chip *chip, int irq)
811{
812 u16 reg;
813
Vivien Didelot3b19df72017-06-19 10:55:44 -0400814 mv88e6xxx_g2_read(chip, MV88E6352_G2_WDOG_CTL, &reg);
Andrew Lunnfcd25162017-02-09 00:03:42 +0100815
816 dev_info(chip->dev, "Watchdog event: 0x%04x", reg);
817
818 return IRQ_HANDLED;
819}
820
821static void mv88e6097_watchdog_free(struct mv88e6xxx_chip *chip)
822{
823 u16 reg;
824
Vivien Didelot3b19df72017-06-19 10:55:44 -0400825 mv88e6xxx_g2_read(chip, MV88E6352_G2_WDOG_CTL, &reg);
Andrew Lunnfcd25162017-02-09 00:03:42 +0100826
Vivien Didelot3b19df72017-06-19 10:55:44 -0400827 reg &= ~(MV88E6352_G2_WDOG_CTL_EGRESS_ENABLE |
828 MV88E6352_G2_WDOG_CTL_QC_ENABLE);
Andrew Lunnfcd25162017-02-09 00:03:42 +0100829
Vivien Didelot3b19df72017-06-19 10:55:44 -0400830 mv88e6xxx_g2_write(chip, MV88E6352_G2_WDOG_CTL, reg);
Andrew Lunnfcd25162017-02-09 00:03:42 +0100831}
832
833static int mv88e6097_watchdog_setup(struct mv88e6xxx_chip *chip)
834{
Vivien Didelot3b19df72017-06-19 10:55:44 -0400835 return mv88e6xxx_g2_write(chip, MV88E6352_G2_WDOG_CTL,
836 MV88E6352_G2_WDOG_CTL_EGRESS_ENABLE |
837 MV88E6352_G2_WDOG_CTL_QC_ENABLE |
838 MV88E6352_G2_WDOG_CTL_SWRESET);
Andrew Lunnfcd25162017-02-09 00:03:42 +0100839}
840
841const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = {
842 .irq_action = mv88e6097_watchdog_action,
843 .irq_setup = mv88e6097_watchdog_setup,
844 .irq_free = mv88e6097_watchdog_free,
845};
846
Rasmus Villemoes855cdfd2019-06-04 07:34:28 +0000847static void mv88e6250_watchdog_free(struct mv88e6xxx_chip *chip)
848{
849 u16 reg;
850
851 mv88e6xxx_g2_read(chip, MV88E6250_G2_WDOG_CTL, &reg);
852
853 reg &= ~(MV88E6250_G2_WDOG_CTL_EGRESS_ENABLE |
854 MV88E6250_G2_WDOG_CTL_QC_ENABLE);
855
856 mv88e6xxx_g2_write(chip, MV88E6250_G2_WDOG_CTL, reg);
857}
858
859static int mv88e6250_watchdog_setup(struct mv88e6xxx_chip *chip)
860{
861 return mv88e6xxx_g2_write(chip, MV88E6250_G2_WDOG_CTL,
862 MV88E6250_G2_WDOG_CTL_EGRESS_ENABLE |
863 MV88E6250_G2_WDOG_CTL_QC_ENABLE |
864 MV88E6250_G2_WDOG_CTL_SWRESET);
865}
866
867const struct mv88e6xxx_irq_ops mv88e6250_watchdog_ops = {
868 .irq_action = mv88e6097_watchdog_action,
869 .irq_setup = mv88e6250_watchdog_setup,
870 .irq_free = mv88e6250_watchdog_free,
871};
872
Andrew Lunn61303732017-02-09 00:03:43 +0100873static int mv88e6390_watchdog_setup(struct mv88e6xxx_chip *chip)
874{
Vivien Didelot2ad4da72019-08-09 18:47:57 -0400875 return mv88e6xxx_g2_write(chip, MV88E6390_G2_WDOG_CTL,
876 MV88E6390_G2_WDOG_CTL_UPDATE |
877 MV88E6390_G2_WDOG_CTL_PTR_INT_ENABLE |
878 MV88E6390_G2_WDOG_CTL_CUT_THROUGH |
879 MV88E6390_G2_WDOG_CTL_QUEUE_CONTROLLER |
880 MV88E6390_G2_WDOG_CTL_EGRESS |
881 MV88E6390_G2_WDOG_CTL_FORCE_IRQ);
Andrew Lunn61303732017-02-09 00:03:43 +0100882}
883
884static int mv88e6390_watchdog_action(struct mv88e6xxx_chip *chip, int irq)
885{
886 int err;
887 u16 reg;
888
Vivien Didelot3b19df72017-06-19 10:55:44 -0400889 mv88e6xxx_g2_write(chip, MV88E6390_G2_WDOG_CTL,
890 MV88E6390_G2_WDOG_CTL_PTR_EVENT);
891 err = mv88e6xxx_g2_read(chip, MV88E6390_G2_WDOG_CTL, &reg);
Andrew Lunn61303732017-02-09 00:03:43 +0100892
893 dev_info(chip->dev, "Watchdog event: 0x%04x",
Vivien Didelot3b19df72017-06-19 10:55:44 -0400894 reg & MV88E6390_G2_WDOG_CTL_DATA_MASK);
Andrew Lunn61303732017-02-09 00:03:43 +0100895
Vivien Didelot3b19df72017-06-19 10:55:44 -0400896 mv88e6xxx_g2_write(chip, MV88E6390_G2_WDOG_CTL,
897 MV88E6390_G2_WDOG_CTL_PTR_HISTORY);
898 err = mv88e6xxx_g2_read(chip, MV88E6390_G2_WDOG_CTL, &reg);
Andrew Lunn61303732017-02-09 00:03:43 +0100899
900 dev_info(chip->dev, "Watchdog history: 0x%04x",
Vivien Didelot3b19df72017-06-19 10:55:44 -0400901 reg & MV88E6390_G2_WDOG_CTL_DATA_MASK);
Andrew Lunn61303732017-02-09 00:03:43 +0100902
903 /* Trigger a software reset to try to recover the switch */
904 if (chip->info->ops->reset)
905 chip->info->ops->reset(chip);
906
907 mv88e6390_watchdog_setup(chip);
908
909 return IRQ_HANDLED;
910}
911
912static void mv88e6390_watchdog_free(struct mv88e6xxx_chip *chip)
913{
Vivien Didelot2ad4da72019-08-09 18:47:57 -0400914 mv88e6xxx_g2_write(chip, MV88E6390_G2_WDOG_CTL,
915 MV88E6390_G2_WDOG_CTL_UPDATE |
916 MV88E6390_G2_WDOG_CTL_PTR_INT_ENABLE);
Andrew Lunn61303732017-02-09 00:03:43 +0100917}
918
919const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = {
920 .irq_action = mv88e6390_watchdog_action,
921 .irq_setup = mv88e6390_watchdog_setup,
922 .irq_free = mv88e6390_watchdog_free,
923};
924
Andrew Lunnfcd25162017-02-09 00:03:42 +0100925static irqreturn_t mv88e6xxx_g2_watchdog_thread_fn(int irq, void *dev_id)
926{
927 struct mv88e6xxx_chip *chip = dev_id;
928 irqreturn_t ret = IRQ_NONE;
929
Rasmus Villemoesc9acece2019-06-20 13:50:42 +0000930 mv88e6xxx_reg_lock(chip);
Andrew Lunnfcd25162017-02-09 00:03:42 +0100931 if (chip->info->ops->watchdog_ops->irq_action)
932 ret = chip->info->ops->watchdog_ops->irq_action(chip, irq);
Rasmus Villemoesc9acece2019-06-20 13:50:42 +0000933 mv88e6xxx_reg_unlock(chip);
Andrew Lunnfcd25162017-02-09 00:03:42 +0100934
935 return ret;
936}
937
938static void mv88e6xxx_g2_watchdog_free(struct mv88e6xxx_chip *chip)
939{
Rasmus Villemoesc9acece2019-06-20 13:50:42 +0000940 mv88e6xxx_reg_lock(chip);
Andrew Lunnfcd25162017-02-09 00:03:42 +0100941 if (chip->info->ops->watchdog_ops->irq_free)
942 chip->info->ops->watchdog_ops->irq_free(chip);
Rasmus Villemoesc9acece2019-06-20 13:50:42 +0000943 mv88e6xxx_reg_unlock(chip);
Andrew Lunnfcd25162017-02-09 00:03:42 +0100944
945 free_irq(chip->watchdog_irq, chip);
946 irq_dispose_mapping(chip->watchdog_irq);
947}
948
949static int mv88e6xxx_g2_watchdog_setup(struct mv88e6xxx_chip *chip)
950{
951 int err;
952
953 chip->watchdog_irq = irq_find_mapping(chip->g2_irq.domain,
Vivien Didelot1d900162017-06-19 10:55:45 -0400954 MV88E6XXX_G2_INT_SOURCE_WATCHDOG);
Andrew Lunnfcd25162017-02-09 00:03:42 +0100955 if (chip->watchdog_irq < 0)
956 return chip->watchdog_irq;
957
958 err = request_threaded_irq(chip->watchdog_irq, NULL,
959 mv88e6xxx_g2_watchdog_thread_fn,
960 IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
961 "mv88e6xxx-watchdog", chip);
962 if (err)
963 return err;
964
Rasmus Villemoesc9acece2019-06-20 13:50:42 +0000965 mv88e6xxx_reg_lock(chip);
Andrew Lunnfcd25162017-02-09 00:03:42 +0100966 if (chip->info->ops->watchdog_ops->irq_setup)
967 err = chip->info->ops->watchdog_ops->irq_setup(chip);
Rasmus Villemoesc9acece2019-06-20 13:50:42 +0000968 mv88e6xxx_reg_unlock(chip);
Andrew Lunnfcd25162017-02-09 00:03:42 +0100969
970 return err;
971}
972
Vivien Didelot81228992017-03-30 17:37:08 -0400973/* Offset 0x1D: Misc Register */
974
975static int mv88e6xxx_g2_misc_5_bit_port(struct mv88e6xxx_chip *chip,
976 bool port_5_bit)
977{
978 u16 val;
979 int err;
980
Vivien Didelot1d900162017-06-19 10:55:45 -0400981 err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_MISC, &val);
Vivien Didelot81228992017-03-30 17:37:08 -0400982 if (err)
983 return err;
984
985 if (port_5_bit)
Vivien Didelot1d900162017-06-19 10:55:45 -0400986 val |= MV88E6XXX_G2_MISC_5_BIT_PORT;
Vivien Didelot81228992017-03-30 17:37:08 -0400987 else
Vivien Didelot1d900162017-06-19 10:55:45 -0400988 val &= ~MV88E6XXX_G2_MISC_5_BIT_PORT;
Vivien Didelot81228992017-03-30 17:37:08 -0400989
Vivien Didelot1d900162017-06-19 10:55:45 -0400990 return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_MISC, val);
Vivien Didelot81228992017-03-30 17:37:08 -0400991}
992
993int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip)
994{
995 return mv88e6xxx_g2_misc_5_bit_port(chip, false);
996}
997
Andrew Lunndc30c352016-10-16 19:56:49 +0200998static void mv88e6xxx_g2_irq_mask(struct irq_data *d)
999{
1000 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
1001 unsigned int n = d->hwirq;
1002
1003 chip->g2_irq.masked |= (1 << n);
1004}
1005
1006static void mv88e6xxx_g2_irq_unmask(struct irq_data *d)
1007{
1008 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
1009 unsigned int n = d->hwirq;
1010
1011 chip->g2_irq.masked &= ~(1 << n);
1012}
1013
1014static irqreturn_t mv88e6xxx_g2_irq_thread_fn(int irq, void *dev_id)
1015{
1016 struct mv88e6xxx_chip *chip = dev_id;
1017 unsigned int nhandled = 0;
1018 unsigned int sub_irq;
1019 unsigned int n;
1020 int err;
1021 u16 reg;
1022
Rasmus Villemoesc9acece2019-06-20 13:50:42 +00001023 mv88e6xxx_reg_lock(chip);
Vivien Didelotd6c5e6a2017-07-17 13:03:40 -04001024 err = mv88e6xxx_g2_int_source(chip, &reg);
Rasmus Villemoesc9acece2019-06-20 13:50:42 +00001025 mv88e6xxx_reg_unlock(chip);
Andrew Lunndc30c352016-10-16 19:56:49 +02001026 if (err)
1027 goto out;
1028
1029 for (n = 0; n < 16; ++n) {
1030 if (reg & (1 << n)) {
1031 sub_irq = irq_find_mapping(chip->g2_irq.domain, n);
1032 handle_nested_irq(sub_irq);
1033 ++nhandled;
1034 }
1035 }
1036out:
1037 return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
1038}
1039
1040static void mv88e6xxx_g2_irq_bus_lock(struct irq_data *d)
1041{
1042 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
1043
Rasmus Villemoesc9acece2019-06-20 13:50:42 +00001044 mv88e6xxx_reg_lock(chip);
Andrew Lunndc30c352016-10-16 19:56:49 +02001045}
1046
1047static void mv88e6xxx_g2_irq_bus_sync_unlock(struct irq_data *d)
1048{
1049 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
Vivien Didelotd6c5e6a2017-07-17 13:03:40 -04001050 int err;
Andrew Lunndc30c352016-10-16 19:56:49 +02001051
Vivien Didelotd6c5e6a2017-07-17 13:03:40 -04001052 err = mv88e6xxx_g2_int_mask(chip, ~chip->g2_irq.masked);
1053 if (err)
1054 dev_err(chip->dev, "failed to mask interrupts\n");
Andrew Lunndc30c352016-10-16 19:56:49 +02001055
Rasmus Villemoesc9acece2019-06-20 13:50:42 +00001056 mv88e6xxx_reg_unlock(chip);
Andrew Lunndc30c352016-10-16 19:56:49 +02001057}
1058
Bhumika Goyal6eb15e22017-08-19 16:25:52 +05301059static const struct irq_chip mv88e6xxx_g2_irq_chip = {
Andrew Lunndc30c352016-10-16 19:56:49 +02001060 .name = "mv88e6xxx-g2",
1061 .irq_mask = mv88e6xxx_g2_irq_mask,
1062 .irq_unmask = mv88e6xxx_g2_irq_unmask,
1063 .irq_bus_lock = mv88e6xxx_g2_irq_bus_lock,
1064 .irq_bus_sync_unlock = mv88e6xxx_g2_irq_bus_sync_unlock,
1065};
1066
1067static int mv88e6xxx_g2_irq_domain_map(struct irq_domain *d,
1068 unsigned int irq,
1069 irq_hw_number_t hwirq)
1070{
1071 struct mv88e6xxx_chip *chip = d->host_data;
1072
1073 irq_set_chip_data(irq, d->host_data);
1074 irq_set_chip_and_handler(irq, &chip->g2_irq.chip, handle_level_irq);
1075 irq_set_noprobe(irq);
1076
1077 return 0;
1078}
1079
1080static const struct irq_domain_ops mv88e6xxx_g2_irq_domain_ops = {
1081 .map = mv88e6xxx_g2_irq_domain_map,
1082 .xlate = irq_domain_xlate_twocell,
1083};
1084
1085void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip)
1086{
1087 int irq, virq;
1088
Andrew Lunnfcd25162017-02-09 00:03:42 +01001089 mv88e6xxx_g2_watchdog_free(chip);
1090
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001091 free_irq(chip->device_irq, chip);
1092 irq_dispose_mapping(chip->device_irq);
1093
Andrew Lunndc30c352016-10-16 19:56:49 +02001094 for (irq = 0; irq < 16; irq++) {
1095 virq = irq_find_mapping(chip->g2_irq.domain, irq);
1096 irq_dispose_mapping(virq);
1097 }
1098
1099 irq_domain_remove(chip->g2_irq.domain);
1100}
1101
1102int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip)
1103{
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001104 int err, irq, virq;
Andrew Lunndc30c352016-10-16 19:56:49 +02001105
Andrew Lunndc30c352016-10-16 19:56:49 +02001106 chip->g2_irq.domain = irq_domain_add_simple(
1107 chip->dev->of_node, 16, 0, &mv88e6xxx_g2_irq_domain_ops, chip);
1108 if (!chip->g2_irq.domain)
1109 return -ENOMEM;
1110
1111 for (irq = 0; irq < 16; irq++)
1112 irq_create_mapping(chip->g2_irq.domain, irq);
1113
1114 chip->g2_irq.chip = mv88e6xxx_g2_irq_chip;
1115 chip->g2_irq.masked = ~0;
1116
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001117 chip->device_irq = irq_find_mapping(chip->g1_irq.domain,
Vivien Didelot82466922017-06-15 12:13:59 -04001118 MV88E6XXX_G1_STS_IRQ_DEVICE);
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001119 if (chip->device_irq < 0) {
1120 err = chip->device_irq;
Andrew Lunndc30c352016-10-16 19:56:49 +02001121 goto out;
1122 }
1123
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001124 err = request_threaded_irq(chip->device_irq, NULL,
1125 mv88e6xxx_g2_irq_thread_fn,
Uwe Kleine-König36d6ea92018-03-20 10:44:42 +01001126 IRQF_ONESHOT, "mv88e6xxx-g2", chip);
Andrew Lunndc30c352016-10-16 19:56:49 +02001127 if (err)
1128 goto out;
1129
Andrew Lunnfcd25162017-02-09 00:03:42 +01001130 return mv88e6xxx_g2_watchdog_setup(chip);
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001131
Andrew Lunndc30c352016-10-16 19:56:49 +02001132out:
Andrew Lunn8e757eb2016-11-20 20:14:18 +01001133 for (irq = 0; irq < 16; irq++) {
1134 virq = irq_find_mapping(chip->g2_irq.domain, irq);
1135 irq_dispose_mapping(virq);
1136 }
1137
1138 irq_domain_remove(chip->g2_irq.domain);
Andrew Lunndc30c352016-10-16 19:56:49 +02001139
1140 return err;
1141}
1142
Andrew Lunn6f882842018-03-17 20:32:05 +01001143int mv88e6xxx_g2_irq_mdio_setup(struct mv88e6xxx_chip *chip,
1144 struct mii_bus *bus)
1145{
1146 int phy, irq, err, err_phy;
1147
1148 for (phy = 0; phy < chip->info->num_internal_phys; phy++) {
1149 irq = irq_find_mapping(chip->g2_irq.domain, phy);
1150 if (irq < 0) {
1151 err = irq;
1152 goto out;
1153 }
Andrew Lunn9255bac2018-05-05 20:58:22 +02001154 bus->irq[chip->info->phy_base_addr + phy] = irq;
Andrew Lunn6f882842018-03-17 20:32:05 +01001155 }
1156 return 0;
1157out:
1158 err_phy = phy;
1159
1160 for (phy = 0; phy < err_phy; phy++)
1161 irq_dispose_mapping(bus->irq[phy]);
1162
1163 return err;
1164}
1165
1166void mv88e6xxx_g2_irq_mdio_free(struct mv88e6xxx_chip *chip,
1167 struct mii_bus *bus)
1168{
1169 int phy;
1170
1171 for (phy = 0; phy < chip->info->num_internal_phys; phy++)
1172 irq_dispose_mapping(bus->irq[phy]);
1173}