xref: /linux/drivers/net/dsa/mv88e6xxx/serdes.c (revision f2a3b12b305c7bb72467b2a56d19a4587b6007f9)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Marvell 88E6xxx SERDES manipulation, via SMI bus
4  *
5  * Copyright (c) 2008 Marvell Semiconductor
6  *
7  * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch>
8  */
9 
10 #include <linux/interrupt.h>
11 #include <linux/irqdomain.h>
12 #include <linux/mii.h>
13 
14 #include "chip.h"
15 #include "global2.h"
16 #include "phy.h"
17 #include "port.h"
18 #include "serdes.h"
19 
mv88e6352_serdes_read(struct mv88e6xxx_chip * chip,int reg,u16 * val)20 static int mv88e6352_serdes_read(struct mv88e6xxx_chip *chip, int reg,
21 				 u16 *val)
22 {
23 	return mv88e6xxx_phy_page_read(chip, MV88E6352_ADDR_SERDES,
24 				       MV88E6352_SERDES_PAGE_FIBER,
25 				       reg, val);
26 }
27 
mv88e6390_serdes_read(struct mv88e6xxx_chip * chip,int lane,int device,int reg,u16 * val)28 static int mv88e6390_serdes_read(struct mv88e6xxx_chip *chip,
29 				 int lane, int device, int reg, u16 *val)
30 {
31 	return mv88e6xxx_phy_read_c45(chip, lane, device, reg, val);
32 }
33 
mv88e6xxx_pcs_decode_state(struct device * dev,u16 bmsr,u16 lpa,u16 status,struct phylink_link_state * state)34 int mv88e6xxx_pcs_decode_state(struct device *dev, u16 bmsr, u16 lpa,
35 			       u16 status, struct phylink_link_state *state)
36 {
37 	state->link = false;
38 
39 	/* If the BMSR reports that the link had failed, report this to
40 	 * phylink.
41 	 */
42 	if (!(bmsr & BMSR_LSTATUS))
43 		return 0;
44 
45 	state->link = !!(status & MV88E6390_SGMII_PHY_STATUS_LINK);
46 	state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE);
47 
48 	if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) {
49 		/* The Spped and Duplex Resolved register is 1 if AN is enabled
50 		 * and complete, or if AN is disabled. So with disabled AN we
51 		 * still get here on link up.
52 		 */
53 		state->duplex = status &
54 				MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL ?
55 			                         DUPLEX_FULL : DUPLEX_HALF;
56 
57 		if (status & MV88E6390_SGMII_PHY_STATUS_TX_PAUSE)
58 			state->pause |= MLO_PAUSE_TX;
59 		if (status & MV88E6390_SGMII_PHY_STATUS_RX_PAUSE)
60 			state->pause |= MLO_PAUSE_RX;
61 
62 		switch (status & MV88E6390_SGMII_PHY_STATUS_SPEED_MASK) {
63 		case MV88E6390_SGMII_PHY_STATUS_SPEED_1000:
64 			if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
65 				state->speed = SPEED_2500;
66 			else
67 				state->speed = SPEED_1000;
68 			break;
69 		case MV88E6390_SGMII_PHY_STATUS_SPEED_100:
70 			state->speed = SPEED_100;
71 			break;
72 		case MV88E6390_SGMII_PHY_STATUS_SPEED_10:
73 			state->speed = SPEED_10;
74 			break;
75 		default:
76 			dev_err(dev, "invalid PHY speed\n");
77 			return -EINVAL;
78 		}
79 	} else if (state->link &&
80 		   state->interface != PHY_INTERFACE_MODE_SGMII) {
81 		/* If Speed and Duplex Resolved register is 0 and link is up, it
82 		 * means that AN was enabled, but link partner had it disabled
83 		 * and the PHY invoked the Auto-Negotiation Bypass feature and
84 		 * linked anyway.
85 		 */
86 		state->duplex = DUPLEX_FULL;
87 		if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
88 			state->speed = SPEED_2500;
89 		else
90 			state->speed = SPEED_1000;
91 	} else {
92 		state->link = false;
93 	}
94 
95 	if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
96 		mii_lpa_mod_linkmode_x(state->lp_advertising, lpa,
97 				       ETHTOOL_LINK_MODE_2500baseX_Full_BIT);
98 	else if (state->interface == PHY_INTERFACE_MODE_1000BASEX)
99 		mii_lpa_mod_linkmode_x(state->lp_advertising, lpa,
100 				       ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
101 
102 	return 0;
103 }
104 
105 struct mv88e6352_serdes_hw_stat {
106 	char string[ETH_GSTRING_LEN];
107 	int sizeof_stat;
108 	int reg;
109 };
110 
111 static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = {
112 	{ "serdes_fibre_rx_error", 16, 21 },
113 	{ "serdes_PRBS_error", 32, 24 },
114 };
115 
mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip * chip,int port)116 int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port)
117 {
118 	int err;
119 
120 	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
121 	if (err <= 0)
122 		return err;
123 
124 	return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
125 }
126 
mv88e6352_serdes_get_strings(struct mv88e6xxx_chip * chip,int port,uint8_t ** data)127 int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip, int port,
128 				 uint8_t **data)
129 {
130 	struct mv88e6352_serdes_hw_stat *stat;
131 	int err, i;
132 
133 	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
134 	if (err <= 0)
135 		return err;
136 
137 	for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
138 		stat = &mv88e6352_serdes_hw_stats[i];
139 		ethtool_puts(data, stat->string);
140 	}
141 	return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
142 }
143 
mv88e6352_serdes_get_stat(struct mv88e6xxx_chip * chip,struct mv88e6352_serdes_hw_stat * stat)144 static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip,
145 					  struct mv88e6352_serdes_hw_stat *stat)
146 {
147 	u64 val = 0;
148 	u16 reg;
149 	int err;
150 
151 	err = mv88e6352_serdes_read(chip, stat->reg, &reg);
152 	if (err) {
153 		dev_err(chip->dev, "failed to read statistic\n");
154 		return 0;
155 	}
156 
157 	val = reg;
158 
159 	if (stat->sizeof_stat == 32) {
160 		err = mv88e6352_serdes_read(chip, stat->reg + 1, &reg);
161 		if (err) {
162 			dev_err(chip->dev, "failed to read statistic\n");
163 			return 0;
164 		}
165 		val = val << 16 | reg;
166 	}
167 
168 	return val;
169 }
170 
mv88e6352_serdes_get_stats(struct mv88e6xxx_chip * chip,int port,uint64_t * data)171 size_t mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
172 				  uint64_t *data)
173 {
174 	struct mv88e6xxx_port *mv88e6xxx_port = &chip->ports[port];
175 	struct mv88e6352_serdes_hw_stat *stat;
176 	int i, err;
177 	u64 value;
178 
179 	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
180 	if (err <= 0)
181 		return 0;
182 
183 	BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) >
184 		     ARRAY_SIZE(mv88e6xxx_port->serdes_stats));
185 
186 	for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
187 		stat = &mv88e6352_serdes_hw_stats[i];
188 		value = mv88e6352_serdes_get_stat(chip, stat);
189 		mv88e6xxx_port->serdes_stats[i] += value;
190 		data[i] = mv88e6xxx_port->serdes_stats[i];
191 	}
192 
193 	return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
194 }
195 
mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip * chip,int port)196 unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port)
197 {
198 	return irq_find_mapping(chip->g2_irq.domain, MV88E6352_SERDES_IRQ);
199 }
200 
mv88e6352_serdes_get_regs_len(struct mv88e6xxx_chip * chip,int port)201 int mv88e6352_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port)
202 {
203 	int err;
204 
205 	mv88e6xxx_reg_lock(chip);
206 	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
207 	mv88e6xxx_reg_unlock(chip);
208 	if (err <= 0)
209 		return err;
210 
211 	return 32 * sizeof(u16);
212 }
213 
mv88e6352_serdes_get_regs(struct mv88e6xxx_chip * chip,int port,void * _p)214 void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p)
215 {
216 	u16 *p = _p;
217 	u16 reg;
218 	int err;
219 	int i;
220 
221 	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
222 	if (err <= 0)
223 		return;
224 
225 	for (i = 0 ; i < 32; i++) {
226 		err = mv88e6352_serdes_read(chip, i, &reg);
227 		if (!err)
228 			p[i] = reg;
229 	}
230 }
231 
mv88e6341_serdes_get_lane(struct mv88e6xxx_chip * chip,int port)232 int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
233 {
234 	u8 cmode = chip->ports[port].cmode;
235 	int lane = -ENODEV;
236 
237 	switch (port) {
238 	case 5:
239 		if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
240 		    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
241 		    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
242 			lane = MV88E6341_PORT5_LANE;
243 		break;
244 	}
245 
246 	return lane;
247 }
248 
mv88e6390_serdes_get_lane(struct mv88e6xxx_chip * chip,int port)249 int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
250 {
251 	u8 cmode = chip->ports[port].cmode;
252 	int lane = -ENODEV;
253 
254 	switch (port) {
255 	case 9:
256 		if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
257 		    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
258 		    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
259 			lane = MV88E6390_PORT9_LANE0;
260 		break;
261 	case 10:
262 		if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
263 		    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
264 		    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
265 			lane = MV88E6390_PORT10_LANE0;
266 		break;
267 	}
268 
269 	return lane;
270 }
271 
mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip * chip,int port)272 int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
273 {
274 	u8 cmode_port = chip->ports[port].cmode;
275 	u8 cmode_port10 = chip->ports[10].cmode;
276 	u8 cmode_port9 = chip->ports[9].cmode;
277 	int lane = -ENODEV;
278 
279 	switch (port) {
280 	case 2:
281 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
282 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
283 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
284 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
285 				lane = MV88E6390_PORT9_LANE1;
286 		break;
287 	case 3:
288 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
289 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
290 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
291 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
292 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
293 				lane = MV88E6390_PORT9_LANE2;
294 		break;
295 	case 4:
296 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
297 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
298 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
299 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
300 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
301 				lane = MV88E6390_PORT9_LANE3;
302 		break;
303 	case 5:
304 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
305 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
306 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
307 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
308 				lane = MV88E6390_PORT10_LANE1;
309 		break;
310 	case 6:
311 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
312 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
313 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
314 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
315 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
316 				lane = MV88E6390_PORT10_LANE2;
317 		break;
318 	case 7:
319 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
320 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
321 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
322 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
323 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
324 				lane = MV88E6390_PORT10_LANE3;
325 		break;
326 	case 9:
327 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
328 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
329 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
330 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
331 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
332 			lane = MV88E6390_PORT9_LANE0;
333 		break;
334 	case 10:
335 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
336 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
337 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
338 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
339 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
340 			lane = MV88E6390_PORT10_LANE0;
341 		break;
342 	}
343 
344 	return lane;
345 }
346 
347 /* Only Ports 0, 9 and 10 have SERDES lanes. Return the SERDES lane address
348  * a port is using else Returns -ENODEV.
349  */
mv88e6393x_serdes_get_lane(struct mv88e6xxx_chip * chip,int port)350 int mv88e6393x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
351 {
352 	u8 cmode = chip->ports[port].cmode;
353 	int lane = -ENODEV;
354 
355 	if (port != 0 && port != 9 && port != 10)
356 		return -EOPNOTSUPP;
357 
358 	if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
359 	    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
360 	    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
361 	    cmode == MV88E6393X_PORT_STS_CMODE_5GBASER ||
362 	    cmode == MV88E6393X_PORT_STS_CMODE_10GBASER ||
363 	    cmode == MV88E6393X_PORT_STS_CMODE_USXGMII)
364 		lane = port;
365 
366 	return lane;
367 }
368 
369 struct mv88e6390_serdes_hw_stat {
370 	char string[ETH_GSTRING_LEN];
371 	int reg;
372 };
373 
374 static struct mv88e6390_serdes_hw_stat mv88e6390_serdes_hw_stats[] = {
375 	{ "serdes_rx_pkts", 0xf021 },
376 	{ "serdes_rx_bytes", 0xf024 },
377 	{ "serdes_rx_pkts_error", 0xf027 },
378 };
379 
mv88e6390_serdes_get_sset_count(struct mv88e6xxx_chip * chip,int port)380 int mv88e6390_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port)
381 {
382 	if (mv88e6xxx_serdes_get_lane(chip, port) < 0)
383 		return 0;
384 
385 	return ARRAY_SIZE(mv88e6390_serdes_hw_stats);
386 }
387 
mv88e6390_serdes_get_strings(struct mv88e6xxx_chip * chip,int port,uint8_t ** data)388 int mv88e6390_serdes_get_strings(struct mv88e6xxx_chip *chip, int port,
389 				 uint8_t **data)
390 {
391 	struct mv88e6390_serdes_hw_stat *stat;
392 	int i;
393 
394 	if (mv88e6xxx_serdes_get_lane(chip, port) < 0)
395 		return 0;
396 
397 	for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) {
398 		stat = &mv88e6390_serdes_hw_stats[i];
399 		ethtool_puts(data, stat->string);
400 	}
401 	return ARRAY_SIZE(mv88e6390_serdes_hw_stats);
402 }
403 
mv88e6390_serdes_get_stat(struct mv88e6xxx_chip * chip,int lane,struct mv88e6390_serdes_hw_stat * stat)404 static uint64_t mv88e6390_serdes_get_stat(struct mv88e6xxx_chip *chip, int lane,
405 					  struct mv88e6390_serdes_hw_stat *stat)
406 {
407 	u16 reg[3];
408 	int err, i;
409 
410 	for (i = 0; i < 3; i++) {
411 		err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
412 					    stat->reg + i, &reg[i]);
413 		if (err) {
414 			dev_err(chip->dev, "failed to read statistic\n");
415 			return 0;
416 		}
417 	}
418 
419 	return reg[0] | ((u64)reg[1] << 16) | ((u64)reg[2] << 32);
420 }
421 
mv88e6390_serdes_get_stats(struct mv88e6xxx_chip * chip,int port,uint64_t * data)422 size_t mv88e6390_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
423 				  uint64_t *data)
424 {
425 	struct mv88e6390_serdes_hw_stat *stat;
426 	int lane;
427 	int i;
428 
429 	lane = mv88e6xxx_serdes_get_lane(chip, port);
430 	if (lane < 0)
431 		return 0;
432 
433 	for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) {
434 		stat = &mv88e6390_serdes_hw_stats[i];
435 		data[i] = mv88e6390_serdes_get_stat(chip, lane, stat);
436 	}
437 
438 	return ARRAY_SIZE(mv88e6390_serdes_hw_stats);
439 }
440 
mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip * chip,int port)441 unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port)
442 {
443 	return irq_find_mapping(chip->g2_irq.domain, port);
444 }
445 
446 static const u16 mv88e6390_serdes_regs[] = {
447 	/* SERDES common registers */
448 	0xf00a, 0xf00b, 0xf00c,
449 	0xf010, 0xf011, 0xf012, 0xf013,
450 	0xf016, 0xf017, 0xf018,
451 	0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f,
452 	0xf020, 0xf021, 0xf022, 0xf023, 0xf024, 0xf025, 0xf026, 0xf027,
453 	0xf028, 0xf029,
454 	0xf030, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037,
455 	0xf038, 0xf039,
456 	/* SGMII */
457 	0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007,
458 	0x2008,
459 	0x200f,
460 	0xa000, 0xa001, 0xa002, 0xa003,
461 	/* 10Gbase-X */
462 	0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007,
463 	0x1008,
464 	0x100e, 0x100f,
465 	0x1018, 0x1019,
466 	0x9000, 0x9001, 0x9002, 0x9003, 0x9004,
467 	0x9006,
468 	0x9010, 0x9011, 0x9012, 0x9013, 0x9014, 0x9015, 0x9016,
469 	/* 10Gbase-R */
470 	0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027,
471 	0x1028, 0x1029, 0x102a, 0x102b,
472 };
473 
mv88e6390_serdes_get_regs_len(struct mv88e6xxx_chip * chip,int port)474 int mv88e6390_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port)
475 {
476 	if (mv88e6xxx_serdes_get_lane(chip, port) < 0)
477 		return 0;
478 
479 	return ARRAY_SIZE(mv88e6390_serdes_regs) * sizeof(u16);
480 }
481 
mv88e6390_serdes_get_regs(struct mv88e6xxx_chip * chip,int port,void * _p)482 void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p)
483 {
484 	u16 *p = _p;
485 	int lane;
486 	u16 reg;
487 	int err;
488 	int i;
489 
490 	lane = mv88e6xxx_serdes_get_lane(chip, port);
491 	if (lane < 0)
492 		return;
493 
494 	for (i = 0 ; i < ARRAY_SIZE(mv88e6390_serdes_regs); i++) {
495 		err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
496 					    mv88e6390_serdes_regs[i], &reg);
497 		if (!err)
498 			p[i] = reg;
499 	}
500 }
501