1ec561276SVivien Didelot /*
2dc30c35bSAndrew Lunn  * Marvell 88E6xxx Switch Global 2 Registers support (device address
3dc30c35bSAndrew Lunn  * 0x1C)
4ec561276SVivien Didelot  *
5ec561276SVivien Didelot  * Copyright (c) 2008 Marvell Semiconductor
6ec561276SVivien Didelot  *
74333d619SVivien Didelot  * Copyright (c) 2016-2017 Savoir-faire Linux Inc.
84333d619SVivien Didelot  *	Vivien Didelot <vivien.didelot@savoirfairelinux.com>
9ec561276SVivien Didelot  *
10ec561276SVivien Didelot  * This program is free software; you can redistribute it and/or modify
11ec561276SVivien Didelot  * it under the terms of the GNU General Public License as published by
12ec561276SVivien Didelot  * the Free Software Foundation; either version 2 of the License, or
13ec561276SVivien Didelot  * (at your option) any later version.
14ec561276SVivien Didelot  */
15ec561276SVivien Didelot 
16282ccf6eSFlorian Westphal #include <linux/interrupt.h>
17dc30c35bSAndrew Lunn #include <linux/irqdomain.h>
184d5f2ba7SVivien Didelot 
194d5f2ba7SVivien Didelot #include "chip.h"
2082466921SVivien Didelot #include "global1.h" /* for MV88E6XXX_G1_STS_IRQ_DEVICE */
21ec561276SVivien Didelot #include "global2.h"
22ec561276SVivien Didelot 
239fe850fbSVivien Didelot static int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
249fe850fbSVivien Didelot {
259fe850fbSVivien Didelot 	return mv88e6xxx_read(chip, ADDR_GLOBAL2, reg, val);
269fe850fbSVivien Didelot }
279fe850fbSVivien Didelot 
289fe850fbSVivien Didelot static int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
299fe850fbSVivien Didelot {
309fe850fbSVivien Didelot 	return mv88e6xxx_write(chip, ADDR_GLOBAL2, reg, val);
319fe850fbSVivien Didelot }
329fe850fbSVivien Didelot 
339fe850fbSVivien Didelot static int mv88e6xxx_g2_update(struct mv88e6xxx_chip *chip, int reg, u16 update)
349fe850fbSVivien Didelot {
359fe850fbSVivien Didelot 	return mv88e6xxx_update(chip, ADDR_GLOBAL2, reg, update);
369fe850fbSVivien Didelot }
379fe850fbSVivien Didelot 
389fe850fbSVivien Didelot static int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask)
399fe850fbSVivien Didelot {
409fe850fbSVivien Didelot 	return mv88e6xxx_wait(chip, ADDR_GLOBAL2, reg, mask);
419fe850fbSVivien Didelot }
429fe850fbSVivien Didelot 
436e55f698SAndrew Lunn /* Offset 0x02: Management Enable 2x */
446e55f698SAndrew Lunn /* Offset 0x03: Management Enable 0x */
456e55f698SAndrew Lunn 
466e55f698SAndrew Lunn int mv88e6095_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip)
476e55f698SAndrew Lunn {
486e55f698SAndrew Lunn 	int err;
496e55f698SAndrew Lunn 
506e55f698SAndrew Lunn 	/* Consider the frames with reserved multicast destination
516e55f698SAndrew Lunn 	 * addresses matching 01:80:c2:00:00:2x as MGMT.
526e55f698SAndrew Lunn 	 */
536e55f698SAndrew Lunn 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) {
546e55f698SAndrew Lunn 		err = mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_2X, 0xffff);
556e55f698SAndrew Lunn 		if (err)
566e55f698SAndrew Lunn 			return err;
576e55f698SAndrew Lunn 	}
586e55f698SAndrew Lunn 
596e55f698SAndrew Lunn 	/* Consider the frames with reserved multicast destination
606e55f698SAndrew Lunn 	 * addresses matching 01:80:c2:00:00:0x as MGMT.
616e55f698SAndrew Lunn 	 */
626e55f698SAndrew Lunn 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X))
636e55f698SAndrew Lunn 		return mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_0X, 0xffff);
646e55f698SAndrew Lunn 
656e55f698SAndrew Lunn 	return 0;
666e55f698SAndrew Lunn }
676e55f698SAndrew Lunn 
68ec561276SVivien Didelot /* Offset 0x06: Device Mapping Table register */
69ec561276SVivien Didelot 
70ec561276SVivien Didelot static int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip,
71ec561276SVivien Didelot 					     int target, int port)
72ec561276SVivien Didelot {
73ec561276SVivien Didelot 	u16 val = (target << 8) | (port & 0xf);
74ec561276SVivien Didelot 
759fe850fbSVivien Didelot 	return mv88e6xxx_g2_update(chip, GLOBAL2_DEVICE_MAPPING, val);
76ec561276SVivien Didelot }
77ec561276SVivien Didelot 
78ec561276SVivien Didelot static int mv88e6xxx_g2_set_device_mapping(struct mv88e6xxx_chip *chip)
79ec561276SVivien Didelot {
80ec561276SVivien Didelot 	int target, port;
81ec561276SVivien Didelot 	int err;
82ec561276SVivien Didelot 
83ec561276SVivien Didelot 	/* Initialize the routing port to the 32 possible target devices */
84ec561276SVivien Didelot 	for (target = 0; target < 32; ++target) {
85ec561276SVivien Didelot 		port = 0xf;
86ec561276SVivien Didelot 
87ec561276SVivien Didelot 		if (target < DSA_MAX_SWITCHES) {
88ec561276SVivien Didelot 			port = chip->ds->rtable[target];
89ec561276SVivien Didelot 			if (port == DSA_RTABLE_NONE)
90ec561276SVivien Didelot 				port = 0xf;
91ec561276SVivien Didelot 		}
92ec561276SVivien Didelot 
93ec561276SVivien Didelot 		err = mv88e6xxx_g2_device_mapping_write(chip, target, port);
94ec561276SVivien Didelot 		if (err)
95ec561276SVivien Didelot 			break;
96ec561276SVivien Didelot 	}
97ec561276SVivien Didelot 
98ec561276SVivien Didelot 	return err;
99ec561276SVivien Didelot }
100ec561276SVivien Didelot 
101ec561276SVivien Didelot /* Offset 0x07: Trunk Mask Table register */
102ec561276SVivien Didelot 
103ec561276SVivien Didelot static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
104ec561276SVivien Didelot 					 bool hask, u16 mask)
105ec561276SVivien Didelot {
106370b4ffbSVivien Didelot 	const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
107ec561276SVivien Didelot 	u16 val = (num << 12) | (mask & port_mask);
108ec561276SVivien Didelot 
109ec561276SVivien Didelot 	if (hask)
110ec561276SVivien Didelot 		val |= GLOBAL2_TRUNK_MASK_HASK;
111ec561276SVivien Didelot 
1129fe850fbSVivien Didelot 	return mv88e6xxx_g2_update(chip, GLOBAL2_TRUNK_MASK, val);
113ec561276SVivien Didelot }
114ec561276SVivien Didelot 
115ec561276SVivien Didelot /* Offset 0x08: Trunk Mapping Table register */
116ec561276SVivien Didelot 
117ec561276SVivien Didelot static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id,
118ec561276SVivien Didelot 					    u16 map)
119ec561276SVivien Didelot {
120370b4ffbSVivien Didelot 	const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
121ec561276SVivien Didelot 	u16 val = (id << 11) | (map & port_mask);
122ec561276SVivien Didelot 
1239fe850fbSVivien Didelot 	return mv88e6xxx_g2_update(chip, GLOBAL2_TRUNK_MAPPING, val);
124ec561276SVivien Didelot }
125ec561276SVivien Didelot 
126ec561276SVivien Didelot static int mv88e6xxx_g2_clear_trunk(struct mv88e6xxx_chip *chip)
127ec561276SVivien Didelot {
128370b4ffbSVivien Didelot 	const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
129ec561276SVivien Didelot 	int i, err;
130ec561276SVivien Didelot 
131ec561276SVivien Didelot 	/* Clear all eight possible Trunk Mask vectors */
132ec561276SVivien Didelot 	for (i = 0; i < 8; ++i) {
133ec561276SVivien Didelot 		err = mv88e6xxx_g2_trunk_mask_write(chip, i, false, port_mask);
134ec561276SVivien Didelot 		if (err)
135ec561276SVivien Didelot 			return err;
136ec561276SVivien Didelot 	}
137ec561276SVivien Didelot 
138ec561276SVivien Didelot 	/* Clear all sixteen possible Trunk ID routing vectors */
139ec561276SVivien Didelot 	for (i = 0; i < 16; ++i) {
140ec561276SVivien Didelot 		err = mv88e6xxx_g2_trunk_mapping_write(chip, i, 0);
141ec561276SVivien Didelot 		if (err)
142ec561276SVivien Didelot 			return err;
143ec561276SVivien Didelot 	}
144ec561276SVivien Didelot 
145ec561276SVivien Didelot 	return 0;
146ec561276SVivien Didelot }
147ec561276SVivien Didelot 
148ec561276SVivien Didelot /* Offset 0x09: Ingress Rate Command register
149ec561276SVivien Didelot  * Offset 0x0A: Ingress Rate Data register
150ec561276SVivien Didelot  */
151ec561276SVivien Didelot 
152ec561276SVivien Didelot static int mv88e6xxx_g2_clear_irl(struct mv88e6xxx_chip *chip)
153ec561276SVivien Didelot {
154ec561276SVivien Didelot 	int port, err;
155ec561276SVivien Didelot 
156ec561276SVivien Didelot 	/* Init all Ingress Rate Limit resources of all ports */
157370b4ffbSVivien Didelot 	for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) {
158ec561276SVivien Didelot 		/* XXX newer chips (like 88E6390) have different 2-bit ops */
1599fe850fbSVivien Didelot 		err = mv88e6xxx_g2_write(chip, GLOBAL2_IRL_CMD,
160ec561276SVivien Didelot 					 GLOBAL2_IRL_CMD_OP_INIT_ALL |
161ec561276SVivien Didelot 					 (port << 8));
162ec561276SVivien Didelot 		if (err)
163ec561276SVivien Didelot 			break;
164ec561276SVivien Didelot 
165ec561276SVivien Didelot 		/* Wait for the operation to complete */
1669fe850fbSVivien Didelot 		err = mv88e6xxx_g2_wait(chip, GLOBAL2_IRL_CMD,
167ec561276SVivien Didelot 					GLOBAL2_IRL_CMD_BUSY);
168ec561276SVivien Didelot 		if (err)
169ec561276SVivien Didelot 			break;
170ec561276SVivien Didelot 	}
171ec561276SVivien Didelot 
172ec561276SVivien Didelot 	return err;
173ec561276SVivien Didelot }
174ec561276SVivien Didelot 
17517a1594eSVivien Didelot /* Offset 0x0B: Cross-chip Port VLAN (Addr) Register
17617a1594eSVivien Didelot  * Offset 0x0C: Cross-chip Port VLAN Data Register
17717a1594eSVivien Didelot  */
17817a1594eSVivien Didelot 
17917a1594eSVivien Didelot static int mv88e6xxx_g2_pvt_op_wait(struct mv88e6xxx_chip *chip)
18017a1594eSVivien Didelot {
18117a1594eSVivien Didelot 	return mv88e6xxx_g2_wait(chip, GLOBAL2_PVT_ADDR, GLOBAL2_PVT_ADDR_BUSY);
18217a1594eSVivien Didelot }
18317a1594eSVivien Didelot 
18417a1594eSVivien Didelot static int mv88e6xxx_g2_pvt_op(struct mv88e6xxx_chip *chip, int src_dev,
18517a1594eSVivien Didelot 			       int src_port, u16 op)
18617a1594eSVivien Didelot {
18717a1594eSVivien Didelot 	int err;
18817a1594eSVivien Didelot 
18917a1594eSVivien Didelot 	/* 9-bit Cross-chip PVT pointer: with GLOBAL2_MISC_5_BIT_PORT cleared,
19017a1594eSVivien Didelot 	 * source device is 5-bit, source port is 4-bit.
19117a1594eSVivien Didelot 	 */
19217a1594eSVivien Didelot 	op |= (src_dev & 0x1f) << 4;
19317a1594eSVivien Didelot 	op |= (src_port & 0xf);
19417a1594eSVivien Didelot 
19517a1594eSVivien Didelot 	err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_ADDR, op);
19617a1594eSVivien Didelot 	if (err)
19717a1594eSVivien Didelot 		return err;
19817a1594eSVivien Didelot 
19917a1594eSVivien Didelot 	return mv88e6xxx_g2_pvt_op_wait(chip);
20017a1594eSVivien Didelot }
20117a1594eSVivien Didelot 
20217a1594eSVivien Didelot int mv88e6xxx_g2_pvt_write(struct mv88e6xxx_chip *chip, int src_dev,
20317a1594eSVivien Didelot 			   int src_port, u16 data)
20417a1594eSVivien Didelot {
20517a1594eSVivien Didelot 	int err;
20617a1594eSVivien Didelot 
20717a1594eSVivien Didelot 	err = mv88e6xxx_g2_pvt_op_wait(chip);
20817a1594eSVivien Didelot 	if (err)
20917a1594eSVivien Didelot 		return err;
21017a1594eSVivien Didelot 
21117a1594eSVivien Didelot 	err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_DATA, data);
21217a1594eSVivien Didelot 	if (err)
21317a1594eSVivien Didelot 		return err;
21417a1594eSVivien Didelot 
21517a1594eSVivien Didelot 	return mv88e6xxx_g2_pvt_op(chip, src_dev, src_port,
21617a1594eSVivien Didelot 				   GLOBAL2_PVT_ADDR_OP_WRITE_PVLAN);
21717a1594eSVivien Didelot }
21817a1594eSVivien Didelot 
219ec561276SVivien Didelot /* Offset 0x0D: Switch MAC/WoL/WoF register */
220ec561276SVivien Didelot 
221ec561276SVivien Didelot static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip,
222ec561276SVivien Didelot 					 unsigned int pointer, u8 data)
223ec561276SVivien Didelot {
224ec561276SVivien Didelot 	u16 val = (pointer << 8) | data;
225ec561276SVivien Didelot 
2269fe850fbSVivien Didelot 	return mv88e6xxx_g2_update(chip, GLOBAL2_SWITCH_MAC, val);
227ec561276SVivien Didelot }
228ec561276SVivien Didelot 
229ec561276SVivien Didelot int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
230ec561276SVivien Didelot {
231ec561276SVivien Didelot 	int i, err;
232ec561276SVivien Didelot 
233ec561276SVivien Didelot 	for (i = 0; i < 6; i++) {
234ec561276SVivien Didelot 		err = mv88e6xxx_g2_switch_mac_write(chip, i, addr[i]);
235ec561276SVivien Didelot 		if (err)
236ec561276SVivien Didelot 			break;
237ec561276SVivien Didelot 	}
238ec561276SVivien Didelot 
239ec561276SVivien Didelot 	return err;
240ec561276SVivien Didelot }
241ec561276SVivien Didelot 
242ec561276SVivien Didelot /* Offset 0x0F: Priority Override Table */
243ec561276SVivien Didelot 
244ec561276SVivien Didelot static int mv88e6xxx_g2_pot_write(struct mv88e6xxx_chip *chip, int pointer,
245ec561276SVivien Didelot 				  u8 data)
246ec561276SVivien Didelot {
247ec561276SVivien Didelot 	u16 val = (pointer << 8) | (data & 0x7);
248ec561276SVivien Didelot 
2499fe850fbSVivien Didelot 	return mv88e6xxx_g2_update(chip, GLOBAL2_PRIO_OVERRIDE, val);
250ec561276SVivien Didelot }
251ec561276SVivien Didelot 
252ec561276SVivien Didelot static int mv88e6xxx_g2_clear_pot(struct mv88e6xxx_chip *chip)
253ec561276SVivien Didelot {
254ec561276SVivien Didelot 	int i, err;
255ec561276SVivien Didelot 
256ec561276SVivien Didelot 	/* Clear all sixteen possible Priority Override entries */
257ec561276SVivien Didelot 	for (i = 0; i < 16; i++) {
258ec561276SVivien Didelot 		err = mv88e6xxx_g2_pot_write(chip, i, 0);
259ec561276SVivien Didelot 		if (err)
260ec561276SVivien Didelot 			break;
261ec561276SVivien Didelot 	}
262ec561276SVivien Didelot 
263ec561276SVivien Didelot 	return err;
264ec561276SVivien Didelot }
265ec561276SVivien Didelot 
266ec561276SVivien Didelot /* Offset 0x14: EEPROM Command
26798fc3c6fSVivien Didelot  * Offset 0x15: EEPROM Data (for 16-bit data access)
26898fc3c6fSVivien Didelot  * Offset 0x15: EEPROM Addr (for 8-bit data access)
269ec561276SVivien Didelot  */
270ec561276SVivien Didelot 
271ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip)
272ec561276SVivien Didelot {
2739fe850fbSVivien Didelot 	return mv88e6xxx_g2_wait(chip, GLOBAL2_EEPROM_CMD,
274ec561276SVivien Didelot 				 GLOBAL2_EEPROM_CMD_BUSY |
275ec561276SVivien Didelot 				 GLOBAL2_EEPROM_CMD_RUNNING);
276ec561276SVivien Didelot }
277ec561276SVivien Didelot 
278ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
279ec561276SVivien Didelot {
280ec561276SVivien Didelot 	int err;
281ec561276SVivien Didelot 
2829fe850fbSVivien Didelot 	err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_CMD, cmd);
283ec561276SVivien Didelot 	if (err)
284ec561276SVivien Didelot 		return err;
285ec561276SVivien Didelot 
286ec561276SVivien Didelot 	return mv88e6xxx_g2_eeprom_wait(chip);
287ec561276SVivien Didelot }
288ec561276SVivien Didelot 
28998fc3c6fSVivien Didelot static int mv88e6xxx_g2_eeprom_read8(struct mv88e6xxx_chip *chip,
29098fc3c6fSVivien Didelot 				     u16 addr, u8 *data)
29198fc3c6fSVivien Didelot {
29298fc3c6fSVivien Didelot 	u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ;
29398fc3c6fSVivien Didelot 	int err;
29498fc3c6fSVivien Didelot 
29598fc3c6fSVivien Didelot 	err = mv88e6xxx_g2_eeprom_wait(chip);
29698fc3c6fSVivien Didelot 	if (err)
29798fc3c6fSVivien Didelot 		return err;
29898fc3c6fSVivien Didelot 
29998fc3c6fSVivien Didelot 	err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_ADDR, addr);
30098fc3c6fSVivien Didelot 	if (err)
30198fc3c6fSVivien Didelot 		return err;
30298fc3c6fSVivien Didelot 
30398fc3c6fSVivien Didelot 	err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
30498fc3c6fSVivien Didelot 	if (err)
30598fc3c6fSVivien Didelot 		return err;
30698fc3c6fSVivien Didelot 
30798fc3c6fSVivien Didelot 	err = mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_CMD, &cmd);
30898fc3c6fSVivien Didelot 	if (err)
30998fc3c6fSVivien Didelot 		return err;
31098fc3c6fSVivien Didelot 
31198fc3c6fSVivien Didelot 	*data = cmd & 0xff;
31298fc3c6fSVivien Didelot 
31398fc3c6fSVivien Didelot 	return 0;
31498fc3c6fSVivien Didelot }
31598fc3c6fSVivien Didelot 
31698fc3c6fSVivien Didelot static int mv88e6xxx_g2_eeprom_write8(struct mv88e6xxx_chip *chip,
31798fc3c6fSVivien Didelot 				      u16 addr, u8 data)
31898fc3c6fSVivien Didelot {
31998fc3c6fSVivien Didelot 	u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | GLOBAL2_EEPROM_CMD_WRITE_EN;
32098fc3c6fSVivien Didelot 	int err;
32198fc3c6fSVivien Didelot 
32298fc3c6fSVivien Didelot 	err = mv88e6xxx_g2_eeprom_wait(chip);
32398fc3c6fSVivien Didelot 	if (err)
32498fc3c6fSVivien Didelot 		return err;
32598fc3c6fSVivien Didelot 
32698fc3c6fSVivien Didelot 	err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_ADDR, addr);
32798fc3c6fSVivien Didelot 	if (err)
32898fc3c6fSVivien Didelot 		return err;
32998fc3c6fSVivien Didelot 
33098fc3c6fSVivien Didelot 	return mv88e6xxx_g2_eeprom_cmd(chip, cmd | data);
33198fc3c6fSVivien Didelot }
33298fc3c6fSVivien Didelot 
333ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_read16(struct mv88e6xxx_chip *chip,
334ec561276SVivien Didelot 				      u8 addr, u16 *data)
335ec561276SVivien Didelot {
336ec561276SVivien Didelot 	u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ | addr;
337ec561276SVivien Didelot 	int err;
338ec561276SVivien Didelot 
339ec561276SVivien Didelot 	err = mv88e6xxx_g2_eeprom_wait(chip);
340ec561276SVivien Didelot 	if (err)
341ec561276SVivien Didelot 		return err;
342ec561276SVivien Didelot 
343ec561276SVivien Didelot 	err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
344ec561276SVivien Didelot 	if (err)
345ec561276SVivien Didelot 		return err;
346ec561276SVivien Didelot 
3479fe850fbSVivien Didelot 	return mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_DATA, data);
348ec561276SVivien Didelot }
349ec561276SVivien Didelot 
350ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_write16(struct mv88e6xxx_chip *chip,
351ec561276SVivien Didelot 				       u8 addr, u16 data)
352ec561276SVivien Didelot {
353ec561276SVivien Didelot 	u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | addr;
354ec561276SVivien Didelot 	int err;
355ec561276SVivien Didelot 
356ec561276SVivien Didelot 	err = mv88e6xxx_g2_eeprom_wait(chip);
357ec561276SVivien Didelot 	if (err)
358ec561276SVivien Didelot 		return err;
359ec561276SVivien Didelot 
3609fe850fbSVivien Didelot 	err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_DATA, data);
361ec561276SVivien Didelot 	if (err)
362ec561276SVivien Didelot 		return err;
363ec561276SVivien Didelot 
364ec561276SVivien Didelot 	return mv88e6xxx_g2_eeprom_cmd(chip, cmd);
365ec561276SVivien Didelot }
366ec561276SVivien Didelot 
36798fc3c6fSVivien Didelot int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip,
36898fc3c6fSVivien Didelot 			     struct ethtool_eeprom *eeprom, u8 *data)
36998fc3c6fSVivien Didelot {
37098fc3c6fSVivien Didelot 	unsigned int offset = eeprom->offset;
37198fc3c6fSVivien Didelot 	unsigned int len = eeprom->len;
37298fc3c6fSVivien Didelot 	int err;
37398fc3c6fSVivien Didelot 
37498fc3c6fSVivien Didelot 	eeprom->len = 0;
37598fc3c6fSVivien Didelot 
37698fc3c6fSVivien Didelot 	while (len) {
37798fc3c6fSVivien Didelot 		err = mv88e6xxx_g2_eeprom_read8(chip, offset, data);
37898fc3c6fSVivien Didelot 		if (err)
37998fc3c6fSVivien Didelot 			return err;
38098fc3c6fSVivien Didelot 
38198fc3c6fSVivien Didelot 		eeprom->len++;
38298fc3c6fSVivien Didelot 		offset++;
38398fc3c6fSVivien Didelot 		data++;
38498fc3c6fSVivien Didelot 		len--;
38598fc3c6fSVivien Didelot 	}
38698fc3c6fSVivien Didelot 
38798fc3c6fSVivien Didelot 	return 0;
38898fc3c6fSVivien Didelot }
38998fc3c6fSVivien Didelot 
39098fc3c6fSVivien Didelot int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip,
39198fc3c6fSVivien Didelot 			     struct ethtool_eeprom *eeprom, u8 *data)
39298fc3c6fSVivien Didelot {
39398fc3c6fSVivien Didelot 	unsigned int offset = eeprom->offset;
39498fc3c6fSVivien Didelot 	unsigned int len = eeprom->len;
39598fc3c6fSVivien Didelot 	int err;
39698fc3c6fSVivien Didelot 
39798fc3c6fSVivien Didelot 	eeprom->len = 0;
39898fc3c6fSVivien Didelot 
39998fc3c6fSVivien Didelot 	while (len) {
40098fc3c6fSVivien Didelot 		err = mv88e6xxx_g2_eeprom_write8(chip, offset, *data);
40198fc3c6fSVivien Didelot 		if (err)
40298fc3c6fSVivien Didelot 			return err;
40398fc3c6fSVivien Didelot 
40498fc3c6fSVivien Didelot 		eeprom->len++;
40598fc3c6fSVivien Didelot 		offset++;
40698fc3c6fSVivien Didelot 		data++;
40798fc3c6fSVivien Didelot 		len--;
40898fc3c6fSVivien Didelot 	}
40998fc3c6fSVivien Didelot 
41098fc3c6fSVivien Didelot 	return 0;
41198fc3c6fSVivien Didelot }
41298fc3c6fSVivien Didelot 
413ec561276SVivien Didelot int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip,
414ec561276SVivien Didelot 			      struct ethtool_eeprom *eeprom, u8 *data)
415ec561276SVivien Didelot {
416ec561276SVivien Didelot 	unsigned int offset = eeprom->offset;
417ec561276SVivien Didelot 	unsigned int len = eeprom->len;
418ec561276SVivien Didelot 	u16 val;
419ec561276SVivien Didelot 	int err;
420ec561276SVivien Didelot 
421ec561276SVivien Didelot 	eeprom->len = 0;
422ec561276SVivien Didelot 
423ec561276SVivien Didelot 	if (offset & 1) {
424ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
425ec561276SVivien Didelot 		if (err)
426ec561276SVivien Didelot 			return err;
427ec561276SVivien Didelot 
428ec561276SVivien Didelot 		*data++ = (val >> 8) & 0xff;
429ec561276SVivien Didelot 
430ec561276SVivien Didelot 		offset++;
431ec561276SVivien Didelot 		len--;
432ec561276SVivien Didelot 		eeprom->len++;
433ec561276SVivien Didelot 	}
434ec561276SVivien Didelot 
435ec561276SVivien Didelot 	while (len >= 2) {
436ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
437ec561276SVivien Didelot 		if (err)
438ec561276SVivien Didelot 			return err;
439ec561276SVivien Didelot 
440ec561276SVivien Didelot 		*data++ = val & 0xff;
441ec561276SVivien Didelot 		*data++ = (val >> 8) & 0xff;
442ec561276SVivien Didelot 
443ec561276SVivien Didelot 		offset += 2;
444ec561276SVivien Didelot 		len -= 2;
445ec561276SVivien Didelot 		eeprom->len += 2;
446ec561276SVivien Didelot 	}
447ec561276SVivien Didelot 
448ec561276SVivien Didelot 	if (len) {
449ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
450ec561276SVivien Didelot 		if (err)
451ec561276SVivien Didelot 			return err;
452ec561276SVivien Didelot 
453ec561276SVivien Didelot 		*data++ = val & 0xff;
454ec561276SVivien Didelot 
455ec561276SVivien Didelot 		offset++;
456ec561276SVivien Didelot 		len--;
457ec561276SVivien Didelot 		eeprom->len++;
458ec561276SVivien Didelot 	}
459ec561276SVivien Didelot 
460ec561276SVivien Didelot 	return 0;
461ec561276SVivien Didelot }
462ec561276SVivien Didelot 
463ec561276SVivien Didelot int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip,
464ec561276SVivien Didelot 			      struct ethtool_eeprom *eeprom, u8 *data)
465ec561276SVivien Didelot {
466ec561276SVivien Didelot 	unsigned int offset = eeprom->offset;
467ec561276SVivien Didelot 	unsigned int len = eeprom->len;
468ec561276SVivien Didelot 	u16 val;
469ec561276SVivien Didelot 	int err;
470ec561276SVivien Didelot 
471ec561276SVivien Didelot 	/* Ensure the RO WriteEn bit is set */
4729fe850fbSVivien Didelot 	err = mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_CMD, &val);
473ec561276SVivien Didelot 	if (err)
474ec561276SVivien Didelot 		return err;
475ec561276SVivien Didelot 
476ec561276SVivien Didelot 	if (!(val & GLOBAL2_EEPROM_CMD_WRITE_EN))
477ec561276SVivien Didelot 		return -EROFS;
478ec561276SVivien Didelot 
479ec561276SVivien Didelot 	eeprom->len = 0;
480ec561276SVivien Didelot 
481ec561276SVivien Didelot 	if (offset & 1) {
482ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
483ec561276SVivien Didelot 		if (err)
484ec561276SVivien Didelot 			return err;
485ec561276SVivien Didelot 
486ec561276SVivien Didelot 		val = (*data++ << 8) | (val & 0xff);
487ec561276SVivien Didelot 
488ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
489ec561276SVivien Didelot 		if (err)
490ec561276SVivien Didelot 			return err;
491ec561276SVivien Didelot 
492ec561276SVivien Didelot 		offset++;
493ec561276SVivien Didelot 		len--;
494ec561276SVivien Didelot 		eeprom->len++;
495ec561276SVivien Didelot 	}
496ec561276SVivien Didelot 
497ec561276SVivien Didelot 	while (len >= 2) {
498ec561276SVivien Didelot 		val = *data++;
499ec561276SVivien Didelot 		val |= *data++ << 8;
500ec561276SVivien Didelot 
501ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
502ec561276SVivien Didelot 		if (err)
503ec561276SVivien Didelot 			return err;
504ec561276SVivien Didelot 
505ec561276SVivien Didelot 		offset += 2;
506ec561276SVivien Didelot 		len -= 2;
507ec561276SVivien Didelot 		eeprom->len += 2;
508ec561276SVivien Didelot 	}
509ec561276SVivien Didelot 
510ec561276SVivien Didelot 	if (len) {
511ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
512ec561276SVivien Didelot 		if (err)
513ec561276SVivien Didelot 			return err;
514ec561276SVivien Didelot 
515ec561276SVivien Didelot 		val = (val & 0xff00) | *data++;
516ec561276SVivien Didelot 
517ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
518ec561276SVivien Didelot 		if (err)
519ec561276SVivien Didelot 			return err;
520ec561276SVivien Didelot 
521ec561276SVivien Didelot 		offset++;
522ec561276SVivien Didelot 		len--;
523ec561276SVivien Didelot 		eeprom->len++;
524ec561276SVivien Didelot 	}
525ec561276SVivien Didelot 
526ec561276SVivien Didelot 	return 0;
527ec561276SVivien Didelot }
528ec561276SVivien Didelot 
529ec561276SVivien Didelot /* Offset 0x18: SMI PHY Command Register
530ec561276SVivien Didelot  * Offset 0x19: SMI PHY Data Register
531ec561276SVivien Didelot  */
532ec561276SVivien Didelot 
533ec561276SVivien Didelot static int mv88e6xxx_g2_smi_phy_wait(struct mv88e6xxx_chip *chip)
534ec561276SVivien Didelot {
5359fe850fbSVivien Didelot 	return mv88e6xxx_g2_wait(chip, GLOBAL2_SMI_PHY_CMD,
536ec561276SVivien Didelot 				 GLOBAL2_SMI_PHY_CMD_BUSY);
537ec561276SVivien Didelot }
538ec561276SVivien Didelot 
539ec561276SVivien Didelot static int mv88e6xxx_g2_smi_phy_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
540ec561276SVivien Didelot {
541ec561276SVivien Didelot 	int err;
542ec561276SVivien Didelot 
5439fe850fbSVivien Didelot 	err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_CMD, cmd);
544ec561276SVivien Didelot 	if (err)
545ec561276SVivien Didelot 		return err;
546ec561276SVivien Didelot 
547ec561276SVivien Didelot 	return mv88e6xxx_g2_smi_phy_wait(chip);
548ec561276SVivien Didelot }
549ec561276SVivien Didelot 
550cf3e80dfSAndrew Lunn static int mv88e6xxx_g2_smi_phy_write_addr(struct mv88e6xxx_chip *chip,
551cf3e80dfSAndrew Lunn 					   int addr, int device, int reg,
552cf3e80dfSAndrew Lunn 					   bool external)
553ec561276SVivien Didelot {
554cf3e80dfSAndrew Lunn 	int cmd = SMI_CMD_OP_45_WRITE_ADDR | (addr << 5) | device;
555ec561276SVivien Didelot 	int err;
556ec561276SVivien Didelot 
557cf3e80dfSAndrew Lunn 	if (external)
558cf3e80dfSAndrew Lunn 		cmd |= GLOBAL2_SMI_PHY_CMD_EXTERNAL;
559cf3e80dfSAndrew Lunn 
560cf3e80dfSAndrew Lunn 	err = mv88e6xxx_g2_smi_phy_wait(chip);
561cf3e80dfSAndrew Lunn 	if (err)
562cf3e80dfSAndrew Lunn 		return err;
563cf3e80dfSAndrew Lunn 
564cf3e80dfSAndrew Lunn 	err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_DATA, reg);
565cf3e80dfSAndrew Lunn 	if (err)
566cf3e80dfSAndrew Lunn 		return err;
567cf3e80dfSAndrew Lunn 
568cf3e80dfSAndrew Lunn 	return mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
569cf3e80dfSAndrew Lunn }
570cf3e80dfSAndrew Lunn 
57154a88e4cSFlorian Fainelli static int mv88e6xxx_g2_smi_phy_read_c45(struct mv88e6xxx_chip *chip,
57254a88e4cSFlorian Fainelli 					 int addr, int reg_c45, u16 *val,
57354a88e4cSFlorian Fainelli 					 bool external)
574cf3e80dfSAndrew Lunn {
575cf3e80dfSAndrew Lunn 	int device = (reg_c45 >> 16) & 0x1f;
576cf3e80dfSAndrew Lunn 	int reg = reg_c45 & 0xffff;
577cf3e80dfSAndrew Lunn 	int err;
578cf3e80dfSAndrew Lunn 	u16 cmd;
579cf3e80dfSAndrew Lunn 
580cf3e80dfSAndrew Lunn 	err = mv88e6xxx_g2_smi_phy_write_addr(chip, addr, device, reg,
581cf3e80dfSAndrew Lunn 					      external);
582cf3e80dfSAndrew Lunn 	if (err)
583cf3e80dfSAndrew Lunn 		return err;
584cf3e80dfSAndrew Lunn 
585cf3e80dfSAndrew Lunn 	cmd = GLOBAL2_SMI_PHY_CMD_OP_45_READ_DATA | (addr << 5) | device;
586cf3e80dfSAndrew Lunn 
587cf3e80dfSAndrew Lunn 	if (external)
588cf3e80dfSAndrew Lunn 		cmd |= GLOBAL2_SMI_PHY_CMD_EXTERNAL;
589cf3e80dfSAndrew Lunn 
590cf3e80dfSAndrew Lunn 	err = mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
591cf3e80dfSAndrew Lunn 	if (err)
592cf3e80dfSAndrew Lunn 		return err;
593cf3e80dfSAndrew Lunn 
594cf3e80dfSAndrew Lunn 	err = mv88e6xxx_g2_read(chip, GLOBAL2_SMI_PHY_DATA, val);
595cf3e80dfSAndrew Lunn 	if (err)
596cf3e80dfSAndrew Lunn 		return err;
597cf3e80dfSAndrew Lunn 
598cf3e80dfSAndrew Lunn 	err = *val;
599cf3e80dfSAndrew Lunn 
600cf3e80dfSAndrew Lunn 	return 0;
601cf3e80dfSAndrew Lunn }
602cf3e80dfSAndrew Lunn 
60354a88e4cSFlorian Fainelli static int mv88e6xxx_g2_smi_phy_read_c22(struct mv88e6xxx_chip *chip,
60454a88e4cSFlorian Fainelli 					 int addr, int reg, u16 *val,
60554a88e4cSFlorian Fainelli 					 bool external)
606cf3e80dfSAndrew Lunn {
607cf3e80dfSAndrew Lunn 	u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_READ_DATA | (addr << 5) | reg;
608cf3e80dfSAndrew Lunn 	int err;
609cf3e80dfSAndrew Lunn 
610cf3e80dfSAndrew Lunn 	if (external)
611c61a6a71SAndrew Lunn 		cmd |= GLOBAL2_SMI_PHY_CMD_EXTERNAL;
612c61a6a71SAndrew Lunn 
613ec561276SVivien Didelot 	err = mv88e6xxx_g2_smi_phy_wait(chip);
614ec561276SVivien Didelot 	if (err)
615ec561276SVivien Didelot 		return err;
616ec561276SVivien Didelot 
617ec561276SVivien Didelot 	err = mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
618ec561276SVivien Didelot 	if (err)
619ec561276SVivien Didelot 		return err;
620ec561276SVivien Didelot 
6219fe850fbSVivien Didelot 	return mv88e6xxx_g2_read(chip, GLOBAL2_SMI_PHY_DATA, val);
622ec561276SVivien Didelot }
623ec561276SVivien Didelot 
624cf3e80dfSAndrew Lunn int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip,
625ee26a228SAndrew Lunn 			      struct mii_bus *bus,
626cf3e80dfSAndrew Lunn 			      int addr, int reg, u16 *val)
627cf3e80dfSAndrew Lunn {
628cf3e80dfSAndrew Lunn 	struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
629cf3e80dfSAndrew Lunn 	bool external = mdio_bus->external;
630cf3e80dfSAndrew Lunn 
631cf3e80dfSAndrew Lunn 	if (reg & MII_ADDR_C45)
632cf3e80dfSAndrew Lunn 		return mv88e6xxx_g2_smi_phy_read_c45(chip, addr, reg, val,
633cf3e80dfSAndrew Lunn 						     external);
634cf3e80dfSAndrew Lunn 	return mv88e6xxx_g2_smi_phy_read_c22(chip, addr, reg, val, external);
635cf3e80dfSAndrew Lunn }
636cf3e80dfSAndrew Lunn 
63754a88e4cSFlorian Fainelli static int mv88e6xxx_g2_smi_phy_write_c45(struct mv88e6xxx_chip *chip,
63854a88e4cSFlorian Fainelli 					  int addr, int reg_c45, u16 val,
63954a88e4cSFlorian Fainelli 					  bool external)
640cf3e80dfSAndrew Lunn {
641cf3e80dfSAndrew Lunn 	int device = (reg_c45 >> 16) & 0x1f;
642cf3e80dfSAndrew Lunn 	int reg = reg_c45 & 0xffff;
643cf3e80dfSAndrew Lunn 	int err;
644cf3e80dfSAndrew Lunn 	u16 cmd;
645cf3e80dfSAndrew Lunn 
646cf3e80dfSAndrew Lunn 	err = mv88e6xxx_g2_smi_phy_write_addr(chip, addr, device, reg,
647cf3e80dfSAndrew Lunn 					      external);
648cf3e80dfSAndrew Lunn 	if (err)
649cf3e80dfSAndrew Lunn 		return err;
650cf3e80dfSAndrew Lunn 
651cf3e80dfSAndrew Lunn 	cmd = GLOBAL2_SMI_PHY_CMD_OP_45_WRITE_DATA | (addr << 5) | device;
652cf3e80dfSAndrew Lunn 
653cf3e80dfSAndrew Lunn 	if (external)
654cf3e80dfSAndrew Lunn 		cmd |= GLOBAL2_SMI_PHY_CMD_EXTERNAL;
655cf3e80dfSAndrew Lunn 
656cf3e80dfSAndrew Lunn 	err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_DATA, val);
657cf3e80dfSAndrew Lunn 	if (err)
658cf3e80dfSAndrew Lunn 		return err;
659cf3e80dfSAndrew Lunn 
660cf3e80dfSAndrew Lunn 	err = mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
661cf3e80dfSAndrew Lunn 	if (err)
662cf3e80dfSAndrew Lunn 		return err;
663cf3e80dfSAndrew Lunn 
664cf3e80dfSAndrew Lunn 	return 0;
665cf3e80dfSAndrew Lunn }
666cf3e80dfSAndrew Lunn 
66754a88e4cSFlorian Fainelli static int mv88e6xxx_g2_smi_phy_write_c22(struct mv88e6xxx_chip *chip,
66854a88e4cSFlorian Fainelli 					  int addr, int reg, u16 val,
66954a88e4cSFlorian Fainelli 					  bool external)
670ec561276SVivien Didelot {
671ec561276SVivien Didelot 	u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_WRITE_DATA | (addr << 5) | reg;
672ec561276SVivien Didelot 	int err;
673ec561276SVivien Didelot 
674cf3e80dfSAndrew Lunn 	if (external)
675c61a6a71SAndrew Lunn 		cmd |= GLOBAL2_SMI_PHY_CMD_EXTERNAL;
676c61a6a71SAndrew Lunn 
677ec561276SVivien Didelot 	err = mv88e6xxx_g2_smi_phy_wait(chip);
678ec561276SVivien Didelot 	if (err)
679ec561276SVivien Didelot 		return err;
680ec561276SVivien Didelot 
6819fe850fbSVivien Didelot 	err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_DATA, val);
682ec561276SVivien Didelot 	if (err)
683ec561276SVivien Didelot 		return err;
684ec561276SVivien Didelot 
685ec561276SVivien Didelot 	return mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
686ec561276SVivien Didelot }
687ec561276SVivien Didelot 
688cf3e80dfSAndrew Lunn int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip,
689cf3e80dfSAndrew Lunn 			       struct mii_bus *bus,
690cf3e80dfSAndrew Lunn 			       int addr, int reg, u16 val)
691cf3e80dfSAndrew Lunn {
692cf3e80dfSAndrew Lunn 	struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
693cf3e80dfSAndrew Lunn 	bool external = mdio_bus->external;
694cf3e80dfSAndrew Lunn 
695cf3e80dfSAndrew Lunn 	if (reg & MII_ADDR_C45)
696cf3e80dfSAndrew Lunn 		return mv88e6xxx_g2_smi_phy_write_c45(chip, addr, reg, val,
697cf3e80dfSAndrew Lunn 						      external);
698cf3e80dfSAndrew Lunn 
699cf3e80dfSAndrew Lunn 	return mv88e6xxx_g2_smi_phy_write_c22(chip, addr, reg, val, external);
700cf3e80dfSAndrew Lunn }
701cf3e80dfSAndrew Lunn 
702fcd25166SAndrew Lunn static int mv88e6097_watchdog_action(struct mv88e6xxx_chip *chip, int irq)
703fcd25166SAndrew Lunn {
704fcd25166SAndrew Lunn 	u16 reg;
705fcd25166SAndrew Lunn 
706fcd25166SAndrew Lunn 	mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
707fcd25166SAndrew Lunn 
708fcd25166SAndrew Lunn 	dev_info(chip->dev, "Watchdog event: 0x%04x", reg);
709fcd25166SAndrew Lunn 
710fcd25166SAndrew Lunn 	return IRQ_HANDLED;
711fcd25166SAndrew Lunn }
712fcd25166SAndrew Lunn 
713fcd25166SAndrew Lunn static void mv88e6097_watchdog_free(struct mv88e6xxx_chip *chip)
714fcd25166SAndrew Lunn {
715fcd25166SAndrew Lunn 	u16 reg;
716fcd25166SAndrew Lunn 
717fcd25166SAndrew Lunn 	mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
718fcd25166SAndrew Lunn 
719fcd25166SAndrew Lunn 	reg &= ~(GLOBAL2_WDOG_CONTROL_EGRESS_ENABLE |
720fcd25166SAndrew Lunn 		 GLOBAL2_WDOG_CONTROL_QC_ENABLE);
721fcd25166SAndrew Lunn 
722fcd25166SAndrew Lunn 	mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, reg);
723fcd25166SAndrew Lunn }
724fcd25166SAndrew Lunn 
725fcd25166SAndrew Lunn static int mv88e6097_watchdog_setup(struct mv88e6xxx_chip *chip)
726fcd25166SAndrew Lunn {
727fcd25166SAndrew Lunn 	return mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL,
728fcd25166SAndrew Lunn 				  GLOBAL2_WDOG_CONTROL_EGRESS_ENABLE |
729fcd25166SAndrew Lunn 				  GLOBAL2_WDOG_CONTROL_QC_ENABLE |
730fcd25166SAndrew Lunn 				  GLOBAL2_WDOG_CONTROL_SWRESET);
731fcd25166SAndrew Lunn }
732fcd25166SAndrew Lunn 
733fcd25166SAndrew Lunn const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = {
734fcd25166SAndrew Lunn 	.irq_action = mv88e6097_watchdog_action,
735fcd25166SAndrew Lunn 	.irq_setup = mv88e6097_watchdog_setup,
736fcd25166SAndrew Lunn 	.irq_free = mv88e6097_watchdog_free,
737fcd25166SAndrew Lunn };
738fcd25166SAndrew Lunn 
73961303736SAndrew Lunn static int mv88e6390_watchdog_setup(struct mv88e6xxx_chip *chip)
74061303736SAndrew Lunn {
74161303736SAndrew Lunn 	return mv88e6xxx_g2_update(chip, GLOBAL2_WDOG_CONTROL,
74261303736SAndrew Lunn 				   GLOBAL2_WDOG_INT_ENABLE |
74361303736SAndrew Lunn 				   GLOBAL2_WDOG_CUT_THROUGH |
74461303736SAndrew Lunn 				   GLOBAL2_WDOG_QUEUE_CONTROLLER |
74561303736SAndrew Lunn 				   GLOBAL2_WDOG_EGRESS |
74661303736SAndrew Lunn 				   GLOBAL2_WDOG_FORCE_IRQ);
74761303736SAndrew Lunn }
74861303736SAndrew Lunn 
74961303736SAndrew Lunn static int mv88e6390_watchdog_action(struct mv88e6xxx_chip *chip, int irq)
75061303736SAndrew Lunn {
75161303736SAndrew Lunn 	int err;
75261303736SAndrew Lunn 	u16 reg;
75361303736SAndrew Lunn 
75461303736SAndrew Lunn 	mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, GLOBAL2_WDOG_EVENT);
75561303736SAndrew Lunn 	err = mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
75661303736SAndrew Lunn 
75761303736SAndrew Lunn 	dev_info(chip->dev, "Watchdog event: 0x%04x",
75861303736SAndrew Lunn 		 reg & GLOBAL2_WDOG_DATA_MASK);
75961303736SAndrew Lunn 
76061303736SAndrew Lunn 	mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, GLOBAL2_WDOG_HISTORY);
76161303736SAndrew Lunn 	err = mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, &reg);
76261303736SAndrew Lunn 
76361303736SAndrew Lunn 	dev_info(chip->dev, "Watchdog history: 0x%04x",
76461303736SAndrew Lunn 		 reg & GLOBAL2_WDOG_DATA_MASK);
76561303736SAndrew Lunn 
76661303736SAndrew Lunn 	/* Trigger a software reset to try to recover the switch */
76761303736SAndrew Lunn 	if (chip->info->ops->reset)
76861303736SAndrew Lunn 		chip->info->ops->reset(chip);
76961303736SAndrew Lunn 
77061303736SAndrew Lunn 	mv88e6390_watchdog_setup(chip);
77161303736SAndrew Lunn 
77261303736SAndrew Lunn 	return IRQ_HANDLED;
77361303736SAndrew Lunn }
77461303736SAndrew Lunn 
77561303736SAndrew Lunn static void mv88e6390_watchdog_free(struct mv88e6xxx_chip *chip)
77661303736SAndrew Lunn {
77761303736SAndrew Lunn 	mv88e6xxx_g2_update(chip, GLOBAL2_WDOG_CONTROL,
77861303736SAndrew Lunn 			    GLOBAL2_WDOG_INT_ENABLE);
77961303736SAndrew Lunn }
78061303736SAndrew Lunn 
78161303736SAndrew Lunn const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = {
78261303736SAndrew Lunn 	.irq_action = mv88e6390_watchdog_action,
78361303736SAndrew Lunn 	.irq_setup = mv88e6390_watchdog_setup,
78461303736SAndrew Lunn 	.irq_free = mv88e6390_watchdog_free,
78561303736SAndrew Lunn };
78661303736SAndrew Lunn 
787fcd25166SAndrew Lunn static irqreturn_t mv88e6xxx_g2_watchdog_thread_fn(int irq, void *dev_id)
788fcd25166SAndrew Lunn {
789fcd25166SAndrew Lunn 	struct mv88e6xxx_chip *chip = dev_id;
790fcd25166SAndrew Lunn 	irqreturn_t ret = IRQ_NONE;
791fcd25166SAndrew Lunn 
792fcd25166SAndrew Lunn 	mutex_lock(&chip->reg_lock);
793fcd25166SAndrew Lunn 	if (chip->info->ops->watchdog_ops->irq_action)
794fcd25166SAndrew Lunn 		ret = chip->info->ops->watchdog_ops->irq_action(chip, irq);
795fcd25166SAndrew Lunn 	mutex_unlock(&chip->reg_lock);
796fcd25166SAndrew Lunn 
797fcd25166SAndrew Lunn 	return ret;
798fcd25166SAndrew Lunn }
799fcd25166SAndrew Lunn 
800fcd25166SAndrew Lunn static void mv88e6xxx_g2_watchdog_free(struct mv88e6xxx_chip *chip)
801fcd25166SAndrew Lunn {
802fcd25166SAndrew Lunn 	mutex_lock(&chip->reg_lock);
803fcd25166SAndrew Lunn 	if (chip->info->ops->watchdog_ops->irq_free)
804fcd25166SAndrew Lunn 		chip->info->ops->watchdog_ops->irq_free(chip);
805fcd25166SAndrew Lunn 	mutex_unlock(&chip->reg_lock);
806fcd25166SAndrew Lunn 
807fcd25166SAndrew Lunn 	free_irq(chip->watchdog_irq, chip);
808fcd25166SAndrew Lunn 	irq_dispose_mapping(chip->watchdog_irq);
809fcd25166SAndrew Lunn }
810fcd25166SAndrew Lunn 
811fcd25166SAndrew Lunn static int mv88e6xxx_g2_watchdog_setup(struct mv88e6xxx_chip *chip)
812fcd25166SAndrew Lunn {
813fcd25166SAndrew Lunn 	int err;
814fcd25166SAndrew Lunn 
815fcd25166SAndrew Lunn 	chip->watchdog_irq = irq_find_mapping(chip->g2_irq.domain,
816fcd25166SAndrew Lunn 					      GLOBAL2_INT_SOURCE_WATCHDOG);
817fcd25166SAndrew Lunn 	if (chip->watchdog_irq < 0)
818fcd25166SAndrew Lunn 		return chip->watchdog_irq;
819fcd25166SAndrew Lunn 
820fcd25166SAndrew Lunn 	err = request_threaded_irq(chip->watchdog_irq, NULL,
821fcd25166SAndrew Lunn 				   mv88e6xxx_g2_watchdog_thread_fn,
822fcd25166SAndrew Lunn 				   IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
823fcd25166SAndrew Lunn 				   "mv88e6xxx-watchdog", chip);
824fcd25166SAndrew Lunn 	if (err)
825fcd25166SAndrew Lunn 		return err;
826fcd25166SAndrew Lunn 
827fcd25166SAndrew Lunn 	mutex_lock(&chip->reg_lock);
828fcd25166SAndrew Lunn 	if (chip->info->ops->watchdog_ops->irq_setup)
829fcd25166SAndrew Lunn 		err = chip->info->ops->watchdog_ops->irq_setup(chip);
830fcd25166SAndrew Lunn 	mutex_unlock(&chip->reg_lock);
831fcd25166SAndrew Lunn 
832fcd25166SAndrew Lunn 	return err;
833fcd25166SAndrew Lunn }
834fcd25166SAndrew Lunn 
83581228996SVivien Didelot /* Offset 0x1D: Misc Register */
83681228996SVivien Didelot 
83781228996SVivien Didelot static int mv88e6xxx_g2_misc_5_bit_port(struct mv88e6xxx_chip *chip,
83881228996SVivien Didelot 					bool port_5_bit)
83981228996SVivien Didelot {
84081228996SVivien Didelot 	u16 val;
84181228996SVivien Didelot 	int err;
84281228996SVivien Didelot 
84381228996SVivien Didelot 	err = mv88e6xxx_g2_read(chip, GLOBAL2_MISC, &val);
84481228996SVivien Didelot 	if (err)
84581228996SVivien Didelot 		return err;
84681228996SVivien Didelot 
84781228996SVivien Didelot 	if (port_5_bit)
84881228996SVivien Didelot 		val |= GLOBAL2_MISC_5_BIT_PORT;
84981228996SVivien Didelot 	else
85081228996SVivien Didelot 		val &= ~GLOBAL2_MISC_5_BIT_PORT;
85181228996SVivien Didelot 
85281228996SVivien Didelot 	return mv88e6xxx_g2_write(chip, GLOBAL2_MISC, val);
85381228996SVivien Didelot }
85481228996SVivien Didelot 
85581228996SVivien Didelot int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip)
85681228996SVivien Didelot {
85781228996SVivien Didelot 	return mv88e6xxx_g2_misc_5_bit_port(chip, false);
85881228996SVivien Didelot }
85981228996SVivien Didelot 
860dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_mask(struct irq_data *d)
861dc30c35bSAndrew Lunn {
862dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
863dc30c35bSAndrew Lunn 	unsigned int n = d->hwirq;
864dc30c35bSAndrew Lunn 
865dc30c35bSAndrew Lunn 	chip->g2_irq.masked |= (1 << n);
866dc30c35bSAndrew Lunn }
867dc30c35bSAndrew Lunn 
868dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_unmask(struct irq_data *d)
869dc30c35bSAndrew Lunn {
870dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
871dc30c35bSAndrew Lunn 	unsigned int n = d->hwirq;
872dc30c35bSAndrew Lunn 
873dc30c35bSAndrew Lunn 	chip->g2_irq.masked &= ~(1 << n);
874dc30c35bSAndrew Lunn }
875dc30c35bSAndrew Lunn 
876dc30c35bSAndrew Lunn static irqreturn_t mv88e6xxx_g2_irq_thread_fn(int irq, void *dev_id)
877dc30c35bSAndrew Lunn {
878dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = dev_id;
879dc30c35bSAndrew Lunn 	unsigned int nhandled = 0;
880dc30c35bSAndrew Lunn 	unsigned int sub_irq;
881dc30c35bSAndrew Lunn 	unsigned int n;
882dc30c35bSAndrew Lunn 	int err;
883dc30c35bSAndrew Lunn 	u16 reg;
884dc30c35bSAndrew Lunn 
885dc30c35bSAndrew Lunn 	mutex_lock(&chip->reg_lock);
886dc30c35bSAndrew Lunn 	err = mv88e6xxx_g2_read(chip, GLOBAL2_INT_SOURCE, &reg);
887dc30c35bSAndrew Lunn 	mutex_unlock(&chip->reg_lock);
888dc30c35bSAndrew Lunn 	if (err)
889dc30c35bSAndrew Lunn 		goto out;
890dc30c35bSAndrew Lunn 
891dc30c35bSAndrew Lunn 	for (n = 0; n < 16; ++n) {
892dc30c35bSAndrew Lunn 		if (reg & (1 << n)) {
893dc30c35bSAndrew Lunn 			sub_irq = irq_find_mapping(chip->g2_irq.domain, n);
894dc30c35bSAndrew Lunn 			handle_nested_irq(sub_irq);
895dc30c35bSAndrew Lunn 			++nhandled;
896dc30c35bSAndrew Lunn 		}
897dc30c35bSAndrew Lunn 	}
898dc30c35bSAndrew Lunn out:
899dc30c35bSAndrew Lunn 	return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
900dc30c35bSAndrew Lunn }
901dc30c35bSAndrew Lunn 
902dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_bus_lock(struct irq_data *d)
903dc30c35bSAndrew Lunn {
904dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
905dc30c35bSAndrew Lunn 
906dc30c35bSAndrew Lunn 	mutex_lock(&chip->reg_lock);
907dc30c35bSAndrew Lunn }
908dc30c35bSAndrew Lunn 
909dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_bus_sync_unlock(struct irq_data *d)
910dc30c35bSAndrew Lunn {
911dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
912dc30c35bSAndrew Lunn 
913dc30c35bSAndrew Lunn 	mv88e6xxx_g2_write(chip, GLOBAL2_INT_MASK, ~chip->g2_irq.masked);
914dc30c35bSAndrew Lunn 
915dc30c35bSAndrew Lunn 	mutex_unlock(&chip->reg_lock);
916dc30c35bSAndrew Lunn }
917dc30c35bSAndrew Lunn 
918dc30c35bSAndrew Lunn static struct irq_chip mv88e6xxx_g2_irq_chip = {
919dc30c35bSAndrew Lunn 	.name			= "mv88e6xxx-g2",
920dc30c35bSAndrew Lunn 	.irq_mask		= mv88e6xxx_g2_irq_mask,
921dc30c35bSAndrew Lunn 	.irq_unmask		= mv88e6xxx_g2_irq_unmask,
922dc30c35bSAndrew Lunn 	.irq_bus_lock		= mv88e6xxx_g2_irq_bus_lock,
923dc30c35bSAndrew Lunn 	.irq_bus_sync_unlock	= mv88e6xxx_g2_irq_bus_sync_unlock,
924dc30c35bSAndrew Lunn };
925dc30c35bSAndrew Lunn 
926dc30c35bSAndrew Lunn static int mv88e6xxx_g2_irq_domain_map(struct irq_domain *d,
927dc30c35bSAndrew Lunn 				       unsigned int irq,
928dc30c35bSAndrew Lunn 				       irq_hw_number_t hwirq)
929dc30c35bSAndrew Lunn {
930dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = d->host_data;
931dc30c35bSAndrew Lunn 
932dc30c35bSAndrew Lunn 	irq_set_chip_data(irq, d->host_data);
933dc30c35bSAndrew Lunn 	irq_set_chip_and_handler(irq, &chip->g2_irq.chip, handle_level_irq);
934dc30c35bSAndrew Lunn 	irq_set_noprobe(irq);
935dc30c35bSAndrew Lunn 
936dc30c35bSAndrew Lunn 	return 0;
937dc30c35bSAndrew Lunn }
938dc30c35bSAndrew Lunn 
939dc30c35bSAndrew Lunn static const struct irq_domain_ops mv88e6xxx_g2_irq_domain_ops = {
940dc30c35bSAndrew Lunn 	.map	= mv88e6xxx_g2_irq_domain_map,
941dc30c35bSAndrew Lunn 	.xlate	= irq_domain_xlate_twocell,
942dc30c35bSAndrew Lunn };
943dc30c35bSAndrew Lunn 
944dc30c35bSAndrew Lunn void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip)
945dc30c35bSAndrew Lunn {
946dc30c35bSAndrew Lunn 	int irq, virq;
947dc30c35bSAndrew Lunn 
948fcd25166SAndrew Lunn 	mv88e6xxx_g2_watchdog_free(chip);
949fcd25166SAndrew Lunn 
9508e757ebaSAndrew Lunn 	free_irq(chip->device_irq, chip);
9518e757ebaSAndrew Lunn 	irq_dispose_mapping(chip->device_irq);
9528e757ebaSAndrew Lunn 
953dc30c35bSAndrew Lunn 	for (irq = 0; irq < 16; irq++) {
954dc30c35bSAndrew Lunn 		virq = irq_find_mapping(chip->g2_irq.domain, irq);
955dc30c35bSAndrew Lunn 		irq_dispose_mapping(virq);
956dc30c35bSAndrew Lunn 	}
957dc30c35bSAndrew Lunn 
958dc30c35bSAndrew Lunn 	irq_domain_remove(chip->g2_irq.domain);
959dc30c35bSAndrew Lunn }
960dc30c35bSAndrew Lunn 
961dc30c35bSAndrew Lunn int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip)
962dc30c35bSAndrew Lunn {
9638e757ebaSAndrew Lunn 	int err, irq, virq;
964dc30c35bSAndrew Lunn 
965dc30c35bSAndrew Lunn 	if (!chip->dev->of_node)
966dc30c35bSAndrew Lunn 		return -EINVAL;
967dc30c35bSAndrew Lunn 
968dc30c35bSAndrew Lunn 	chip->g2_irq.domain = irq_domain_add_simple(
969dc30c35bSAndrew Lunn 		chip->dev->of_node, 16, 0, &mv88e6xxx_g2_irq_domain_ops, chip);
970dc30c35bSAndrew Lunn 	if (!chip->g2_irq.domain)
971dc30c35bSAndrew Lunn 		return -ENOMEM;
972dc30c35bSAndrew Lunn 
973dc30c35bSAndrew Lunn 	for (irq = 0; irq < 16; irq++)
974dc30c35bSAndrew Lunn 		irq_create_mapping(chip->g2_irq.domain, irq);
975dc30c35bSAndrew Lunn 
976dc30c35bSAndrew Lunn 	chip->g2_irq.chip = mv88e6xxx_g2_irq_chip;
977dc30c35bSAndrew Lunn 	chip->g2_irq.masked = ~0;
978dc30c35bSAndrew Lunn 
9798e757ebaSAndrew Lunn 	chip->device_irq = irq_find_mapping(chip->g1_irq.domain,
98082466921SVivien Didelot 					    MV88E6XXX_G1_STS_IRQ_DEVICE);
9818e757ebaSAndrew Lunn 	if (chip->device_irq < 0) {
9828e757ebaSAndrew Lunn 		err = chip->device_irq;
983dc30c35bSAndrew Lunn 		goto out;
984dc30c35bSAndrew Lunn 	}
985dc30c35bSAndrew Lunn 
9868e757ebaSAndrew Lunn 	err = request_threaded_irq(chip->device_irq, NULL,
987dc30c35bSAndrew Lunn 				   mv88e6xxx_g2_irq_thread_fn,
988dc30c35bSAndrew Lunn 				   IRQF_ONESHOT, "mv88e6xxx-g1", chip);
989dc30c35bSAndrew Lunn 	if (err)
990dc30c35bSAndrew Lunn 		goto out;
991dc30c35bSAndrew Lunn 
992fcd25166SAndrew Lunn 	return mv88e6xxx_g2_watchdog_setup(chip);
9938e757ebaSAndrew Lunn 
994dc30c35bSAndrew Lunn out:
9958e757ebaSAndrew Lunn 	for (irq = 0; irq < 16; irq++) {
9968e757ebaSAndrew Lunn 		virq = irq_find_mapping(chip->g2_irq.domain, irq);
9978e757ebaSAndrew Lunn 		irq_dispose_mapping(virq);
9988e757ebaSAndrew Lunn 	}
9998e757ebaSAndrew Lunn 
10008e757ebaSAndrew Lunn 	irq_domain_remove(chip->g2_irq.domain);
1001dc30c35bSAndrew Lunn 
1002dc30c35bSAndrew Lunn 	return err;
1003dc30c35bSAndrew Lunn }
1004dc30c35bSAndrew Lunn 
1005ec561276SVivien Didelot int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip)
1006ec561276SVivien Didelot {
1007ec561276SVivien Didelot 	u16 reg;
1008ec561276SVivien Didelot 	int err;
1009ec561276SVivien Didelot 
1010ec561276SVivien Didelot 	/* Ignore removed tag data on doubly tagged packets, disable
1011ec561276SVivien Didelot 	 * flow control messages, force flow control priority to the
1012ec561276SVivien Didelot 	 * highest, and send all special multicast frames to the CPU
1013ec561276SVivien Didelot 	 * port at the highest priority.
1014ec561276SVivien Didelot 	 */
1015ec561276SVivien Didelot 	reg = GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI | (0x7 << 4);
1016ec561276SVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X) ||
1017ec561276SVivien Didelot 	    mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X))
1018ec561276SVivien Didelot 		reg |= GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x7;
10199fe850fbSVivien Didelot 	err = mv88e6xxx_g2_write(chip, GLOBAL2_SWITCH_MGMT, reg);
1020ec561276SVivien Didelot 	if (err)
1021ec561276SVivien Didelot 		return err;
1022ec561276SVivien Didelot 
1023ec561276SVivien Didelot 	/* Program the DSA routing table. */
1024ec561276SVivien Didelot 	err = mv88e6xxx_g2_set_device_mapping(chip);
1025ec561276SVivien Didelot 	if (err)
1026ec561276SVivien Didelot 		return err;
1027ec561276SVivien Didelot 
1028ec561276SVivien Didelot 	/* Clear all trunk masks and mapping. */
1029ec561276SVivien Didelot 	err = mv88e6xxx_g2_clear_trunk(chip);
1030ec561276SVivien Didelot 	if (err)
1031ec561276SVivien Didelot 		return err;
1032ec561276SVivien Didelot 
1033ec561276SVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_IRL)) {
1034ec561276SVivien Didelot 		/* Disable ingress rate limiting by resetting all per port
1035ec561276SVivien Didelot 		 * ingress rate limit resources to their initial state.
1036ec561276SVivien Didelot 		 */
1037ec561276SVivien Didelot 		err = mv88e6xxx_g2_clear_irl(chip);
1038ec561276SVivien Didelot 			if (err)
1039ec561276SVivien Didelot 				return err;
1040ec561276SVivien Didelot 	}
1041ec561276SVivien Didelot 
1042ec561276SVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_POT)) {
1043ec561276SVivien Didelot 		/* Clear the priority override table. */
1044ec561276SVivien Didelot 		err = mv88e6xxx_g2_clear_pot(chip);
1045ec561276SVivien Didelot 		if (err)
1046ec561276SVivien Didelot 			return err;
1047ec561276SVivien Didelot 	}
1048ec561276SVivien Didelot 
1049ec561276SVivien Didelot 	return 0;
1050ec561276SVivien Didelot }
1051