12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2ec561276SVivien Didelot /*
31d90016dSVivien Didelot  * Marvell 88E6xxx Switch Global 2 Registers support
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 
11e289ef0dSVivien Didelot #include <linux/bitfield.h>
12282ccf6eSFlorian Westphal #include <linux/interrupt.h>
13dc30c35bSAndrew Lunn #include <linux/irqdomain.h>
144d5f2ba7SVivien Didelot 
154d5f2ba7SVivien Didelot #include "chip.h"
1682466921SVivien Didelot #include "global1.h" /* for MV88E6XXX_G1_STS_IRQ_DEVICE */
17ec561276SVivien Didelot #include "global2.h"
18ec561276SVivien Didelot 
19b000be95SBrandon Streiff int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
209fe850fbSVivien Didelot {
219069c13aSVivien Didelot 	return mv88e6xxx_read(chip, chip->info->global2_addr, reg, val);
229fe850fbSVivien Didelot }
239fe850fbSVivien Didelot 
24b000be95SBrandon Streiff int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
259fe850fbSVivien Didelot {
269069c13aSVivien Didelot 	return mv88e6xxx_write(chip, chip->info->global2_addr, reg, val);
279fe850fbSVivien Didelot }
289fe850fbSVivien Didelot 
2919fb7f69SVivien Didelot int mv88e6xxx_g2_wait_bit(struct mv88e6xxx_chip *chip, int reg, int
3019fb7f69SVivien Didelot 			  bit, int val)
3119fb7f69SVivien Didelot {
3219fb7f69SVivien Didelot 	return mv88e6xxx_wait_bit(chip, chip->info->global2_addr, reg,
3319fb7f69SVivien Didelot 				  bit, val);
3419fb7f69SVivien Didelot }
3519fb7f69SVivien Didelot 
36d6c5e6afSVivien Didelot /* Offset 0x00: Interrupt Source Register */
37d6c5e6afSVivien Didelot 
38d6c5e6afSVivien Didelot static int mv88e6xxx_g2_int_source(struct mv88e6xxx_chip *chip, u16 *src)
39d6c5e6afSVivien Didelot {
40d6c5e6afSVivien Didelot 	/* Read (and clear most of) the Interrupt Source bits */
41d6c5e6afSVivien Didelot 	return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_INT_SRC, src);
42d6c5e6afSVivien Didelot }
43d6c5e6afSVivien Didelot 
44d6c5e6afSVivien Didelot /* Offset 0x01: Interrupt Mask Register */
45d6c5e6afSVivien Didelot 
46d6c5e6afSVivien Didelot static int mv88e6xxx_g2_int_mask(struct mv88e6xxx_chip *chip, u16 mask)
47d6c5e6afSVivien Didelot {
48d6c5e6afSVivien Didelot 	return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_INT_MASK, mask);
49d6c5e6afSVivien Didelot }
50d6c5e6afSVivien Didelot 
516e55f698SAndrew Lunn /* Offset 0x02: Management Enable 2x */
5251c901a7SVivien Didelot 
5351c901a7SVivien Didelot static int mv88e6xxx_g2_mgmt_enable_2x(struct mv88e6xxx_chip *chip, u16 en2x)
5451c901a7SVivien Didelot {
5551c901a7SVivien Didelot 	return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_MGMT_EN_2X, en2x);
5651c901a7SVivien Didelot }
5751c901a7SVivien Didelot 
586e55f698SAndrew Lunn /* Offset 0x03: Management Enable 0x */
596e55f698SAndrew Lunn 
6051c901a7SVivien Didelot static int mv88e6xxx_g2_mgmt_enable_0x(struct mv88e6xxx_chip *chip, u16 en0x)
6151c901a7SVivien Didelot {
6251c901a7SVivien Didelot 	return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_MGMT_EN_0X, en0x);
6351c901a7SVivien Didelot }
6451c901a7SVivien Didelot 
6551c901a7SVivien Didelot /* Offset 0x05: Switch Management Register */
6651c901a7SVivien Didelot 
6751c901a7SVivien Didelot static int mv88e6xxx_g2_switch_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip,
6851c901a7SVivien Didelot 					     bool enable)
6951c901a7SVivien Didelot {
7051c901a7SVivien Didelot 	u16 val;
7151c901a7SVivien Didelot 	int err;
7251c901a7SVivien Didelot 
7351c901a7SVivien Didelot 	err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SWITCH_MGMT, &val);
7451c901a7SVivien Didelot 	if (err)
7551c901a7SVivien Didelot 		return err;
7651c901a7SVivien Didelot 
7751c901a7SVivien Didelot 	if (enable)
7851c901a7SVivien Didelot 		val |= MV88E6XXX_G2_SWITCH_MGMT_RSVD2CPU;
7951c901a7SVivien Didelot 	else
8051c901a7SVivien Didelot 		val &= ~MV88E6XXX_G2_SWITCH_MGMT_RSVD2CPU;
8151c901a7SVivien Didelot 
8251c901a7SVivien Didelot 	return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SWITCH_MGMT, val);
8351c901a7SVivien Didelot }
8451c901a7SVivien Didelot 
8551c901a7SVivien Didelot int mv88e6185_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip)
8651c901a7SVivien Didelot {
8751c901a7SVivien Didelot 	int err;
8851c901a7SVivien Didelot 
8951c901a7SVivien Didelot 	/* Consider the frames with reserved multicast destination
9051c901a7SVivien Didelot 	 * addresses matching 01:80:c2:00:00:0x as MGMT.
9151c901a7SVivien Didelot 	 */
9251c901a7SVivien Didelot 	err = mv88e6xxx_g2_mgmt_enable_0x(chip, 0xffff);
9351c901a7SVivien Didelot 	if (err)
9451c901a7SVivien Didelot 		return err;
9551c901a7SVivien Didelot 
9651c901a7SVivien Didelot 	return mv88e6xxx_g2_switch_mgmt_rsvd2cpu(chip, true);
9751c901a7SVivien Didelot }
9851c901a7SVivien Didelot 
9951c901a7SVivien Didelot int mv88e6352_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip)
1006e55f698SAndrew Lunn {
1016e55f698SAndrew Lunn 	int err;
1026e55f698SAndrew Lunn 
1036e55f698SAndrew Lunn 	/* Consider the frames with reserved multicast destination
1046e55f698SAndrew Lunn 	 * addresses matching 01:80:c2:00:00:2x as MGMT.
1056e55f698SAndrew Lunn 	 */
10651c901a7SVivien Didelot 	err = mv88e6xxx_g2_mgmt_enable_2x(chip, 0xffff);
1076e55f698SAndrew Lunn 	if (err)
1086e55f698SAndrew Lunn 		return err;
1096e55f698SAndrew Lunn 
11051c901a7SVivien Didelot 	return mv88e6185_g2_mgmt_rsvd2cpu(chip);
1116e55f698SAndrew Lunn }
1126e55f698SAndrew Lunn 
113ec561276SVivien Didelot /* Offset 0x06: Device Mapping Table register */
114ec561276SVivien Didelot 
115c7f047b6SVivien Didelot int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, int target,
116c7f047b6SVivien Didelot 				      int port)
117ec561276SVivien Didelot {
118c7f047b6SVivien Didelot 	u16 val = (target << 8) | (port & 0x1f);
119c7f047b6SVivien Didelot 	/* Modern chips use 5 bits to define a device mapping port,
120c7f047b6SVivien Didelot 	 * but bit 4 is reserved on older chips, so it is safe to use.
121c7f047b6SVivien Didelot 	 */
122ec561276SVivien Didelot 
1232ad4da77SVivien Didelot 	return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_DEVICE_MAPPING,
1242ad4da77SVivien Didelot 				  MV88E6XXX_G2_DEVICE_MAPPING_UPDATE | val);
125ec561276SVivien Didelot }
126ec561276SVivien Didelot 
127ec561276SVivien Didelot /* Offset 0x07: Trunk Mask Table register */
128ec561276SVivien Didelot 
129ec561276SVivien Didelot static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
13056dc7347SVivien Didelot 					 bool hash, u16 mask)
131ec561276SVivien Didelot {
13256dc7347SVivien Didelot 	u16 val = (num << 12) | (mask & mv88e6xxx_port_mask(chip));
133ec561276SVivien Didelot 
13456dc7347SVivien Didelot 	if (hash)
13556dc7347SVivien Didelot 		val |= MV88E6XXX_G2_TRUNK_MASK_HASH;
136ec561276SVivien Didelot 
1372ad4da77SVivien Didelot 	return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_TRUNK_MASK,
1382ad4da77SVivien Didelot 				  MV88E6XXX_G2_TRUNK_MASK_UPDATE | val);
139ec561276SVivien Didelot }
140ec561276SVivien Didelot 
141ec561276SVivien Didelot /* Offset 0x08: Trunk Mapping Table register */
142ec561276SVivien Didelot 
143ec561276SVivien Didelot static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id,
144ec561276SVivien Didelot 					    u16 map)
145ec561276SVivien Didelot {
146370b4ffbSVivien Didelot 	const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
147ec561276SVivien Didelot 	u16 val = (id << 11) | (map & port_mask);
148ec561276SVivien Didelot 
1492ad4da77SVivien Didelot 	return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_TRUNK_MAPPING,
1502ad4da77SVivien Didelot 				  MV88E6XXX_G2_TRUNK_MAPPING_UPDATE | val);
151ec561276SVivien Didelot }
152ec561276SVivien Didelot 
153b28f872dSVivien Didelot int mv88e6xxx_g2_trunk_clear(struct mv88e6xxx_chip *chip)
154ec561276SVivien Didelot {
155370b4ffbSVivien Didelot 	const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
156ec561276SVivien Didelot 	int i, err;
157ec561276SVivien Didelot 
158ec561276SVivien Didelot 	/* Clear all eight possible Trunk Mask vectors */
159ec561276SVivien Didelot 	for (i = 0; i < 8; ++i) {
160ec561276SVivien Didelot 		err = mv88e6xxx_g2_trunk_mask_write(chip, i, false, port_mask);
161ec561276SVivien Didelot 		if (err)
162ec561276SVivien Didelot 			return err;
163ec561276SVivien Didelot 	}
164ec561276SVivien Didelot 
165ec561276SVivien Didelot 	/* Clear all sixteen possible Trunk ID routing vectors */
166ec561276SVivien Didelot 	for (i = 0; i < 16; ++i) {
167ec561276SVivien Didelot 		err = mv88e6xxx_g2_trunk_mapping_write(chip, i, 0);
168ec561276SVivien Didelot 		if (err)
169ec561276SVivien Didelot 			return err;
170ec561276SVivien Didelot 	}
171ec561276SVivien Didelot 
172ec561276SVivien Didelot 	return 0;
173ec561276SVivien Didelot }
174ec561276SVivien Didelot 
175ec561276SVivien Didelot /* Offset 0x09: Ingress Rate Command register
176ec561276SVivien Didelot  * Offset 0x0A: Ingress Rate Data register
177ec561276SVivien Didelot  */
178ec561276SVivien Didelot 
179cd8da8bbSVivien Didelot static int mv88e6xxx_g2_irl_wait(struct mv88e6xxx_chip *chip)
180ec561276SVivien Didelot {
18119fb7f69SVivien Didelot 	int bit = __bf_shf(MV88E6XXX_G2_IRL_CMD_BUSY);
18219fb7f69SVivien Didelot 
18319fb7f69SVivien Didelot 	return mv88e6xxx_g2_wait_bit(chip, MV88E6XXX_G2_IRL_CMD, bit, 0);
184ec561276SVivien Didelot }
185ec561276SVivien Didelot 
186cd8da8bbSVivien Didelot static int mv88e6xxx_g2_irl_op(struct mv88e6xxx_chip *chip, u16 op, int port,
187cd8da8bbSVivien Didelot 			       int res, int reg)
188cd8da8bbSVivien Didelot {
189cd8da8bbSVivien Didelot 	int err;
190cd8da8bbSVivien Didelot 
191cd8da8bbSVivien Didelot 	err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_IRL_CMD,
192cd8da8bbSVivien Didelot 				 MV88E6XXX_G2_IRL_CMD_BUSY | op | (port << 8) |
193cd8da8bbSVivien Didelot 				 (res << 5) | reg);
194cd8da8bbSVivien Didelot 	if (err)
195ec561276SVivien Didelot 		return err;
196cd8da8bbSVivien Didelot 
197cd8da8bbSVivien Didelot 	return mv88e6xxx_g2_irl_wait(chip);
198cd8da8bbSVivien Didelot }
199cd8da8bbSVivien Didelot 
200cd8da8bbSVivien Didelot int mv88e6352_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port)
201cd8da8bbSVivien Didelot {
202cd8da8bbSVivien Didelot 	return mv88e6xxx_g2_irl_op(chip, MV88E6352_G2_IRL_CMD_OP_INIT_ALL, port,
203cd8da8bbSVivien Didelot 				   0, 0);
204cd8da8bbSVivien Didelot }
205cd8da8bbSVivien Didelot 
206cd8da8bbSVivien Didelot int mv88e6390_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port)
207cd8da8bbSVivien Didelot {
208cd8da8bbSVivien Didelot 	return mv88e6xxx_g2_irl_op(chip, MV88E6390_G2_IRL_CMD_OP_INIT_ALL, port,
209cd8da8bbSVivien Didelot 				   0, 0);
210ec561276SVivien Didelot }
211ec561276SVivien Didelot 
21217a1594eSVivien Didelot /* Offset 0x0B: Cross-chip Port VLAN (Addr) Register
21317a1594eSVivien Didelot  * Offset 0x0C: Cross-chip Port VLAN Data Register
21417a1594eSVivien Didelot  */
21517a1594eSVivien Didelot 
21617a1594eSVivien Didelot static int mv88e6xxx_g2_pvt_op_wait(struct mv88e6xxx_chip *chip)
21717a1594eSVivien Didelot {
21819fb7f69SVivien Didelot 	int bit = __bf_shf(MV88E6XXX_G2_PVT_ADDR_BUSY);
21919fb7f69SVivien Didelot 
22019fb7f69SVivien Didelot 	return mv88e6xxx_g2_wait_bit(chip, MV88E6XXX_G2_PVT_ADDR, bit, 0);
22117a1594eSVivien Didelot }
22217a1594eSVivien Didelot 
22317a1594eSVivien Didelot static int mv88e6xxx_g2_pvt_op(struct mv88e6xxx_chip *chip, int src_dev,
22417a1594eSVivien Didelot 			       int src_port, u16 op)
22517a1594eSVivien Didelot {
22617a1594eSVivien Didelot 	int err;
22717a1594eSVivien Didelot 
22867d1ea8eSVivien Didelot 	/* 9-bit Cross-chip PVT pointer: with MV88E6XXX_G2_MISC_5_BIT_PORT
22967d1ea8eSVivien Didelot 	 * cleared, source device is 5-bit, source port is 4-bit.
23017a1594eSVivien Didelot 	 */
23167d1ea8eSVivien Didelot 	op |= MV88E6XXX_G2_PVT_ADDR_BUSY;
23217a1594eSVivien Didelot 	op |= (src_dev & 0x1f) << 4;
23317a1594eSVivien Didelot 	op |= (src_port & 0xf);
23417a1594eSVivien Didelot 
23567d1ea8eSVivien Didelot 	err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_PVT_ADDR, op);
23617a1594eSVivien Didelot 	if (err)
23717a1594eSVivien Didelot 		return err;
23817a1594eSVivien Didelot 
23917a1594eSVivien Didelot 	return mv88e6xxx_g2_pvt_op_wait(chip);
24017a1594eSVivien Didelot }
24117a1594eSVivien Didelot 
24217a1594eSVivien Didelot int mv88e6xxx_g2_pvt_write(struct mv88e6xxx_chip *chip, int src_dev,
24317a1594eSVivien Didelot 			   int src_port, u16 data)
24417a1594eSVivien Didelot {
24517a1594eSVivien Didelot 	int err;
24617a1594eSVivien Didelot 
24717a1594eSVivien Didelot 	err = mv88e6xxx_g2_pvt_op_wait(chip);
24817a1594eSVivien Didelot 	if (err)
24917a1594eSVivien Didelot 		return err;
25017a1594eSVivien Didelot 
25167d1ea8eSVivien Didelot 	err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_PVT_DATA, data);
25217a1594eSVivien Didelot 	if (err)
25317a1594eSVivien Didelot 		return err;
25417a1594eSVivien Didelot 
25517a1594eSVivien Didelot 	return mv88e6xxx_g2_pvt_op(chip, src_dev, src_port,
25667d1ea8eSVivien Didelot 				   MV88E6XXX_G2_PVT_ADDR_OP_WRITE_PVLAN);
25717a1594eSVivien Didelot }
25817a1594eSVivien Didelot 
259ec561276SVivien Didelot /* Offset 0x0D: Switch MAC/WoL/WoF register */
260ec561276SVivien Didelot 
261ec561276SVivien Didelot static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip,
262ec561276SVivien Didelot 					 unsigned int pointer, u8 data)
263ec561276SVivien Didelot {
264ec561276SVivien Didelot 	u16 val = (pointer << 8) | data;
265ec561276SVivien Didelot 
2662ad4da77SVivien Didelot 	return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SWITCH_MAC,
2672ad4da77SVivien Didelot 				  MV88E6XXX_G2_SWITCH_MAC_UPDATE | val);
268ec561276SVivien Didelot }
269ec561276SVivien Didelot 
270ec561276SVivien Didelot int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
271ec561276SVivien Didelot {
272ec561276SVivien Didelot 	int i, err;
273ec561276SVivien Didelot 
274ec561276SVivien Didelot 	for (i = 0; i < 6; i++) {
275ec561276SVivien Didelot 		err = mv88e6xxx_g2_switch_mac_write(chip, i, addr[i]);
276ec561276SVivien Didelot 		if (err)
277ec561276SVivien Didelot 			break;
278ec561276SVivien Didelot 	}
279ec561276SVivien Didelot 
280ec561276SVivien Didelot 	return err;
281ec561276SVivien Didelot }
282ec561276SVivien Didelot 
2836239a386SAndrew Lunn /* Offset 0x0E: ATU Statistics */
2846239a386SAndrew Lunn 
2856239a386SAndrew Lunn int mv88e6xxx_g2_atu_stats_set(struct mv88e6xxx_chip *chip, u16 kind, u16 bin)
2866239a386SAndrew Lunn {
2876239a386SAndrew Lunn 	return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_ATU_STATS,
2886239a386SAndrew Lunn 				  kind | bin);
2896239a386SAndrew Lunn }
2906239a386SAndrew Lunn 
291c5f299d5SAndrew Lunn int mv88e6xxx_g2_atu_stats_get(struct mv88e6xxx_chip *chip, u16 *stats)
2926239a386SAndrew Lunn {
293c5f299d5SAndrew Lunn 	return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_ATU_STATS, stats);
2946239a386SAndrew Lunn }
2956239a386SAndrew Lunn 
296ec561276SVivien Didelot /* Offset 0x0F: Priority Override Table */
297ec561276SVivien Didelot 
298ec561276SVivien Didelot static int mv88e6xxx_g2_pot_write(struct mv88e6xxx_chip *chip, int pointer,
299ec561276SVivien Didelot 				  u8 data)
300ec561276SVivien Didelot {
301ec561276SVivien Didelot 	u16 val = (pointer << 8) | (data & 0x7);
302ec561276SVivien Didelot 
3032ad4da77SVivien Didelot 	return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_PRIO_OVERRIDE,
3042ad4da77SVivien Didelot 				  MV88E6XXX_G2_PRIO_OVERRIDE_UPDATE | val);
305ec561276SVivien Didelot }
306ec561276SVivien Didelot 
3079e907d73SVivien Didelot int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip)
308ec561276SVivien Didelot {
309ec561276SVivien Didelot 	int i, err;
310ec561276SVivien Didelot 
311ec561276SVivien Didelot 	/* Clear all sixteen possible Priority Override entries */
312ec561276SVivien Didelot 	for (i = 0; i < 16; i++) {
313ec561276SVivien Didelot 		err = mv88e6xxx_g2_pot_write(chip, i, 0);
314ec561276SVivien Didelot 		if (err)
315ec561276SVivien Didelot 			break;
316ec561276SVivien Didelot 	}
317ec561276SVivien Didelot 
318ec561276SVivien Didelot 	return err;
319ec561276SVivien Didelot }
320ec561276SVivien Didelot 
321ec561276SVivien Didelot /* Offset 0x14: EEPROM Command
32298fc3c6fSVivien Didelot  * Offset 0x15: EEPROM Data (for 16-bit data access)
32398fc3c6fSVivien Didelot  * Offset 0x15: EEPROM Addr (for 8-bit data access)
324ec561276SVivien Didelot  */
325ec561276SVivien Didelot 
326ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip)
327ec561276SVivien Didelot {
32819fb7f69SVivien Didelot 	int bit = __bf_shf(MV88E6XXX_G2_EEPROM_CMD_BUSY);
32919fb7f69SVivien Didelot 	int err;
33019fb7f69SVivien Didelot 
33119fb7f69SVivien Didelot 	err = mv88e6xxx_g2_wait_bit(chip, MV88E6XXX_G2_EEPROM_CMD, bit, 0);
33219fb7f69SVivien Didelot 	if (err)
33319fb7f69SVivien Didelot 		return err;
33419fb7f69SVivien Didelot 
33519fb7f69SVivien Didelot 	bit = __bf_shf(MV88E6XXX_G2_EEPROM_CMD_RUNNING);
33619fb7f69SVivien Didelot 
33719fb7f69SVivien Didelot 	return mv88e6xxx_g2_wait_bit(chip, MV88E6XXX_G2_EEPROM_CMD, bit, 0);
338ec561276SVivien Didelot }
339ec561276SVivien Didelot 
340ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
341ec561276SVivien Didelot {
342ec561276SVivien Didelot 	int err;
343ec561276SVivien Didelot 
3447fc8c9d5SVivien Didelot 	err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_EEPROM_CMD,
3457fc8c9d5SVivien Didelot 				 MV88E6XXX_G2_EEPROM_CMD_BUSY | cmd);
346ec561276SVivien Didelot 	if (err)
347ec561276SVivien Didelot 		return err;
348ec561276SVivien Didelot 
349ec561276SVivien Didelot 	return mv88e6xxx_g2_eeprom_wait(chip);
350ec561276SVivien Didelot }
351ec561276SVivien Didelot 
35298fc3c6fSVivien Didelot static int mv88e6xxx_g2_eeprom_read8(struct mv88e6xxx_chip *chip,
35398fc3c6fSVivien Didelot 				     u16 addr, u8 *data)
35498fc3c6fSVivien Didelot {
3557fc8c9d5SVivien Didelot 	u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_READ;
35698fc3c6fSVivien Didelot 	int err;
35798fc3c6fSVivien Didelot 
35898fc3c6fSVivien Didelot 	err = mv88e6xxx_g2_eeprom_wait(chip);
35998fc3c6fSVivien Didelot 	if (err)
36098fc3c6fSVivien Didelot 		return err;
36198fc3c6fSVivien Didelot 
3627fc8c9d5SVivien Didelot 	err = mv88e6xxx_g2_write(chip, MV88E6390_G2_EEPROM_ADDR, addr);
36398fc3c6fSVivien Didelot 	if (err)
36498fc3c6fSVivien Didelot 		return err;
36598fc3c6fSVivien Didelot 
36698fc3c6fSVivien Didelot 	err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
36798fc3c6fSVivien Didelot 	if (err)
36898fc3c6fSVivien Didelot 		return err;
36998fc3c6fSVivien Didelot 
3707fc8c9d5SVivien Didelot 	err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_EEPROM_CMD, &cmd);
37198fc3c6fSVivien Didelot 	if (err)
37298fc3c6fSVivien Didelot 		return err;
37398fc3c6fSVivien Didelot 
37498fc3c6fSVivien Didelot 	*data = cmd & 0xff;
37598fc3c6fSVivien Didelot 
37698fc3c6fSVivien Didelot 	return 0;
37798fc3c6fSVivien Didelot }
37898fc3c6fSVivien Didelot 
37998fc3c6fSVivien Didelot static int mv88e6xxx_g2_eeprom_write8(struct mv88e6xxx_chip *chip,
38098fc3c6fSVivien Didelot 				      u16 addr, u8 data)
38198fc3c6fSVivien Didelot {
3827fc8c9d5SVivien Didelot 	u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_WRITE |
3837fc8c9d5SVivien Didelot 		MV88E6XXX_G2_EEPROM_CMD_WRITE_EN;
38498fc3c6fSVivien Didelot 	int err;
38598fc3c6fSVivien Didelot 
38698fc3c6fSVivien Didelot 	err = mv88e6xxx_g2_eeprom_wait(chip);
38798fc3c6fSVivien Didelot 	if (err)
38898fc3c6fSVivien Didelot 		return err;
38998fc3c6fSVivien Didelot 
3907fc8c9d5SVivien Didelot 	err = mv88e6xxx_g2_write(chip, MV88E6390_G2_EEPROM_ADDR, addr);
39198fc3c6fSVivien Didelot 	if (err)
39298fc3c6fSVivien Didelot 		return err;
39398fc3c6fSVivien Didelot 
39498fc3c6fSVivien Didelot 	return mv88e6xxx_g2_eeprom_cmd(chip, cmd | data);
39598fc3c6fSVivien Didelot }
39698fc3c6fSVivien Didelot 
397ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_read16(struct mv88e6xxx_chip *chip,
398ec561276SVivien Didelot 				      u8 addr, u16 *data)
399ec561276SVivien Didelot {
4007fc8c9d5SVivien Didelot 	u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_READ | addr;
401ec561276SVivien Didelot 	int err;
402ec561276SVivien Didelot 
403ec561276SVivien Didelot 	err = mv88e6xxx_g2_eeprom_wait(chip);
404ec561276SVivien Didelot 	if (err)
405ec561276SVivien Didelot 		return err;
406ec561276SVivien Didelot 
407ec561276SVivien Didelot 	err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
408ec561276SVivien Didelot 	if (err)
409ec561276SVivien Didelot 		return err;
410ec561276SVivien Didelot 
4117fc8c9d5SVivien Didelot 	return mv88e6xxx_g2_read(chip, MV88E6352_G2_EEPROM_DATA, data);
412ec561276SVivien Didelot }
413ec561276SVivien Didelot 
414ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_write16(struct mv88e6xxx_chip *chip,
415ec561276SVivien Didelot 				       u8 addr, u16 data)
416ec561276SVivien Didelot {
4177fc8c9d5SVivien Didelot 	u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_WRITE | addr;
418ec561276SVivien Didelot 	int err;
419ec561276SVivien Didelot 
420ec561276SVivien Didelot 	err = mv88e6xxx_g2_eeprom_wait(chip);
421ec561276SVivien Didelot 	if (err)
422ec561276SVivien Didelot 		return err;
423ec561276SVivien Didelot 
4247fc8c9d5SVivien Didelot 	err = mv88e6xxx_g2_write(chip, MV88E6352_G2_EEPROM_DATA, data);
425ec561276SVivien Didelot 	if (err)
426ec561276SVivien Didelot 		return err;
427ec561276SVivien Didelot 
428ec561276SVivien Didelot 	return mv88e6xxx_g2_eeprom_cmd(chip, cmd);
429ec561276SVivien Didelot }
430ec561276SVivien Didelot 
43198fc3c6fSVivien Didelot int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip,
43298fc3c6fSVivien Didelot 			     struct ethtool_eeprom *eeprom, u8 *data)
43398fc3c6fSVivien Didelot {
43498fc3c6fSVivien Didelot 	unsigned int offset = eeprom->offset;
43598fc3c6fSVivien Didelot 	unsigned int len = eeprom->len;
43698fc3c6fSVivien Didelot 	int err;
43798fc3c6fSVivien Didelot 
43898fc3c6fSVivien Didelot 	eeprom->len = 0;
43998fc3c6fSVivien Didelot 
44098fc3c6fSVivien Didelot 	while (len) {
44198fc3c6fSVivien Didelot 		err = mv88e6xxx_g2_eeprom_read8(chip, offset, data);
44298fc3c6fSVivien Didelot 		if (err)
44398fc3c6fSVivien Didelot 			return err;
44498fc3c6fSVivien Didelot 
44598fc3c6fSVivien Didelot 		eeprom->len++;
44698fc3c6fSVivien Didelot 		offset++;
44798fc3c6fSVivien Didelot 		data++;
44898fc3c6fSVivien Didelot 		len--;
44998fc3c6fSVivien Didelot 	}
45098fc3c6fSVivien Didelot 
45198fc3c6fSVivien Didelot 	return 0;
45298fc3c6fSVivien Didelot }
45398fc3c6fSVivien Didelot 
45498fc3c6fSVivien Didelot int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip,
45598fc3c6fSVivien Didelot 			     struct ethtool_eeprom *eeprom, u8 *data)
45698fc3c6fSVivien Didelot {
45798fc3c6fSVivien Didelot 	unsigned int offset = eeprom->offset;
45898fc3c6fSVivien Didelot 	unsigned int len = eeprom->len;
45998fc3c6fSVivien Didelot 	int err;
46098fc3c6fSVivien Didelot 
46198fc3c6fSVivien Didelot 	eeprom->len = 0;
46298fc3c6fSVivien Didelot 
46398fc3c6fSVivien Didelot 	while (len) {
46498fc3c6fSVivien Didelot 		err = mv88e6xxx_g2_eeprom_write8(chip, offset, *data);
46598fc3c6fSVivien Didelot 		if (err)
46698fc3c6fSVivien Didelot 			return err;
46798fc3c6fSVivien Didelot 
46898fc3c6fSVivien Didelot 		eeprom->len++;
46998fc3c6fSVivien Didelot 		offset++;
47098fc3c6fSVivien Didelot 		data++;
47198fc3c6fSVivien Didelot 		len--;
47298fc3c6fSVivien Didelot 	}
47398fc3c6fSVivien Didelot 
47498fc3c6fSVivien Didelot 	return 0;
47598fc3c6fSVivien Didelot }
47698fc3c6fSVivien Didelot 
477ec561276SVivien Didelot int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip,
478ec561276SVivien Didelot 			      struct ethtool_eeprom *eeprom, u8 *data)
479ec561276SVivien Didelot {
480ec561276SVivien Didelot 	unsigned int offset = eeprom->offset;
481ec561276SVivien Didelot 	unsigned int len = eeprom->len;
482ec561276SVivien Didelot 	u16 val;
483ec561276SVivien Didelot 	int err;
484ec561276SVivien Didelot 
485ec561276SVivien Didelot 	eeprom->len = 0;
486ec561276SVivien Didelot 
487ec561276SVivien Didelot 	if (offset & 1) {
488ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
489ec561276SVivien Didelot 		if (err)
490ec561276SVivien Didelot 			return err;
491ec561276SVivien Didelot 
492ec561276SVivien Didelot 		*data++ = (val >> 8) & 0xff;
493ec561276SVivien Didelot 
494ec561276SVivien Didelot 		offset++;
495ec561276SVivien Didelot 		len--;
496ec561276SVivien Didelot 		eeprom->len++;
497ec561276SVivien Didelot 	}
498ec561276SVivien Didelot 
499ec561276SVivien Didelot 	while (len >= 2) {
500ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
501ec561276SVivien Didelot 		if (err)
502ec561276SVivien Didelot 			return err;
503ec561276SVivien Didelot 
504ec561276SVivien Didelot 		*data++ = val & 0xff;
505ec561276SVivien Didelot 		*data++ = (val >> 8) & 0xff;
506ec561276SVivien Didelot 
507ec561276SVivien Didelot 		offset += 2;
508ec561276SVivien Didelot 		len -= 2;
509ec561276SVivien Didelot 		eeprom->len += 2;
510ec561276SVivien Didelot 	}
511ec561276SVivien Didelot 
512ec561276SVivien Didelot 	if (len) {
513ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
514ec561276SVivien Didelot 		if (err)
515ec561276SVivien Didelot 			return err;
516ec561276SVivien Didelot 
517ec561276SVivien Didelot 		*data++ = val & 0xff;
518ec561276SVivien Didelot 
519ec561276SVivien Didelot 		offset++;
520ec561276SVivien Didelot 		len--;
521ec561276SVivien Didelot 		eeprom->len++;
522ec561276SVivien Didelot 	}
523ec561276SVivien Didelot 
524ec561276SVivien Didelot 	return 0;
525ec561276SVivien Didelot }
526ec561276SVivien Didelot 
527ec561276SVivien Didelot int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip,
528ec561276SVivien Didelot 			      struct ethtool_eeprom *eeprom, u8 *data)
529ec561276SVivien Didelot {
530ec561276SVivien Didelot 	unsigned int offset = eeprom->offset;
531ec561276SVivien Didelot 	unsigned int len = eeprom->len;
532ec561276SVivien Didelot 	u16 val;
533ec561276SVivien Didelot 	int err;
534ec561276SVivien Didelot 
535ec561276SVivien Didelot 	/* Ensure the RO WriteEn bit is set */
5367fc8c9d5SVivien Didelot 	err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_EEPROM_CMD, &val);
537ec561276SVivien Didelot 	if (err)
538ec561276SVivien Didelot 		return err;
539ec561276SVivien Didelot 
5407fc8c9d5SVivien Didelot 	if (!(val & MV88E6XXX_G2_EEPROM_CMD_WRITE_EN))
541ec561276SVivien Didelot 		return -EROFS;
542ec561276SVivien Didelot 
543ec561276SVivien Didelot 	eeprom->len = 0;
544ec561276SVivien Didelot 
545ec561276SVivien Didelot 	if (offset & 1) {
546ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
547ec561276SVivien Didelot 		if (err)
548ec561276SVivien Didelot 			return err;
549ec561276SVivien Didelot 
550ec561276SVivien Didelot 		val = (*data++ << 8) | (val & 0xff);
551ec561276SVivien Didelot 
552ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
553ec561276SVivien Didelot 		if (err)
554ec561276SVivien Didelot 			return err;
555ec561276SVivien Didelot 
556ec561276SVivien Didelot 		offset++;
557ec561276SVivien Didelot 		len--;
558ec561276SVivien Didelot 		eeprom->len++;
559ec561276SVivien Didelot 	}
560ec561276SVivien Didelot 
561ec561276SVivien Didelot 	while (len >= 2) {
562ec561276SVivien Didelot 		val = *data++;
563ec561276SVivien Didelot 		val |= *data++ << 8;
564ec561276SVivien Didelot 
565ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
566ec561276SVivien Didelot 		if (err)
567ec561276SVivien Didelot 			return err;
568ec561276SVivien Didelot 
569ec561276SVivien Didelot 		offset += 2;
570ec561276SVivien Didelot 		len -= 2;
571ec561276SVivien Didelot 		eeprom->len += 2;
572ec561276SVivien Didelot 	}
573ec561276SVivien Didelot 
574ec561276SVivien Didelot 	if (len) {
575ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
576ec561276SVivien Didelot 		if (err)
577ec561276SVivien Didelot 			return err;
578ec561276SVivien Didelot 
579ec561276SVivien Didelot 		val = (val & 0xff00) | *data++;
580ec561276SVivien Didelot 
581ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
582ec561276SVivien Didelot 		if (err)
583ec561276SVivien Didelot 			return err;
584ec561276SVivien Didelot 
585ec561276SVivien Didelot 		offset++;
586ec561276SVivien Didelot 		len--;
587ec561276SVivien Didelot 		eeprom->len++;
588ec561276SVivien Didelot 	}
589ec561276SVivien Didelot 
590ec561276SVivien Didelot 	return 0;
591ec561276SVivien Didelot }
592ec561276SVivien Didelot 
593ec561276SVivien Didelot /* Offset 0x18: SMI PHY Command Register
594ec561276SVivien Didelot  * Offset 0x19: SMI PHY Data Register
595ec561276SVivien Didelot  */
596ec561276SVivien Didelot 
597ec561276SVivien Didelot static int mv88e6xxx_g2_smi_phy_wait(struct mv88e6xxx_chip *chip)
598ec561276SVivien Didelot {
59919fb7f69SVivien Didelot 	int bit = __bf_shf(MV88E6XXX_G2_SMI_PHY_CMD_BUSY);
60019fb7f69SVivien Didelot 
60119fb7f69SVivien Didelot 	return mv88e6xxx_g2_wait_bit(chip, MV88E6XXX_G2_SMI_PHY_CMD, bit, 0);
602ec561276SVivien Didelot }
603ec561276SVivien Didelot 
604ec561276SVivien Didelot static int mv88e6xxx_g2_smi_phy_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
605ec561276SVivien Didelot {
606ec561276SVivien Didelot 	int err;
607ec561276SVivien Didelot 
608e289ef0dSVivien Didelot 	err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_CMD,
609e289ef0dSVivien Didelot 				 MV88E6XXX_G2_SMI_PHY_CMD_BUSY | cmd);
610ec561276SVivien Didelot 	if (err)
611ec561276SVivien Didelot 		return err;
612ec561276SVivien Didelot 
613ec561276SVivien Didelot 	return mv88e6xxx_g2_smi_phy_wait(chip);
614ec561276SVivien Didelot }
615ec561276SVivien Didelot 
616e289ef0dSVivien Didelot static int mv88e6xxx_g2_smi_phy_access(struct mv88e6xxx_chip *chip,
617e289ef0dSVivien Didelot 				       bool external, bool c45, u16 op, int dev,
618e289ef0dSVivien Didelot 				       int reg)
619ec561276SVivien Didelot {
620e289ef0dSVivien Didelot 	u16 cmd = op;
621ec561276SVivien Didelot 
622cf3e80dfSAndrew Lunn 	if (external)
623e289ef0dSVivien Didelot 		cmd |= MV88E6390_G2_SMI_PHY_CMD_FUNC_EXTERNAL;
624e289ef0dSVivien Didelot 	else
625e289ef0dSVivien Didelot 		cmd |= MV88E6390_G2_SMI_PHY_CMD_FUNC_INTERNAL; /* empty mask */
626cf3e80dfSAndrew Lunn 
627e289ef0dSVivien Didelot 	if (c45)
628e289ef0dSVivien Didelot 		cmd |= MV88E6XXX_G2_SMI_PHY_CMD_MODE_45; /* empty mask */
629e289ef0dSVivien Didelot 	else
630e289ef0dSVivien Didelot 		cmd |= MV88E6XXX_G2_SMI_PHY_CMD_MODE_22;
631cf3e80dfSAndrew Lunn 
632e289ef0dSVivien Didelot 	dev <<= __bf_shf(MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK);
633e289ef0dSVivien Didelot 	cmd |= dev & MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK;
634e289ef0dSVivien Didelot 	cmd |= reg & MV88E6XXX_G2_SMI_PHY_CMD_REG_ADDR_MASK;
635cf3e80dfSAndrew Lunn 
636cf3e80dfSAndrew Lunn 	return mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
637cf3e80dfSAndrew Lunn }
638cf3e80dfSAndrew Lunn 
639e289ef0dSVivien Didelot static int mv88e6xxx_g2_smi_phy_access_c22(struct mv88e6xxx_chip *chip,
640e289ef0dSVivien Didelot 					   bool external, u16 op, int dev,
641e289ef0dSVivien Didelot 					   int reg)
642cf3e80dfSAndrew Lunn {
643e289ef0dSVivien Didelot 	return mv88e6xxx_g2_smi_phy_access(chip, external, false, op, dev, reg);
644cf3e80dfSAndrew Lunn }
645cf3e80dfSAndrew Lunn 
646e289ef0dSVivien Didelot /* IEEE 802.3 Clause 22 Read Data Register */
647e289ef0dSVivien Didelot static int mv88e6xxx_g2_smi_phy_read_data_c22(struct mv88e6xxx_chip *chip,
648e289ef0dSVivien Didelot 					      bool external, int dev, int reg,
649e289ef0dSVivien Didelot 					      u16 *data)
650cf3e80dfSAndrew Lunn {
651e289ef0dSVivien Didelot 	u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_22_READ_DATA;
652cf3e80dfSAndrew Lunn 	int err;
653cf3e80dfSAndrew Lunn 
654ec561276SVivien Didelot 	err = mv88e6xxx_g2_smi_phy_wait(chip);
655ec561276SVivien Didelot 	if (err)
656ec561276SVivien Didelot 		return err;
657ec561276SVivien Didelot 
658e289ef0dSVivien Didelot 	err = mv88e6xxx_g2_smi_phy_access_c22(chip, external, op, dev, reg);
659ec561276SVivien Didelot 	if (err)
660ec561276SVivien Didelot 		return err;
661ec561276SVivien Didelot 
662e289ef0dSVivien Didelot 	return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
663ec561276SVivien Didelot }
664ec561276SVivien Didelot 
665e289ef0dSVivien Didelot /* IEEE 802.3 Clause 22 Write Data Register */
666e289ef0dSVivien Didelot static int mv88e6xxx_g2_smi_phy_write_data_c22(struct mv88e6xxx_chip *chip,
667e289ef0dSVivien Didelot 					       bool external, int dev, int reg,
668e289ef0dSVivien Didelot 					       u16 data)
669e289ef0dSVivien Didelot {
670e289ef0dSVivien Didelot 	u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_22_WRITE_DATA;
671e289ef0dSVivien Didelot 	int err;
672e289ef0dSVivien Didelot 
673e289ef0dSVivien Didelot 	err = mv88e6xxx_g2_smi_phy_wait(chip);
674e289ef0dSVivien Didelot 	if (err)
675e289ef0dSVivien Didelot 		return err;
676e289ef0dSVivien Didelot 
677e289ef0dSVivien Didelot 	err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
678e289ef0dSVivien Didelot 	if (err)
679e289ef0dSVivien Didelot 		return err;
680e289ef0dSVivien Didelot 
681e289ef0dSVivien Didelot 	return mv88e6xxx_g2_smi_phy_access_c22(chip, external, op, dev, reg);
682e289ef0dSVivien Didelot }
683e289ef0dSVivien Didelot 
684e289ef0dSVivien Didelot static int mv88e6xxx_g2_smi_phy_access_c45(struct mv88e6xxx_chip *chip,
685e289ef0dSVivien Didelot 					   bool external, u16 op, int port,
686e289ef0dSVivien Didelot 					   int dev)
687e289ef0dSVivien Didelot {
688e289ef0dSVivien Didelot 	return mv88e6xxx_g2_smi_phy_access(chip, external, true, op, port, dev);
689e289ef0dSVivien Didelot }
690e289ef0dSVivien Didelot 
691e289ef0dSVivien Didelot /* IEEE 802.3 Clause 45 Write Address Register */
692e289ef0dSVivien Didelot static int mv88e6xxx_g2_smi_phy_write_addr_c45(struct mv88e6xxx_chip *chip,
693e289ef0dSVivien Didelot 					       bool external, int port, int dev,
694e289ef0dSVivien Didelot 					       int addr)
695e289ef0dSVivien Didelot {
696e289ef0dSVivien Didelot 	u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_ADDR;
697e289ef0dSVivien Didelot 	int err;
698e289ef0dSVivien Didelot 
699e289ef0dSVivien Didelot 	err = mv88e6xxx_g2_smi_phy_wait(chip);
700e289ef0dSVivien Didelot 	if (err)
701e289ef0dSVivien Didelot 		return err;
702e289ef0dSVivien Didelot 
703e289ef0dSVivien Didelot 	err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, addr);
704e289ef0dSVivien Didelot 	if (err)
705e289ef0dSVivien Didelot 		return err;
706e289ef0dSVivien Didelot 
707e289ef0dSVivien Didelot 	return mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev);
708e289ef0dSVivien Didelot }
709e289ef0dSVivien Didelot 
710e289ef0dSVivien Didelot /* IEEE 802.3 Clause 45 Read Data Register */
711e289ef0dSVivien Didelot static int mv88e6xxx_g2_smi_phy_read_data_c45(struct mv88e6xxx_chip *chip,
712e289ef0dSVivien Didelot 					      bool external, int port, int dev,
713e289ef0dSVivien Didelot 					      u16 *data)
714e289ef0dSVivien Didelot {
715e289ef0dSVivien Didelot 	u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_READ_DATA;
716e289ef0dSVivien Didelot 	int err;
717e289ef0dSVivien Didelot 
718e289ef0dSVivien Didelot 	err = mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev);
719e289ef0dSVivien Didelot 	if (err)
720e289ef0dSVivien Didelot 		return err;
721e289ef0dSVivien Didelot 
722e289ef0dSVivien Didelot 	return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
723e289ef0dSVivien Didelot }
724e289ef0dSVivien Didelot 
725e289ef0dSVivien Didelot static int mv88e6xxx_g2_smi_phy_read_c45(struct mv88e6xxx_chip *chip,
726e289ef0dSVivien Didelot 					 bool external, int port, int reg,
727e289ef0dSVivien Didelot 					 u16 *data)
728e289ef0dSVivien Didelot {
729e289ef0dSVivien Didelot 	int dev = (reg >> 16) & 0x1f;
730e289ef0dSVivien Didelot 	int addr = reg & 0xffff;
731e289ef0dSVivien Didelot 	int err;
732e289ef0dSVivien Didelot 
733e289ef0dSVivien Didelot 	err = mv88e6xxx_g2_smi_phy_write_addr_c45(chip, external, port, dev,
734e289ef0dSVivien Didelot 						  addr);
735e289ef0dSVivien Didelot 	if (err)
736e289ef0dSVivien Didelot 		return err;
737e289ef0dSVivien Didelot 
738e289ef0dSVivien Didelot 	return mv88e6xxx_g2_smi_phy_read_data_c45(chip, external, port, dev,
739e289ef0dSVivien Didelot 						  data);
740e289ef0dSVivien Didelot }
741e289ef0dSVivien Didelot 
742e289ef0dSVivien Didelot /* IEEE 802.3 Clause 45 Write Data Register */
743e289ef0dSVivien Didelot static int mv88e6xxx_g2_smi_phy_write_data_c45(struct mv88e6xxx_chip *chip,
744e289ef0dSVivien Didelot 					       bool external, int port, int dev,
745e289ef0dSVivien Didelot 					       u16 data)
746e289ef0dSVivien Didelot {
747e289ef0dSVivien Didelot 	u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_DATA;
748e289ef0dSVivien Didelot 	int err;
749e289ef0dSVivien Didelot 
750e289ef0dSVivien Didelot 	err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, data);
751e289ef0dSVivien Didelot 	if (err)
752e289ef0dSVivien Didelot 		return err;
753e289ef0dSVivien Didelot 
754e289ef0dSVivien Didelot 	return mv88e6xxx_g2_smi_phy_access_c45(chip, external, op, port, dev);
755e289ef0dSVivien Didelot }
756e289ef0dSVivien Didelot 
757e289ef0dSVivien Didelot static int mv88e6xxx_g2_smi_phy_write_c45(struct mv88e6xxx_chip *chip,
758e289ef0dSVivien Didelot 					  bool external, int port, int reg,
759e289ef0dSVivien Didelot 					  u16 data)
760e289ef0dSVivien Didelot {
761e289ef0dSVivien Didelot 	int dev = (reg >> 16) & 0x1f;
762e289ef0dSVivien Didelot 	int addr = reg & 0xffff;
763e289ef0dSVivien Didelot 	int err;
764e289ef0dSVivien Didelot 
765e289ef0dSVivien Didelot 	err = mv88e6xxx_g2_smi_phy_write_addr_c45(chip, external, port, dev,
766e289ef0dSVivien Didelot 						  addr);
767e289ef0dSVivien Didelot 	if (err)
768e289ef0dSVivien Didelot 		return err;
769e289ef0dSVivien Didelot 
770e289ef0dSVivien Didelot 	return mv88e6xxx_g2_smi_phy_write_data_c45(chip, external, port, dev,
771e289ef0dSVivien Didelot 						   data);
772e289ef0dSVivien Didelot }
773e289ef0dSVivien Didelot 
774e289ef0dSVivien Didelot int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
775cf3e80dfSAndrew Lunn 			      int addr, int reg, u16 *val)
776cf3e80dfSAndrew Lunn {
777cf3e80dfSAndrew Lunn 	struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
778cf3e80dfSAndrew Lunn 	bool external = mdio_bus->external;
779cf3e80dfSAndrew Lunn 
780cf3e80dfSAndrew Lunn 	if (reg & MII_ADDR_C45)
781e289ef0dSVivien Didelot 		return mv88e6xxx_g2_smi_phy_read_c45(chip, external, addr, reg,
782e289ef0dSVivien Didelot 						     val);
783e289ef0dSVivien Didelot 
784e289ef0dSVivien Didelot 	return mv88e6xxx_g2_smi_phy_read_data_c22(chip, external, addr, reg,
785e289ef0dSVivien Didelot 						  val);
786cf3e80dfSAndrew Lunn }
787cf3e80dfSAndrew Lunn 
788e289ef0dSVivien Didelot int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
789cf3e80dfSAndrew Lunn 			       int addr, int reg, u16 val)
790cf3e80dfSAndrew Lunn {
791cf3e80dfSAndrew Lunn 	struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
792cf3e80dfSAndrew Lunn 	bool external = mdio_bus->external;
793cf3e80dfSAndrew Lunn 
794cf3e80dfSAndrew Lunn 	if (reg & MII_ADDR_C45)
795e289ef0dSVivien Didelot 		return mv88e6xxx_g2_smi_phy_write_c45(chip, external, addr, reg,
796e289ef0dSVivien Didelot 						      val);
797cf3e80dfSAndrew Lunn 
798e289ef0dSVivien Didelot 	return mv88e6xxx_g2_smi_phy_write_data_c22(chip, external, addr, reg,
799e289ef0dSVivien Didelot 						   val);
800cf3e80dfSAndrew Lunn }
801cf3e80dfSAndrew Lunn 
802a73ccd61SBrandon Streiff /* Offset 0x1B: Watchdog Control */
803fcd25166SAndrew Lunn static int mv88e6097_watchdog_action(struct mv88e6xxx_chip *chip, int irq)
804fcd25166SAndrew Lunn {
805fcd25166SAndrew Lunn 	u16 reg;
806fcd25166SAndrew Lunn 
8073b19df73SVivien Didelot 	mv88e6xxx_g2_read(chip, MV88E6352_G2_WDOG_CTL, &reg);
808fcd25166SAndrew Lunn 
809fcd25166SAndrew Lunn 	dev_info(chip->dev, "Watchdog event: 0x%04x", reg);
810fcd25166SAndrew Lunn 
811fcd25166SAndrew Lunn 	return IRQ_HANDLED;
812fcd25166SAndrew Lunn }
813fcd25166SAndrew Lunn 
814fcd25166SAndrew Lunn static void mv88e6097_watchdog_free(struct mv88e6xxx_chip *chip)
815fcd25166SAndrew Lunn {
816fcd25166SAndrew Lunn 	u16 reg;
817fcd25166SAndrew Lunn 
8183b19df73SVivien Didelot 	mv88e6xxx_g2_read(chip, MV88E6352_G2_WDOG_CTL, &reg);
819fcd25166SAndrew Lunn 
8203b19df73SVivien Didelot 	reg &= ~(MV88E6352_G2_WDOG_CTL_EGRESS_ENABLE |
8213b19df73SVivien Didelot 		 MV88E6352_G2_WDOG_CTL_QC_ENABLE);
822fcd25166SAndrew Lunn 
8233b19df73SVivien Didelot 	mv88e6xxx_g2_write(chip, MV88E6352_G2_WDOG_CTL, reg);
824fcd25166SAndrew Lunn }
825fcd25166SAndrew Lunn 
826fcd25166SAndrew Lunn static int mv88e6097_watchdog_setup(struct mv88e6xxx_chip *chip)
827fcd25166SAndrew Lunn {
8283b19df73SVivien Didelot 	return mv88e6xxx_g2_write(chip, MV88E6352_G2_WDOG_CTL,
8293b19df73SVivien Didelot 				  MV88E6352_G2_WDOG_CTL_EGRESS_ENABLE |
8303b19df73SVivien Didelot 				  MV88E6352_G2_WDOG_CTL_QC_ENABLE |
8313b19df73SVivien Didelot 				  MV88E6352_G2_WDOG_CTL_SWRESET);
832fcd25166SAndrew Lunn }
833fcd25166SAndrew Lunn 
834fcd25166SAndrew Lunn const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = {
835fcd25166SAndrew Lunn 	.irq_action = mv88e6097_watchdog_action,
836fcd25166SAndrew Lunn 	.irq_setup = mv88e6097_watchdog_setup,
837fcd25166SAndrew Lunn 	.irq_free = mv88e6097_watchdog_free,
838fcd25166SAndrew Lunn };
839fcd25166SAndrew Lunn 
840855cdfdeSRasmus Villemoes static void mv88e6250_watchdog_free(struct mv88e6xxx_chip *chip)
841855cdfdeSRasmus Villemoes {
842855cdfdeSRasmus Villemoes 	u16 reg;
843855cdfdeSRasmus Villemoes 
844855cdfdeSRasmus Villemoes 	mv88e6xxx_g2_read(chip, MV88E6250_G2_WDOG_CTL, &reg);
845855cdfdeSRasmus Villemoes 
846855cdfdeSRasmus Villemoes 	reg &= ~(MV88E6250_G2_WDOG_CTL_EGRESS_ENABLE |
847855cdfdeSRasmus Villemoes 		 MV88E6250_G2_WDOG_CTL_QC_ENABLE);
848855cdfdeSRasmus Villemoes 
849855cdfdeSRasmus Villemoes 	mv88e6xxx_g2_write(chip, MV88E6250_G2_WDOG_CTL, reg);
850855cdfdeSRasmus Villemoes }
851855cdfdeSRasmus Villemoes 
852855cdfdeSRasmus Villemoes static int mv88e6250_watchdog_setup(struct mv88e6xxx_chip *chip)
853855cdfdeSRasmus Villemoes {
854855cdfdeSRasmus Villemoes 	return mv88e6xxx_g2_write(chip, MV88E6250_G2_WDOG_CTL,
855855cdfdeSRasmus Villemoes 				  MV88E6250_G2_WDOG_CTL_EGRESS_ENABLE |
856855cdfdeSRasmus Villemoes 				  MV88E6250_G2_WDOG_CTL_QC_ENABLE |
857855cdfdeSRasmus Villemoes 				  MV88E6250_G2_WDOG_CTL_SWRESET);
858855cdfdeSRasmus Villemoes }
859855cdfdeSRasmus Villemoes 
860855cdfdeSRasmus Villemoes const struct mv88e6xxx_irq_ops mv88e6250_watchdog_ops = {
861855cdfdeSRasmus Villemoes 	.irq_action = mv88e6097_watchdog_action,
862855cdfdeSRasmus Villemoes 	.irq_setup = mv88e6250_watchdog_setup,
863855cdfdeSRasmus Villemoes 	.irq_free = mv88e6250_watchdog_free,
864855cdfdeSRasmus Villemoes };
865855cdfdeSRasmus Villemoes 
86661303736SAndrew Lunn static int mv88e6390_watchdog_setup(struct mv88e6xxx_chip *chip)
86761303736SAndrew Lunn {
8682ad4da77SVivien Didelot 	return mv88e6xxx_g2_write(chip, MV88E6390_G2_WDOG_CTL,
8692ad4da77SVivien Didelot 				  MV88E6390_G2_WDOG_CTL_UPDATE |
8703b19df73SVivien Didelot 				  MV88E6390_G2_WDOG_CTL_PTR_INT_ENABLE |
8713b19df73SVivien Didelot 				  MV88E6390_G2_WDOG_CTL_CUT_THROUGH |
8723b19df73SVivien Didelot 				  MV88E6390_G2_WDOG_CTL_QUEUE_CONTROLLER |
8733b19df73SVivien Didelot 				  MV88E6390_G2_WDOG_CTL_EGRESS |
8743b19df73SVivien Didelot 				  MV88E6390_G2_WDOG_CTL_FORCE_IRQ);
87561303736SAndrew Lunn }
87661303736SAndrew Lunn 
87761303736SAndrew Lunn static int mv88e6390_watchdog_action(struct mv88e6xxx_chip *chip, int irq)
87861303736SAndrew Lunn {
87961303736SAndrew Lunn 	int err;
88061303736SAndrew Lunn 	u16 reg;
88161303736SAndrew Lunn 
8823b19df73SVivien Didelot 	mv88e6xxx_g2_write(chip, MV88E6390_G2_WDOG_CTL,
8833b19df73SVivien Didelot 			   MV88E6390_G2_WDOG_CTL_PTR_EVENT);
8843b19df73SVivien Didelot 	err = mv88e6xxx_g2_read(chip, MV88E6390_G2_WDOG_CTL, &reg);
88561303736SAndrew Lunn 
88661303736SAndrew Lunn 	dev_info(chip->dev, "Watchdog event: 0x%04x",
8873b19df73SVivien Didelot 		 reg & MV88E6390_G2_WDOG_CTL_DATA_MASK);
88861303736SAndrew Lunn 
8893b19df73SVivien Didelot 	mv88e6xxx_g2_write(chip, MV88E6390_G2_WDOG_CTL,
8903b19df73SVivien Didelot 			   MV88E6390_G2_WDOG_CTL_PTR_HISTORY);
8913b19df73SVivien Didelot 	err = mv88e6xxx_g2_read(chip, MV88E6390_G2_WDOG_CTL, &reg);
89261303736SAndrew Lunn 
89361303736SAndrew Lunn 	dev_info(chip->dev, "Watchdog history: 0x%04x",
8943b19df73SVivien Didelot 		 reg & MV88E6390_G2_WDOG_CTL_DATA_MASK);
89561303736SAndrew Lunn 
89661303736SAndrew Lunn 	/* Trigger a software reset to try to recover the switch */
89761303736SAndrew Lunn 	if (chip->info->ops->reset)
89861303736SAndrew Lunn 		chip->info->ops->reset(chip);
89961303736SAndrew Lunn 
90061303736SAndrew Lunn 	mv88e6390_watchdog_setup(chip);
90161303736SAndrew Lunn 
90261303736SAndrew Lunn 	return IRQ_HANDLED;
90361303736SAndrew Lunn }
90461303736SAndrew Lunn 
90561303736SAndrew Lunn static void mv88e6390_watchdog_free(struct mv88e6xxx_chip *chip)
90661303736SAndrew Lunn {
9072ad4da77SVivien Didelot 	mv88e6xxx_g2_write(chip, MV88E6390_G2_WDOG_CTL,
9082ad4da77SVivien Didelot 			   MV88E6390_G2_WDOG_CTL_UPDATE |
9093b19df73SVivien Didelot 			   MV88E6390_G2_WDOG_CTL_PTR_INT_ENABLE);
91061303736SAndrew Lunn }
91161303736SAndrew Lunn 
91261303736SAndrew Lunn const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = {
91361303736SAndrew Lunn 	.irq_action = mv88e6390_watchdog_action,
91461303736SAndrew Lunn 	.irq_setup = mv88e6390_watchdog_setup,
91561303736SAndrew Lunn 	.irq_free = mv88e6390_watchdog_free,
91661303736SAndrew Lunn };
91761303736SAndrew Lunn 
918fcd25166SAndrew Lunn static irqreturn_t mv88e6xxx_g2_watchdog_thread_fn(int irq, void *dev_id)
919fcd25166SAndrew Lunn {
920fcd25166SAndrew Lunn 	struct mv88e6xxx_chip *chip = dev_id;
921fcd25166SAndrew Lunn 	irqreturn_t ret = IRQ_NONE;
922fcd25166SAndrew Lunn 
923c9acece0SRasmus Villemoes 	mv88e6xxx_reg_lock(chip);
924fcd25166SAndrew Lunn 	if (chip->info->ops->watchdog_ops->irq_action)
925fcd25166SAndrew Lunn 		ret = chip->info->ops->watchdog_ops->irq_action(chip, irq);
926c9acece0SRasmus Villemoes 	mv88e6xxx_reg_unlock(chip);
927fcd25166SAndrew Lunn 
928fcd25166SAndrew Lunn 	return ret;
929fcd25166SAndrew Lunn }
930fcd25166SAndrew Lunn 
931fcd25166SAndrew Lunn static void mv88e6xxx_g2_watchdog_free(struct mv88e6xxx_chip *chip)
932fcd25166SAndrew Lunn {
933c9acece0SRasmus Villemoes 	mv88e6xxx_reg_lock(chip);
934fcd25166SAndrew Lunn 	if (chip->info->ops->watchdog_ops->irq_free)
935fcd25166SAndrew Lunn 		chip->info->ops->watchdog_ops->irq_free(chip);
936c9acece0SRasmus Villemoes 	mv88e6xxx_reg_unlock(chip);
937fcd25166SAndrew Lunn 
938fcd25166SAndrew Lunn 	free_irq(chip->watchdog_irq, chip);
939fcd25166SAndrew Lunn 	irq_dispose_mapping(chip->watchdog_irq);
940fcd25166SAndrew Lunn }
941fcd25166SAndrew Lunn 
942fcd25166SAndrew Lunn static int mv88e6xxx_g2_watchdog_setup(struct mv88e6xxx_chip *chip)
943fcd25166SAndrew Lunn {
944fcd25166SAndrew Lunn 	int err;
945fcd25166SAndrew Lunn 
946fcd25166SAndrew Lunn 	chip->watchdog_irq = irq_find_mapping(chip->g2_irq.domain,
9471d90016dSVivien Didelot 					      MV88E6XXX_G2_INT_SOURCE_WATCHDOG);
948fcd25166SAndrew Lunn 	if (chip->watchdog_irq < 0)
949fcd25166SAndrew Lunn 		return chip->watchdog_irq;
950fcd25166SAndrew Lunn 
9518b4db289SAndrew Lunn 	snprintf(chip->watchdog_irq_name, sizeof(chip->watchdog_irq_name),
9528b4db289SAndrew Lunn 		 "mv88e6xxx-%s-watchdog", dev_name(chip->dev));
9538b4db289SAndrew Lunn 
954fcd25166SAndrew Lunn 	err = request_threaded_irq(chip->watchdog_irq, NULL,
955fcd25166SAndrew Lunn 				   mv88e6xxx_g2_watchdog_thread_fn,
956fcd25166SAndrew Lunn 				   IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
9578b4db289SAndrew Lunn 				   chip->watchdog_irq_name, chip);
958fcd25166SAndrew Lunn 	if (err)
959fcd25166SAndrew Lunn 		return err;
960fcd25166SAndrew Lunn 
961c9acece0SRasmus Villemoes 	mv88e6xxx_reg_lock(chip);
962fcd25166SAndrew Lunn 	if (chip->info->ops->watchdog_ops->irq_setup)
963fcd25166SAndrew Lunn 		err = chip->info->ops->watchdog_ops->irq_setup(chip);
964c9acece0SRasmus Villemoes 	mv88e6xxx_reg_unlock(chip);
965fcd25166SAndrew Lunn 
966fcd25166SAndrew Lunn 	return err;
967fcd25166SAndrew Lunn }
968fcd25166SAndrew Lunn 
96981228996SVivien Didelot /* Offset 0x1D: Misc Register */
97081228996SVivien Didelot 
97181228996SVivien Didelot static int mv88e6xxx_g2_misc_5_bit_port(struct mv88e6xxx_chip *chip,
97281228996SVivien Didelot 					bool port_5_bit)
97381228996SVivien Didelot {
97481228996SVivien Didelot 	u16 val;
97581228996SVivien Didelot 	int err;
97681228996SVivien Didelot 
9771d90016dSVivien Didelot 	err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_MISC, &val);
97881228996SVivien Didelot 	if (err)
97981228996SVivien Didelot 		return err;
98081228996SVivien Didelot 
98181228996SVivien Didelot 	if (port_5_bit)
9821d90016dSVivien Didelot 		val |= MV88E6XXX_G2_MISC_5_BIT_PORT;
98381228996SVivien Didelot 	else
9841d90016dSVivien Didelot 		val &= ~MV88E6XXX_G2_MISC_5_BIT_PORT;
98581228996SVivien Didelot 
9861d90016dSVivien Didelot 	return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_MISC, val);
98781228996SVivien Didelot }
98881228996SVivien Didelot 
98981228996SVivien Didelot int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip)
99081228996SVivien Didelot {
99181228996SVivien Didelot 	return mv88e6xxx_g2_misc_5_bit_port(chip, false);
99281228996SVivien Didelot }
99381228996SVivien Didelot 
994dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_mask(struct irq_data *d)
995dc30c35bSAndrew Lunn {
996dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
997dc30c35bSAndrew Lunn 	unsigned int n = d->hwirq;
998dc30c35bSAndrew Lunn 
999dc30c35bSAndrew Lunn 	chip->g2_irq.masked |= (1 << n);
1000dc30c35bSAndrew Lunn }
1001dc30c35bSAndrew Lunn 
1002dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_unmask(struct irq_data *d)
1003dc30c35bSAndrew Lunn {
1004dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
1005dc30c35bSAndrew Lunn 	unsigned int n = d->hwirq;
1006dc30c35bSAndrew Lunn 
1007dc30c35bSAndrew Lunn 	chip->g2_irq.masked &= ~(1 << n);
1008dc30c35bSAndrew Lunn }
1009dc30c35bSAndrew Lunn 
1010dc30c35bSAndrew Lunn static irqreturn_t mv88e6xxx_g2_irq_thread_fn(int irq, void *dev_id)
1011dc30c35bSAndrew Lunn {
1012dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = dev_id;
1013dc30c35bSAndrew Lunn 	unsigned int nhandled = 0;
1014dc30c35bSAndrew Lunn 	unsigned int sub_irq;
1015dc30c35bSAndrew Lunn 	unsigned int n;
1016dc30c35bSAndrew Lunn 	int err;
1017dc30c35bSAndrew Lunn 	u16 reg;
1018dc30c35bSAndrew Lunn 
1019c9acece0SRasmus Villemoes 	mv88e6xxx_reg_lock(chip);
1020d6c5e6afSVivien Didelot 	err = mv88e6xxx_g2_int_source(chip, &reg);
1021c9acece0SRasmus Villemoes 	mv88e6xxx_reg_unlock(chip);
1022dc30c35bSAndrew Lunn 	if (err)
1023dc30c35bSAndrew Lunn 		goto out;
1024dc30c35bSAndrew Lunn 
1025dc30c35bSAndrew Lunn 	for (n = 0; n < 16; ++n) {
1026dc30c35bSAndrew Lunn 		if (reg & (1 << n)) {
1027dc30c35bSAndrew Lunn 			sub_irq = irq_find_mapping(chip->g2_irq.domain, n);
1028dc30c35bSAndrew Lunn 			handle_nested_irq(sub_irq);
1029dc30c35bSAndrew Lunn 			++nhandled;
1030dc30c35bSAndrew Lunn 		}
1031dc30c35bSAndrew Lunn 	}
1032dc30c35bSAndrew Lunn out:
1033dc30c35bSAndrew Lunn 	return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
1034dc30c35bSAndrew Lunn }
1035dc30c35bSAndrew Lunn 
1036dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_bus_lock(struct irq_data *d)
1037dc30c35bSAndrew Lunn {
1038dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
1039dc30c35bSAndrew Lunn 
1040c9acece0SRasmus Villemoes 	mv88e6xxx_reg_lock(chip);
1041dc30c35bSAndrew Lunn }
1042dc30c35bSAndrew Lunn 
1043dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_bus_sync_unlock(struct irq_data *d)
1044dc30c35bSAndrew Lunn {
1045dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
1046d6c5e6afSVivien Didelot 	int err;
1047dc30c35bSAndrew Lunn 
1048d6c5e6afSVivien Didelot 	err = mv88e6xxx_g2_int_mask(chip, ~chip->g2_irq.masked);
1049d6c5e6afSVivien Didelot 	if (err)
1050d6c5e6afSVivien Didelot 		dev_err(chip->dev, "failed to mask interrupts\n");
1051dc30c35bSAndrew Lunn 
1052c9acece0SRasmus Villemoes 	mv88e6xxx_reg_unlock(chip);
1053dc30c35bSAndrew Lunn }
1054dc30c35bSAndrew Lunn 
10556eb15e21SBhumika Goyal static const struct irq_chip mv88e6xxx_g2_irq_chip = {
1056dc30c35bSAndrew Lunn 	.name			= "mv88e6xxx-g2",
1057dc30c35bSAndrew Lunn 	.irq_mask		= mv88e6xxx_g2_irq_mask,
1058dc30c35bSAndrew Lunn 	.irq_unmask		= mv88e6xxx_g2_irq_unmask,
1059dc30c35bSAndrew Lunn 	.irq_bus_lock		= mv88e6xxx_g2_irq_bus_lock,
1060dc30c35bSAndrew Lunn 	.irq_bus_sync_unlock	= mv88e6xxx_g2_irq_bus_sync_unlock,
1061dc30c35bSAndrew Lunn };
1062dc30c35bSAndrew Lunn 
1063dc30c35bSAndrew Lunn static int mv88e6xxx_g2_irq_domain_map(struct irq_domain *d,
1064dc30c35bSAndrew Lunn 				       unsigned int irq,
1065dc30c35bSAndrew Lunn 				       irq_hw_number_t hwirq)
1066dc30c35bSAndrew Lunn {
1067dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = d->host_data;
1068dc30c35bSAndrew Lunn 
1069dc30c35bSAndrew Lunn 	irq_set_chip_data(irq, d->host_data);
1070dc30c35bSAndrew Lunn 	irq_set_chip_and_handler(irq, &chip->g2_irq.chip, handle_level_irq);
1071dc30c35bSAndrew Lunn 	irq_set_noprobe(irq);
1072dc30c35bSAndrew Lunn 
1073dc30c35bSAndrew Lunn 	return 0;
1074dc30c35bSAndrew Lunn }
1075dc30c35bSAndrew Lunn 
1076dc30c35bSAndrew Lunn static const struct irq_domain_ops mv88e6xxx_g2_irq_domain_ops = {
1077dc30c35bSAndrew Lunn 	.map	= mv88e6xxx_g2_irq_domain_map,
1078dc30c35bSAndrew Lunn 	.xlate	= irq_domain_xlate_twocell,
1079dc30c35bSAndrew Lunn };
1080dc30c35bSAndrew Lunn 
1081dc30c35bSAndrew Lunn void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip)
1082dc30c35bSAndrew Lunn {
1083dc30c35bSAndrew Lunn 	int irq, virq;
1084dc30c35bSAndrew Lunn 
1085fcd25166SAndrew Lunn 	mv88e6xxx_g2_watchdog_free(chip);
1086fcd25166SAndrew Lunn 
10878e757ebaSAndrew Lunn 	free_irq(chip->device_irq, chip);
10888e757ebaSAndrew Lunn 	irq_dispose_mapping(chip->device_irq);
10898e757ebaSAndrew Lunn 
1090dc30c35bSAndrew Lunn 	for (irq = 0; irq < 16; irq++) {
1091dc30c35bSAndrew Lunn 		virq = irq_find_mapping(chip->g2_irq.domain, irq);
1092dc30c35bSAndrew Lunn 		irq_dispose_mapping(virq);
1093dc30c35bSAndrew Lunn 	}
1094dc30c35bSAndrew Lunn 
1095dc30c35bSAndrew Lunn 	irq_domain_remove(chip->g2_irq.domain);
1096dc30c35bSAndrew Lunn }
1097dc30c35bSAndrew Lunn 
1098dc30c35bSAndrew Lunn int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip)
1099dc30c35bSAndrew Lunn {
11008e757ebaSAndrew Lunn 	int err, irq, virq;
1101dc30c35bSAndrew Lunn 
1102dc30c35bSAndrew Lunn 	chip->g2_irq.domain = irq_domain_add_simple(
1103dc30c35bSAndrew Lunn 		chip->dev->of_node, 16, 0, &mv88e6xxx_g2_irq_domain_ops, chip);
1104dc30c35bSAndrew Lunn 	if (!chip->g2_irq.domain)
1105dc30c35bSAndrew Lunn 		return -ENOMEM;
1106dc30c35bSAndrew Lunn 
1107dc30c35bSAndrew Lunn 	for (irq = 0; irq < 16; irq++)
1108dc30c35bSAndrew Lunn 		irq_create_mapping(chip->g2_irq.domain, irq);
1109dc30c35bSAndrew Lunn 
1110dc30c35bSAndrew Lunn 	chip->g2_irq.chip = mv88e6xxx_g2_irq_chip;
1111dc30c35bSAndrew Lunn 	chip->g2_irq.masked = ~0;
1112dc30c35bSAndrew Lunn 
11138e757ebaSAndrew Lunn 	chip->device_irq = irq_find_mapping(chip->g1_irq.domain,
111482466921SVivien Didelot 					    MV88E6XXX_G1_STS_IRQ_DEVICE);
11158e757ebaSAndrew Lunn 	if (chip->device_irq < 0) {
11168e757ebaSAndrew Lunn 		err = chip->device_irq;
1117dc30c35bSAndrew Lunn 		goto out;
1118dc30c35bSAndrew Lunn 	}
1119dc30c35bSAndrew Lunn 
11208e757ebaSAndrew Lunn 	err = request_threaded_irq(chip->device_irq, NULL,
1121dc30c35bSAndrew Lunn 				   mv88e6xxx_g2_irq_thread_fn,
112236d6ea94SUwe Kleine-König 				   IRQF_ONESHOT, "mv88e6xxx-g2", chip);
1123dc30c35bSAndrew Lunn 	if (err)
1124dc30c35bSAndrew Lunn 		goto out;
1125dc30c35bSAndrew Lunn 
1126fcd25166SAndrew Lunn 	return mv88e6xxx_g2_watchdog_setup(chip);
11278e757ebaSAndrew Lunn 
1128dc30c35bSAndrew Lunn out:
11298e757ebaSAndrew Lunn 	for (irq = 0; irq < 16; irq++) {
11308e757ebaSAndrew Lunn 		virq = irq_find_mapping(chip->g2_irq.domain, irq);
11318e757ebaSAndrew Lunn 		irq_dispose_mapping(virq);
11328e757ebaSAndrew Lunn 	}
11338e757ebaSAndrew Lunn 
11348e757ebaSAndrew Lunn 	irq_domain_remove(chip->g2_irq.domain);
1135dc30c35bSAndrew Lunn 
1136dc30c35bSAndrew Lunn 	return err;
1137dc30c35bSAndrew Lunn }
1138dc30c35bSAndrew Lunn 
11396f88284fSAndrew Lunn int mv88e6xxx_g2_irq_mdio_setup(struct mv88e6xxx_chip *chip,
11406f88284fSAndrew Lunn 				struct mii_bus *bus)
11416f88284fSAndrew Lunn {
11426f88284fSAndrew Lunn 	int phy, irq, err, err_phy;
11436f88284fSAndrew Lunn 
11446f88284fSAndrew Lunn 	for (phy = 0; phy < chip->info->num_internal_phys; phy++) {
11456f88284fSAndrew Lunn 		irq = irq_find_mapping(chip->g2_irq.domain, phy);
11466f88284fSAndrew Lunn 		if (irq < 0) {
11476f88284fSAndrew Lunn 			err = irq;
11486f88284fSAndrew Lunn 			goto out;
11496f88284fSAndrew Lunn 		}
11509255bacdSAndrew Lunn 		bus->irq[chip->info->phy_base_addr + phy] = irq;
11516f88284fSAndrew Lunn 	}
11526f88284fSAndrew Lunn 	return 0;
11536f88284fSAndrew Lunn out:
11546f88284fSAndrew Lunn 	err_phy = phy;
11556f88284fSAndrew Lunn 
11566f88284fSAndrew Lunn 	for (phy = 0; phy < err_phy; phy++)
11576f88284fSAndrew Lunn 		irq_dispose_mapping(bus->irq[phy]);
11586f88284fSAndrew Lunn 
11596f88284fSAndrew Lunn 	return err;
11606f88284fSAndrew Lunn }
11616f88284fSAndrew Lunn 
11626f88284fSAndrew Lunn void mv88e6xxx_g2_irq_mdio_free(struct mv88e6xxx_chip *chip,
11636f88284fSAndrew Lunn 				struct mii_bus *bus)
11646f88284fSAndrew Lunn {
11656f88284fSAndrew Lunn 	int phy;
11666f88284fSAndrew Lunn 
11676f88284fSAndrew Lunn 	for (phy = 0; phy < chip->info->num_internal_phys; phy++)
11686f88284fSAndrew Lunn 		irq_dispose_mapping(bus->irq[phy]);
11696f88284fSAndrew Lunn }
1170