1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28#include <npi_ipp.h>
29
30uint64_t ipp_fzc_offset[] = {
31		IPP_CONFIG_REG,
32		IPP_DISCARD_PKT_CNT_REG,
33		IPP_BAD_CKSUM_ERR_CNT_REG,
34		IPP_ECC_ERR_COUNTER_REG,
35		IPP_INT_STATUS_REG,
36		IPP_INT_MASK_REG,
37		IPP_PFIFO_RD_DATA0_REG,
38		IPP_PFIFO_RD_DATA1_REG,
39		IPP_PFIFO_RD_DATA2_REG,
40		IPP_PFIFO_RD_DATA3_REG,
41		IPP_PFIFO_RD_DATA4_REG,
42		IPP_PFIFO_WR_DATA0_REG,
43		IPP_PFIFO_WR_DATA1_REG,
44		IPP_PFIFO_WR_DATA2_REG,
45		IPP_PFIFO_WR_DATA3_REG,
46		IPP_PFIFO_WR_DATA4_REG,
47		IPP_PFIFO_RD_PTR_REG,
48		IPP_PFIFO_WR_PTR_REG,
49		IPP_DFIFO_RD_DATA0_REG,
50		IPP_DFIFO_RD_DATA1_REG,
51		IPP_DFIFO_RD_DATA2_REG,
52		IPP_DFIFO_RD_DATA3_REG,
53		IPP_DFIFO_RD_DATA4_REG,
54		IPP_DFIFO_WR_DATA0_REG,
55		IPP_DFIFO_WR_DATA1_REG,
56		IPP_DFIFO_WR_DATA2_REG,
57		IPP_DFIFO_WR_DATA3_REG,
58		IPP_DFIFO_WR_DATA4_REG,
59		IPP_DFIFO_RD_PTR_REG,
60		IPP_DFIFO_WR_PTR_REG,
61		IPP_STATE_MACHINE_REG,
62		IPP_CKSUM_STATUS_REG,
63		IPP_FFLP_CKSUM_INFO_REG,
64		IPP_DEBUG_SELECT_REG,
65		IPP_DFIFO_ECC_SYNDROME_REG,
66		IPP_DFIFO_EOPM_RD_PTR_REG,
67		IPP_ECC_CTRL_REG
68};
69
70const char *ipp_fzc_name[] = {
71		"IPP_CONFIG_REG",
72		"IPP_DISCARD_PKT_CNT_REG",
73		"IPP_BAD_CKSUM_ERR_CNT_REG",
74		"IPP_ECC_ERR_COUNTER_REG",
75		"IPP_INT_STATUS_REG",
76		"IPP_INT_MASK_REG",
77		"IPP_PFIFO_RD_DATA0_REG",
78		"IPP_PFIFO_RD_DATA1_REG",
79		"IPP_PFIFO_RD_DATA2_REG",
80		"IPP_PFIFO_RD_DATA3_REG",
81		"IPP_PFIFO_RD_DATA4_REG",
82		"IPP_PFIFO_WR_DATA0_REG",
83		"IPP_PFIFO_WR_DATA1_REG",
84		"IPP_PFIFO_WR_DATA2_REG",
85		"IPP_PFIFO_WR_DATA3_REG",
86		"IPP_PFIFO_WR_DATA4_REG",
87		"IPP_PFIFO_RD_PTR_REG",
88		"IPP_PFIFO_WR_PTR_REG",
89		"IPP_DFIFO_RD_DATA0_REG",
90		"IPP_DFIFO_RD_DATA1_REG",
91		"IPP_DFIFO_RD_DATA2_REG",
92		"IPP_DFIFO_RD_DATA3_REG",
93		"IPP_DFIFO_RD_DATA4_REG",
94		"IPP_DFIFO_WR_DATA0_REG",
95		"IPP_DFIFO_WR_DATA1_REG",
96		"IPP_DFIFO_WR_DATA2_REG",
97		"IPP_DFIFO_WR_DATA3_REG",
98		"IPP_DFIFO_WR_DATA4_REG",
99		"IPP_DFIFO_RD_PTR_REG",
100		"IPP_DFIFO_WR_PTR_REG",
101		"IPP_STATE_MACHINE_REG",
102		"IPP_CKSUM_STATUS_REG",
103		"IPP_FFLP_CKSUM_INFO_REG",
104		"IPP_DEBUG_SELECT_REG",
105		"IPP_DFIFO_ECC_SYNDROME_REG",
106		"IPP_DFIFO_EOPM_RD_PTR_REG",
107		"IPP_ECC_CTRL_REG",
108};
109
110npi_status_t
111npi_ipp_dump_regs(npi_handle_t handle, uint8_t port)
112{
113	uint64_t		value, offset;
114	int 			num_regs, i;
115
116	ASSERT(IS_PORT_NUM_VALID(port));
117
118	NPI_REG_DUMP_MSG((handle.function, NPI_REG_CTL,
119	    "\nIPP PORT Register Dump for port %d\n", port));
120
121	num_regs = sizeof (ipp_fzc_offset) / sizeof (uint64_t);
122	for (i = 0; i < num_regs; i++) {
123		offset = IPP_REG_ADDR(port, ipp_fzc_offset[i]);
124#if defined(__i386)
125		NXGE_REG_RD64(handle, (uint32_t)offset, &value);
126#else
127		NXGE_REG_RD64(handle, offset, &value);
128#endif
129		NPI_REG_DUMP_MSG((handle.function, NPI_REG_CTL, "0x%08llx "
130		    "%s\t 0x%08llx \n",
131		    offset, ipp_fzc_name[i], value));
132	}
133
134	NPI_REG_DUMP_MSG((handle.function, NPI_REG_CTL,
135	    "\n IPP FZC Register Dump for port %d done\n", port));
136
137	return (NPI_SUCCESS);
138}
139
140void
141npi_ipp_read_regs(npi_handle_t handle, uint8_t port)
142{
143	uint64_t		value, offset;
144	int 			num_regs, i;
145
146	ASSERT(IS_PORT_NUM_VALID(port));
147
148	NPI_DEBUG_MSG((handle.function, NPI_IPP_CTL,
149	    "\nIPP PORT Register read (to clear) for port %d\n", port));
150
151	num_regs = sizeof (ipp_fzc_offset) / sizeof (uint64_t);
152	for (i = 0; i < num_regs; i++) {
153		offset = IPP_REG_ADDR(port, ipp_fzc_offset[i]);
154#if defined(__i386)
155		NXGE_REG_RD64(handle, (uint32_t)offset, &value);
156#else
157		NXGE_REG_RD64(handle, offset, &value);
158#endif
159	}
160
161}
162
163/*
164 * IPP Reset Routine
165 */
166npi_status_t
167npi_ipp_reset(npi_handle_t handle, uint8_t portn)
168{
169	uint64_t val = 0;
170	uint32_t cnt = MAX_PIO_RETRIES;
171
172	ASSERT(IS_PORT_NUM_VALID(portn));
173
174	IPP_REG_RD(handle, portn, IPP_CONFIG_REG, &val);
175	val |= IPP_SOFT_RESET;
176	IPP_REG_WR(handle, portn, IPP_CONFIG_REG, val);
177
178	do {
179		NXGE_DELAY(IPP_RESET_WAIT);
180		IPP_REG_RD(handle, portn, IPP_CONFIG_REG, &val);
181		cnt--;
182	} while (((val & IPP_SOFT_RESET) != 0) && (cnt > 0));
183
184	if (cnt == 0) {
185		NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
186		    " npi_ipp_reset"
187		    " HW Error: IPP_RESET  <0x%x>", val));
188		return (NPI_FAILURE | NPI_IPP_RESET_FAILED(portn));
189	}
190
191	return (NPI_SUCCESS);
192}
193
194
195/*
196 * IPP Configuration Routine
197 */
198npi_status_t
199npi_ipp_config(npi_handle_t handle, config_op_t op, uint8_t portn,
200		ipp_config_t config)
201{
202	uint64_t val = 0;
203
204	ASSERT(IS_PORT_NUM_VALID(portn));
205
206	switch (op) {
207
208	case ENABLE:
209	case DISABLE:
210		if ((config == 0) || ((config & ~CFG_IPP_ALL) != 0)) {
211			NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
212			    " npi_ipp_config",
213			    " Invalid Input config <0x%x>",
214			    config));
215			return (NPI_FAILURE | NPI_IPP_CONFIG_INVALID(portn));
216		}
217
218		IPP_REG_RD(handle, portn, IPP_CONFIG_REG, &val);
219
220		if (op == ENABLE)
221			val |= config;
222		else
223			val &= ~config;
224		break;
225
226	case INIT:
227		if ((config & ~CFG_IPP_ALL) != 0) {
228			NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
229			    " npi_ipp_config"
230			    " Invalid Input config <0x%x>",
231			    config));
232			return (NPI_FAILURE | NPI_IPP_CONFIG_INVALID(portn));
233		}
234		IPP_REG_RD(handle, portn, IPP_CONFIG_REG, &val);
235
236
237		val &= (IPP_IP_MAX_PKT_BYTES_MASK);
238		val |= config;
239		break;
240
241	default:
242		NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
243		    " npi_ipp_config"
244		    " Invalid Input op <0x%x>", op));
245		return (NPI_FAILURE | NPI_IPP_OPCODE_INVALID(portn));
246	}
247
248	IPP_REG_WR(handle, portn, IPP_CONFIG_REG, val);
249	return (NPI_SUCCESS);
250}
251
252npi_status_t
253npi_ipp_set_max_pktsize(npi_handle_t handle, uint8_t portn, uint32_t bytes)
254{
255	uint64_t val = 0;
256
257	ASSERT(IS_PORT_NUM_VALID(portn));
258
259	if (bytes > IPP_IP_MAX_PKT_BYTES_MASK) {
260		NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
261		    " npi_ipp_set_max_pktsize"
262		    " Invalid Input Max bytes <0x%x>",
263		    bytes));
264		return (NPI_FAILURE | NPI_IPP_MAX_PKT_BYTES_INVALID(portn));
265	}
266
267	IPP_REG_RD(handle, portn, IPP_CONFIG_REG, &val);
268	val &= ~(IPP_IP_MAX_PKT_BYTES_MASK << IPP_IP_MAX_PKT_BYTES_SHIFT);
269
270	val |= (bytes << IPP_IP_MAX_PKT_BYTES_SHIFT);
271	IPP_REG_WR(handle, portn, IPP_CONFIG_REG, val);
272
273	return (NPI_SUCCESS);
274}
275
276/*
277 * IPP Interrupt Configuration Routine
278 */
279npi_status_t
280npi_ipp_iconfig(npi_handle_t handle, config_op_t op, uint8_t portn,
281		ipp_iconfig_t iconfig)
282{
283	uint64_t val = 0;
284
285	ASSERT(IS_PORT_NUM_VALID(portn));
286
287	switch (op) {
288	case ENABLE:
289	case DISABLE:
290
291		if ((iconfig == 0) || ((iconfig & ~ICFG_IPP_ALL) != 0)) {
292			NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
293			    " npi_ipp_iconfig"
294			    " Invalid Input iconfig <0x%x>",
295			    iconfig));
296			return (NPI_FAILURE | NPI_IPP_CONFIG_INVALID(portn));
297		}
298
299		IPP_REG_RD(handle, portn, IPP_INT_MASK_REG, &val);
300		if (op == ENABLE)
301			val &= ~iconfig;
302		else
303			val |= iconfig;
304		IPP_REG_WR(handle, portn, IPP_INT_MASK_REG, val);
305
306		break;
307	case INIT:
308
309		if ((iconfig & ~ICFG_IPP_ALL) != 0) {
310			NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
311			    " npi_ipp_iconfig"
312			    " Invalid Input iconfig <0x%x>",
313			    iconfig));
314			return (NPI_FAILURE | NPI_IPP_CONFIG_INVALID(portn));
315		}
316		IPP_REG_WR(handle, portn, IPP_INT_MASK_REG, ~iconfig);
317
318		break;
319	default:
320		NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
321		    " npi_ipp_iconfig"
322		    " Invalid Input iconfig <0x%x>",
323		    iconfig));
324		return (NPI_FAILURE | NPI_IPP_OPCODE_INVALID(portn));
325	}
326
327	return (NPI_SUCCESS);
328}
329
330npi_status_t
331npi_ipp_get_status(npi_handle_t handle, uint8_t portn, ipp_status_t *status)
332{
333	uint64_t val;
334
335	ASSERT(IS_PORT_NUM_VALID(portn));
336
337	IPP_REG_RD(handle, portn, IPP_INT_STATUS_REG, &val);
338
339	status->value = val;
340	return (NPI_SUCCESS);
341}
342
343npi_status_t
344npi_ipp_get_pfifo_rd_ptr(npi_handle_t handle, uint8_t portn, uint16_t *rd_ptr)
345{
346	uint64_t value;
347
348	ASSERT(IS_PORT_NUM_VALID(portn));
349
350	IPP_REG_RD(handle, portn, IPP_PFIFO_RD_PTR_REG, &value);
351	*rd_ptr = value & 0xfff;
352	return (NPI_SUCCESS);
353}
354
355npi_status_t
356npi_ipp_get_pfifo_wr_ptr(npi_handle_t handle, uint8_t portn, uint16_t *wr_ptr)
357{
358	uint64_t value;
359
360	ASSERT(IS_PORT_NUM_VALID(portn));
361
362	IPP_REG_RD(handle, portn, IPP_PFIFO_WR_PTR_REG, &value);
363	*wr_ptr = value & 0xfff;
364	return (NPI_SUCCESS);
365}
366
367npi_status_t
368npi_ipp_get_dfifo_rd_ptr(npi_handle_t handle, uint8_t portn, uint16_t *rd_ptr)
369{
370	uint64_t value;
371
372	ASSERT(IS_PORT_NUM_VALID(portn));
373
374	IPP_REG_RD(handle, portn, IPP_DFIFO_RD_PTR_REG, &value);
375	*rd_ptr = (uint16_t)(value & ((portn < 2) ? IPP_XMAC_DFIFO_PTR_MASK :
376	    IPP_BMAC_DFIFO_PTR_MASK));
377	return (NPI_SUCCESS);
378}
379
380npi_status_t
381npi_ipp_get_dfifo_wr_ptr(npi_handle_t handle, uint8_t portn, uint16_t *wr_ptr)
382{
383	uint64_t value;
384
385	ASSERT(IS_PORT_NUM_VALID(portn));
386
387	IPP_REG_RD(handle, portn, IPP_DFIFO_WR_PTR_REG, &value);
388	*wr_ptr = (uint16_t)(value & ((portn < 2) ? IPP_XMAC_DFIFO_PTR_MASK :
389	    IPP_BMAC_DFIFO_PTR_MASK));
390	return (NPI_SUCCESS);
391}
392
393npi_status_t
394npi_ipp_write_pfifo(npi_handle_t handle, uint8_t portn, uint8_t addr,
395		uint32_t d0, uint32_t d1, uint32_t d2, uint32_t d3, uint32_t d4)
396{
397	uint64_t val;
398
399	ASSERT(IS_PORT_NUM_VALID(portn));
400
401	if (addr >= 64) {
402		NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
403		    " npi_ipp_write_pfifo"
404		    " Invalid PFIFO address <0x%x>", addr));
405		return (NPI_FAILURE | NPI_IPP_FIFO_ADDR_INVALID(portn));
406	}
407
408	IPP_REG_RD(handle, portn, IPP_CONFIG_REG, &val);
409	val |= IPP_PRE_FIFO_PIO_WR_EN;
410	IPP_REG_WR(handle, portn, IPP_CONFIG_REG, val);
411
412	IPP_REG_WR(handle, portn, IPP_PFIFO_WR_PTR_REG, addr);
413	IPP_REG_WR(handle, portn, IPP_PFIFO_WR_DATA0_REG, d0);
414	IPP_REG_WR(handle, portn, IPP_PFIFO_WR_DATA1_REG, d1);
415	IPP_REG_WR(handle, portn, IPP_PFIFO_WR_DATA2_REG, d2);
416	IPP_REG_WR(handle, portn, IPP_PFIFO_WR_DATA3_REG, d3);
417	IPP_REG_WR(handle, portn, IPP_PFIFO_WR_DATA4_REG, d4);
418
419	val &= ~IPP_PRE_FIFO_PIO_WR_EN;
420	IPP_REG_WR(handle, portn, IPP_CONFIG_REG, val);
421
422	return (NPI_SUCCESS);
423}
424
425npi_status_t
426npi_ipp_read_pfifo(npi_handle_t handle, uint8_t portn, uint8_t addr,
427		uint32_t *d0, uint32_t *d1, uint32_t *d2, uint32_t *d3,
428		uint32_t *d4)
429{
430	ASSERT(IS_PORT_NUM_VALID(portn));
431
432	if (addr >= 64) {
433		NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
434		    " npi_ipp_read_pfifo"
435		    " Invalid PFIFO address <0x%x>", addr));
436		return (NPI_FAILURE | NPI_IPP_FIFO_ADDR_INVALID(portn));
437	}
438
439	IPP_REG_WR(handle, portn, IPP_PFIFO_RD_PTR_REG, addr);
440	IPP_REG_RD(handle, portn, IPP_PFIFO_RD_DATA0_REG, d0);
441	IPP_REG_RD(handle, portn, IPP_PFIFO_RD_DATA1_REG, d1);
442	IPP_REG_RD(handle, portn, IPP_PFIFO_RD_DATA2_REG, d2);
443	IPP_REG_RD(handle, portn, IPP_PFIFO_RD_DATA3_REG, d3);
444	IPP_REG_RD(handle, portn, IPP_PFIFO_RD_DATA4_REG, d4);
445
446	return (NPI_SUCCESS);
447}
448
449npi_status_t
450npi_ipp_write_dfifo(npi_handle_t handle, uint8_t portn, uint16_t addr,
451		uint32_t d0, uint32_t d1, uint32_t d2, uint32_t d3, uint32_t d4)
452{
453	uint64_t val;
454
455	ASSERT(IS_PORT_NUM_VALID(portn));
456
457	if (addr >= 2048) {
458		NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
459		    " npi_ipp_write_dfifo"
460		    " Invalid DFIFO address <0x%x>", addr));
461		return (NPI_FAILURE | NPI_IPP_FIFO_ADDR_INVALID(portn));
462	}
463
464	IPP_REG_RD(handle, portn, IPP_CONFIG_REG, &val);
465	val |= IPP_DFIFO_PIO_WR_EN;
466	IPP_REG_WR(handle, portn, IPP_CONFIG_REG, val);
467
468	IPP_REG_WR(handle, portn, IPP_DFIFO_WR_PTR_REG, addr);
469	IPP_REG_WR(handle, portn, IPP_DFIFO_WR_DATA0_REG, d0);
470	IPP_REG_WR(handle, portn, IPP_DFIFO_WR_DATA1_REG, d1);
471	IPP_REG_WR(handle, portn, IPP_DFIFO_WR_DATA2_REG, d2);
472	IPP_REG_WR(handle, portn, IPP_DFIFO_WR_DATA3_REG, d3);
473	IPP_REG_WR(handle, portn, IPP_DFIFO_WR_DATA4_REG, d4);
474
475	val &= ~IPP_DFIFO_PIO_WR_EN;
476	IPP_REG_WR(handle, portn, IPP_CONFIG_REG, val);
477
478	return (NPI_SUCCESS);
479}
480
481npi_status_t
482npi_ipp_read_dfifo(npi_handle_t handle, uint8_t portn, uint16_t addr,
483		uint32_t *d0, uint32_t *d1, uint32_t *d2, uint32_t *d3,
484		uint32_t *d4)
485{
486	ASSERT(IS_PORT_NUM_VALID(portn));
487
488	if (addr >= 2048) {
489		NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
490		    " npi_ipp_read_dfifo"
491		    " Invalid DFIFO address <0x%x>", addr));
492		return (NPI_FAILURE | NPI_IPP_FIFO_ADDR_INVALID(portn));
493	}
494
495	IPP_REG_WR(handle, portn, IPP_DFIFO_RD_PTR_REG, addr);
496	IPP_REG_RD(handle, portn, IPP_DFIFO_RD_DATA0_REG, d0);
497	IPP_REG_RD(handle, portn, IPP_DFIFO_RD_DATA1_REG, d1);
498	IPP_REG_RD(handle, portn, IPP_DFIFO_RD_DATA2_REG, d2);
499	IPP_REG_RD(handle, portn, IPP_DFIFO_RD_DATA3_REG, d3);
500	IPP_REG_RD(handle, portn, IPP_DFIFO_RD_DATA4_REG, d4);
501
502	return (NPI_SUCCESS);
503}
504
505npi_status_t
506npi_ipp_get_ecc_syndrome(npi_handle_t handle, uint8_t portn, uint16_t *syndrome)
507{
508	uint64_t val;
509
510	ASSERT(IS_PORT_NUM_VALID(portn));
511
512	IPP_REG_RD(handle, portn, IPP_DFIFO_ECC_SYNDROME_REG, &val);
513
514	*syndrome = (uint16_t)val;
515	return (NPI_SUCCESS);
516}
517
518npi_status_t
519npi_ipp_get_dfifo_eopm_rdptr(npi_handle_t handle, uint8_t portn,
520							uint16_t *rdptr)
521{
522	uint64_t val;
523
524	ASSERT(IS_PORT_NUM_VALID(portn));
525
526	IPP_REG_RD(handle, portn, IPP_DFIFO_EOPM_RD_PTR_REG, &val);
527
528	*rdptr = (uint16_t)val;
529	return (NPI_SUCCESS);
530}
531
532npi_status_t
533npi_ipp_get_state_mach(npi_handle_t handle, uint8_t portn, uint32_t *sm)
534{
535	uint64_t val;
536
537	ASSERT(IS_PORT_NUM_VALID(portn));
538
539	IPP_REG_RD(handle, portn, IPP_STATE_MACHINE_REG, &val);
540
541	*sm = (uint32_t)val;
542	return (NPI_SUCCESS);
543}
544
545npi_status_t
546npi_ipp_get_ecc_err_count(npi_handle_t handle, uint8_t portn, uint8_t *err_cnt)
547{
548	ASSERT(IS_PORT_NUM_VALID(portn));
549
550	IPP_REG_RD(handle, portn, IPP_ECC_ERR_COUNTER_REG, err_cnt);
551
552	return (NPI_SUCCESS);
553}
554
555npi_status_t
556npi_ipp_get_pkt_dis_count(npi_handle_t handle, uint8_t portn, uint16_t *dis_cnt)
557{
558	ASSERT(IS_PORT_NUM_VALID(portn));
559
560	IPP_REG_RD(handle, portn, IPP_DISCARD_PKT_CNT_REG, dis_cnt);
561
562	return (NPI_SUCCESS);
563}
564
565npi_status_t
566npi_ipp_get_cs_err_count(npi_handle_t handle, uint8_t portn, uint16_t *err_cnt)
567{
568	ASSERT(IS_PORT_NUM_VALID(portn));
569
570	IPP_REG_RD(handle, portn, IPP_BAD_CKSUM_ERR_CNT_REG, err_cnt);
571
572	return (NPI_SUCCESS);
573}
574