xref: /freebsd/sys/dev/dpaa2/dpaa2_console.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
1*72ef722bSBjoern A. Zeeb /*-
2*72ef722bSBjoern A. Zeeb  * SPDX-License-Identifier: BSD-3-Clause AND BSD-2-Clause
3*72ef722bSBjoern A. Zeeb  */
4*72ef722bSBjoern A. Zeeb /*
5*72ef722bSBjoern A. Zeeb  * // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
6*72ef722bSBjoern A. Zeeb  *
7*72ef722bSBjoern A. Zeeb  * Freescale DPAA2 Platforms Console Driver
8*72ef722bSBjoern A. Zeeb  *
9*72ef722bSBjoern A. Zeeb  * Copyright 2015-2016 Freescale Semiconductor Inc.
10*72ef722bSBjoern A. Zeeb  * Copyright 2018 NXP
11*72ef722bSBjoern A. Zeeb  *
12*72ef722bSBjoern A. Zeeb  * git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/drivers/soc/fsl/dpaa2-console.c#8120bd469f5525da229953c1197f2b826c0109f4
13*72ef722bSBjoern A. Zeeb  */
14*72ef722bSBjoern A. Zeeb /*
15*72ef722bSBjoern A. Zeeb  * Copyright (c) 2022-2023 Bjoern A. Zeeb
16*72ef722bSBjoern A. Zeeb  *
17*72ef722bSBjoern A. Zeeb  * Redistribution and use in source and binary forms, with or without
18*72ef722bSBjoern A. Zeeb  * modification, are permitted provided that the following conditions
19*72ef722bSBjoern A. Zeeb  * are met:
20*72ef722bSBjoern A. Zeeb  * 1. Redistributions of source code must retain the above copyright
21*72ef722bSBjoern A. Zeeb  *    notice, this list of conditions and the following disclaimer.
22*72ef722bSBjoern A. Zeeb  * 2. Redistributions in binary form must reproduce the above copyright
23*72ef722bSBjoern A. Zeeb  *    notice, this list of conditions and the following disclaimer in the
24*72ef722bSBjoern A. Zeeb  *    documentation and/or other materials provided with the distribution.
25*72ef722bSBjoern A. Zeeb  *
26*72ef722bSBjoern A. Zeeb  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
27*72ef722bSBjoern A. Zeeb  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28*72ef722bSBjoern A. Zeeb  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29*72ef722bSBjoern A. Zeeb  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
30*72ef722bSBjoern A. Zeeb  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31*72ef722bSBjoern A. Zeeb  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32*72ef722bSBjoern A. Zeeb  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33*72ef722bSBjoern A. Zeeb  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34*72ef722bSBjoern A. Zeeb  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35*72ef722bSBjoern A. Zeeb  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36*72ef722bSBjoern A. Zeeb  * SUCH DAMAGE.
37*72ef722bSBjoern A. Zeeb  */
38*72ef722bSBjoern A. Zeeb 
39*72ef722bSBjoern A. Zeeb /*
40*72ef722bSBjoern A. Zeeb  * Some docs are in:
41*72ef722bSBjoern A. Zeeb  * - https://www.nxp.com.cn/docs/en/application-note/AN13329.pdf
42*72ef722bSBjoern A. Zeeb  * - DPAA2UM
43*72ef722bSBjoern A. Zeeb  * - git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/drivers/soc/fsl/dpaa2-console.c
44*72ef722bSBjoern A. Zeeb  */
45*72ef722bSBjoern A. Zeeb 
46*72ef722bSBjoern A. Zeeb #include "opt_platform.h"
47*72ef722bSBjoern A. Zeeb #ifdef __notyet__
48*72ef722bSBjoern A. Zeeb #include "opt_acpi.h"
49*72ef722bSBjoern A. Zeeb #endif
50*72ef722bSBjoern A. Zeeb 
51*72ef722bSBjoern A. Zeeb #include <sys/param.h>
52*72ef722bSBjoern A. Zeeb #include <sys/bus.h>
53*72ef722bSBjoern A. Zeeb #include <sys/conf.h>
54*72ef722bSBjoern A. Zeeb #include <sys/fcntl.h>
55*72ef722bSBjoern A. Zeeb #include <sys/kernel.h>
56*72ef722bSBjoern A. Zeeb #include <sys/malloc.h>
57*72ef722bSBjoern A. Zeeb #include <sys/module.h>
58*72ef722bSBjoern A. Zeeb #include <sys/mutex.h>
59*72ef722bSBjoern A. Zeeb #include <sys/rman.h>
60*72ef722bSBjoern A. Zeeb #include <sys/stat.h>
61*72ef722bSBjoern A. Zeeb #include <sys/uio.h>
62*72ef722bSBjoern A. Zeeb 
63*72ef722bSBjoern A. Zeeb #include <machine/bus.h>
64*72ef722bSBjoern A. Zeeb #include <machine/resource.h>
65*72ef722bSBjoern A. Zeeb 
66*72ef722bSBjoern A. Zeeb #ifdef FDT
67*72ef722bSBjoern A. Zeeb #include <dev/ofw/ofw_bus.h>
68*72ef722bSBjoern A. Zeeb #include <dev/ofw/ofw_bus_subr.h>
69*72ef722bSBjoern A. Zeeb #endif
70*72ef722bSBjoern A. Zeeb 
71*72ef722bSBjoern A. Zeeb /* Table 6-1. MC Memory Map */
72*72ef722bSBjoern A. Zeeb #define	MC_REG_MCFBA			0x20
73*72ef722bSBjoern A. Zeeb #define	MC_REG_MCFBALR_OFF		0x00
74*72ef722bSBjoern A. Zeeb #define	MC_REG_MCFBALR_MASK		0xe0000000
75*72ef722bSBjoern A. Zeeb #define	MC_REG_MCFBAHR_OFF		0x04
76*72ef722bSBjoern A. Zeeb #define	MC_REG_MCFBAHR_MASK		0x0001ffff
77*72ef722bSBjoern A. Zeeb 
78*72ef722bSBjoern A. Zeeb /* Firmware Consoles. */
79*72ef722bSBjoern A. Zeeb #define	MC_BUFFER_OFFSET		0x01000000
80*72ef722bSBjoern A. Zeeb #define	MC_BUFFER_SIZE			(1024 * 1024 * 16)
81*72ef722bSBjoern A. Zeeb #define	MC_OFFSET_DELTA			MC_BUFFER_OFFSET
82*72ef722bSBjoern A. Zeeb 
83*72ef722bSBjoern A. Zeeb #define	AIOP_BUFFER_OFFSET		0x06000000
84*72ef722bSBjoern A. Zeeb #define	AIOP_BUFFER_SIZE		(1024 * 1024 * 16)
85*72ef722bSBjoern A. Zeeb #define	AIOP_OFFSET_DELTA		0
86*72ef722bSBjoern A. Zeeb 
87*72ef722bSBjoern A. Zeeb /* MC and AIOP Magic words */
88*72ef722bSBjoern A. Zeeb #define	MAGIC_MC			0x4d430100
89*72ef722bSBjoern A. Zeeb #define	MAGIC_AIOP			0X41494f50
90*72ef722bSBjoern A. Zeeb 
91*72ef722bSBjoern A. Zeeb #define	LOG_HEADER_FLAG_BUFFER_WRAPAROUND				\
92*72ef722bSBjoern A. Zeeb     0x80000000
93*72ef722bSBjoern A. Zeeb #define	LAST_BYTE(a)							\
94*72ef722bSBjoern A. Zeeb     ((a) & ~(LOG_HEADER_FLAG_BUFFER_WRAPAROUND))
95*72ef722bSBjoern A. Zeeb 
96*72ef722bSBjoern A. Zeeb struct dpaa2_cons_dev {
97*72ef722bSBjoern A. Zeeb 	struct cdev			*cdev;
98*72ef722bSBjoern A. Zeeb 	struct mtx			mtx;
99*72ef722bSBjoern A. Zeeb 	size_t				offset;
100*72ef722bSBjoern A. Zeeb 	uint32_t			magic;
101*72ef722bSBjoern A. Zeeb 
102*72ef722bSBjoern A. Zeeb 	uint32_t			hdr_magic;
103*72ef722bSBjoern A. Zeeb 	uint32_t			hdr_eobyte;
104*72ef722bSBjoern A. Zeeb 	uint32_t			hdr_start;
105*72ef722bSBjoern A. Zeeb 	uint32_t			hdr_len;
106*72ef722bSBjoern A. Zeeb 
107*72ef722bSBjoern A. Zeeb 	uoff_t				start;
108*72ef722bSBjoern A. Zeeb 	uoff_t				end;
109*72ef722bSBjoern A. Zeeb 	uoff_t				eod;
110*72ef722bSBjoern A. Zeeb 	uoff_t				cur;
111*72ef722bSBjoern A. Zeeb 
112*72ef722bSBjoern A. Zeeb 	bus_space_tag_t			bst;
113*72ef722bSBjoern A. Zeeb 	bus_space_handle_t		bsh;
114*72ef722bSBjoern A. Zeeb 	vm_size_t			size;
115*72ef722bSBjoern A. Zeeb };
116*72ef722bSBjoern A. Zeeb 
117*72ef722bSBjoern A. Zeeb struct dpaa2_cons_softc {
118*72ef722bSBjoern A. Zeeb 	struct resource			*res;
119*72ef722bSBjoern A. Zeeb 	bus_space_tag_t			bst;
120*72ef722bSBjoern A. Zeeb 	uint64_t			mcfba;
121*72ef722bSBjoern A. Zeeb 	struct dpaa2_cons_dev		mc_cd;
122*72ef722bSBjoern A. Zeeb 	struct dpaa2_cons_dev		aiop_cd;
123*72ef722bSBjoern A. Zeeb };
124*72ef722bSBjoern A. Zeeb 
125*72ef722bSBjoern A. Zeeb struct dpaa2_cons_hdr {
126*72ef722bSBjoern A. Zeeb 	uint32_t	magic;
127*72ef722bSBjoern A. Zeeb 	uint32_t	_reserved;
128*72ef722bSBjoern A. Zeeb 	uint32_t	start;
129*72ef722bSBjoern A. Zeeb 	uint32_t	len;
130*72ef722bSBjoern A. Zeeb 	uint32_t	eobyte;
131*72ef722bSBjoern A. Zeeb };
132*72ef722bSBjoern A. Zeeb 
133*72ef722bSBjoern A. Zeeb #define	DPAA2_MC_READ_4(_sc, _r)	bus_read_4((_sc)->res, (_r))
134*72ef722bSBjoern A. Zeeb 
135*72ef722bSBjoern A. Zeeb /* Management interface */
136*72ef722bSBjoern A. Zeeb static d_open_t				dpaa2_cons_open;
137*72ef722bSBjoern A. Zeeb static d_close_t			dpaa2_cons_close;
138*72ef722bSBjoern A. Zeeb static d_read_t				dpaa2_cons_read;
139*72ef722bSBjoern A. Zeeb 
140*72ef722bSBjoern A. Zeeb static struct cdevsw dpaa2_mc_cons_cdevsw = {
141*72ef722bSBjoern A. Zeeb 	.d_version =	D_VERSION,
142*72ef722bSBjoern A. Zeeb 	.d_flags =	0,
143*72ef722bSBjoern A. Zeeb 	.d_open =	dpaa2_cons_open,
144*72ef722bSBjoern A. Zeeb 	.d_close =	dpaa2_cons_close,
145*72ef722bSBjoern A. Zeeb 	.d_read =	dpaa2_cons_read,
146*72ef722bSBjoern A. Zeeb 	.d_name =	"fsl_mc_console",
147*72ef722bSBjoern A. Zeeb };
148*72ef722bSBjoern A. Zeeb 
149*72ef722bSBjoern A. Zeeb static struct cdevsw dpaa2_aiop_cons_cdevsw = {
150*72ef722bSBjoern A. Zeeb 	.d_version =	D_VERSION,
151*72ef722bSBjoern A. Zeeb 	.d_flags =	0,
152*72ef722bSBjoern A. Zeeb 	.d_open =	dpaa2_cons_open,
153*72ef722bSBjoern A. Zeeb 	.d_close =	dpaa2_cons_close,
154*72ef722bSBjoern A. Zeeb 	.d_read =	dpaa2_cons_read,
155*72ef722bSBjoern A. Zeeb 	.d_name =	"fsl_aiop_console",
156*72ef722bSBjoern A. Zeeb };
157*72ef722bSBjoern A. Zeeb 
158*72ef722bSBjoern A. Zeeb static size_t
dpaa2_cons_read_bs(struct dpaa2_cons_dev * cd,size_t offset,void * dst,size_t len)159*72ef722bSBjoern A. Zeeb dpaa2_cons_read_bs(struct dpaa2_cons_dev *cd, size_t offset, void *dst, size_t len)
160*72ef722bSBjoern A. Zeeb {
161*72ef722bSBjoern A. Zeeb 	size_t count, l;
162*72ef722bSBjoern A. Zeeb 	uint8_t *p;
163*72ef722bSBjoern A. Zeeb 
164*72ef722bSBjoern A. Zeeb 	count = 0;
165*72ef722bSBjoern A. Zeeb 	p = dst;
166*72ef722bSBjoern A. Zeeb 	l = offset % 8;
167*72ef722bSBjoern A. Zeeb 	if (l != 0) {
168*72ef722bSBjoern A. Zeeb 		bus_space_read_region_1(cd->bst, cd->bsh, offset + count, p + count, l);
169*72ef722bSBjoern A. Zeeb 		count += l;
170*72ef722bSBjoern A. Zeeb 		len -= l;
171*72ef722bSBjoern A. Zeeb 	}
172*72ef722bSBjoern A. Zeeb 
173*72ef722bSBjoern A. Zeeb 	l = len / 8;
174*72ef722bSBjoern A. Zeeb 	if (l != 0) {
175*72ef722bSBjoern A. Zeeb 		bus_space_read_region_8(cd->bst, cd->bsh, offset + count, (uint64_t *)(p + count), l);
176*72ef722bSBjoern A. Zeeb 		l *= 8;
177*72ef722bSBjoern A. Zeeb 		count += l;
178*72ef722bSBjoern A. Zeeb 		len -= l;
179*72ef722bSBjoern A. Zeeb 	}
180*72ef722bSBjoern A. Zeeb 	l = len / 4;
181*72ef722bSBjoern A. Zeeb 	if (l != 0) {
182*72ef722bSBjoern A. Zeeb 		bus_space_read_region_4(cd->bst, cd->bsh, offset + count, (uint32_t *)(p + count), l);
183*72ef722bSBjoern A. Zeeb 		l *= 4;
184*72ef722bSBjoern A. Zeeb 		count += l;
185*72ef722bSBjoern A. Zeeb 		len -= l;
186*72ef722bSBjoern A. Zeeb 	}
187*72ef722bSBjoern A. Zeeb 	l = len / 2;
188*72ef722bSBjoern A. Zeeb 	if (l != 0) {
189*72ef722bSBjoern A. Zeeb 		bus_space_read_region_2(cd->bst, cd->bsh, offset + count, (uint16_t *)(p + count), l);
190*72ef722bSBjoern A. Zeeb 		l *= 2;
191*72ef722bSBjoern A. Zeeb 		count += l;
192*72ef722bSBjoern A. Zeeb 		len -= l;
193*72ef722bSBjoern A. Zeeb 	}
194*72ef722bSBjoern A. Zeeb 	if (len != 0) {
195*72ef722bSBjoern A. Zeeb 		bus_space_read_region_1(cd->bst, cd->bsh, offset + count, p + count, len);
196*72ef722bSBjoern A. Zeeb 		count += len;
197*72ef722bSBjoern A. Zeeb 	}
198*72ef722bSBjoern A. Zeeb 
199*72ef722bSBjoern A. Zeeb 	return (count);
200*72ef722bSBjoern A. Zeeb }
201*72ef722bSBjoern A. Zeeb 
202*72ef722bSBjoern A. Zeeb static int
dpaa2_cons_open(struct cdev * cdev,int flags,int fmt,struct thread * td)203*72ef722bSBjoern A. Zeeb dpaa2_cons_open(struct cdev *cdev, int flags, int fmt, struct thread *td)
204*72ef722bSBjoern A. Zeeb {
205*72ef722bSBjoern A. Zeeb 	struct dpaa2_cons_dev *cd;
206*72ef722bSBjoern A. Zeeb 	struct dpaa2_cons_hdr hdr;
207*72ef722bSBjoern A. Zeeb 	size_t rlen __diagused;
208*72ef722bSBjoern A. Zeeb 	uint32_t wrapped;
209*72ef722bSBjoern A. Zeeb 
210*72ef722bSBjoern A. Zeeb 	if (flags & FWRITE)
211*72ef722bSBjoern A. Zeeb 		return (EACCES);
212*72ef722bSBjoern A. Zeeb 
213*72ef722bSBjoern A. Zeeb 	cd = cdev->si_drv1;
214*72ef722bSBjoern A. Zeeb 	if (cd->size == 0)
215*72ef722bSBjoern A. Zeeb 		return (ENODEV);
216*72ef722bSBjoern A. Zeeb 
217*72ef722bSBjoern A. Zeeb 	mtx_lock(&cd->mtx);
218*72ef722bSBjoern A. Zeeb 	rlen = dpaa2_cons_read_bs(cd, 0, &hdr, sizeof(hdr));
219*72ef722bSBjoern A. Zeeb 	KASSERT(rlen == sizeof(hdr), ("%s:%d: rlen %zu != count %zu, cdev %p "
220*72ef722bSBjoern A. Zeeb 	    "cd %p\n", __func__, __LINE__, rlen, sizeof(hdr), cdev, cd));
221*72ef722bSBjoern A. Zeeb 
222*72ef722bSBjoern A. Zeeb 	cd->hdr_magic = hdr.magic;
223*72ef722bSBjoern A. Zeeb 	if (cd->hdr_magic != cd->magic) {
224*72ef722bSBjoern A. Zeeb 		mtx_unlock(&cd->mtx);
225*72ef722bSBjoern A. Zeeb 		return (ENODEV);
226*72ef722bSBjoern A. Zeeb 	}
227*72ef722bSBjoern A. Zeeb 
228*72ef722bSBjoern A. Zeeb 	cd->hdr_eobyte = hdr.eobyte;
229*72ef722bSBjoern A. Zeeb 	cd->hdr_start = hdr.start;
230*72ef722bSBjoern A. Zeeb 	cd->hdr_len = hdr.len;
231*72ef722bSBjoern A. Zeeb 
232*72ef722bSBjoern A. Zeeb 	cd->start = cd->hdr_start - cd->offset;
233*72ef722bSBjoern A. Zeeb 	cd->end = cd->start + cd->hdr_len;
234*72ef722bSBjoern A. Zeeb 
235*72ef722bSBjoern A. Zeeb 	wrapped = cd->hdr_eobyte & LOG_HEADER_FLAG_BUFFER_WRAPAROUND;
236*72ef722bSBjoern A. Zeeb 	cd->eod = cd->start + LAST_BYTE(cd->hdr_eobyte);
237*72ef722bSBjoern A. Zeeb 
238*72ef722bSBjoern A. Zeeb 	if (wrapped && cd->eod != cd->end)
239*72ef722bSBjoern A. Zeeb 		cd->cur = cd->eod + 1;
240*72ef722bSBjoern A. Zeeb 	else
241*72ef722bSBjoern A. Zeeb 		cd->cur = cd->start;
242*72ef722bSBjoern A. Zeeb 	mtx_unlock(&cd->mtx);
243*72ef722bSBjoern A. Zeeb 
244*72ef722bSBjoern A. Zeeb 	return (0);
245*72ef722bSBjoern A. Zeeb }
246*72ef722bSBjoern A. Zeeb 
247*72ef722bSBjoern A. Zeeb static int
dpaa2_cons_close(struct cdev * cdev,int flags,int fmt,struct thread * td)248*72ef722bSBjoern A. Zeeb dpaa2_cons_close(struct cdev *cdev, int flags, int fmt, struct thread *td)
249*72ef722bSBjoern A. Zeeb {
250*72ef722bSBjoern A. Zeeb 	struct dpaa2_cons_dev *cd;
251*72ef722bSBjoern A. Zeeb 
252*72ef722bSBjoern A. Zeeb 	cd = cdev->si_drv1;
253*72ef722bSBjoern A. Zeeb 	mtx_lock(&cd->mtx);
254*72ef722bSBjoern A. Zeeb 	cd->hdr_magic = cd->hdr_eobyte = cd->hdr_start = cd->hdr_len = 0;
255*72ef722bSBjoern A. Zeeb 	cd->start = cd->end = 0;
256*72ef722bSBjoern A. Zeeb 	mtx_unlock(&cd->mtx);
257*72ef722bSBjoern A. Zeeb 
258*72ef722bSBjoern A. Zeeb 	return (0);
259*72ef722bSBjoern A. Zeeb }
260*72ef722bSBjoern A. Zeeb 
261*72ef722bSBjoern A. Zeeb static int
dpaa2_cons_read(struct cdev * cdev,struct uio * uio,int flag)262*72ef722bSBjoern A. Zeeb dpaa2_cons_read(struct cdev *cdev, struct uio *uio, int flag)
263*72ef722bSBjoern A. Zeeb {
264*72ef722bSBjoern A. Zeeb         char buf[128];
265*72ef722bSBjoern A. Zeeb 	struct dpaa2_cons_dev *cd;
266*72ef722bSBjoern A. Zeeb 	size_t len, size, count;
267*72ef722bSBjoern A. Zeeb 	size_t rlen __diagused;
268*72ef722bSBjoern A. Zeeb 	int error;
269*72ef722bSBjoern A. Zeeb 
270*72ef722bSBjoern A. Zeeb 	cd = cdev->si_drv1;
271*72ef722bSBjoern A. Zeeb 	mtx_lock(&cd->mtx);
272*72ef722bSBjoern A. Zeeb 	if (cd->cur == cd->eod) {
273*72ef722bSBjoern A. Zeeb 		mtx_unlock(&cd->mtx);
274*72ef722bSBjoern A. Zeeb 		return (0);
275*72ef722bSBjoern A. Zeeb 	} else if (cd->cur > cd->eod) {
276*72ef722bSBjoern A. Zeeb 		len = (cd->end - cd->cur) + (cd->eod - cd->start);
277*72ef722bSBjoern A. Zeeb 	} else {
278*72ef722bSBjoern A. Zeeb 		len = cd->eod - cd->cur;
279*72ef722bSBjoern A. Zeeb 	}
280*72ef722bSBjoern A. Zeeb 	size = cd->end - cd->cur;
281*72ef722bSBjoern A. Zeeb 
282*72ef722bSBjoern A. Zeeb 	if (len > size) {
283*72ef722bSBjoern A. Zeeb 		/* dump cur [size] */
284*72ef722bSBjoern A. Zeeb 		while (uio->uio_resid > 0) {
285*72ef722bSBjoern A. Zeeb 			count = imin(sizeof(buf), uio->uio_resid);
286*72ef722bSBjoern A. Zeeb 			if (count > size)
287*72ef722bSBjoern A. Zeeb 				count = size;
288*72ef722bSBjoern A. Zeeb 
289*72ef722bSBjoern A. Zeeb 			rlen = dpaa2_cons_read_bs(cd, cd->cur, buf, count);
290*72ef722bSBjoern A. Zeeb 			KASSERT(rlen == count, ("%s:%d: rlen %zu != count %zu, "
291*72ef722bSBjoern A. Zeeb 			    "cdev %p cd %p\n", __func__, __LINE__, rlen, count,
292*72ef722bSBjoern A. Zeeb 			    cdev, cd));
293*72ef722bSBjoern A. Zeeb 
294*72ef722bSBjoern A. Zeeb 			cd->cur += count;
295*72ef722bSBjoern A. Zeeb 			len -= count;
296*72ef722bSBjoern A. Zeeb 			size -= count;
297*72ef722bSBjoern A. Zeeb 			mtx_unlock(&cd->mtx);
298*72ef722bSBjoern A. Zeeb 			error = uiomove(buf, count, uio);
299*72ef722bSBjoern A. Zeeb 			if (error != 0 || uio->uio_resid == 0)
300*72ef722bSBjoern A. Zeeb 				return (error);
301*72ef722bSBjoern A. Zeeb 			mtx_lock(&cd->mtx);
302*72ef722bSBjoern A. Zeeb 		}
303*72ef722bSBjoern A. Zeeb 		cd->cur = cd->start;
304*72ef722bSBjoern A. Zeeb 	}
305*72ef722bSBjoern A. Zeeb 
306*72ef722bSBjoern A. Zeeb 	/* dump cur [len] */
307*72ef722bSBjoern A. Zeeb 	while (len > 0 && uio->uio_resid > 0) {
308*72ef722bSBjoern A. Zeeb 		count = imin(sizeof(buf), uio->uio_resid);
309*72ef722bSBjoern A. Zeeb 		if (count > len)
310*72ef722bSBjoern A. Zeeb 			count = len;
311*72ef722bSBjoern A. Zeeb 
312*72ef722bSBjoern A. Zeeb 		rlen = dpaa2_cons_read_bs(cd, cd->cur, buf, count);
313*72ef722bSBjoern A. Zeeb 		KASSERT(rlen == count, ("%s:%d: rlen %zu != count %zu, cdev %p "
314*72ef722bSBjoern A. Zeeb 		    "cd %p\n", __func__, __LINE__, rlen, count, cdev, cd));
315*72ef722bSBjoern A. Zeeb 
316*72ef722bSBjoern A. Zeeb 		cd->cur += count;
317*72ef722bSBjoern A. Zeeb 		len -= count;
318*72ef722bSBjoern A. Zeeb 		mtx_unlock(&cd->mtx);
319*72ef722bSBjoern A. Zeeb 		error = uiomove(buf, count, uio);
320*72ef722bSBjoern A. Zeeb 		if (error != 0 || uio->uio_resid == 0)
321*72ef722bSBjoern A. Zeeb 			return (error);
322*72ef722bSBjoern A. Zeeb 		mtx_lock(&cd->mtx);
323*72ef722bSBjoern A. Zeeb 	}
324*72ef722bSBjoern A. Zeeb 	mtx_unlock(&cd->mtx);
325*72ef722bSBjoern A. Zeeb 	return (0);
326*72ef722bSBjoern A. Zeeb }
327*72ef722bSBjoern A. Zeeb 
328*72ef722bSBjoern A. Zeeb static int
dpaa2_cons_create_dev(device_t dev,bus_addr_t pa,size_t size,size_t offset,uint32_t magic,struct cdevsw * devsw,struct dpaa2_cons_dev * cd)329*72ef722bSBjoern A. Zeeb dpaa2_cons_create_dev(device_t dev, bus_addr_t pa, size_t size,
330*72ef722bSBjoern A. Zeeb     size_t offset, uint32_t magic, struct cdevsw *devsw,
331*72ef722bSBjoern A. Zeeb     struct dpaa2_cons_dev *cd)
332*72ef722bSBjoern A. Zeeb {
333*72ef722bSBjoern A. Zeeb 	struct dpaa2_cons_softc *sc;
334*72ef722bSBjoern A. Zeeb 	struct dpaa2_cons_hdr hdr;
335*72ef722bSBjoern A. Zeeb 	struct make_dev_args md_args;
336*72ef722bSBjoern A. Zeeb 	size_t len;
337*72ef722bSBjoern A. Zeeb 	int error;
338*72ef722bSBjoern A. Zeeb 
339*72ef722bSBjoern A. Zeeb 	sc = device_get_softc(dev);
340*72ef722bSBjoern A. Zeeb 
341*72ef722bSBjoern A. Zeeb 	error = bus_space_map(sc->bst, pa, size, 0, &cd->bsh);
342*72ef722bSBjoern A. Zeeb 	if (error != 0) {
343*72ef722bSBjoern A. Zeeb 		device_printf(dev, "%s: failed to map bus space %#jx/%zu: %d\n",
344*72ef722bSBjoern A. Zeeb 		    __func__, (uintmax_t)pa, size, error);
345*72ef722bSBjoern A. Zeeb 		return (error);
346*72ef722bSBjoern A. Zeeb 	}
347*72ef722bSBjoern A. Zeeb 
348*72ef722bSBjoern A. Zeeb 	cd->bst = sc->bst;
349*72ef722bSBjoern A. Zeeb 	cd->size = size;
350*72ef722bSBjoern A. Zeeb 
351*72ef722bSBjoern A. Zeeb 	len = dpaa2_cons_read_bs(cd, 0, &hdr, sizeof(hdr));
352*72ef722bSBjoern A. Zeeb 	if (len != sizeof(hdr)) {
353*72ef722bSBjoern A. Zeeb 		device_printf(dev, "%s: failed to read hdr for %#jx/%zu: %d\n",
354*72ef722bSBjoern A. Zeeb 		    __func__, (uintmax_t)pa, size, error);
355*72ef722bSBjoern A. Zeeb 		bus_space_unmap(cd->bst, cd->bsh, cd->size);
356*72ef722bSBjoern A. Zeeb 		cd->size = 0;
357*72ef722bSBjoern A. Zeeb 		return (EIO);
358*72ef722bSBjoern A. Zeeb 	}
359*72ef722bSBjoern A. Zeeb 
360*72ef722bSBjoern A. Zeeb 	/* This checks probably needs to be removed one day? */
361*72ef722bSBjoern A. Zeeb 	if (hdr.magic != magic) {
362*72ef722bSBjoern A. Zeeb 		if (bootverbose)
363*72ef722bSBjoern A. Zeeb 			device_printf(dev, "%s: magic wrong for %#jx/%zu: "
364*72ef722bSBjoern A. Zeeb 			    "%#010x != %#010x, no console?\n", __func__,
365*72ef722bSBjoern A. Zeeb 			    (uintmax_t)pa, size, hdr.magic, magic);
366*72ef722bSBjoern A. Zeeb 		bus_space_unmap(cd->bst, cd->bsh, cd->size);
367*72ef722bSBjoern A. Zeeb 		cd->size = 0;
368*72ef722bSBjoern A. Zeeb 		return (ENOENT);
369*72ef722bSBjoern A. Zeeb 	}
370*72ef722bSBjoern A. Zeeb 
371*72ef722bSBjoern A. Zeeb 	if (hdr.start - offset > size) {
372*72ef722bSBjoern A. Zeeb 		device_printf(dev, "%s: range wrong for %#jx/%zu: %u - %zu > %zu\n",
373*72ef722bSBjoern A. Zeeb 		    __func__, (uintmax_t)pa, size, hdr.start, offset, size);
374*72ef722bSBjoern A. Zeeb 		bus_space_unmap(cd->bst, cd->bsh, cd->size);
375*72ef722bSBjoern A. Zeeb 		cd->size = 0;
376*72ef722bSBjoern A. Zeeb 		return (ERANGE);
377*72ef722bSBjoern A. Zeeb 	}
378*72ef722bSBjoern A. Zeeb 
379*72ef722bSBjoern A. Zeeb 	cd->offset = offset;
380*72ef722bSBjoern A. Zeeb 	cd->magic = magic;
381*72ef722bSBjoern A. Zeeb 	mtx_init(&cd->mtx, "dpaa2 cons", NULL, MTX_DEF);
382*72ef722bSBjoern A. Zeeb 
383*72ef722bSBjoern A. Zeeb 	make_dev_args_init(&md_args);
384*72ef722bSBjoern A. Zeeb 	md_args.mda_devsw = devsw;
385*72ef722bSBjoern A. Zeeb 	md_args.mda_uid = 0;
386*72ef722bSBjoern A. Zeeb 	md_args.mda_gid = 69;
387*72ef722bSBjoern A. Zeeb 	md_args.mda_mode = S_IRUSR | S_IRGRP;
388*72ef722bSBjoern A. Zeeb 	md_args.mda_unit = 0;
389*72ef722bSBjoern A. Zeeb 	md_args.mda_si_drv1 = cd;
390*72ef722bSBjoern A. Zeeb 	error = make_dev_s(&md_args, &cd->cdev, "%s", devsw->d_name);
391*72ef722bSBjoern A. Zeeb 	if (error != 0) {
392*72ef722bSBjoern A. Zeeb 		device_printf(dev, "%s: make_dev_s failed for %#jx/%zu: %d\n",
393*72ef722bSBjoern A. Zeeb 		    __func__, (uintmax_t)pa, size, error);
394*72ef722bSBjoern A. Zeeb 		mtx_destroy(&cd->mtx);
395*72ef722bSBjoern A. Zeeb 		bus_space_unmap(cd->bst, cd->bsh, cd->size);
396*72ef722bSBjoern A. Zeeb 		cd->size = 0;
397*72ef722bSBjoern A. Zeeb 		return (error);
398*72ef722bSBjoern A. Zeeb 	}
399*72ef722bSBjoern A. Zeeb 
400*72ef722bSBjoern A. Zeeb 	if (bootverbose)
401*72ef722bSBjoern A. Zeeb 		device_printf(dev, "dpaa2 console %s at pa %#jx size %#010zx "
402*72ef722bSBjoern A. Zeeb 		    "(%zu) offset %zu, hdr: magic %#010x start %#010x len "
403*72ef722bSBjoern A. Zeeb 		    "%#010x eobyte %#010x\n", devsw->d_name, (uintmax_t)pa,
404*72ef722bSBjoern A. Zeeb 		    size, size, offset, hdr.magic, hdr.start, hdr.len, hdr.eobyte);
405*72ef722bSBjoern A. Zeeb 
406*72ef722bSBjoern A. Zeeb 	return (0);
407*72ef722bSBjoern A. Zeeb }
408*72ef722bSBjoern A. Zeeb 
409*72ef722bSBjoern A. Zeeb static int
dpaa2_cons_attach_common(device_t dev)410*72ef722bSBjoern A. Zeeb dpaa2_cons_attach_common(device_t dev)
411*72ef722bSBjoern A. Zeeb {
412*72ef722bSBjoern A. Zeeb 	struct dpaa2_cons_softc *sc;
413*72ef722bSBjoern A. Zeeb 
414*72ef722bSBjoern A. Zeeb 	sc = device_get_softc(dev);
415*72ef722bSBjoern A. Zeeb 
416*72ef722bSBjoern A. Zeeb 	dpaa2_cons_create_dev(dev, (bus_addr_t)(sc->mcfba + MC_BUFFER_OFFSET),
417*72ef722bSBjoern A. Zeeb 	    MC_BUFFER_SIZE, MC_OFFSET_DELTA, MAGIC_MC,
418*72ef722bSBjoern A. Zeeb 	    &dpaa2_mc_cons_cdevsw, &sc->mc_cd);
419*72ef722bSBjoern A. Zeeb 
420*72ef722bSBjoern A. Zeeb 	dpaa2_cons_create_dev(dev, (bus_addr_t)(sc->mcfba + AIOP_BUFFER_OFFSET),
421*72ef722bSBjoern A. Zeeb 	    AIOP_BUFFER_SIZE, AIOP_OFFSET_DELTA, MAGIC_AIOP,
422*72ef722bSBjoern A. Zeeb 	    &dpaa2_aiop_cons_cdevsw, &sc->aiop_cd);
423*72ef722bSBjoern A. Zeeb 
424*72ef722bSBjoern A. Zeeb 	return (0);
425*72ef722bSBjoern A. Zeeb }
426*72ef722bSBjoern A. Zeeb 
427*72ef722bSBjoern A. Zeeb static void
dpaa2_cons_detach_common(struct dpaa2_cons_dev * cd)428*72ef722bSBjoern A. Zeeb dpaa2_cons_detach_common(struct dpaa2_cons_dev *cd)
429*72ef722bSBjoern A. Zeeb {
430*72ef722bSBjoern A. Zeeb 
431*72ef722bSBjoern A. Zeeb 	bus_space_unmap(cd->bst, cd->bsh, cd->size);
432*72ef722bSBjoern A. Zeeb 	mtx_destroy(&cd->mtx);
433*72ef722bSBjoern A. Zeeb }
434*72ef722bSBjoern A. Zeeb 
435*72ef722bSBjoern A. Zeeb static int
dpaa2_cons_detach(device_t dev)436*72ef722bSBjoern A. Zeeb dpaa2_cons_detach(device_t dev)
437*72ef722bSBjoern A. Zeeb {
438*72ef722bSBjoern A. Zeeb 	struct dpaa2_cons_softc *sc;
439*72ef722bSBjoern A. Zeeb 
440*72ef722bSBjoern A. Zeeb 	sc = device_get_softc(dev);
441*72ef722bSBjoern A. Zeeb 
442*72ef722bSBjoern A. Zeeb 	if (sc->aiop_cd.cdev != NULL)
443*72ef722bSBjoern A. Zeeb 		destroy_dev(sc->aiop_cd.cdev);
444*72ef722bSBjoern A. Zeeb 	if (sc->mc_cd.cdev != NULL)
445*72ef722bSBjoern A. Zeeb 		destroy_dev(sc->mc_cd.cdev);
446*72ef722bSBjoern A. Zeeb 
447*72ef722bSBjoern A. Zeeb 	if (sc->aiop_cd.size != 0)
448*72ef722bSBjoern A. Zeeb 		dpaa2_cons_detach_common(&sc->aiop_cd);
449*72ef722bSBjoern A. Zeeb 	if (sc->mc_cd.size != 0)
450*72ef722bSBjoern A. Zeeb 		dpaa2_cons_detach_common(&sc->mc_cd);
451*72ef722bSBjoern A. Zeeb 
452*72ef722bSBjoern A. Zeeb 	if (sc->res != NULL)
453*72ef722bSBjoern A. Zeeb 		bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->res),
454*72ef722bSBjoern A. Zeeb 		    sc->res);
455*72ef722bSBjoern A. Zeeb 
456*72ef722bSBjoern A. Zeeb 	return (0);
457*72ef722bSBjoern A. Zeeb }
458*72ef722bSBjoern A. Zeeb 
459*72ef722bSBjoern A. Zeeb #ifdef __notyet__
460*72ef722bSBjoern A. Zeeb #ifdef ACPI
461*72ef722bSBjoern A. Zeeb static int
dpaa2_cons_acpi_probe(device_t dev)462*72ef722bSBjoern A. Zeeb dpaa2_cons_acpi_probe(device_t dev)
463*72ef722bSBjoern A. Zeeb {
464*72ef722bSBjoern A. Zeeb 
465*72ef722bSBjoern A. Zeeb 	if (!ofw_bus_status_okay(dev))
466*72ef722bSBjoern A. Zeeb 		return (ENXIO);
467*72ef722bSBjoern A. Zeeb 
468*72ef722bSBjoern A. Zeeb 	device_set_desc(dev, "DPAA2 Console Driver");
469*72ef722bSBjoern A. Zeeb 	return (BUS_PROBE_DEFAULT);
470*72ef722bSBjoern A. Zeeb }
471*72ef722bSBjoern A. Zeeb 
472*72ef722bSBjoern A. Zeeb static int
dpaa2_cons_acpi_attach(device_t dev)473*72ef722bSBjoern A. Zeeb dpaa2_cons_acpi_attach(device_t dev)
474*72ef722bSBjoern A. Zeeb {
475*72ef722bSBjoern A. Zeeb 	struct dpaa2_cons_softc *sc;
476*72ef722bSBjoern A. Zeeb 	uint32_t val;
477*72ef722bSBjoern A. Zeeb 	int error, rid;
478*72ef722bSBjoern A. Zeeb 
479*72ef722bSBjoern A. Zeeb 	sc = device_get_softc(dev);
480*72ef722bSBjoern A. Zeeb 
481*72ef722bSBjoern A. Zeeb 	rid = 0;
482*72ef722bSBjoern A. Zeeb 	sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
483*72ef722bSBjoern A. Zeeb 	if (sc->res == NULL) {
484*72ef722bSBjoern A. Zeeb 		device_printf(dev, "Could not allocate memory\n");
485*72ef722bSBjoern A. Zeeb 		return (ENXIO);
486*72ef722bSBjoern A. Zeeb 	}
487*72ef722bSBjoern A. Zeeb 	sc->bst = rman_get_bustag(sc->res);
488*72ef722bSBjoern A. Zeeb 
489*72ef722bSBjoern A. Zeeb 	val = DPAA2_MC_READ_4(sc, MC_REG_MCFBALR_OFF);
490*72ef722bSBjoern A. Zeeb 	val &= MC_REG_MCFBALR_MASK;
491*72ef722bSBjoern A. Zeeb 	sc->mcfba |= val;
492*72ef722bSBjoern A. Zeeb 	val = DPAA2_MC_READ_4(sc, MC_REG_MCFBAHR_OFF);
493*72ef722bSBjoern A. Zeeb 	val &= MC_REG_MCFBAHR_MASK;
494*72ef722bSBjoern A. Zeeb 	sc->mcfba |= ((uint64_t)val << 32);
495*72ef722bSBjoern A. Zeeb 
496*72ef722bSBjoern A. Zeeb 	error = dpaa2_cons_attach_common(dev);
497*72ef722bSBjoern A. Zeeb 
498*72ef722bSBjoern A. Zeeb 	return (error);
499*72ef722bSBjoern A. Zeeb }
500*72ef722bSBjoern A. Zeeb 
501*72ef722bSBjoern A. Zeeb static device_method_t dpaa2_cons_acpi_methods[] = {
502*72ef722bSBjoern A. Zeeb 	/* Device interface */
503*72ef722bSBjoern A. Zeeb 	DEVMETHOD(device_probe,		dpaa2_cons_acpi_probe),
504*72ef722bSBjoern A. Zeeb 	DEVMETHOD(device_attach,	dpaa2_cons_acpi_attach),
505*72ef722bSBjoern A. Zeeb 	DEVMETHOD(device_detach,	dpaa2_cons_detach),
506*72ef722bSBjoern A. Zeeb 
507*72ef722bSBjoern A. Zeeb 	DEVMETHOD_END
508*72ef722bSBjoern A. Zeeb };
509*72ef722bSBjoern A. Zeeb 
510*72ef722bSBjoern A. Zeeb DEFINE_CLASS_0(dpaa2_cons_acpi, dpaa2_cons_acpi_driver, dpaa2_cons_acpi_methods,
511*72ef722bSBjoern A. Zeeb     sizeof(struct dpaa2_cons_softc));
512*72ef722bSBjoern A. Zeeb 
513*72ef722bSBjoern A. Zeeb DRIVER_MODULE(dpaa2_cons_acpi, simplebus, dpaa2_cons_acpi_driver, 0, 0);
514*72ef722bSBjoern A. Zeeb MODULE_DEPEND(dpaa2_cons_acpi, dpaa2_mc, 1, 1, 1);
515*72ef722bSBjoern A. Zeeb #endif
516*72ef722bSBjoern A. Zeeb #endif /* 0 */
517*72ef722bSBjoern A. Zeeb 
518*72ef722bSBjoern A. Zeeb #ifdef FDT
519*72ef722bSBjoern A. Zeeb static struct ofw_compat_data compat_data[] = {
520*72ef722bSBjoern A. Zeeb 	{ "fsl,dpaa2-console",		1 },
521*72ef722bSBjoern A. Zeeb 	{ NULL,				0 }
522*72ef722bSBjoern A. Zeeb };
523*72ef722bSBjoern A. Zeeb 
524*72ef722bSBjoern A. Zeeb static int
dpaa2_cons_fdt_probe(device_t dev)525*72ef722bSBjoern A. Zeeb dpaa2_cons_fdt_probe(device_t dev)
526*72ef722bSBjoern A. Zeeb {
527*72ef722bSBjoern A. Zeeb 
528*72ef722bSBjoern A. Zeeb 	if (!ofw_bus_status_okay(dev))
529*72ef722bSBjoern A. Zeeb 		return (ENXIO);
530*72ef722bSBjoern A. Zeeb 
531*72ef722bSBjoern A. Zeeb 	if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
532*72ef722bSBjoern A. Zeeb 		return (ENXIO);
533*72ef722bSBjoern A. Zeeb 
534*72ef722bSBjoern A. Zeeb 	device_set_desc(dev, "DPAA2 Console Driver");
535*72ef722bSBjoern A. Zeeb 	return (BUS_PROBE_DEFAULT);
536*72ef722bSBjoern A. Zeeb }
537*72ef722bSBjoern A. Zeeb 
538*72ef722bSBjoern A. Zeeb static int
dpaa2_cons_fdt_attach(device_t dev)539*72ef722bSBjoern A. Zeeb dpaa2_cons_fdt_attach(device_t dev)
540*72ef722bSBjoern A. Zeeb {
541*72ef722bSBjoern A. Zeeb 	struct dpaa2_cons_softc *sc;
542*72ef722bSBjoern A. Zeeb 	uint32_t val;
543*72ef722bSBjoern A. Zeeb 	int error, rid;
544*72ef722bSBjoern A. Zeeb 
545*72ef722bSBjoern A. Zeeb 	sc = device_get_softc(dev);
546*72ef722bSBjoern A. Zeeb 
547*72ef722bSBjoern A. Zeeb 	rid = 0;
548*72ef722bSBjoern A. Zeeb 	sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
549*72ef722bSBjoern A. Zeeb 	if (sc->res == NULL) {
550*72ef722bSBjoern A. Zeeb 		device_printf(dev, "Could not allocate memory\n");
551*72ef722bSBjoern A. Zeeb 		return (ENXIO);
552*72ef722bSBjoern A. Zeeb 	}
553*72ef722bSBjoern A. Zeeb 	sc->bst = rman_get_bustag(sc->res);
554*72ef722bSBjoern A. Zeeb 
555*72ef722bSBjoern A. Zeeb 	val = DPAA2_MC_READ_4(sc, MC_REG_MCFBALR_OFF);
556*72ef722bSBjoern A. Zeeb 	val &= MC_REG_MCFBALR_MASK;
557*72ef722bSBjoern A. Zeeb 	sc->mcfba |= val;
558*72ef722bSBjoern A. Zeeb 	val = DPAA2_MC_READ_4(sc, MC_REG_MCFBAHR_OFF);
559*72ef722bSBjoern A. Zeeb 	val &= MC_REG_MCFBAHR_MASK;
560*72ef722bSBjoern A. Zeeb 	sc->mcfba |= ((uint64_t)val << 32);
561*72ef722bSBjoern A. Zeeb 
562*72ef722bSBjoern A. Zeeb 	error = dpaa2_cons_attach_common(dev);
563*72ef722bSBjoern A. Zeeb 
564*72ef722bSBjoern A. Zeeb 	return (error);
565*72ef722bSBjoern A. Zeeb }
566*72ef722bSBjoern A. Zeeb 
567*72ef722bSBjoern A. Zeeb static device_method_t dpaa2_cons_fdt_methods[] = {
568*72ef722bSBjoern A. Zeeb 	/* Device interface */
569*72ef722bSBjoern A. Zeeb 	DEVMETHOD(device_probe,		dpaa2_cons_fdt_probe),
570*72ef722bSBjoern A. Zeeb 	DEVMETHOD(device_attach,	dpaa2_cons_fdt_attach),
571*72ef722bSBjoern A. Zeeb 	DEVMETHOD(device_detach,	dpaa2_cons_detach),
572*72ef722bSBjoern A. Zeeb 
573*72ef722bSBjoern A. Zeeb 	DEVMETHOD_END
574*72ef722bSBjoern A. Zeeb };
575*72ef722bSBjoern A. Zeeb 
576*72ef722bSBjoern A. Zeeb DEFINE_CLASS_0(dpaa2_cons_fdt, dpaa2_cons_fdt_driver, dpaa2_cons_fdt_methods,
577*72ef722bSBjoern A. Zeeb     sizeof(struct dpaa2_cons_softc));
578*72ef722bSBjoern A. Zeeb 
579*72ef722bSBjoern A. Zeeb DRIVER_MODULE(dpaa2_cons_fdt, simplebus, dpaa2_cons_fdt_driver, 0, 0);
580*72ef722bSBjoern A. Zeeb #endif
581