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  *
7ec561276SVivien Didelot  * Copyright (c) 2016 Vivien Didelot <vivien.didelot@savoirfairelinux.com>
8ec561276SVivien Didelot  *
9ec561276SVivien Didelot  * This program is free software; you can redistribute it and/or modify
10ec561276SVivien Didelot  * it under the terms of the GNU General Public License as published by
11ec561276SVivien Didelot  * the Free Software Foundation; either version 2 of the License, or
12ec561276SVivien Didelot  * (at your option) any later version.
13ec561276SVivien Didelot  */
14ec561276SVivien Didelot 
15dc30c35bSAndrew Lunn #include <linux/irqdomain.h>
16ec561276SVivien Didelot #include "mv88e6xxx.h"
17ec561276SVivien Didelot #include "global2.h"
18ec561276SVivien Didelot 
199fe850fbSVivien Didelot #define ADDR_GLOBAL2	0x1c
209fe850fbSVivien Didelot 
219fe850fbSVivien Didelot static int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
229fe850fbSVivien Didelot {
239fe850fbSVivien Didelot 	return mv88e6xxx_read(chip, ADDR_GLOBAL2, reg, val);
249fe850fbSVivien Didelot }
259fe850fbSVivien Didelot 
269fe850fbSVivien Didelot static int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
279fe850fbSVivien Didelot {
289fe850fbSVivien Didelot 	return mv88e6xxx_write(chip, ADDR_GLOBAL2, reg, val);
299fe850fbSVivien Didelot }
309fe850fbSVivien Didelot 
319fe850fbSVivien Didelot static int mv88e6xxx_g2_update(struct mv88e6xxx_chip *chip, int reg, u16 update)
329fe850fbSVivien Didelot {
339fe850fbSVivien Didelot 	return mv88e6xxx_update(chip, ADDR_GLOBAL2, reg, update);
349fe850fbSVivien Didelot }
359fe850fbSVivien Didelot 
369fe850fbSVivien Didelot static int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask)
379fe850fbSVivien Didelot {
389fe850fbSVivien Didelot 	return mv88e6xxx_wait(chip, ADDR_GLOBAL2, reg, mask);
399fe850fbSVivien Didelot }
409fe850fbSVivien Didelot 
416e55f698SAndrew Lunn /* Offset 0x02: Management Enable 2x */
426e55f698SAndrew Lunn /* Offset 0x03: Management Enable 0x */
436e55f698SAndrew Lunn 
446e55f698SAndrew Lunn int mv88e6095_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip)
456e55f698SAndrew Lunn {
466e55f698SAndrew Lunn 	int err;
476e55f698SAndrew Lunn 
486e55f698SAndrew Lunn 	/* Consider the frames with reserved multicast destination
496e55f698SAndrew Lunn 	 * addresses matching 01:80:c2:00:00:2x as MGMT.
506e55f698SAndrew Lunn 	 */
516e55f698SAndrew Lunn 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) {
526e55f698SAndrew Lunn 		err = mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_2X, 0xffff);
536e55f698SAndrew Lunn 		if (err)
546e55f698SAndrew Lunn 			return err;
556e55f698SAndrew Lunn 	}
566e55f698SAndrew Lunn 
576e55f698SAndrew Lunn 	/* Consider the frames with reserved multicast destination
586e55f698SAndrew Lunn 	 * addresses matching 01:80:c2:00:00:0x as MGMT.
596e55f698SAndrew Lunn 	 */
606e55f698SAndrew Lunn 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X))
616e55f698SAndrew Lunn 		return mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_0X, 0xffff);
626e55f698SAndrew Lunn 
636e55f698SAndrew Lunn 	return 0;
646e55f698SAndrew Lunn }
656e55f698SAndrew Lunn 
66ec561276SVivien Didelot /* Offset 0x06: Device Mapping Table register */
67ec561276SVivien Didelot 
68ec561276SVivien Didelot static int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip,
69ec561276SVivien Didelot 					     int target, int port)
70ec561276SVivien Didelot {
71ec561276SVivien Didelot 	u16 val = (target << 8) | (port & 0xf);
72ec561276SVivien Didelot 
739fe850fbSVivien Didelot 	return mv88e6xxx_g2_update(chip, GLOBAL2_DEVICE_MAPPING, val);
74ec561276SVivien Didelot }
75ec561276SVivien Didelot 
76ec561276SVivien Didelot static int mv88e6xxx_g2_set_device_mapping(struct mv88e6xxx_chip *chip)
77ec561276SVivien Didelot {
78ec561276SVivien Didelot 	int target, port;
79ec561276SVivien Didelot 	int err;
80ec561276SVivien Didelot 
81ec561276SVivien Didelot 	/* Initialize the routing port to the 32 possible target devices */
82ec561276SVivien Didelot 	for (target = 0; target < 32; ++target) {
83ec561276SVivien Didelot 		port = 0xf;
84ec561276SVivien Didelot 
85ec561276SVivien Didelot 		if (target < DSA_MAX_SWITCHES) {
86ec561276SVivien Didelot 			port = chip->ds->rtable[target];
87ec561276SVivien Didelot 			if (port == DSA_RTABLE_NONE)
88ec561276SVivien Didelot 				port = 0xf;
89ec561276SVivien Didelot 		}
90ec561276SVivien Didelot 
91ec561276SVivien Didelot 		err = mv88e6xxx_g2_device_mapping_write(chip, target, port);
92ec561276SVivien Didelot 		if (err)
93ec561276SVivien Didelot 			break;
94ec561276SVivien Didelot 	}
95ec561276SVivien Didelot 
96ec561276SVivien Didelot 	return err;
97ec561276SVivien Didelot }
98ec561276SVivien Didelot 
99ec561276SVivien Didelot /* Offset 0x07: Trunk Mask Table register */
100ec561276SVivien Didelot 
101ec561276SVivien Didelot static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
102ec561276SVivien Didelot 					 bool hask, u16 mask)
103ec561276SVivien Didelot {
104370b4ffbSVivien Didelot 	const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
105ec561276SVivien Didelot 	u16 val = (num << 12) | (mask & port_mask);
106ec561276SVivien Didelot 
107ec561276SVivien Didelot 	if (hask)
108ec561276SVivien Didelot 		val |= GLOBAL2_TRUNK_MASK_HASK;
109ec561276SVivien Didelot 
1109fe850fbSVivien Didelot 	return mv88e6xxx_g2_update(chip, GLOBAL2_TRUNK_MASK, val);
111ec561276SVivien Didelot }
112ec561276SVivien Didelot 
113ec561276SVivien Didelot /* Offset 0x08: Trunk Mapping Table register */
114ec561276SVivien Didelot 
115ec561276SVivien Didelot static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id,
116ec561276SVivien Didelot 					    u16 map)
117ec561276SVivien Didelot {
118370b4ffbSVivien Didelot 	const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
119ec561276SVivien Didelot 	u16 val = (id << 11) | (map & port_mask);
120ec561276SVivien Didelot 
1219fe850fbSVivien Didelot 	return mv88e6xxx_g2_update(chip, GLOBAL2_TRUNK_MAPPING, val);
122ec561276SVivien Didelot }
123ec561276SVivien Didelot 
124ec561276SVivien Didelot static int mv88e6xxx_g2_clear_trunk(struct mv88e6xxx_chip *chip)
125ec561276SVivien Didelot {
126370b4ffbSVivien Didelot 	const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
127ec561276SVivien Didelot 	int i, err;
128ec561276SVivien Didelot 
129ec561276SVivien Didelot 	/* Clear all eight possible Trunk Mask vectors */
130ec561276SVivien Didelot 	for (i = 0; i < 8; ++i) {
131ec561276SVivien Didelot 		err = mv88e6xxx_g2_trunk_mask_write(chip, i, false, port_mask);
132ec561276SVivien Didelot 		if (err)
133ec561276SVivien Didelot 			return err;
134ec561276SVivien Didelot 	}
135ec561276SVivien Didelot 
136ec561276SVivien Didelot 	/* Clear all sixteen possible Trunk ID routing vectors */
137ec561276SVivien Didelot 	for (i = 0; i < 16; ++i) {
138ec561276SVivien Didelot 		err = mv88e6xxx_g2_trunk_mapping_write(chip, i, 0);
139ec561276SVivien Didelot 		if (err)
140ec561276SVivien Didelot 			return err;
141ec561276SVivien Didelot 	}
142ec561276SVivien Didelot 
143ec561276SVivien Didelot 	return 0;
144ec561276SVivien Didelot }
145ec561276SVivien Didelot 
146ec561276SVivien Didelot /* Offset 0x09: Ingress Rate Command register
147ec561276SVivien Didelot  * Offset 0x0A: Ingress Rate Data register
148ec561276SVivien Didelot  */
149ec561276SVivien Didelot 
150ec561276SVivien Didelot static int mv88e6xxx_g2_clear_irl(struct mv88e6xxx_chip *chip)
151ec561276SVivien Didelot {
152ec561276SVivien Didelot 	int port, err;
153ec561276SVivien Didelot 
154ec561276SVivien Didelot 	/* Init all Ingress Rate Limit resources of all ports */
155370b4ffbSVivien Didelot 	for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) {
156ec561276SVivien Didelot 		/* XXX newer chips (like 88E6390) have different 2-bit ops */
1579fe850fbSVivien Didelot 		err = mv88e6xxx_g2_write(chip, GLOBAL2_IRL_CMD,
158ec561276SVivien Didelot 					 GLOBAL2_IRL_CMD_OP_INIT_ALL |
159ec561276SVivien Didelot 					 (port << 8));
160ec561276SVivien Didelot 		if (err)
161ec561276SVivien Didelot 			break;
162ec561276SVivien Didelot 
163ec561276SVivien Didelot 		/* Wait for the operation to complete */
1649fe850fbSVivien Didelot 		err = mv88e6xxx_g2_wait(chip, GLOBAL2_IRL_CMD,
165ec561276SVivien Didelot 					GLOBAL2_IRL_CMD_BUSY);
166ec561276SVivien Didelot 		if (err)
167ec561276SVivien Didelot 			break;
168ec561276SVivien Didelot 	}
169ec561276SVivien Didelot 
170ec561276SVivien Didelot 	return err;
171ec561276SVivien Didelot }
172ec561276SVivien Didelot 
173ec561276SVivien Didelot /* Offset 0x0D: Switch MAC/WoL/WoF register */
174ec561276SVivien Didelot 
175ec561276SVivien Didelot static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip,
176ec561276SVivien Didelot 					 unsigned int pointer, u8 data)
177ec561276SVivien Didelot {
178ec561276SVivien Didelot 	u16 val = (pointer << 8) | data;
179ec561276SVivien Didelot 
1809fe850fbSVivien Didelot 	return mv88e6xxx_g2_update(chip, GLOBAL2_SWITCH_MAC, val);
181ec561276SVivien Didelot }
182ec561276SVivien Didelot 
183ec561276SVivien Didelot int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
184ec561276SVivien Didelot {
185ec561276SVivien Didelot 	int i, err;
186ec561276SVivien Didelot 
187ec561276SVivien Didelot 	for (i = 0; i < 6; i++) {
188ec561276SVivien Didelot 		err = mv88e6xxx_g2_switch_mac_write(chip, i, addr[i]);
189ec561276SVivien Didelot 		if (err)
190ec561276SVivien Didelot 			break;
191ec561276SVivien Didelot 	}
192ec561276SVivien Didelot 
193ec561276SVivien Didelot 	return err;
194ec561276SVivien Didelot }
195ec561276SVivien Didelot 
196ec561276SVivien Didelot /* Offset 0x0F: Priority Override Table */
197ec561276SVivien Didelot 
198ec561276SVivien Didelot static int mv88e6xxx_g2_pot_write(struct mv88e6xxx_chip *chip, int pointer,
199ec561276SVivien Didelot 				  u8 data)
200ec561276SVivien Didelot {
201ec561276SVivien Didelot 	u16 val = (pointer << 8) | (data & 0x7);
202ec561276SVivien Didelot 
2039fe850fbSVivien Didelot 	return mv88e6xxx_g2_update(chip, GLOBAL2_PRIO_OVERRIDE, val);
204ec561276SVivien Didelot }
205ec561276SVivien Didelot 
206ec561276SVivien Didelot static int mv88e6xxx_g2_clear_pot(struct mv88e6xxx_chip *chip)
207ec561276SVivien Didelot {
208ec561276SVivien Didelot 	int i, err;
209ec561276SVivien Didelot 
210ec561276SVivien Didelot 	/* Clear all sixteen possible Priority Override entries */
211ec561276SVivien Didelot 	for (i = 0; i < 16; i++) {
212ec561276SVivien Didelot 		err = mv88e6xxx_g2_pot_write(chip, i, 0);
213ec561276SVivien Didelot 		if (err)
214ec561276SVivien Didelot 			break;
215ec561276SVivien Didelot 	}
216ec561276SVivien Didelot 
217ec561276SVivien Didelot 	return err;
218ec561276SVivien Didelot }
219ec561276SVivien Didelot 
220ec561276SVivien Didelot /* Offset 0x14: EEPROM Command
22198fc3c6fSVivien Didelot  * Offset 0x15: EEPROM Data (for 16-bit data access)
22298fc3c6fSVivien Didelot  * Offset 0x15: EEPROM Addr (for 8-bit data access)
223ec561276SVivien Didelot  */
224ec561276SVivien Didelot 
225ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip)
226ec561276SVivien Didelot {
2279fe850fbSVivien Didelot 	return mv88e6xxx_g2_wait(chip, GLOBAL2_EEPROM_CMD,
228ec561276SVivien Didelot 				 GLOBAL2_EEPROM_CMD_BUSY |
229ec561276SVivien Didelot 				 GLOBAL2_EEPROM_CMD_RUNNING);
230ec561276SVivien Didelot }
231ec561276SVivien Didelot 
232ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
233ec561276SVivien Didelot {
234ec561276SVivien Didelot 	int err;
235ec561276SVivien Didelot 
2369fe850fbSVivien Didelot 	err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_CMD, cmd);
237ec561276SVivien Didelot 	if (err)
238ec561276SVivien Didelot 		return err;
239ec561276SVivien Didelot 
240ec561276SVivien Didelot 	return mv88e6xxx_g2_eeprom_wait(chip);
241ec561276SVivien Didelot }
242ec561276SVivien Didelot 
24398fc3c6fSVivien Didelot static int mv88e6xxx_g2_eeprom_read8(struct mv88e6xxx_chip *chip,
24498fc3c6fSVivien Didelot 				     u16 addr, u8 *data)
24598fc3c6fSVivien Didelot {
24698fc3c6fSVivien Didelot 	u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ;
24798fc3c6fSVivien Didelot 	int err;
24898fc3c6fSVivien Didelot 
24998fc3c6fSVivien Didelot 	err = mv88e6xxx_g2_eeprom_wait(chip);
25098fc3c6fSVivien Didelot 	if (err)
25198fc3c6fSVivien Didelot 		return err;
25298fc3c6fSVivien Didelot 
25398fc3c6fSVivien Didelot 	err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_ADDR, addr);
25498fc3c6fSVivien Didelot 	if (err)
25598fc3c6fSVivien Didelot 		return err;
25698fc3c6fSVivien Didelot 
25798fc3c6fSVivien Didelot 	err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
25898fc3c6fSVivien Didelot 	if (err)
25998fc3c6fSVivien Didelot 		return err;
26098fc3c6fSVivien Didelot 
26198fc3c6fSVivien Didelot 	err = mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_CMD, &cmd);
26298fc3c6fSVivien Didelot 	if (err)
26398fc3c6fSVivien Didelot 		return err;
26498fc3c6fSVivien Didelot 
26598fc3c6fSVivien Didelot 	*data = cmd & 0xff;
26698fc3c6fSVivien Didelot 
26798fc3c6fSVivien Didelot 	return 0;
26898fc3c6fSVivien Didelot }
26998fc3c6fSVivien Didelot 
27098fc3c6fSVivien Didelot static int mv88e6xxx_g2_eeprom_write8(struct mv88e6xxx_chip *chip,
27198fc3c6fSVivien Didelot 				      u16 addr, u8 data)
27298fc3c6fSVivien Didelot {
27398fc3c6fSVivien Didelot 	u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | GLOBAL2_EEPROM_CMD_WRITE_EN;
27498fc3c6fSVivien Didelot 	int err;
27598fc3c6fSVivien Didelot 
27698fc3c6fSVivien Didelot 	err = mv88e6xxx_g2_eeprom_wait(chip);
27798fc3c6fSVivien Didelot 	if (err)
27898fc3c6fSVivien Didelot 		return err;
27998fc3c6fSVivien Didelot 
28098fc3c6fSVivien Didelot 	err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_ADDR, addr);
28198fc3c6fSVivien Didelot 	if (err)
28298fc3c6fSVivien Didelot 		return err;
28398fc3c6fSVivien Didelot 
28498fc3c6fSVivien Didelot 	return mv88e6xxx_g2_eeprom_cmd(chip, cmd | data);
28598fc3c6fSVivien Didelot }
28698fc3c6fSVivien Didelot 
287ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_read16(struct mv88e6xxx_chip *chip,
288ec561276SVivien Didelot 				      u8 addr, u16 *data)
289ec561276SVivien Didelot {
290ec561276SVivien Didelot 	u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ | addr;
291ec561276SVivien Didelot 	int err;
292ec561276SVivien Didelot 
293ec561276SVivien Didelot 	err = mv88e6xxx_g2_eeprom_wait(chip);
294ec561276SVivien Didelot 	if (err)
295ec561276SVivien Didelot 		return err;
296ec561276SVivien Didelot 
297ec561276SVivien Didelot 	err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
298ec561276SVivien Didelot 	if (err)
299ec561276SVivien Didelot 		return err;
300ec561276SVivien Didelot 
3019fe850fbSVivien Didelot 	return mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_DATA, data);
302ec561276SVivien Didelot }
303ec561276SVivien Didelot 
304ec561276SVivien Didelot static int mv88e6xxx_g2_eeprom_write16(struct mv88e6xxx_chip *chip,
305ec561276SVivien Didelot 				       u8 addr, u16 data)
306ec561276SVivien Didelot {
307ec561276SVivien Didelot 	u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | addr;
308ec561276SVivien Didelot 	int err;
309ec561276SVivien Didelot 
310ec561276SVivien Didelot 	err = mv88e6xxx_g2_eeprom_wait(chip);
311ec561276SVivien Didelot 	if (err)
312ec561276SVivien Didelot 		return err;
313ec561276SVivien Didelot 
3149fe850fbSVivien Didelot 	err = mv88e6xxx_g2_write(chip, GLOBAL2_EEPROM_DATA, data);
315ec561276SVivien Didelot 	if (err)
316ec561276SVivien Didelot 		return err;
317ec561276SVivien Didelot 
318ec561276SVivien Didelot 	return mv88e6xxx_g2_eeprom_cmd(chip, cmd);
319ec561276SVivien Didelot }
320ec561276SVivien Didelot 
32198fc3c6fSVivien Didelot int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip,
32298fc3c6fSVivien Didelot 			     struct ethtool_eeprom *eeprom, u8 *data)
32398fc3c6fSVivien Didelot {
32498fc3c6fSVivien Didelot 	unsigned int offset = eeprom->offset;
32598fc3c6fSVivien Didelot 	unsigned int len = eeprom->len;
32698fc3c6fSVivien Didelot 	int err;
32798fc3c6fSVivien Didelot 
32898fc3c6fSVivien Didelot 	eeprom->len = 0;
32998fc3c6fSVivien Didelot 
33098fc3c6fSVivien Didelot 	while (len) {
33198fc3c6fSVivien Didelot 		err = mv88e6xxx_g2_eeprom_read8(chip, offset, data);
33298fc3c6fSVivien Didelot 		if (err)
33398fc3c6fSVivien Didelot 			return err;
33498fc3c6fSVivien Didelot 
33598fc3c6fSVivien Didelot 		eeprom->len++;
33698fc3c6fSVivien Didelot 		offset++;
33798fc3c6fSVivien Didelot 		data++;
33898fc3c6fSVivien Didelot 		len--;
33998fc3c6fSVivien Didelot 	}
34098fc3c6fSVivien Didelot 
34198fc3c6fSVivien Didelot 	return 0;
34298fc3c6fSVivien Didelot }
34398fc3c6fSVivien Didelot 
34498fc3c6fSVivien Didelot int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip,
34598fc3c6fSVivien Didelot 			     struct ethtool_eeprom *eeprom, u8 *data)
34698fc3c6fSVivien Didelot {
34798fc3c6fSVivien Didelot 	unsigned int offset = eeprom->offset;
34898fc3c6fSVivien Didelot 	unsigned int len = eeprom->len;
34998fc3c6fSVivien Didelot 	int err;
35098fc3c6fSVivien Didelot 
35198fc3c6fSVivien Didelot 	eeprom->len = 0;
35298fc3c6fSVivien Didelot 
35398fc3c6fSVivien Didelot 	while (len) {
35498fc3c6fSVivien Didelot 		err = mv88e6xxx_g2_eeprom_write8(chip, offset, *data);
35598fc3c6fSVivien Didelot 		if (err)
35698fc3c6fSVivien Didelot 			return err;
35798fc3c6fSVivien Didelot 
35898fc3c6fSVivien Didelot 		eeprom->len++;
35998fc3c6fSVivien Didelot 		offset++;
36098fc3c6fSVivien Didelot 		data++;
36198fc3c6fSVivien Didelot 		len--;
36298fc3c6fSVivien Didelot 	}
36398fc3c6fSVivien Didelot 
36498fc3c6fSVivien Didelot 	return 0;
36598fc3c6fSVivien Didelot }
36698fc3c6fSVivien Didelot 
367ec561276SVivien Didelot int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip,
368ec561276SVivien Didelot 			      struct ethtool_eeprom *eeprom, u8 *data)
369ec561276SVivien Didelot {
370ec561276SVivien Didelot 	unsigned int offset = eeprom->offset;
371ec561276SVivien Didelot 	unsigned int len = eeprom->len;
372ec561276SVivien Didelot 	u16 val;
373ec561276SVivien Didelot 	int err;
374ec561276SVivien Didelot 
375ec561276SVivien Didelot 	eeprom->len = 0;
376ec561276SVivien Didelot 
377ec561276SVivien Didelot 	if (offset & 1) {
378ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
379ec561276SVivien Didelot 		if (err)
380ec561276SVivien Didelot 			return err;
381ec561276SVivien Didelot 
382ec561276SVivien Didelot 		*data++ = (val >> 8) & 0xff;
383ec561276SVivien Didelot 
384ec561276SVivien Didelot 		offset++;
385ec561276SVivien Didelot 		len--;
386ec561276SVivien Didelot 		eeprom->len++;
387ec561276SVivien Didelot 	}
388ec561276SVivien Didelot 
389ec561276SVivien Didelot 	while (len >= 2) {
390ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
391ec561276SVivien Didelot 		if (err)
392ec561276SVivien Didelot 			return err;
393ec561276SVivien Didelot 
394ec561276SVivien Didelot 		*data++ = val & 0xff;
395ec561276SVivien Didelot 		*data++ = (val >> 8) & 0xff;
396ec561276SVivien Didelot 
397ec561276SVivien Didelot 		offset += 2;
398ec561276SVivien Didelot 		len -= 2;
399ec561276SVivien Didelot 		eeprom->len += 2;
400ec561276SVivien Didelot 	}
401ec561276SVivien Didelot 
402ec561276SVivien Didelot 	if (len) {
403ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
404ec561276SVivien Didelot 		if (err)
405ec561276SVivien Didelot 			return err;
406ec561276SVivien Didelot 
407ec561276SVivien Didelot 		*data++ = val & 0xff;
408ec561276SVivien Didelot 
409ec561276SVivien Didelot 		offset++;
410ec561276SVivien Didelot 		len--;
411ec561276SVivien Didelot 		eeprom->len++;
412ec561276SVivien Didelot 	}
413ec561276SVivien Didelot 
414ec561276SVivien Didelot 	return 0;
415ec561276SVivien Didelot }
416ec561276SVivien Didelot 
417ec561276SVivien Didelot int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip,
418ec561276SVivien Didelot 			      struct ethtool_eeprom *eeprom, u8 *data)
419ec561276SVivien Didelot {
420ec561276SVivien Didelot 	unsigned int offset = eeprom->offset;
421ec561276SVivien Didelot 	unsigned int len = eeprom->len;
422ec561276SVivien Didelot 	u16 val;
423ec561276SVivien Didelot 	int err;
424ec561276SVivien Didelot 
425ec561276SVivien Didelot 	/* Ensure the RO WriteEn bit is set */
4269fe850fbSVivien Didelot 	err = mv88e6xxx_g2_read(chip, GLOBAL2_EEPROM_CMD, &val);
427ec561276SVivien Didelot 	if (err)
428ec561276SVivien Didelot 		return err;
429ec561276SVivien Didelot 
430ec561276SVivien Didelot 	if (!(val & GLOBAL2_EEPROM_CMD_WRITE_EN))
431ec561276SVivien Didelot 		return -EROFS;
432ec561276SVivien Didelot 
433ec561276SVivien Didelot 	eeprom->len = 0;
434ec561276SVivien Didelot 
435ec561276SVivien Didelot 	if (offset & 1) {
436ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
437ec561276SVivien Didelot 		if (err)
438ec561276SVivien Didelot 			return err;
439ec561276SVivien Didelot 
440ec561276SVivien Didelot 		val = (*data++ << 8) | (val & 0xff);
441ec561276SVivien Didelot 
442ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
443ec561276SVivien Didelot 		if (err)
444ec561276SVivien Didelot 			return err;
445ec561276SVivien Didelot 
446ec561276SVivien Didelot 		offset++;
447ec561276SVivien Didelot 		len--;
448ec561276SVivien Didelot 		eeprom->len++;
449ec561276SVivien Didelot 	}
450ec561276SVivien Didelot 
451ec561276SVivien Didelot 	while (len >= 2) {
452ec561276SVivien Didelot 		val = *data++;
453ec561276SVivien Didelot 		val |= *data++ << 8;
454ec561276SVivien Didelot 
455ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
456ec561276SVivien Didelot 		if (err)
457ec561276SVivien Didelot 			return err;
458ec561276SVivien Didelot 
459ec561276SVivien Didelot 		offset += 2;
460ec561276SVivien Didelot 		len -= 2;
461ec561276SVivien Didelot 		eeprom->len += 2;
462ec561276SVivien Didelot 	}
463ec561276SVivien Didelot 
464ec561276SVivien Didelot 	if (len) {
465ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
466ec561276SVivien Didelot 		if (err)
467ec561276SVivien Didelot 			return err;
468ec561276SVivien Didelot 
469ec561276SVivien Didelot 		val = (val & 0xff00) | *data++;
470ec561276SVivien Didelot 
471ec561276SVivien Didelot 		err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
472ec561276SVivien Didelot 		if (err)
473ec561276SVivien Didelot 			return err;
474ec561276SVivien Didelot 
475ec561276SVivien Didelot 		offset++;
476ec561276SVivien Didelot 		len--;
477ec561276SVivien Didelot 		eeprom->len++;
478ec561276SVivien Didelot 	}
479ec561276SVivien Didelot 
480ec561276SVivien Didelot 	return 0;
481ec561276SVivien Didelot }
482ec561276SVivien Didelot 
483ec561276SVivien Didelot /* Offset 0x18: SMI PHY Command Register
484ec561276SVivien Didelot  * Offset 0x19: SMI PHY Data Register
485ec561276SVivien Didelot  */
486ec561276SVivien Didelot 
487ec561276SVivien Didelot static int mv88e6xxx_g2_smi_phy_wait(struct mv88e6xxx_chip *chip)
488ec561276SVivien Didelot {
4899fe850fbSVivien Didelot 	return mv88e6xxx_g2_wait(chip, GLOBAL2_SMI_PHY_CMD,
490ec561276SVivien Didelot 				 GLOBAL2_SMI_PHY_CMD_BUSY);
491ec561276SVivien Didelot }
492ec561276SVivien Didelot 
493ec561276SVivien Didelot static int mv88e6xxx_g2_smi_phy_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
494ec561276SVivien Didelot {
495ec561276SVivien Didelot 	int err;
496ec561276SVivien Didelot 
4979fe850fbSVivien Didelot 	err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_CMD, cmd);
498ec561276SVivien Didelot 	if (err)
499ec561276SVivien Didelot 		return err;
500ec561276SVivien Didelot 
501ec561276SVivien Didelot 	return mv88e6xxx_g2_smi_phy_wait(chip);
502ec561276SVivien Didelot }
503ec561276SVivien Didelot 
504ee26a228SAndrew Lunn int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip,
505ee26a228SAndrew Lunn 			      struct mii_bus *bus,
506ee26a228SAndrew Lunn 			      int addr, int reg, u16 *val)
507ec561276SVivien Didelot {
508ec561276SVivien Didelot 	u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_READ_DATA | (addr << 5) | reg;
509ec561276SVivien Didelot 	int err;
510ec561276SVivien Didelot 
511ec561276SVivien Didelot 	err = mv88e6xxx_g2_smi_phy_wait(chip);
512ec561276SVivien Didelot 	if (err)
513ec561276SVivien Didelot 		return err;
514ec561276SVivien Didelot 
515ec561276SVivien Didelot 	err = mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
516ec561276SVivien Didelot 	if (err)
517ec561276SVivien Didelot 		return err;
518ec561276SVivien Didelot 
5199fe850fbSVivien Didelot 	return mv88e6xxx_g2_read(chip, GLOBAL2_SMI_PHY_DATA, val);
520ec561276SVivien Didelot }
521ec561276SVivien Didelot 
522ee26a228SAndrew Lunn int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip,
523ee26a228SAndrew Lunn 			       struct mii_bus *bus,
524ee26a228SAndrew Lunn 			       int addr, int reg, u16 val)
525ec561276SVivien Didelot {
526ec561276SVivien Didelot 	u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_WRITE_DATA | (addr << 5) | reg;
527ec561276SVivien Didelot 	int err;
528ec561276SVivien Didelot 
529ec561276SVivien Didelot 	err = mv88e6xxx_g2_smi_phy_wait(chip);
530ec561276SVivien Didelot 	if (err)
531ec561276SVivien Didelot 		return err;
532ec561276SVivien Didelot 
5339fe850fbSVivien Didelot 	err = mv88e6xxx_g2_write(chip, GLOBAL2_SMI_PHY_DATA, val);
534ec561276SVivien Didelot 	if (err)
535ec561276SVivien Didelot 		return err;
536ec561276SVivien Didelot 
537ec561276SVivien Didelot 	return mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
538ec561276SVivien Didelot }
539ec561276SVivien Didelot 
540dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_mask(struct irq_data *d)
541dc30c35bSAndrew Lunn {
542dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
543dc30c35bSAndrew Lunn 	unsigned int n = d->hwirq;
544dc30c35bSAndrew Lunn 
545dc30c35bSAndrew Lunn 	chip->g2_irq.masked |= (1 << n);
546dc30c35bSAndrew Lunn }
547dc30c35bSAndrew Lunn 
548dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_unmask(struct irq_data *d)
549dc30c35bSAndrew Lunn {
550dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
551dc30c35bSAndrew Lunn 	unsigned int n = d->hwirq;
552dc30c35bSAndrew Lunn 
553dc30c35bSAndrew Lunn 	chip->g2_irq.masked &= ~(1 << n);
554dc30c35bSAndrew Lunn }
555dc30c35bSAndrew Lunn 
556dc30c35bSAndrew Lunn static irqreturn_t mv88e6xxx_g2_irq_thread_fn(int irq, void *dev_id)
557dc30c35bSAndrew Lunn {
558dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = dev_id;
559dc30c35bSAndrew Lunn 	unsigned int nhandled = 0;
560dc30c35bSAndrew Lunn 	unsigned int sub_irq;
561dc30c35bSAndrew Lunn 	unsigned int n;
562dc30c35bSAndrew Lunn 	int err;
563dc30c35bSAndrew Lunn 	u16 reg;
564dc30c35bSAndrew Lunn 
565dc30c35bSAndrew Lunn 	mutex_lock(&chip->reg_lock);
566dc30c35bSAndrew Lunn 	err = mv88e6xxx_g2_read(chip, GLOBAL2_INT_SOURCE, &reg);
567dc30c35bSAndrew Lunn 	mutex_unlock(&chip->reg_lock);
568dc30c35bSAndrew Lunn 	if (err)
569dc30c35bSAndrew Lunn 		goto out;
570dc30c35bSAndrew Lunn 
571dc30c35bSAndrew Lunn 	for (n = 0; n < 16; ++n) {
572dc30c35bSAndrew Lunn 		if (reg & (1 << n)) {
573dc30c35bSAndrew Lunn 			sub_irq = irq_find_mapping(chip->g2_irq.domain, n);
574dc30c35bSAndrew Lunn 			handle_nested_irq(sub_irq);
575dc30c35bSAndrew Lunn 			++nhandled;
576dc30c35bSAndrew Lunn 		}
577dc30c35bSAndrew Lunn 	}
578dc30c35bSAndrew Lunn out:
579dc30c35bSAndrew Lunn 	return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
580dc30c35bSAndrew Lunn }
581dc30c35bSAndrew Lunn 
582dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_bus_lock(struct irq_data *d)
583dc30c35bSAndrew Lunn {
584dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
585dc30c35bSAndrew Lunn 
586dc30c35bSAndrew Lunn 	mutex_lock(&chip->reg_lock);
587dc30c35bSAndrew Lunn }
588dc30c35bSAndrew Lunn 
589dc30c35bSAndrew Lunn static void mv88e6xxx_g2_irq_bus_sync_unlock(struct irq_data *d)
590dc30c35bSAndrew Lunn {
591dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
592dc30c35bSAndrew Lunn 
593dc30c35bSAndrew Lunn 	mv88e6xxx_g2_write(chip, GLOBAL2_INT_MASK, ~chip->g2_irq.masked);
594dc30c35bSAndrew Lunn 
595dc30c35bSAndrew Lunn 	mutex_unlock(&chip->reg_lock);
596dc30c35bSAndrew Lunn }
597dc30c35bSAndrew Lunn 
598dc30c35bSAndrew Lunn static struct irq_chip mv88e6xxx_g2_irq_chip = {
599dc30c35bSAndrew Lunn 	.name			= "mv88e6xxx-g2",
600dc30c35bSAndrew Lunn 	.irq_mask		= mv88e6xxx_g2_irq_mask,
601dc30c35bSAndrew Lunn 	.irq_unmask		= mv88e6xxx_g2_irq_unmask,
602dc30c35bSAndrew Lunn 	.irq_bus_lock		= mv88e6xxx_g2_irq_bus_lock,
603dc30c35bSAndrew Lunn 	.irq_bus_sync_unlock	= mv88e6xxx_g2_irq_bus_sync_unlock,
604dc30c35bSAndrew Lunn };
605dc30c35bSAndrew Lunn 
606dc30c35bSAndrew Lunn static int mv88e6xxx_g2_irq_domain_map(struct irq_domain *d,
607dc30c35bSAndrew Lunn 				       unsigned int irq,
608dc30c35bSAndrew Lunn 				       irq_hw_number_t hwirq)
609dc30c35bSAndrew Lunn {
610dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = d->host_data;
611dc30c35bSAndrew Lunn 
612dc30c35bSAndrew Lunn 	irq_set_chip_data(irq, d->host_data);
613dc30c35bSAndrew Lunn 	irq_set_chip_and_handler(irq, &chip->g2_irq.chip, handle_level_irq);
614dc30c35bSAndrew Lunn 	irq_set_noprobe(irq);
615dc30c35bSAndrew Lunn 
616dc30c35bSAndrew Lunn 	return 0;
617dc30c35bSAndrew Lunn }
618dc30c35bSAndrew Lunn 
619dc30c35bSAndrew Lunn static const struct irq_domain_ops mv88e6xxx_g2_irq_domain_ops = {
620dc30c35bSAndrew Lunn 	.map	= mv88e6xxx_g2_irq_domain_map,
621dc30c35bSAndrew Lunn 	.xlate	= irq_domain_xlate_twocell,
622dc30c35bSAndrew Lunn };
623dc30c35bSAndrew Lunn 
624dc30c35bSAndrew Lunn void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip)
625dc30c35bSAndrew Lunn {
626dc30c35bSAndrew Lunn 	int irq, virq;
627dc30c35bSAndrew Lunn 
6288e757ebaSAndrew Lunn 	free_irq(chip->device_irq, chip);
6298e757ebaSAndrew Lunn 	irq_dispose_mapping(chip->device_irq);
6308e757ebaSAndrew Lunn 
631dc30c35bSAndrew Lunn 	for (irq = 0; irq < 16; irq++) {
632dc30c35bSAndrew Lunn 		virq = irq_find_mapping(chip->g2_irq.domain, irq);
633dc30c35bSAndrew Lunn 		irq_dispose_mapping(virq);
634dc30c35bSAndrew Lunn 	}
635dc30c35bSAndrew Lunn 
636dc30c35bSAndrew Lunn 	irq_domain_remove(chip->g2_irq.domain);
637dc30c35bSAndrew Lunn }
638dc30c35bSAndrew Lunn 
639dc30c35bSAndrew Lunn int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip)
640dc30c35bSAndrew Lunn {
6418e757ebaSAndrew Lunn 	int err, irq, virq;
642dc30c35bSAndrew Lunn 
643dc30c35bSAndrew Lunn 	if (!chip->dev->of_node)
644dc30c35bSAndrew Lunn 		return -EINVAL;
645dc30c35bSAndrew Lunn 
646dc30c35bSAndrew Lunn 	chip->g2_irq.domain = irq_domain_add_simple(
647dc30c35bSAndrew Lunn 		chip->dev->of_node, 16, 0, &mv88e6xxx_g2_irq_domain_ops, chip);
648dc30c35bSAndrew Lunn 	if (!chip->g2_irq.domain)
649dc30c35bSAndrew Lunn 		return -ENOMEM;
650dc30c35bSAndrew Lunn 
651dc30c35bSAndrew Lunn 	for (irq = 0; irq < 16; irq++)
652dc30c35bSAndrew Lunn 		irq_create_mapping(chip->g2_irq.domain, irq);
653dc30c35bSAndrew Lunn 
654dc30c35bSAndrew Lunn 	chip->g2_irq.chip = mv88e6xxx_g2_irq_chip;
655dc30c35bSAndrew Lunn 	chip->g2_irq.masked = ~0;
656dc30c35bSAndrew Lunn 
6578e757ebaSAndrew Lunn 	chip->device_irq = irq_find_mapping(chip->g1_irq.domain,
658dc30c35bSAndrew Lunn 					    GLOBAL_STATUS_IRQ_DEVICE);
6598e757ebaSAndrew Lunn 	if (chip->device_irq < 0) {
6608e757ebaSAndrew Lunn 		err = chip->device_irq;
661dc30c35bSAndrew Lunn 		goto out;
662dc30c35bSAndrew Lunn 	}
663dc30c35bSAndrew Lunn 
6648e757ebaSAndrew Lunn 	err = request_threaded_irq(chip->device_irq, NULL,
665dc30c35bSAndrew Lunn 				   mv88e6xxx_g2_irq_thread_fn,
666dc30c35bSAndrew Lunn 				   IRQF_ONESHOT, "mv88e6xxx-g1", chip);
667dc30c35bSAndrew Lunn 	if (err)
668dc30c35bSAndrew Lunn 		goto out;
669dc30c35bSAndrew Lunn 
670dc30c35bSAndrew Lunn 	return 0;
6718e757ebaSAndrew Lunn 
672dc30c35bSAndrew Lunn out:
6738e757ebaSAndrew Lunn 	for (irq = 0; irq < 16; irq++) {
6748e757ebaSAndrew Lunn 		virq = irq_find_mapping(chip->g2_irq.domain, irq);
6758e757ebaSAndrew Lunn 		irq_dispose_mapping(virq);
6768e757ebaSAndrew Lunn 	}
6778e757ebaSAndrew Lunn 
6788e757ebaSAndrew Lunn 	irq_domain_remove(chip->g2_irq.domain);
679dc30c35bSAndrew Lunn 
680dc30c35bSAndrew Lunn 	return err;
681dc30c35bSAndrew Lunn }
682dc30c35bSAndrew Lunn 
683ec561276SVivien Didelot int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip)
684ec561276SVivien Didelot {
685ec561276SVivien Didelot 	u16 reg;
686ec561276SVivien Didelot 	int err;
687ec561276SVivien Didelot 
688ec561276SVivien Didelot 	/* Ignore removed tag data on doubly tagged packets, disable
689ec561276SVivien Didelot 	 * flow control messages, force flow control priority to the
690ec561276SVivien Didelot 	 * highest, and send all special multicast frames to the CPU
691ec561276SVivien Didelot 	 * port at the highest priority.
692ec561276SVivien Didelot 	 */
693ec561276SVivien Didelot 	reg = GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI | (0x7 << 4);
694ec561276SVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X) ||
695ec561276SVivien Didelot 	    mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X))
696ec561276SVivien Didelot 		reg |= GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x7;
6979fe850fbSVivien Didelot 	err = mv88e6xxx_g2_write(chip, GLOBAL2_SWITCH_MGMT, reg);
698ec561276SVivien Didelot 	if (err)
699ec561276SVivien Didelot 		return err;
700ec561276SVivien Didelot 
701ec561276SVivien Didelot 	/* Program the DSA routing table. */
702ec561276SVivien Didelot 	err = mv88e6xxx_g2_set_device_mapping(chip);
703ec561276SVivien Didelot 	if (err)
704ec561276SVivien Didelot 		return err;
705ec561276SVivien Didelot 
706ec561276SVivien Didelot 	/* Clear all trunk masks and mapping. */
707ec561276SVivien Didelot 	err = mv88e6xxx_g2_clear_trunk(chip);
708ec561276SVivien Didelot 	if (err)
709ec561276SVivien Didelot 		return err;
710ec561276SVivien Didelot 
711ec561276SVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_IRL)) {
712ec561276SVivien Didelot 		/* Disable ingress rate limiting by resetting all per port
713ec561276SVivien Didelot 		 * ingress rate limit resources to their initial state.
714ec561276SVivien Didelot 		 */
715ec561276SVivien Didelot 		err = mv88e6xxx_g2_clear_irl(chip);
716ec561276SVivien Didelot 			if (err)
717ec561276SVivien Didelot 				return err;
718ec561276SVivien Didelot 	}
719ec561276SVivien Didelot 
720ec561276SVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_PVT)) {
721ec561276SVivien Didelot 		/* Initialize Cross-chip Port VLAN Table to reset defaults */
7229fe850fbSVivien Didelot 		err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_ADDR,
723ec561276SVivien Didelot 					 GLOBAL2_PVT_ADDR_OP_INIT_ONES);
724ec561276SVivien Didelot 		if (err)
725ec561276SVivien Didelot 			return err;
726ec561276SVivien Didelot 	}
727ec561276SVivien Didelot 
728ec561276SVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_POT)) {
729ec561276SVivien Didelot 		/* Clear the priority override table. */
730ec561276SVivien Didelot 		err = mv88e6xxx_g2_clear_pot(chip);
731ec561276SVivien Didelot 		if (err)
732ec561276SVivien Didelot 			return err;
733ec561276SVivien Didelot 	}
734ec561276SVivien Didelot 
735ec561276SVivien Didelot 	return 0;
736ec561276SVivien Didelot }
737