xref: /netbsd-src/sys/arch/mips/cavium/dev/octeon_fpa.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: octeon_fpa.c,v 1.1 2015/04/29 08:32:01 hikaru Exp $	*/
2 
3 /*
4  * Copyright (c) 2007 Internet Initiative Japan, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #undef	FPADEBUG
30 
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: octeon_fpa.c,v 1.1 2015/04/29 08:32:01 hikaru Exp $");
33 
34 #include "opt_octeon.h"
35 
36 #include "opt_octeon.h"
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/types.h>
41 #include <sys/malloc.h>
42 
43 #include <sys/bus.h>
44 #include <machine/locore.h>
45 #include <machine/vmparam.h>
46 
47 #include <mips/cavium/octeonvar.h>
48 #include <mips/cavium/include/iobusvar.h>
49 #include <mips/cavium/dev/octeon_fpavar.h>
50 #include <mips/cavium/dev/octeon_fpareg.h>
51 
52 #ifdef FPADEBUG
53 #define	DPRINTF(x)	printf x
54 #else
55 #define	DPRINTF(x)
56 #endif
57 
58 #define	_DMA_NSEGS	1
59 #define	_DMA_BUFLEN	0x01000000
60 
61 /* pool descriptor */
62 struct octeon_fpa_desc {
63 };
64 
65 struct octeon_fpa_softc {
66 	int			sc_initialized;
67 
68 	bus_space_tag_t		sc_regt;
69 	bus_space_handle_t	sc_regh;
70 
71 	bus_space_tag_t		sc_opst;
72 	bus_space_handle_t	sc_opsh;
73 
74 	bus_dma_tag_t		sc_dmat;
75 
76 	struct octeon_fpa_desc	sc_descs[8];
77 
78 #ifdef OCTEON_ETH_DEBUG
79 	struct evcnt		sc_ev_fpaq7perr;
80 	struct evcnt		sc_ev_fpaq7coff;
81 	struct evcnt		sc_ev_fpaq7und;
82 	struct evcnt		sc_ev_fpaq6perr;
83 	struct evcnt		sc_ev_fpaq6coff;
84 	struct evcnt		sc_ev_fpaq6und;
85 	struct evcnt		sc_ev_fpaq5perr;
86 	struct evcnt		sc_ev_fpaq5coff;
87 #endif
88 };
89 
90 void			octeon_fpa_bootstrap(struct octeon_config *);
91 void			octeon_fpa_reset(void);
92 void			octeon_fpa_int_enable(struct octeon_fpa_softc *, int);
93 void			octeon_fpa_buf_dma_alloc(struct octeon_fpa_buf *);
94 
95 static void		octeon_fpa_init(struct octeon_fpa_softc *);
96 #ifdef notyet
97 static uint64_t		octeon_fpa_iobdma(struct octeon_fpa_softc *, int, int);
98 #endif
99 
100 #ifdef OCTEON_ETH_DEBUG
101 void			octeon_fpa_intr_evcnt_attach(struct octeon_fpa_softc *);
102 void			octeon_fpa_intr_rml(void *);
103 #endif
104 
105 static struct octeon_fpa_softc	octeon_fpa_softc;
106 
107 /* ---- global functions */
108 
109 void
110 octeon_fpa_bootstrap(struct octeon_config *mcp)
111 {
112 	struct octeon_fpa_softc *sc = &octeon_fpa_softc;
113 
114 	sc->sc_regt = &mcp->mc_iobus_bust;
115 	sc->sc_opst = &mcp->mc_iobus_bust;
116 	sc->sc_dmat = &mcp->mc_iobus_dmat;
117 
118 	octeon_fpa_init(sc);
119 }
120 
121 void
122 octeon_fpa_reset(void)
123 {
124 	/* XXX */
125 }
126 
127 #ifdef OCTEON_ETH_DEBUG
128 int			octeon_fpa_intr_rml_verbose;
129 struct evcnt		octeon_fpa_intr_evcnt;
130 
131 static const struct octeon_evcnt_entry octeon_fpa_intr_evcnt_entries[] = {
132 #define	_ENTRY(name, type, parent, descr) \
133 	OCTEON_EVCNT_ENTRY(struct octeon_fpa_softc, name, type, parent, descr)
134 	_ENTRY(fpaq7perr,          MISC, NULL, "fpa q7 pointer"),
135 	_ENTRY(fpaq7coff,          MISC, NULL, "fpa q7 counter offset"),
136 	_ENTRY(fpaq7und,           MISC, NULL, "fpa q7 underflow"),
137 	_ENTRY(fpaq6perr,          MISC, NULL, "fpa q6 pointer"),
138 	_ENTRY(fpaq6coff,          MISC, NULL, "fpa q6 counter offset"),
139 	_ENTRY(fpaq6und,           MISC, NULL, "fpa q6 underflow"),
140 	_ENTRY(fpaq5perr,          MISC, NULL, "fpa q5 pointer"),
141 	_ENTRY(fpaq5coff,          MISC, NULL, "fpa q5 counter offset"),
142 #undef	_ENTRY
143 };
144 
145 void
146 octeon_fpa_intr_evcnt_attach(struct octeon_fpa_softc *sc)
147 {
148 	OCTEON_EVCNT_ATTACH_EVCNTS(sc, octeon_fpa_intr_evcnt_entries, "fpa0");
149 }
150 
151 void
152 octeon_fpa_intr_rml(void *arg)
153 {
154 	struct octeon_fpa_softc *sc;
155 	uint64_t reg;
156 
157 	octeon_fpa_intr_evcnt.ev_count++;
158 	sc = &octeon_fpa_softc;
159 	KASSERT(sc != NULL);
160 	reg = octeon_fpa_int_summary();
161 	if (octeon_fpa_intr_rml_verbose)
162 		printf("%s: FPA_INT_SUM=0x%016" PRIx64 "\n", __func__, reg);
163 	if (reg & FPA_INT_SUM_Q7_PERR)
164 		OCTEON_EVCNT_INC(sc, fpaq7perr);
165 	if (reg & FPA_INT_SUM_Q7_COFF)
166 		OCTEON_EVCNT_INC(sc, fpaq7coff);
167 	if (reg & FPA_INT_SUM_Q7_UND)
168 		OCTEON_EVCNT_INC(sc, fpaq7und);
169 	if (reg & FPA_INT_SUM_Q6_PERR)
170 		OCTEON_EVCNT_INC(sc, fpaq6perr);
171 	if (reg & FPA_INT_SUM_Q6_COFF)
172 		OCTEON_EVCNT_INC(sc, fpaq6coff);
173 	if (reg & FPA_INT_SUM_Q6_UND)
174 		OCTEON_EVCNT_INC(sc, fpaq6und);
175 	if (reg & FPA_INT_SUM_Q5_PERR)
176 		OCTEON_EVCNT_INC(sc, fpaq5perr);
177 	if (reg & FPA_INT_SUM_Q5_COFF)
178 		OCTEON_EVCNT_INC(sc, fpaq5coff);
179 }
180 
181 void
182 octeon_fpa_int_enable(struct octeon_fpa_softc *sc, int enable)
183 {
184 	const uint64_t int_xxx =
185 	    FPA_INT_ENB_Q7_PERR | FPA_INT_ENB_Q7_COFF | FPA_INT_ENB_Q7_UND |
186 	    FPA_INT_ENB_Q6_PERR | FPA_INT_ENB_Q6_COFF | FPA_INT_ENB_Q6_UND |
187 	    FPA_INT_ENB_Q5_PERR | FPA_INT_ENB_Q5_COFF | FPA_INT_ENB_Q5_UND |
188 	    FPA_INT_ENB_Q4_PERR | FPA_INT_ENB_Q4_COFF | FPA_INT_ENB_Q4_UND |
189 	    FPA_INT_ENB_Q3_PERR | FPA_INT_ENB_Q3_COFF | FPA_INT_ENB_Q3_UND |
190 	    FPA_INT_ENB_Q2_PERR | FPA_INT_ENB_Q2_COFF | FPA_INT_ENB_Q2_UND |
191 	    FPA_INT_ENB_Q1_PERR | FPA_INT_ENB_Q1_COFF | FPA_INT_ENB_Q1_UND |
192 	    FPA_INT_ENB_Q0_PERR | FPA_INT_ENB_Q0_COFF | FPA_INT_ENB_Q0_UND |
193 	    FPA_INT_ENB_FED1_DBE | FPA_INT_ENB_FED1_SBE |
194 	    FPA_INT_ENB_FED0_DBE | FPA_INT_ENB_FED0_SBE;
195 
196 	bus_space_write_8(sc->sc_regt, sc->sc_regh, FPA_INT_SUM_OFFSET,
197 	    int_xxx);
198 	if (enable)
199 		bus_space_write_8(sc->sc_regt, sc->sc_regh, FPA_INT_ENB_OFFSET,
200 		    int_xxx);
201 }
202 
203 uint64_t
204 octeon_fpa_int_summary(void)
205 {
206 	struct octeon_fpa_softc *sc = &octeon_fpa_softc;
207 	uint64_t summary;
208 
209 	summary = bus_space_read_8(sc->sc_regt, sc->sc_regh, FPA_INT_SUM_OFFSET);
210 	bus_space_write_8(sc->sc_regt, sc->sc_regh, FPA_INT_SUM_OFFSET, summary);
211 	return summary;
212 }
213 #endif
214 
215 int
216 octeon_fpa_buf_init(int poolno, size_t size, size_t nelems,
217     struct octeon_fpa_buf **rfb)
218 {
219 	struct octeon_fpa_softc *sc = &octeon_fpa_softc;
220 	struct octeon_fpa_buf *fb;
221 	int nsegs;
222 	paddr_t paddr;
223 
224 	nsegs = 1/* XXX */;
225 	fb = malloc(sizeof(*fb) + sizeof(*fb->fb_dma_segs) * nsegs, M_DEVBUF,
226 	    M_WAITOK | M_ZERO);
227 	if (fb == NULL)
228 		return 1;
229 	fb->fb_poolno = poolno;
230 	fb->fb_size = size;
231 	fb->fb_nelems = nelems;
232 	fb->fb_len = size * nelems;
233 	fb->fb_dmat = sc->sc_dmat;
234 	fb->fb_dma_segs = (void *)(fb + 1);
235 	fb->fb_dma_nsegs = nsegs;
236 
237 	octeon_fpa_buf_dma_alloc(fb);
238 
239 	for (paddr = fb->fb_paddr; paddr < fb->fb_paddr + fb->fb_len;
240 	    paddr += fb->fb_size)
241 		octeon_fpa_buf_put_paddr(fb, paddr);
242 
243 	*rfb = fb;
244 
245 	return 0;
246 }
247 
248 void *
249 octeon_fpa_buf_get(struct octeon_fpa_buf *fb)
250 {
251 	paddr_t paddr;
252 	vaddr_t addr;
253 
254 	paddr = octeon_fpa_buf_get_paddr(fb);
255 	if (paddr == 0)
256 		addr = 0;
257 	else
258 		addr = fb->fb_addr + (vaddr_t/* XXX */)(paddr - fb->fb_paddr);
259 	return (void *)addr;
260 }
261 
262 void
263 octeon_fpa_buf_dma_alloc(struct octeon_fpa_buf *fb)
264 {
265 	int status;
266 	int nsegs;
267 	void *va;
268 
269 	status = bus_dmamap_create(fb->fb_dmat, fb->fb_len,
270 	    fb->fb_len / PAGE_SIZE,	/* # of segments */
271 	    fb->fb_len,			/* we don't use s/g for FPA buf */
272 	    PAGE_SIZE,			/* OCTEON hates >PAGE_SIZE boundary */
273 	    0, &fb->fb_dmah);
274 	if (status != 0)
275 		panic("%s failed", "bus_dmamap_create");
276 
277 	status = bus_dmamem_alloc(fb->fb_dmat, fb->fb_len, 128, 0,
278 	    fb->fb_dma_segs, fb->fb_dma_nsegs, &nsegs, 0);
279 	if (status != 0 || fb->fb_dma_nsegs != nsegs)
280 		panic("%s failed", "bus_dmamem_alloc");
281 
282 	status = bus_dmamem_map(fb->fb_dmat, fb->fb_dma_segs, fb->fb_dma_nsegs,
283 	    fb->fb_len, &va, 0);
284 	if (status != 0)
285 		panic("%s failed", "bus_dmamem_map");
286 
287 	status = bus_dmamap_load(fb->fb_dmat, fb->fb_dmah, va, fb->fb_len,
288 	    NULL,		/* kernel */
289 	    0);
290 	if (status != 0)
291 		panic("%s failed", "bus_dmamap_load");
292 
293 	fb->fb_addr = (vaddr_t)va;
294 	fb->fb_paddr = fb->fb_dma_segs[0].ds_addr;
295 }
296 
297 uint64_t
298 octeon_fpa_query(int poolno)
299 {
300 	struct octeon_fpa_softc *sc = &octeon_fpa_softc;
301 
302 	return bus_space_read_8(sc->sc_regt, sc->sc_regh,
303 	    FPA_QUE0_AVAILABLE_OFFSET + sizeof(uint64_t) * poolno);
304 }
305 
306 /* ---- local functions */
307 
308 static inline void	octeon_fpa_init_bus(struct octeon_fpa_softc *);
309 static inline void	octeon_fpa_init_bus_space(struct octeon_fpa_softc *);
310 static inline void	octeon_fpa_init_regs(struct octeon_fpa_softc *);
311 
312 void
313 octeon_fpa_init(struct octeon_fpa_softc *sc)
314 {
315 	if (sc->sc_initialized != 0)
316 		panic("%s: already initialized", __func__);
317 	sc->sc_initialized = 1;
318 
319 	octeon_fpa_init_bus(sc);
320 	octeon_fpa_init_regs(sc);
321 #ifdef OCTEON_ETH_DEBUG
322 	octeon_fpa_int_enable(sc, 1);
323 	octeon_fpa_intr_evcnt_attach(sc);
324 #endif
325 }
326 
327 void
328 octeon_fpa_init_bus(struct octeon_fpa_softc *sc)
329 {
330 	octeon_fpa_init_bus_space(sc);
331 }
332 
333 void
334 octeon_fpa_init_bus_space(struct octeon_fpa_softc *sc)
335 {
336 	int status;
337 
338 	status = bus_space_map(sc->sc_regt, FPA_BASE, FPA_SIZE, 0, &sc->sc_regh);
339 	if (status != 0)
340 		panic("can't map %s space", "register");
341 
342 	status = bus_space_map(sc->sc_opst,
343 	    0x0001180028000000ULL/* XXX */, 0x0200/* XXX */, 0, &sc->sc_opsh);
344 	if (status != 0)
345 		panic("can't map %s space", "operations");
346 }
347 
348 void
349 octeon_fpa_init_regs(struct octeon_fpa_softc *sc)
350 {
351 
352 	bus_space_write_8(sc->sc_regt, sc->sc_regh, FPA_CTL_STATUS_OFFSET,
353 	    FPA_CTL_STATUS_ENB);
354 
355 /* XXX */
356 #ifdef OCTEON_ETH_DEBUG
357 	bus_space_write_8(sc->sc_regt, sc->sc_regh, FPA_INT_ENB_OFFSET,
358 	    FPA_INT_ENB_Q7_PERR | FPA_INT_ENB_Q7_COFF | FPA_INT_ENB_Q7_UND |
359 	    FPA_INT_ENB_Q6_PERR | FPA_INT_ENB_Q6_COFF | FPA_INT_ENB_Q6_UND |
360 	    FPA_INT_ENB_Q5_PERR | FPA_INT_ENB_Q5_COFF | FPA_INT_ENB_Q5_UND |
361 	    FPA_INT_ENB_Q4_PERR | FPA_INT_ENB_Q4_COFF | FPA_INT_ENB_Q4_UND |
362 	    FPA_INT_ENB_Q3_PERR | FPA_INT_ENB_Q3_COFF | FPA_INT_ENB_Q3_UND |
363 	    FPA_INT_ENB_Q2_PERR | FPA_INT_ENB_Q2_COFF | FPA_INT_ENB_Q2_UND |
364 	    FPA_INT_ENB_Q1_PERR | FPA_INT_ENB_Q1_COFF | FPA_INT_ENB_Q1_UND |
365 	    FPA_INT_ENB_Q0_PERR | FPA_INT_ENB_Q0_COFF | FPA_INT_ENB_Q0_UND |
366 	    FPA_INT_ENB_FED1_DBE | FPA_INT_ENB_FED1_SBE |
367 	    FPA_INT_ENB_FED0_DBE | FPA_INT_ENB_FED0_SBE);
368 #endif
369 }
370 
371 int
372 octeon_fpa_available_fpa_pool(int *available, int pool_no) {
373 	struct octeon_fpa_softc *sc = &octeon_fpa_softc;
374 	size_t offset;
375 	uint64_t tmp;
376 
377 	switch (pool_no) {
378 	case OCTEON_POOL_NO_PKT:
379 		offset = FPA_QUE0_AVAILABLE_OFFSET;
380 		break;
381 	case OCTEON_POOL_NO_WQE:
382 		offset = FPA_QUE1_AVAILABLE_OFFSET;
383 		break;
384 	case OCTEON_POOL_NO_CMD:
385 		offset = FPA_QUE2_AVAILABLE_OFFSET;
386 		break;
387 	case OCTEON_POOL_NO_SG:
388 		offset = FPA_QUE3_AVAILABLE_OFFSET;
389 		break;
390 	case OCTEON_POOL_NO_XXX_4:
391 		offset = FPA_QUE4_AVAILABLE_OFFSET;
392 		break;
393 	case OCTEON_POOL_NO_XXX_5:
394 		offset = FPA_QUE5_AVAILABLE_OFFSET;
395 		break;
396 	case OCTEON_POOL_NO_XXX_6:
397 		offset = FPA_QUE6_AVAILABLE_OFFSET;
398 		break;
399 	case OCTEON_POOL_NO_DUMP:
400 		offset = FPA_QUE7_AVAILABLE_OFFSET;
401 		break;
402 	default:
403 		return EINVAL;
404 	}
405 	tmp = bus_space_read_8(sc->sc_regt, sc->sc_regh, offset);
406 	if (available) {
407 		*available = (int)(tmp & FPA_QUEX_AVAILABLE_QUE_SIZ);
408 	}
409 
410 	return 0;
411 }
412 
413 #ifdef OCTEON_ETH_DEBUG
414 void		octeon_fpa_dump_regs(void);
415 void		octeon_fpa_dump_bufs(void);
416 void		octeon_fpa_dump_buf(int);
417 
418 #define	_ENTRY(x)	{ #x, x##_BITS, x##_OFFSET }
419 
420 struct octeon_fpa_dump_reg_ {
421 	const char *name;
422 	const char *format;
423 	size_t	offset;
424 };
425 
426 static const struct octeon_fpa_dump_reg_ octeon_fpa_dump_regs_[] = {
427 	_ENTRY(FPA_INT_SUM),
428 	_ENTRY(FPA_INT_ENB),
429 	_ENTRY(FPA_CTL_STATUS),
430 	_ENTRY(FPA_QUE0_AVAILABLE),
431 	_ENTRY(FPA_QUE1_AVAILABLE),
432 	_ENTRY(FPA_QUE2_AVAILABLE),
433 	_ENTRY(FPA_QUE3_AVAILABLE),
434 	_ENTRY(FPA_QUE4_AVAILABLE),
435 	_ENTRY(FPA_QUE5_AVAILABLE),
436 	_ENTRY(FPA_QUE6_AVAILABLE),
437 	_ENTRY(FPA_QUE7_AVAILABLE),
438 	_ENTRY(FPA_WART_CTL),
439 	_ENTRY(FPA_WART_STATUS),
440 	_ENTRY(FPA_BIST_STATUS),
441 	_ENTRY(FPA_QUE0_PAGE_INDEX),
442 	_ENTRY(FPA_QUE1_PAGE_INDEX),
443 	_ENTRY(FPA_QUE2_PAGE_INDEX),
444 	_ENTRY(FPA_QUE3_PAGE_INDEX),
445 	_ENTRY(FPA_QUE4_PAGE_INDEX),
446 	_ENTRY(FPA_QUE5_PAGE_INDEX),
447 	_ENTRY(FPA_QUE6_PAGE_INDEX),
448 	_ENTRY(FPA_QUE7_PAGE_INDEX),
449 	_ENTRY(FPA_QUE_EXP),
450 	_ENTRY(FPA_QUE_ACT),
451 };
452 
453 static const char *octeon_fpa_dump_bufs_[8] = {
454 	[0] = "recv",
455 	[1] = "wq",
456 	[2] = "cmdbuf",
457 	[3] = "gbuf",
458 };
459 
460 void
461 octeon_fpa_dump(void)
462 {
463 	octeon_fpa_dump_regs();
464 	octeon_fpa_dump_bufs();
465 }
466 
467 void
468 octeon_fpa_dump_regs(void)
469 {
470 	struct octeon_fpa_softc *sc = &octeon_fpa_softc;
471 	const struct octeon_fpa_dump_reg_ *reg;
472 	uint64_t tmp;
473 	char buf[512];
474 	int i;
475 
476 	for (i = 0; i < (int)__arraycount(octeon_fpa_dump_regs_); i++) {
477 		reg = &octeon_fpa_dump_regs_[i];
478 		tmp = bus_space_read_8(sc->sc_regt, sc->sc_regh, reg->offset);
479 		if (reg->format == NULL) {
480 			snprintf(buf, sizeof(buf), "%16" PRIx64, tmp);
481 		} else {
482 			snprintb(buf, sizeof(buf), reg->format, tmp);
483 		}
484 		printf("\t%-24s: %s\n", reg->name, buf);
485 	}
486 }
487 
488 /*
489  * XXX assume pool 7 is unused!
490  */
491 void
492 octeon_fpa_dump_bufs(void)
493 {
494 	int i;
495 
496 	for (i = 0; i < 8; i++)
497 		octeon_fpa_dump_buf(i);
498 }
499 
500 void
501 octeon_fpa_dump_buf(int pool)
502 {
503 	int i;
504 	uint64_t ptr;
505 	const char *name;
506 
507 	name = octeon_fpa_dump_bufs_[pool];
508 	if (name == NULL)
509 		return;
510 	printf("%s pool:\n", name);
511 	for (i = 0; (ptr = octeon_fpa_load(pool)) != 0; i++) {
512 		printf("\t%016" PRIx64 "%s", ptr, ((i % 4) == 3) ? "\n" : "");
513 		octeon_fpa_store(ptr, OCTEON_POOL_NO_DUMP, 0);
514 	}
515 	if (i % 4 != 3)
516 		printf("\n");
517 	printf("total = %d buffers\n", i);
518 	while ((ptr = octeon_fpa_load(OCTEON_POOL_NO_DUMP)) != 0)
519 		octeon_fpa_store(ptr, pool, 0);
520 }
521 #endif
522