xref: /openbsd/sys/dev/fdt/sxipio.c (revision d2e03d49)
1 /*	$OpenBSD: sxipio.c,v 1.19 2024/02/08 00:00:16 jsg Exp $	*/
2 /*
3  * Copyright (c) 2010 Miodrag Vallat.
4  * Copyright (c) 2013 Artturi Alm
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/device.h>
22 #include <sys/gpio.h>
23 #include <sys/evcount.h>
24 #include <sys/malloc.h>
25 
26 #include <machine/bus.h>
27 #include <machine/fdt.h>
28 #include <machine/intr.h>
29 
30 #include <dev/gpio/gpiovar.h>
31 #include <dev/ofw/openfirm.h>
32 #include <dev/ofw/ofw_clock.h>
33 #include <dev/ofw/ofw_gpio.h>
34 #include <dev/ofw/ofw_pinctrl.h>
35 #include <dev/ofw/ofw_regulator.h>
36 #include <dev/ofw/fdt.h>
37 
38 #include <dev/fdt/sunxireg.h>
39 #include <dev/fdt/sxipiovar.h>
40 
41 #include "gpio.h"
42 
43 #define	SXIPIO_NPORT		9
44 
45 struct sxipio_softc;
46 
47 struct sxipio_gpio {
48 	struct sxipio_softc *sc;
49 	int port;
50 };
51 
52 struct intrhand {
53 	int (*ih_func)(void *);		/* handler */
54 	void *ih_arg;			/* arg for handler */
55 	int ih_ipl;			/* IPL_* */
56 	int ih_irq;			/* IRQ number */
57 	int ih_gpio;			/* gpio pin */
58 	struct evcount ih_count;
59 	char *ih_name;
60 };
61 
62 struct sxipio_softc {
63 	struct device		sc_dev;
64 	bus_space_tag_t		sc_iot;
65 	bus_space_handle_t	sc_ioh;
66 	int			sc_node;
67 	void			*sc_ih_h;
68 	void			*sc_ih_l;
69 	int 			sc_max_il;
70 	int 			sc_min_il;
71 
72 	const struct sxipio_pin	*sc_pins;
73 	int			sc_npins;
74 	struct gpio_controller	sc_gc;
75 
76 	struct sxipio_gpio	sc_gpio[SXIPIO_NPORT];
77 	struct gpio_chipset_tag	sc_gpio_tag[SXIPIO_NPORT];
78 	gpio_pin_t		sc_gpio_pins[SXIPIO_NPORT][32];
79 
80 	struct intrhand		*sc_handlers[32];
81 
82 	void			(*sc_bias_cfg)(struct sxipio_softc *,
83 				    int, uint32_t);
84 };
85 
86 #define	SXIPIO_CFG(port, pin)	0x00 + ((port) * 0x24) + (((pin) >> 3) * 0x04)
87 #define	SXIPIO_DAT(port)	0x10 + ((port) * 0x24)
88 #define	SXIPIO_DRV(port, pin)	0x14 + ((port) * 0x24) + (((pin) >> 4) * 0x04)
89 #define	SXIPIO_PUL(port, pin)	0x1c + ((port) * 0x24) + (((pin) >> 4) * 0x04)
90 #define	SXIPIO_INT_CFG0(port)	0x0200 + ((port) * 0x04)
91 #define	SXIPIO_INT_CTL		0x0210
92 #define	SXIPIO_INT_STA		0x0214
93 #define	SXIPIO_INT_DEB		0x0218 /* debounce register */
94 #define	SXIPIO_GRP_CFG(port)	0x0300 + ((port) * 0x04)
95 #define	 SXIPIO_IO_BIAS_MASK		(0xf << 0)
96 #define	 SXIPIO_IO_BIAS_1_8V		0x0
97 #define	 SXIPIO_IO_BIAS_2_5V		0x6
98 #define	 SXIPIO_IO_BIAS_2_8V		0x9
99 #define	 SXIPIO_IO_BIAS_3_0V		0xa
100 #define	 SXIPIO_IO_BIAS_3_3V		0xd
101 
102 #define SXIPIO_GPIO_IN		0
103 #define SXIPIO_GPIO_OUT		1
104 #define SXIPIO_DISABLED		7
105 
106 int	sxipio_match(struct device *, void *, void *);
107 void	sxipio_attach(struct device *, struct device *, void *);
108 
109 const struct cfattach sxipio_ca = {
110 	sizeof (struct sxipio_softc), sxipio_match, sxipio_attach
111 };
112 
113 struct cfdriver sxipio_cd = {
114 	NULL, "sxipio", DV_DULL
115 };
116 
117 void	sxipio_attach_gpio(struct device *);
118 int	sxipio_pinctrl(uint32_t, void *);
119 void	sxipio_config_pin(void *, uint32_t *, int);
120 int	sxipio_get_pin(void *, uint32_t *);
121 void	sxipio_set_pin(void *, uint32_t *, int);
122 void	sxipio_a80_bias_cfg(struct sxipio_softc *, int, uint32_t);
123 
124 #include "sxipio_pins.h"
125 
126 struct sxipio_pins {
127 	const char *compat;
128 	const struct sxipio_pin *pins;
129 	int npins;
130 };
131 
132 const struct sxipio_pins sxipio_pins[] = {
133 	{
134 		"allwinner,sun4i-a10-pinctrl",
135 		sun4i_a10_pins, nitems(sun4i_a10_pins)
136 	},
137 	{
138 		"allwinner,sun5i-a10s-pinctrl",
139 		sun5i_a10s_pins, nitems(sun5i_a10s_pins)
140 	},
141 	{
142 		"allwinner,sun5i-a13-pinctrl",
143 		sun5i_a13_pins, nitems(sun5i_a13_pins)
144 	},
145 	{
146 		"allwinner,sun5i-gr8-pinctrl",
147 		sun5i_gr8_pins, nitems(sun5i_gr8_pins)
148 	},
149 	{
150 		"allwinner,sun7i-a20-pinctrl",
151 		sun7i_a20_pins, nitems(sun7i_a20_pins)
152 	},
153 	{
154 		"allwinner,sun8i-r40-pinctrl",
155 		sun8i_r40_pins, nitems(sun8i_r40_pins)
156 	},
157 	{
158 		"allwinner,sun8i-a33-pinctrl",
159 		sun8i_a33_pins, nitems(sun8i_a33_pins)
160 	},
161 	{
162 		"allwinner,sun8i-h3-pinctrl",
163 		sun8i_h3_pins, nitems(sun8i_h3_pins)
164 	},
165 	{
166 		"allwinner,sun8i-h3-r-pinctrl",
167 		sun8i_h3_r_pins, nitems(sun8i_h3_r_pins)
168 	},
169 	{
170 		"allwinner,sun8i-v3-pinctrl",
171 		sun8i_v3_pins, nitems(sun8i_v3_pins)
172 	},
173 	{
174 		"allwinner,sun8i-v3s-pinctrl",
175 		sun8i_v3s_pins, nitems(sun8i_v3s_pins)
176 	},
177 	{
178 		"allwinner,sun9i-a80-pinctrl",
179 		sun9i_a80_pins, nitems(sun9i_a80_pins)
180 	},
181 	{
182 		"allwinner,sun9i-a80-r-pinctrl",
183 		sun9i_a80_r_pins, nitems(sun9i_a80_r_pins)
184 	},
185 	{
186 		"allwinner,sun20i-d1-pinctrl",
187 		sun20i_d1_pins, nitems(sun20i_d1_pins)
188 	},
189 	{
190 		"allwinner,sun50i-a64-pinctrl",
191 		sun50i_a64_pins, nitems(sun50i_a64_pins)
192 	},
193 	{
194 		"allwinner,sun50i-a64-r-pinctrl",
195 		sun50i_a64_r_pins, nitems(sun50i_a64_r_pins)
196 	},
197 	{
198 		"allwinner,sun50i-h5-pinctrl",
199 		sun50i_h5_pins, nitems(sun50i_h5_pins)
200 	},
201 	{
202 		"allwinner,sun50i-h6-pinctrl",
203 		sun50i_h6_pins, nitems(sun50i_h6_pins)
204 	},
205 	{
206 		"allwinner,sun50i-h6-r-pinctrl",
207 		sun50i_h6_r_pins, nitems(sun50i_h6_r_pins)
208 	},
209 	{
210 		"allwinner,sun50i-h616-pinctrl",
211 		sun50i_h616_pins, nitems(sun50i_h616_pins)
212 	},
213 	{
214 		"allwinner,sun50i-h616-r-pinctrl",
215 		sun50i_h616_r_pins, nitems(sun50i_h616_r_pins)
216 	},
217 };
218 
219 int
sxipio_match(struct device * parent,void * match,void * aux)220 sxipio_match(struct device *parent, void *match, void *aux)
221 {
222 	struct fdt_attach_args *faa = aux;
223 	int i;
224 
225 	for (i = 0; i < nitems(sxipio_pins); i++) {
226 		if (OF_is_compatible(faa->fa_node, sxipio_pins[i].compat))
227 			return 1;
228 	}
229 
230 	return 0;
231 }
232 
233 void
sxipio_attach(struct device * parent,struct device * self,void * aux)234 sxipio_attach(struct device *parent, struct device *self, void *aux)
235 {
236 	struct sxipio_softc *sc = (struct sxipio_softc *)self;
237 	struct fdt_attach_args	*faa = aux;
238 	int i;
239 
240 	sc->sc_iot = faa->fa_iot;
241 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
242 	    faa->fa_reg[0].size, 0, &sc->sc_ioh))
243 		panic("%s: bus_space_map failed!", __func__);
244 	sc->sc_node = faa->fa_node;
245 
246 	clock_enable_all(faa->fa_node);
247 	reset_deassert_all(faa->fa_node);
248 
249 	for (i = 0; i < nitems(sxipio_pins); i++) {
250 		if (OF_is_compatible(faa->fa_node, sxipio_pins[i].compat)) {
251 			sc->sc_pins = sxipio_pins[i].pins;
252 			sc->sc_npins = sxipio_pins[i].npins;
253 			break;
254 		}
255 	}
256 
257 	/* Allwinner A80 needs IO pad bias configuration. */
258 	if (OF_is_compatible(faa->fa_node, "allwinner,sun9i-a80-pinctrl") ||
259 	    OF_is_compatible(faa->fa_node, "allwinner,sun9i-a80-r-pinctrl"))
260 		sc->sc_bias_cfg = sxipio_a80_bias_cfg;
261 
262 	KASSERT(sc->sc_pins);
263 	pinctrl_register(faa->fa_node, sxipio_pinctrl, sc);
264 
265 	sc->sc_gc.gc_node = faa->fa_node;
266 	sc->sc_gc.gc_cookie = sc;
267 	sc->sc_gc.gc_config_pin = sxipio_config_pin;
268 	sc->sc_gc.gc_get_pin = sxipio_get_pin;
269 	sc->sc_gc.gc_set_pin = sxipio_set_pin;
270 	gpio_controller_register(&sc->sc_gc);
271 
272 	config_defer(self, sxipio_attach_gpio);
273 
274 	printf(": %d pins\n", sc->sc_npins);
275 }
276 
277 int
sxipio_drive(int node)278 sxipio_drive(int node)
279 {
280 	int drive;
281 
282 	drive = OF_getpropint(node, "allwinner,drive", -1);
283 	if (drive >= 0)
284 		return drive;
285 	drive = OF_getpropint(node, "drive-strength", 0) - 10;
286 	if (drive >= 0)
287 		return (drive / 10);
288 	return -1;
289 }
290 
291 int
sxipio_pull(int node)292 sxipio_pull(int node)
293 {
294 	int pull;
295 
296 	pull = OF_getpropint(node, "allwinner,pull", -1);
297 	if (pull >= 0)
298 		return pull;
299 	if (OF_getproplen(node, "bias-disable") == 0)
300 		return 0;
301 	if (OF_getproplen(node, "bias-pull-up") == 0)
302 		return 1;
303 	if (OF_getproplen(node, "bias-pull-down") == 0)
304 		return 2;
305 	return -1;
306 }
307 
308 int
sxipio_pinctrl(uint32_t phandle,void * cookie)309 sxipio_pinctrl(uint32_t phandle, void *cookie)
310 {
311 	struct sxipio_softc *sc = cookie;
312 	char func[32];
313 	char vcc[16];
314 	char *names, *name;
315 	uint32_t supply;
316 	int group, port, pin, off, mask;
317 	int mux, drive, pull;
318 	int node;
319 	int len;
320 	int i, j;
321 	int s;
322 
323 	node = OF_getnodebyphandle(phandle);
324 	if (node == 0)
325 		return -1;
326 
327 	len = OF_getprop(node, "allwinner,function", func, sizeof(func));
328 	if (len <= 0 || len >= sizeof(func)) {
329 		len = OF_getprop(node, "function", func, sizeof(func));
330 		if (len <= 0 || len >= sizeof(func))
331 			return -1;
332 	}
333 
334 	len = OF_getproplen(node, "allwinner,pins");
335 	if (len <= 0) {
336 		len = OF_getproplen(node, "pins");
337 		if (len <= 0)
338 			return -1;
339 	}
340 
341 	names = malloc(len, M_TEMP, M_WAITOK);
342 	if (OF_getprop(node, "allwinner,pins", names, len) <= 0)
343 		OF_getprop(node, "pins", names, len);
344 
345 	drive = sxipio_drive(node);
346 	pull = sxipio_pull(node);
347 
348 	name = names;
349 	while (len > 0) {
350 		/* Lookup the pin. */
351 		for (i = 0; i < sc->sc_npins; i++) {
352 			if (strcmp(name, sc->sc_pins[i].name) == 0)
353 				break;
354 		}
355 		if (i >= sc->sc_npins)
356 			goto err;
357 
358 		/* Lookup the function of the pin. */
359 		for (j = 0; j < nitems(sc->sc_pins[i].funcs); j++) {
360 			if (sc->sc_pins[i].funcs[j].name == NULL)
361 				continue;
362 			if (strcmp(func, sc->sc_pins[i].funcs[j].name) == 0)
363 				break;
364 		}
365 		if (j >= nitems(sc->sc_pins[i].funcs))
366 			goto err;
367 
368 		group = sc->sc_pins[i].name[1] - 'A';
369 		port = sc->sc_pins[i].port;
370 		pin = sc->sc_pins[i].pin;
371 		mux = sc->sc_pins[i].funcs[j].mux;
372 
373 		snprintf(vcc, sizeof(vcc), "vcc-p%c-supply", 'a' + group);
374 		supply = OF_getpropint(sc->sc_node, vcc, 0);
375 		if (supply) {
376 			regulator_enable(supply);
377 			if (sc->sc_bias_cfg)
378 				sc->sc_bias_cfg(sc, port, supply);
379 		}
380 
381 		s = splhigh();
382 		off = (pin & 0x7) << 2, mask = (0x7 << off);
383 		SXICMS4(sc, SXIPIO_CFG(port, pin), mask, mux << off);
384 		off = (pin & 0xf) << 1, mask = (0x3 << off);
385 		if (drive >= 0 && drive < 4)
386 			SXICMS4(sc, SXIPIO_DRV(port, pin), mask, drive << off);
387 		if (pull >= 0 && pull < 3)
388 			SXICMS4(sc, SXIPIO_PUL(port, pin), mask, pull << off);
389 		splx(s);
390 
391 		len -= strlen(name) + 1;
392 		name += strlen(name) + 1;
393 	}
394 
395 	free(names, M_TEMP, len);
396 	return 0;
397 
398 err:
399 	free(names, M_TEMP, len);
400 	return -1;
401 }
402 
403 void
sxipio_config_pin(void * cookie,uint32_t * cells,int config)404 sxipio_config_pin(void *cookie, uint32_t *cells, int config)
405 {
406 	struct sxipio_softc *sc = cookie;
407 	uint32_t port = cells[0];
408 	uint32_t pin = cells[1];
409 	int mux, off;
410 
411 	if (port > SXIPIO_NPORT || pin >= 32)
412 		return;
413 
414 	mux = (config & GPIO_CONFIG_OUTPUT) ? 1 : 0;
415 	off = (pin & 0x7) << 2;
416 	SXICMS4(sc, SXIPIO_CFG(port, pin), 0x7 << off, mux << off);
417 }
418 
419 int
sxipio_get_pin(void * cookie,uint32_t * cells)420 sxipio_get_pin(void *cookie, uint32_t *cells)
421 {
422 	struct sxipio_softc *sc = cookie;
423 	uint32_t port = cells[0];
424 	uint32_t pin = cells[1];
425 	uint32_t flags = cells[2];
426 	uint32_t reg;
427 	int val;
428 
429 	if (port > SXIPIO_NPORT || pin >= 32)
430 		return 0;
431 
432 	reg = SXIREAD4(sc, SXIPIO_DAT(port));
433 	reg &= (1 << pin);
434 	val = (reg >> pin) & 1;
435 	if (flags & GPIO_ACTIVE_LOW)
436 		val = !val;
437 	return val;
438 }
439 
440 void
sxipio_set_pin(void * cookie,uint32_t * cells,int val)441 sxipio_set_pin(void *cookie, uint32_t *cells, int val)
442 {
443 	struct sxipio_softc *sc = cookie;
444 	uint32_t port = cells[0];
445 	uint32_t pin = cells[1];
446 	uint32_t flags = cells[2];
447 	uint32_t reg;
448 
449 	if (port > SXIPIO_NPORT || pin >= 32)
450 		return;
451 
452 	reg = SXIREAD4(sc, SXIPIO_DAT(port));
453 	if (flags & GPIO_ACTIVE_LOW)
454 		val = !val;
455 	if (val)
456 		reg |= (1 << pin);
457 	else
458 		reg &= ~(1 << pin);
459 	SXIWRITE4(sc, SXIPIO_DAT(port), reg);
460 }
461 
462 void
sxipio_a80_bias_cfg(struct sxipio_softc * sc,int port,uint32_t supply)463 sxipio_a80_bias_cfg(struct sxipio_softc *sc, int port, uint32_t supply)
464 {
465 	uint32_t voltage;
466 	uint32_t bias;
467 
468 	voltage = regulator_get_voltage(supply);
469 	if (voltage <= 1800000)
470 		bias = SXIPIO_IO_BIAS_1_8V;
471 	else if (voltage <= 2500000)
472 		bias = SXIPIO_IO_BIAS_2_5V;
473 	else if (voltage <= 2800000)
474 		bias = SXIPIO_IO_BIAS_2_8V;
475 	else if (voltage <= 3000000)
476 		bias = SXIPIO_IO_BIAS_3_0V;
477 	else
478 		bias = SXIPIO_IO_BIAS_3_3V;
479 
480 	SXICMS4(sc, SXIPIO_GRP_CFG(port), SXIPIO_IO_BIAS_MASK, bias);
481 }
482 
483 /*
484  * GPIO support code
485  */
486 
487 int	sxipio_pin_read(void *, int);
488 void	sxipio_pin_write(void *, int, int);
489 void	sxipio_pin_ctl(void *, int, int);
490 
491 static const struct gpio_chipset_tag sxipio_gpio_tag = {
492 	.gp_pin_read = sxipio_pin_read,
493 	.gp_pin_write = sxipio_pin_write,
494 	.gp_pin_ctl = sxipio_pin_ctl
495 };
496 
497 int
sxipio_pin_read(void * cookie,int pin)498 sxipio_pin_read(void *cookie, int pin)
499 {
500 	struct sxipio_gpio *gpio = cookie;
501 	uint32_t cells[3];
502 
503 	cells[0] = gpio->port;
504 	cells[1] = pin;
505 	cells[2] = 0;
506 
507 	return sxipio_get_pin(gpio->sc, cells) ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
508 }
509 
510 void
sxipio_pin_write(void * cookie,int pin,int val)511 sxipio_pin_write(void *cookie, int pin, int val)
512 {
513 	struct sxipio_gpio *gpio = cookie;
514 	uint32_t cells[3];
515 
516 	cells[0] = gpio->port;
517 	cells[1] = pin;
518 	cells[2] = 0;
519 
520 	sxipio_set_pin(gpio->sc, cells, val);
521 }
522 
523 void
sxipio_pin_ctl(void * cookie,int pin,int flags)524 sxipio_pin_ctl(void *cookie, int pin, int flags)
525 {
526 	struct sxipio_gpio *gpio = cookie;
527 	uint32_t cells[3];
528 
529 	cells[0] = gpio->port;
530 	cells[1] = pin;
531 	cells[2] = 0;
532 
533 	if (ISSET(flags, GPIO_PIN_OUTPUT))
534 		sxipio_config_pin(gpio->sc, cells, GPIO_CONFIG_OUTPUT);
535 	else
536 		sxipio_config_pin(gpio->sc, cells, 0);
537 }
538 
539 void
sxipio_attach_gpio(struct device * parent)540 sxipio_attach_gpio(struct device *parent)
541 {
542 	struct sxipio_softc *sc = (struct sxipio_softc *)parent;
543 	struct gpiobus_attach_args gba;
544 	uint32_t reg;
545 	int port, pin;
546 	int off, mux;
547 	int state, flags;
548 	int i;
549 
550 	for (i = 0; i < sc->sc_npins; i++) {
551 		/* Skip pins that have no gpio function. */
552 		if (strcmp(sc->sc_pins[i].funcs[0].name, "gpio_in") != 0 ||
553 		    strcmp(sc->sc_pins[i].funcs[1].name, "gpio_out") != 0)
554 			continue;
555 
556 		port = sc->sc_pins[i].port;
557 		pin = sc->sc_pins[i].pin;
558 
559 		/* Get pin configuration. */
560 		reg = SXIREAD4(sc, SXIPIO_CFG(port, pin));
561 		off = (pin & 0x7) << 2;
562 		mux = (reg >> off) & 0x7;
563 
564 		/* Skip pins that have been assigned other functions. */
565 		if (mux != SXIPIO_GPIO_IN && mux != SXIPIO_GPIO_OUT &&
566 		    mux != SXIPIO_DISABLED)
567 			continue;
568 
569 		switch (mux) {
570 		case SXIPIO_GPIO_IN:
571 			flags = GPIO_PIN_SET | GPIO_PIN_INPUT;
572 			break;
573 		case SXIPIO_GPIO_OUT:
574 			flags = GPIO_PIN_SET | GPIO_PIN_OUTPUT;
575 			break;
576 		default:
577 			flags = GPIO_PIN_SET;
578 		}
579 
580 		/* Get pin state. */
581 		reg = SXIREAD4(sc, SXIPIO_DAT(port));
582 		state = (reg >> pin) & 1;
583 
584 		sc->sc_gpio_pins[port][pin].pin_caps =
585 		    GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
586 		sc->sc_gpio_pins[port][pin].pin_flags = flags;
587 		sc->sc_gpio_pins[port][pin].pin_state = state;
588 		sc->sc_gpio_pins[port][pin].pin_num = pin;
589 	}
590 
591 	for (i = 0; i <= port; i++) {
592 		memcpy(&sc->sc_gpio_tag[i], &sxipio_gpio_tag, sizeof(sxipio_gpio_tag));
593 		sc->sc_gpio_tag[i].gp_cookie = &sc->sc_gpio[i];
594 		sc->sc_gpio[i].sc = sc;
595 		sc->sc_gpio[i].port = i;
596 
597 		gba.gba_name = "gpio";
598 		gba.gba_gc = &sc->sc_gpio_tag[i];
599 		gba.gba_pins = &sc->sc_gpio_pins[i][0];
600 		gba.gba_npins = 32;
601 
602 #if NGPIO > 0
603 		config_found(&sc->sc_dev, &gba, gpiobus_print);
604 #endif
605 	}
606 }
607