xref: /freebsd/sys/dev/bhnd/bhnd_erom.c (revision fdafd315)
1664a7497SLandon J. Fuller /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
36e778a7eSPedro F. Giffuni  *
4664a7497SLandon J. Fuller  * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
589294a78SLandon J. Fuller  * Copyright (c) 2017 The FreeBSD Foundation
6664a7497SLandon J. Fuller  * All rights reserved.
7664a7497SLandon J. Fuller  *
889294a78SLandon J. Fuller  * Portions of this software were developed by Landon Fuller
989294a78SLandon J. Fuller  * under sponsorship from the FreeBSD Foundation.
1089294a78SLandon J. Fuller  *
11664a7497SLandon J. Fuller  * Redistribution and use in source and binary forms, with or without
12664a7497SLandon J. Fuller  * modification, are permitted provided that the following conditions
13664a7497SLandon J. Fuller  * are met:
14664a7497SLandon J. Fuller  * 1. Redistributions of source code must retain the above copyright
15664a7497SLandon J. Fuller  *    notice, this list of conditions and the following disclaimer,
16664a7497SLandon J. Fuller  *    without modification.
17664a7497SLandon J. Fuller  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
18664a7497SLandon J. Fuller  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
19664a7497SLandon J. Fuller  *    redistribution must be conditioned upon including a substantially
20664a7497SLandon J. Fuller  *    similar Disclaimer requirement for further binary redistribution.
21664a7497SLandon J. Fuller  *
22664a7497SLandon J. Fuller  * NO WARRANTY
23664a7497SLandon J. Fuller  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24664a7497SLandon J. Fuller  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25664a7497SLandon J. Fuller  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
26664a7497SLandon J. Fuller  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
27664a7497SLandon J. Fuller  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
28664a7497SLandon J. Fuller  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29664a7497SLandon J. Fuller  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30664a7497SLandon J. Fuller  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
31664a7497SLandon J. Fuller  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32664a7497SLandon J. Fuller  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
33664a7497SLandon J. Fuller  * THE POSSIBILITY OF SUCH DAMAGES.
34664a7497SLandon J. Fuller  */
35664a7497SLandon J. Fuller 
36664a7497SLandon J. Fuller #include <sys/param.h>
3789294a78SLandon J. Fuller #include <sys/bus.h>
38664a7497SLandon J. Fuller #include <sys/kobj.h>
39664a7497SLandon J. Fuller 
4089294a78SLandon J. Fuller #include <machine/bus.h>
4189294a78SLandon J. Fuller #include <sys/rman.h>
4289294a78SLandon J. Fuller #include <machine/resource.h>
4389294a78SLandon J. Fuller 
44f3524ec8SLandon J. Fuller #include <dev/bhnd/bhndreg.h>
45664a7497SLandon J. Fuller #include <dev/bhnd/bhndvar.h>
46f3524ec8SLandon J. Fuller 
47664a7497SLandon J. Fuller #include <dev/bhnd/bhnd_erom.h>
4889294a78SLandon J. Fuller #include <dev/bhnd/bhnd_eromvar.h>
4989294a78SLandon J. Fuller 
50f3524ec8SLandon J. Fuller #include <dev/bhnd/cores/chipc/chipcreg.h>
51f3524ec8SLandon J. Fuller 
5289294a78SLandon J. Fuller static int	bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
5389294a78SLandon J. Fuller 		    bhnd_size_t size);
54f3524ec8SLandon J. Fuller static int	bhnd_erom_iores_tell(struct bhnd_erom_io *eio,
55f3524ec8SLandon J. Fuller 		    bhnd_addr_t *addr, bhnd_size_t *size);
5689294a78SLandon J. Fuller static uint32_t	bhnd_erom_iores_read(struct bhnd_erom_io *eio,
5789294a78SLandon J. Fuller 		    bhnd_size_t offset, u_int width);
5889294a78SLandon J. Fuller static void	bhnd_erom_iores_fini(struct bhnd_erom_io *eio);
5989294a78SLandon J. Fuller 
6089294a78SLandon J. Fuller static int	bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
6189294a78SLandon J. Fuller 		    bhnd_size_t size);
62f3524ec8SLandon J. Fuller static int	bhnd_erom_iobus_tell(struct bhnd_erom_io *eio,
63f3524ec8SLandon J. Fuller 		    bhnd_addr_t *addr, bhnd_size_t *size);
6489294a78SLandon J. Fuller static uint32_t	bhnd_erom_iobus_read(struct bhnd_erom_io *eio,
6589294a78SLandon J. Fuller 		    bhnd_size_t offset, u_int width);
6689294a78SLandon J. Fuller 
6789294a78SLandon J. Fuller /**
6889294a78SLandon J. Fuller  * An implementation of bhnd_erom_io that manages mappings via
6989294a78SLandon J. Fuller  * bhnd_alloc_resource() and bhnd_release_resource().
7089294a78SLandon J. Fuller  */
7189294a78SLandon J. Fuller struct bhnd_erom_iores {
7289294a78SLandon J. Fuller 	struct bhnd_erom_io	 eio;
7389294a78SLandon J. Fuller 	device_t		 owner;		/**< device from which we'll allocate resources */
7489294a78SLandon J. Fuller 	int			 owner_rid;	/**< rid to use when allocating new mappings */
7589294a78SLandon J. Fuller 	struct bhnd_resource	*mapped;	/**< current mapping, or NULL */
7689294a78SLandon J. Fuller 	int			 mapped_rid;	/**< resource ID of current mapping, or -1 */
7789294a78SLandon J. Fuller };
7889294a78SLandon J. Fuller 
7989294a78SLandon J. Fuller /**
8089294a78SLandon J. Fuller  * Fetch the device enumeration parser class from all bhnd(4)-compatible drivers
8189294a78SLandon J. Fuller  * registered for @p bus_devclass, probe @p eio for supporting parser classes,
8289294a78SLandon J. Fuller  * and return the best available supporting enumeration parser class.
8389294a78SLandon J. Fuller  *
8489294a78SLandon J. Fuller  * @param	bus_devclass	The bus device class to be queried for
8589294a78SLandon J. Fuller  *				bhnd(4)-compatible drivers.
8689294a78SLandon J. Fuller  * @param	eio		An erom bus I/O instance, configured with a
8789294a78SLandon J. Fuller  *				mapping of the first bus core.
8889294a78SLandon J. Fuller  * @param	hint		Identification hint used to identify the device.
8989294a78SLandon J. Fuller  *				If the chipset supports standard chip
9089294a78SLandon J. Fuller  *				identification registers within the first core,
9189294a78SLandon J. Fuller  *				this parameter should be NULL.
9289294a78SLandon J. Fuller  * @param[out]	cid		On success, the probed chip identifier.
9389294a78SLandon J. Fuller  *
9489294a78SLandon J. Fuller  * @retval non-NULL	on success, the best available EROM class.
9589294a78SLandon J. Fuller  * @retval NULL		if no erom class returned a successful probe result for
9689294a78SLandon J. Fuller  *			@p eio.
9789294a78SLandon J. Fuller  */
9889294a78SLandon J. Fuller bhnd_erom_class_t *
bhnd_erom_probe_driver_classes(devclass_t bus_devclass,struct bhnd_erom_io * eio,const struct bhnd_chipid * hint,struct bhnd_chipid * cid)9989294a78SLandon J. Fuller bhnd_erom_probe_driver_classes(devclass_t bus_devclass,
10089294a78SLandon J. Fuller     struct bhnd_erom_io *eio, const struct bhnd_chipid *hint,
10189294a78SLandon J. Fuller     struct bhnd_chipid *cid)
10289294a78SLandon J. Fuller {
10389294a78SLandon J. Fuller 	driver_t		**drivers;
10489294a78SLandon J. Fuller 	int			 drv_count;
10589294a78SLandon J. Fuller 	bhnd_erom_class_t	*erom_cls;
10689294a78SLandon J. Fuller 	int			 error, prio, result;
10789294a78SLandon J. Fuller 
10889294a78SLandon J. Fuller 	erom_cls = NULL;
10989294a78SLandon J. Fuller 	prio = 0;
11089294a78SLandon J. Fuller 
11189294a78SLandon J. Fuller 	/* Fetch all available drivers */
11289294a78SLandon J. Fuller 	error = devclass_get_drivers(bus_devclass, &drivers, &drv_count);
11389294a78SLandon J. Fuller 	if (error) {
11489294a78SLandon J. Fuller 		printf("error fetching bhnd(4) drivers for %s: %d\n",
11589294a78SLandon J. Fuller 		    devclass_get_name(bus_devclass), error);
11689294a78SLandon J. Fuller 		return (NULL);
11789294a78SLandon J. Fuller 	}
11889294a78SLandon J. Fuller 
11989294a78SLandon J. Fuller 	/* Enumerate the drivers looking for the best available EROM class */
12089294a78SLandon J. Fuller 	for (int i = 0; i < drv_count; i++) {
12189294a78SLandon J. Fuller 		struct bhnd_chipid	 pcid;
12289294a78SLandon J. Fuller 		bhnd_erom_class_t	*cls;
12389294a78SLandon J. Fuller 
12489294a78SLandon J. Fuller 		/* The default implementation of BHND_BUS_GET_EROM_CLASS()
12589294a78SLandon J. Fuller 		 * returns NULL if unimplemented; this should always be safe
12689294a78SLandon J. Fuller 		 * to call on arbitrary drivers */
12789294a78SLandon J. Fuller 		cls = bhnd_driver_get_erom_class(drivers[i]);
12889294a78SLandon J. Fuller 		if (cls == NULL)
12989294a78SLandon J. Fuller 			continue;
13089294a78SLandon J. Fuller 
13189294a78SLandon J. Fuller 		kobj_class_compile(cls);
13289294a78SLandon J. Fuller 
13389294a78SLandon J. Fuller 		/* Probe the bus */
13489294a78SLandon J. Fuller 		result = bhnd_erom_probe(cls, eio, hint, &pcid);
13589294a78SLandon J. Fuller 
13689294a78SLandon J. Fuller 		/* The parser did not match if an error was returned */
13789294a78SLandon J. Fuller 		if (result > 0)
13889294a78SLandon J. Fuller 			continue;
13989294a78SLandon J. Fuller 
14089294a78SLandon J. Fuller 		/* Check for a new highest priority match */
14189294a78SLandon J. Fuller 		if (erom_cls == NULL || result > prio) {
14289294a78SLandon J. Fuller 			prio = result;
14389294a78SLandon J. Fuller 
14489294a78SLandon J. Fuller 			*cid = pcid;
14589294a78SLandon J. Fuller 			erom_cls = cls;
14689294a78SLandon J. Fuller 		}
14789294a78SLandon J. Fuller 
14889294a78SLandon J. Fuller 		/* Terminate immediately on BUS_PROBE_SPECIFIC */
14989294a78SLandon J. Fuller 		if (result == BUS_PROBE_SPECIFIC)
15089294a78SLandon J. Fuller 			break;
15189294a78SLandon J. Fuller 	}
15289294a78SLandon J. Fuller 
1536467a17bSLandon J. Fuller 	free(drivers, M_TEMP);
15489294a78SLandon J. Fuller 	return (erom_cls);
15589294a78SLandon J. Fuller }
156664a7497SLandon J. Fuller 
157664a7497SLandon J. Fuller /**
158664a7497SLandon J. Fuller  * Allocate and return a new device enumeration table parser.
159664a7497SLandon J. Fuller  *
160664a7497SLandon J. Fuller  * @param cls		The parser class for which an instance will be
161664a7497SLandon J. Fuller  *			allocated.
16289294a78SLandon J. Fuller  * @param eio		The bus I/O callbacks to use when reading the device
16389294a78SLandon J. Fuller  *			enumeration table.
164111d7cb2SLandon J. Fuller  * @param cid		The device's chip identifier.
165664a7497SLandon J. Fuller  *
166664a7497SLandon J. Fuller  * @retval non-NULL	success
16788cdf609SGordon Bergling  * @retval NULL		if an error occurred allocating or initializing the
168664a7497SLandon J. Fuller  *			EROM parser.
169664a7497SLandon J. Fuller  */
170664a7497SLandon J. Fuller bhnd_erom_t *
bhnd_erom_alloc(bhnd_erom_class_t * cls,const struct bhnd_chipid * cid,struct bhnd_erom_io * eio)171111d7cb2SLandon J. Fuller bhnd_erom_alloc(bhnd_erom_class_t *cls, const struct bhnd_chipid *cid,
17289294a78SLandon J. Fuller     struct bhnd_erom_io *eio)
173664a7497SLandon J. Fuller {
174664a7497SLandon J. Fuller 	bhnd_erom_t	*erom;
175664a7497SLandon J. Fuller 	int		 error;
176664a7497SLandon J. Fuller 
177664a7497SLandon J. Fuller 	erom = (bhnd_erom_t *)kobj_create((kobj_class_t)cls, M_BHND,
178664a7497SLandon J. Fuller 	    M_WAITOK|M_ZERO);
179664a7497SLandon J. Fuller 
18089294a78SLandon J. Fuller 	if ((error = BHND_EROM_INIT(erom, cid, eio))) {
18189294a78SLandon J. Fuller 		printf("error initializing %s parser at %#jx: %d\n", cls->name,
18289294a78SLandon J. Fuller 		    (uintmax_t)cid->enum_addr, error);
183664a7497SLandon J. Fuller 
184664a7497SLandon J. Fuller 		kobj_delete((kobj_t)erom, M_BHND);
185664a7497SLandon J. Fuller 		return (NULL);
186664a7497SLandon J. Fuller 	}
187664a7497SLandon J. Fuller 
188664a7497SLandon J. Fuller 	return (erom);
189664a7497SLandon J. Fuller }
190664a7497SLandon J. Fuller 
191664a7497SLandon J. Fuller /**
19289294a78SLandon J. Fuller  * Perform static initialization of a device enumeration table parser.
193664a7497SLandon J. Fuller  *
194664a7497SLandon J. Fuller  * This may be used to initialize a caller-allocated erom instance state
195664a7497SLandon J. Fuller  * during early boot, prior to malloc availability.
196664a7497SLandon J. Fuller  *
197664a7497SLandon J. Fuller  * @param cls		The parser class for which an instance will be
198664a7497SLandon J. Fuller  *			allocated.
199664a7497SLandon J. Fuller  * @param erom		The erom parser instance to initialize.
200664a7497SLandon J. Fuller  * @param esize		The total available number of bytes allocated for
201664a7497SLandon J. Fuller  *			@p erom. If this is less than is required by @p cls,
202664a7497SLandon J. Fuller  *			ENOMEM will be returned.
203111d7cb2SLandon J. Fuller  * @param cid		The device's chip identifier.
20489294a78SLandon J. Fuller  * @param eio		The bus I/O callbacks to use when reading the device
20589294a78SLandon J. Fuller  *			enumeration table.
206664a7497SLandon J. Fuller  *
207664a7497SLandon J. Fuller  * @retval 0		success
208664a7497SLandon J. Fuller  * @retval ENOMEM	if @p esize is smaller than required by @p cls.
209664a7497SLandon J. Fuller  * @retval non-zero	if an error occurs initializing the EROM parser,
210664a7497SLandon J. Fuller  *			a regular unix error code will be returned.
211664a7497SLandon J. Fuller  */
212664a7497SLandon J. Fuller int
bhnd_erom_init_static(bhnd_erom_class_t * cls,bhnd_erom_t * erom,size_t esize,const struct bhnd_chipid * cid,struct bhnd_erom_io * eio)213664a7497SLandon J. Fuller bhnd_erom_init_static(bhnd_erom_class_t *cls, bhnd_erom_t *erom, size_t esize,
21489294a78SLandon J. Fuller     const struct bhnd_chipid *cid, struct bhnd_erom_io *eio)
215664a7497SLandon J. Fuller {
216664a7497SLandon J. Fuller 	kobj_class_t	kcls;
217664a7497SLandon J. Fuller 
218664a7497SLandon J. Fuller 	kcls = (kobj_class_t)cls;
219664a7497SLandon J. Fuller 
220664a7497SLandon J. Fuller 	/* Verify allocation size */
221664a7497SLandon J. Fuller 	if (kcls->size > esize)
222664a7497SLandon J. Fuller 		return (ENOMEM);
223664a7497SLandon J. Fuller 
224664a7497SLandon J. Fuller 	/* Perform instance initialization */
225664a7497SLandon J. Fuller 	kobj_init_static((kobj_t)erom, kcls);
22689294a78SLandon J. Fuller 	return (BHND_EROM_INIT(erom, cid, eio));
227664a7497SLandon J. Fuller }
228664a7497SLandon J. Fuller 
229664a7497SLandon J. Fuller /**
230664a7497SLandon J. Fuller  * Release any resources held by a @p erom parser previously
231664a7497SLandon J. Fuller  * initialized via bhnd_erom_init_static().
232664a7497SLandon J. Fuller  *
233664a7497SLandon J. Fuller  * @param	erom	An erom parser instance previously initialized via
234664a7497SLandon J. Fuller  *			bhnd_erom_init_static().
235664a7497SLandon J. Fuller  */
236664a7497SLandon J. Fuller void
bhnd_erom_fini_static(bhnd_erom_t * erom)237664a7497SLandon J. Fuller bhnd_erom_fini_static(bhnd_erom_t *erom)
238664a7497SLandon J. Fuller {
239664a7497SLandon J. Fuller 	return (BHND_EROM_FINI(erom));
240664a7497SLandon J. Fuller }
241664a7497SLandon J. Fuller 
242664a7497SLandon J. Fuller /**
243664a7497SLandon J. Fuller  * Release all resources held by a @p erom parser previously
244664a7497SLandon J. Fuller  * allocated via bhnd_erom_alloc().
245664a7497SLandon J. Fuller  *
246664a7497SLandon J. Fuller  * @param	erom	An erom parser instance previously allocated via
247664a7497SLandon J. Fuller  *			bhnd_erom_alloc().
248664a7497SLandon J. Fuller  */
249664a7497SLandon J. Fuller void
bhnd_erom_free(bhnd_erom_t * erom)250664a7497SLandon J. Fuller bhnd_erom_free(bhnd_erom_t *erom)
251664a7497SLandon J. Fuller {
252664a7497SLandon J. Fuller 	BHND_EROM_FINI(erom);
253664a7497SLandon J. Fuller 	kobj_delete((kobj_t)erom, M_BHND);
254664a7497SLandon J. Fuller }
25589294a78SLandon J. Fuller 
256f3524ec8SLandon J. Fuller /**
257f3524ec8SLandon J. Fuller  * Read the chip identification registers mapped by @p eio, popuating @p cid
258f3524ec8SLandon J. Fuller  * with the parsed result
259f3524ec8SLandon J. Fuller  *
260f3524ec8SLandon J. Fuller  * @param	eio		A bus I/O instance, configured with a mapping
261f3524ec8SLandon J. Fuller  *				of the ChipCommon core.
262f3524ec8SLandon J. Fuller  * @param[out]	cid		On success, the parsed chip identification.
263f3524ec8SLandon J. Fuller  *
264f3524ec8SLandon J. Fuller  * @warning
265f3524ec8SLandon J. Fuller  * On early siba(4) devices, the ChipCommon core does not provide
266f3524ec8SLandon J. Fuller  * a valid CHIPC_ID_NUMCORE field. On these ChipCommon revisions
267f3524ec8SLandon J. Fuller  * (see CHIPC_NCORES_MIN_HWREV()), this function will parse and return
268f3524ec8SLandon J. Fuller  * an invalid `ncores` value.
269f3524ec8SLandon J. Fuller  */
270f3524ec8SLandon J. Fuller int
bhnd_erom_read_chipid(struct bhnd_erom_io * eio,struct bhnd_chipid * cid)271f3524ec8SLandon J. Fuller bhnd_erom_read_chipid(struct bhnd_erom_io *eio, struct bhnd_chipid *cid)
272f3524ec8SLandon J. Fuller {
273f3524ec8SLandon J. Fuller 	bhnd_addr_t	cc_addr;
274f3524ec8SLandon J. Fuller 	bhnd_size_t	cc_size;
275f3524ec8SLandon J. Fuller 	uint32_t	idreg, cc_caps;
276f3524ec8SLandon J. Fuller 	int		error;
277f3524ec8SLandon J. Fuller 
278f3524ec8SLandon J. Fuller 	/* Fetch ChipCommon address */
279f3524ec8SLandon J. Fuller 	if ((error = bhnd_erom_io_tell(eio, &cc_addr, &cc_size)))
280f3524ec8SLandon J. Fuller 		return (error);
281f3524ec8SLandon J. Fuller 
282f3524ec8SLandon J. Fuller 	/* Read chip identifier */
283f3524ec8SLandon J. Fuller 	idreg = bhnd_erom_io_read(eio, CHIPC_ID, 4);
284f3524ec8SLandon J. Fuller 
285f3524ec8SLandon J. Fuller 	/* Extract the basic chip info */
286f3524ec8SLandon J. Fuller 	cid->chip_id = CHIPC_GET_BITS(idreg, CHIPC_ID_CHIP);
287f3524ec8SLandon J. Fuller 	cid->chip_pkg = CHIPC_GET_BITS(idreg, CHIPC_ID_PKG);
288f3524ec8SLandon J. Fuller 	cid->chip_rev = CHIPC_GET_BITS(idreg, CHIPC_ID_REV);
289f3524ec8SLandon J. Fuller 	cid->chip_type = CHIPC_GET_BITS(idreg, CHIPC_ID_BUS);
290f3524ec8SLandon J. Fuller 	cid->ncores = CHIPC_GET_BITS(idreg, CHIPC_ID_NUMCORE);
291f3524ec8SLandon J. Fuller 
292f3524ec8SLandon J. Fuller 	/* Populate EROM address */
293f3524ec8SLandon J. Fuller 	if (BHND_CHIPTYPE_HAS_EROM(cid->chip_type)) {
294f3524ec8SLandon J. Fuller 		cid->enum_addr = bhnd_erom_io_read(eio, CHIPC_EROMPTR, 4);
295f3524ec8SLandon J. Fuller 	} else {
296f3524ec8SLandon J. Fuller 		cid->enum_addr = cc_addr;
297f3524ec8SLandon J. Fuller 	}
298f3524ec8SLandon J. Fuller 
299f3524ec8SLandon J. Fuller 	/* Populate capability flags */
300f3524ec8SLandon J. Fuller 	cc_caps = bhnd_erom_io_read(eio, CHIPC_CAPABILITIES, 4);
301f3524ec8SLandon J. Fuller 	cid->chip_caps = 0x0;
302f3524ec8SLandon J. Fuller 
303f3524ec8SLandon J. Fuller 	if (cc_caps & CHIPC_CAP_BKPLN64)
304f3524ec8SLandon J. Fuller 		cid->chip_caps |= BHND_CAP_BP64;
305f3524ec8SLandon J. Fuller 
306f3524ec8SLandon J. Fuller 	if (cc_caps & CHIPC_CAP_PMU)
307f3524ec8SLandon J. Fuller 		cid->chip_caps |= BHND_CAP_PMU;
308f3524ec8SLandon J. Fuller 
309f3524ec8SLandon J. Fuller 	return (0);
310f3524ec8SLandon J. Fuller }
311f3524ec8SLandon J. Fuller 
31289294a78SLandon J. Fuller /**
31389294a78SLandon J. Fuller  * Attempt to map @p size bytes at @p addr, replacing any existing
31489294a78SLandon J. Fuller  * @p eio mapping.
31589294a78SLandon J. Fuller  *
31689294a78SLandon J. Fuller  * @param eio	I/O instance state.
31789294a78SLandon J. Fuller  * @param addr	The address to be mapped.
31889294a78SLandon J. Fuller  * @param size	The number of bytes to be mapped at @p addr.
31989294a78SLandon J. Fuller  *
32089294a78SLandon J. Fuller  * @retval 0		success
32189294a78SLandon J. Fuller  * @retval non-zero	if mapping @p addr otherwise fails, a regular
32289294a78SLandon J. Fuller  *			unix error code should be returned.
32389294a78SLandon J. Fuller  */
32489294a78SLandon J. Fuller int
bhnd_erom_io_map(struct bhnd_erom_io * eio,bhnd_addr_t addr,bhnd_size_t size)32589294a78SLandon J. Fuller bhnd_erom_io_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, bhnd_size_t size)
32689294a78SLandon J. Fuller {
32789294a78SLandon J. Fuller 	return (eio->map(eio, addr, size));
32889294a78SLandon J. Fuller }
32989294a78SLandon J. Fuller 
33089294a78SLandon J. Fuller /**
331f3524ec8SLandon J. Fuller  * Return the address range mapped by @p eio, if any.
332f3524ec8SLandon J. Fuller  *
333f3524ec8SLandon J. Fuller  * @param	eio	I/O instance state.
334f3524ec8SLandon J. Fuller  * @param[out]	addr	The address mapped by @p eio.
335f3524ec8SLandon J. Fuller  * @param[out]	size	The number of bytes mapped at @p addr.
336f3524ec8SLandon J. Fuller  *
337f3524ec8SLandon J. Fuller  * @retval	0	success
338f3524ec8SLandon J. Fuller  * @retval	ENXIO	if @p eio has no mapping.
339f3524ec8SLandon J. Fuller  */
340f3524ec8SLandon J. Fuller int
bhnd_erom_io_tell(struct bhnd_erom_io * eio,bhnd_addr_t * addr,bhnd_size_t * size)341f3524ec8SLandon J. Fuller bhnd_erom_io_tell(struct bhnd_erom_io *eio, bhnd_addr_t *addr,
342f3524ec8SLandon J. Fuller     bhnd_size_t *size)
343f3524ec8SLandon J. Fuller {
344f3524ec8SLandon J. Fuller 	return (eio->tell(eio, addr, size));
345f3524ec8SLandon J. Fuller }
346f3524ec8SLandon J. Fuller 
347f3524ec8SLandon J. Fuller /**
34889294a78SLandon J. Fuller  * Read a 1, 2, or 4 byte data item from @p eio, at the given @p offset
34989294a78SLandon J. Fuller  * relative to @p eio's current mapping.
35089294a78SLandon J. Fuller  *
35189294a78SLandon J. Fuller  * @param eio		erom I/O callbacks
35289294a78SLandon J. Fuller  * @param offset	read offset.
35389294a78SLandon J. Fuller  * @param width		item width (1, 2, or 4 bytes).
35489294a78SLandon J. Fuller  */
35589294a78SLandon J. Fuller uint32_t
bhnd_erom_io_read(struct bhnd_erom_io * eio,bhnd_size_t offset,u_int width)35689294a78SLandon J. Fuller bhnd_erom_io_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
35789294a78SLandon J. Fuller {
35889294a78SLandon J. Fuller 	return (eio->read(eio, offset, width));
35989294a78SLandon J. Fuller }
36089294a78SLandon J. Fuller 
36189294a78SLandon J. Fuller /**
36289294a78SLandon J. Fuller  * Free all resources held by @p eio.
36389294a78SLandon J. Fuller  */
36489294a78SLandon J. Fuller void
bhnd_erom_io_fini(struct bhnd_erom_io * eio)36589294a78SLandon J. Fuller bhnd_erom_io_fini(struct bhnd_erom_io *eio)
36689294a78SLandon J. Fuller {
36789294a78SLandon J. Fuller 	if (eio->fini != NULL)
36889294a78SLandon J. Fuller 		return (eio->fini(eio));
36989294a78SLandon J. Fuller }
37089294a78SLandon J. Fuller 
37189294a78SLandon J. Fuller /**
37289294a78SLandon J. Fuller  * Allocate, initialize, and return a new I/O instance that will perform
37389294a78SLandon J. Fuller  * mapping by allocating SYS_RES_MEMORY resources from @p dev using @p rid.
37489294a78SLandon J. Fuller  *
37589294a78SLandon J. Fuller  * @param dev	The device to pass to bhnd_alloc_resource() and
37689294a78SLandon J. Fuller  *		bhnd_release_resource() functions.
37789294a78SLandon J. Fuller  * @param rid	The resource ID to be used when allocating memory resources.
37889294a78SLandon J. Fuller  */
37989294a78SLandon J. Fuller struct bhnd_erom_io *
bhnd_erom_iores_new(device_t dev,int rid)38089294a78SLandon J. Fuller bhnd_erom_iores_new(device_t dev, int rid)
38189294a78SLandon J. Fuller {
38289294a78SLandon J. Fuller 	struct bhnd_erom_iores	*iores;
38389294a78SLandon J. Fuller 
38489294a78SLandon J. Fuller 	iores = malloc(sizeof(*iores), M_BHND, M_WAITOK | M_ZERO);
38589294a78SLandon J. Fuller 	iores->eio.map = bhnd_erom_iores_map;
386f3524ec8SLandon J. Fuller 	iores->eio.tell = bhnd_erom_iores_tell;
38789294a78SLandon J. Fuller 	iores->eio.read = bhnd_erom_iores_read;
38889294a78SLandon J. Fuller 	iores->eio.fini = bhnd_erom_iores_fini;
38989294a78SLandon J. Fuller 
39089294a78SLandon J. Fuller 	iores->owner = dev;
39189294a78SLandon J. Fuller 	iores->owner_rid = rid;
39289294a78SLandon J. Fuller 	iores->mapped = NULL;
39389294a78SLandon J. Fuller 	iores->mapped_rid = -1;
39489294a78SLandon J. Fuller 
39589294a78SLandon J. Fuller 	return (&iores->eio);
39689294a78SLandon J. Fuller }
39789294a78SLandon J. Fuller 
39889294a78SLandon J. Fuller static int
bhnd_erom_iores_map(struct bhnd_erom_io * eio,bhnd_addr_t addr,bhnd_size_t size)39989294a78SLandon J. Fuller bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
40089294a78SLandon J. Fuller     bhnd_size_t size)
40189294a78SLandon J. Fuller {
40289294a78SLandon J. Fuller 	struct bhnd_erom_iores *iores;
40389294a78SLandon J. Fuller 
40489294a78SLandon J. Fuller 	iores = (struct bhnd_erom_iores *)eio;
40589294a78SLandon J. Fuller 
40689294a78SLandon J. Fuller 	/* Sanity check the addr/size */
40789294a78SLandon J. Fuller 	if (size == 0)
40889294a78SLandon J. Fuller 		return (EINVAL);
40989294a78SLandon J. Fuller 
41089294a78SLandon J. Fuller 	if (BHND_ADDR_MAX - size < addr)
41189294a78SLandon J. Fuller 		return (EINVAL);	/* would overflow */
41289294a78SLandon J. Fuller 
41389294a78SLandon J. Fuller 	/* Check for an existing mapping */
41489294a78SLandon J. Fuller 	if (iores->mapped) {
41589294a78SLandon J. Fuller 		/* If already mapped, nothing else to do */
41689294a78SLandon J. Fuller 		if (rman_get_start(iores->mapped->res) == addr &&
41789294a78SLandon J. Fuller 		    rman_get_size(iores->mapped->res) == size)
41889294a78SLandon J. Fuller 		{
41989294a78SLandon J. Fuller 			return (0);
42089294a78SLandon J. Fuller 		}
42189294a78SLandon J. Fuller 
42289294a78SLandon J. Fuller 		/* Otherwise, we need to drop the existing mapping */
42389294a78SLandon J. Fuller 		bhnd_release_resource(iores->owner, SYS_RES_MEMORY,
42489294a78SLandon J. Fuller 		    iores->mapped_rid, iores->mapped);
42589294a78SLandon J. Fuller 		iores->mapped = NULL;
42689294a78SLandon J. Fuller 		iores->mapped_rid = -1;
42789294a78SLandon J. Fuller 	}
42889294a78SLandon J. Fuller 
42989294a78SLandon J. Fuller 	/* Try to allocate the new mapping */
43089294a78SLandon J. Fuller 	iores->mapped_rid = iores->owner_rid;
43189294a78SLandon J. Fuller 	iores->mapped = bhnd_alloc_resource(iores->owner, SYS_RES_MEMORY,
43289294a78SLandon J. Fuller 	    &iores->mapped_rid, addr, addr+size-1, size,
43389294a78SLandon J. Fuller 	    RF_ACTIVE|RF_SHAREABLE);
43489294a78SLandon J. Fuller 	if (iores->mapped == NULL) {
43589294a78SLandon J. Fuller 		iores->mapped_rid = -1;
43689294a78SLandon J. Fuller 		return (ENXIO);
43789294a78SLandon J. Fuller 	}
43889294a78SLandon J. Fuller 
43989294a78SLandon J. Fuller 	return (0);
44089294a78SLandon J. Fuller }
44189294a78SLandon J. Fuller 
442f3524ec8SLandon J. Fuller static int
bhnd_erom_iores_tell(struct bhnd_erom_io * eio,bhnd_addr_t * addr,bhnd_size_t * size)443f3524ec8SLandon J. Fuller bhnd_erom_iores_tell(struct bhnd_erom_io *eio, bhnd_addr_t *addr,
444f3524ec8SLandon J. Fuller     bhnd_size_t *size)
445f3524ec8SLandon J. Fuller {
446f3524ec8SLandon J. Fuller 	struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
447f3524ec8SLandon J. Fuller 
448f3524ec8SLandon J. Fuller 	if (iores->mapped == NULL)
449f3524ec8SLandon J. Fuller 		return (ENXIO);
450f3524ec8SLandon J. Fuller 
451f3524ec8SLandon J. Fuller 	*addr = rman_get_start(iores->mapped->res);
452f3524ec8SLandon J. Fuller 	*size = rman_get_size(iores->mapped->res);
453f3524ec8SLandon J. Fuller 
454f3524ec8SLandon J. Fuller 	return (0);
455f3524ec8SLandon J. Fuller }
456f3524ec8SLandon J. Fuller 
45789294a78SLandon J. Fuller static uint32_t
bhnd_erom_iores_read(struct bhnd_erom_io * eio,bhnd_size_t offset,u_int width)45889294a78SLandon J. Fuller bhnd_erom_iores_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
45989294a78SLandon J. Fuller {
46089294a78SLandon J. Fuller 	struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
46189294a78SLandon J. Fuller 
46289294a78SLandon J. Fuller 	if (iores->mapped == NULL)
46389294a78SLandon J. Fuller 		panic("read with invalid mapping");
46489294a78SLandon J. Fuller 
46589294a78SLandon J. Fuller 	switch (width) {
46689294a78SLandon J. Fuller 	case 1:
46789294a78SLandon J. Fuller 		return (bhnd_bus_read_1(iores->mapped, offset));
46889294a78SLandon J. Fuller 	case 2:
46989294a78SLandon J. Fuller 		return (bhnd_bus_read_2(iores->mapped, offset));
47089294a78SLandon J. Fuller 	case 4:
47189294a78SLandon J. Fuller 		return (bhnd_bus_read_4(iores->mapped, offset));
47289294a78SLandon J. Fuller 	default:
47389294a78SLandon J. Fuller 		panic("invalid width %u", width);
47489294a78SLandon J. Fuller 	}
47589294a78SLandon J. Fuller }
47689294a78SLandon J. Fuller 
47789294a78SLandon J. Fuller static void
bhnd_erom_iores_fini(struct bhnd_erom_io * eio)47889294a78SLandon J. Fuller bhnd_erom_iores_fini(struct bhnd_erom_io *eio)
47989294a78SLandon J. Fuller {
48089294a78SLandon J. Fuller 	struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
48189294a78SLandon J. Fuller 
48289294a78SLandon J. Fuller 	/* Release any mapping */
48389294a78SLandon J. Fuller 	if (iores->mapped) {
48489294a78SLandon J. Fuller 		bhnd_release_resource(iores->owner, SYS_RES_MEMORY,
48589294a78SLandon J. Fuller 		    iores->mapped_rid, iores->mapped);
48689294a78SLandon J. Fuller 		iores->mapped = NULL;
48789294a78SLandon J. Fuller 		iores->mapped_rid = -1;
48889294a78SLandon J. Fuller 	}
48989294a78SLandon J. Fuller 
49089294a78SLandon J. Fuller 	free(eio, M_BHND);
49189294a78SLandon J. Fuller }
49289294a78SLandon J. Fuller 
49389294a78SLandon J. Fuller /**
49489294a78SLandon J. Fuller  * Initialize an I/O instance that will perform mapping directly from the
49589294a78SLandon J. Fuller  * given bus space tag and handle.
49689294a78SLandon J. Fuller  *
49705ed3f90SLandon J. Fuller  * @param iobus	The I/O instance to be initialized.
49889294a78SLandon J. Fuller  * @param addr	The base address mapped by @p bsh.
49989294a78SLandon J. Fuller  * @param size	The total size mapped by @p bsh.
50089294a78SLandon J. Fuller  * @param bst	Bus space tag for @p bsh.
50189294a78SLandon J. Fuller  * @param bsh	Bus space handle mapping the full bus enumeration space.
50289294a78SLandon J. Fuller  *
50389294a78SLandon J. Fuller  * @retval 0		success
50489294a78SLandon J. Fuller  * @retval non-zero	if initializing @p iobus otherwise fails, a regular
50589294a78SLandon J. Fuller  *			unix error code will be returned.
50689294a78SLandon J. Fuller  */
50789294a78SLandon J. Fuller int
bhnd_erom_iobus_init(struct bhnd_erom_iobus * iobus,bhnd_addr_t addr,bhnd_size_t size,bus_space_tag_t bst,bus_space_handle_t bsh)50889294a78SLandon J. Fuller bhnd_erom_iobus_init(struct bhnd_erom_iobus *iobus, bhnd_addr_t addr,
50989294a78SLandon J. Fuller     bhnd_size_t size, bus_space_tag_t bst, bus_space_handle_t bsh)
51089294a78SLandon J. Fuller {
51189294a78SLandon J. Fuller 	iobus->eio.map = bhnd_erom_iobus_map;
512f3524ec8SLandon J. Fuller 	iobus->eio.tell = bhnd_erom_iobus_tell;
51389294a78SLandon J. Fuller 	iobus->eio.read = bhnd_erom_iobus_read;
51489294a78SLandon J. Fuller 	iobus->eio.fini = NULL;
51589294a78SLandon J. Fuller 
51689294a78SLandon J. Fuller 	iobus->addr = addr;
51789294a78SLandon J. Fuller 	iobus->size = size;
51889294a78SLandon J. Fuller 	iobus->bst = bst;
51989294a78SLandon J. Fuller 	iobus->bsh = bsh;
52089294a78SLandon J. Fuller 	iobus->mapped = false;
52189294a78SLandon J. Fuller 
52289294a78SLandon J. Fuller 	return (0);
52389294a78SLandon J. Fuller }
52489294a78SLandon J. Fuller 
52589294a78SLandon J. Fuller static int
bhnd_erom_iobus_map(struct bhnd_erom_io * eio,bhnd_addr_t addr,bhnd_size_t size)52689294a78SLandon J. Fuller bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
52789294a78SLandon J. Fuller     bhnd_size_t size)
52889294a78SLandon J. Fuller {
52989294a78SLandon J. Fuller 	struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
53089294a78SLandon J. Fuller 
53189294a78SLandon J. Fuller 	/* Sanity check the addr/size */
53289294a78SLandon J. Fuller 	if (size == 0)
53389294a78SLandon J. Fuller 		return (EINVAL);
53489294a78SLandon J. Fuller 
53589294a78SLandon J. Fuller 	/* addr+size must not overflow */
53689294a78SLandon J. Fuller 	if (BHND_ADDR_MAX - size < addr)
53789294a78SLandon J. Fuller 		return (EINVAL);
53889294a78SLandon J. Fuller 
53989294a78SLandon J. Fuller 	/* addr/size must fit within our bus tag's mapping */
54089294a78SLandon J. Fuller 	if (addr < iobus->addr || size > iobus->size)
54189294a78SLandon J. Fuller 		return (ENXIO);
54289294a78SLandon J. Fuller 
54389294a78SLandon J. Fuller 	if (iobus->size - (addr - iobus->addr) < size)
54489294a78SLandon J. Fuller 		return (ENXIO);
54589294a78SLandon J. Fuller 
54689294a78SLandon J. Fuller 	/* The new addr offset and size must be representible as a bus_size_t */
54789294a78SLandon J. Fuller 	if ((addr - iobus->addr) > BUS_SPACE_MAXSIZE)
54889294a78SLandon J. Fuller 		return (ENXIO);
54989294a78SLandon J. Fuller 
55089294a78SLandon J. Fuller 	if (size > BUS_SPACE_MAXSIZE)
55189294a78SLandon J. Fuller 		return (ENXIO);
55289294a78SLandon J. Fuller 
55389294a78SLandon J. Fuller 	iobus->offset = addr - iobus->addr;
55489294a78SLandon J. Fuller 	iobus->limit = size;
55589294a78SLandon J. Fuller 	iobus->mapped = true;
55689294a78SLandon J. Fuller 
55789294a78SLandon J. Fuller 	return (0);
55889294a78SLandon J. Fuller }
55989294a78SLandon J. Fuller 
560f3524ec8SLandon J. Fuller static int
bhnd_erom_iobus_tell(struct bhnd_erom_io * eio,bhnd_addr_t * addr,bhnd_size_t * size)561f3524ec8SLandon J. Fuller bhnd_erom_iobus_tell(struct bhnd_erom_io *eio, bhnd_addr_t *addr,
562f3524ec8SLandon J. Fuller     bhnd_size_t *size)
563f3524ec8SLandon J. Fuller {
564f3524ec8SLandon J. Fuller 	struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
565f3524ec8SLandon J. Fuller 
566f3524ec8SLandon J. Fuller 	if (!iobus->mapped)
567f3524ec8SLandon J. Fuller 		return (ENXIO);
568f3524ec8SLandon J. Fuller 
569f3524ec8SLandon J. Fuller 	*addr = iobus->addr + iobus->offset;
570f3524ec8SLandon J. Fuller 	*size = iobus->limit;
571f3524ec8SLandon J. Fuller 
572f3524ec8SLandon J. Fuller 	return (0);
573f3524ec8SLandon J. Fuller }
574f3524ec8SLandon J. Fuller 
57589294a78SLandon J. Fuller static uint32_t
bhnd_erom_iobus_read(struct bhnd_erom_io * eio,bhnd_size_t offset,u_int width)57689294a78SLandon J. Fuller bhnd_erom_iobus_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
57789294a78SLandon J. Fuller {
57889294a78SLandon J. Fuller 	struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
57989294a78SLandon J. Fuller 
58089294a78SLandon J. Fuller 	if (!iobus->mapped)
58189294a78SLandon J. Fuller 		panic("no active mapping");
58289294a78SLandon J. Fuller 
58389294a78SLandon J. Fuller 	if (iobus->limit < width || iobus->limit - width < offset)
58489294a78SLandon J. Fuller 		panic("invalid offset %#jx", offset);
58589294a78SLandon J. Fuller 
58689294a78SLandon J. Fuller 	switch (width) {
58789294a78SLandon J. Fuller 	case 1:
58889294a78SLandon J. Fuller 		return (bus_space_read_1(iobus->bst, iobus->bsh,
58989294a78SLandon J. Fuller 		    iobus->offset + offset));
59089294a78SLandon J. Fuller 	case 2:
59189294a78SLandon J. Fuller 		return (bus_space_read_2(iobus->bst, iobus->bsh,
59289294a78SLandon J. Fuller 		    iobus->offset + offset));
59389294a78SLandon J. Fuller 	case 4:
59489294a78SLandon J. Fuller 		return (bus_space_read_4(iobus->bst, iobus->bsh,
59589294a78SLandon J. Fuller 		    iobus->offset + offset));
59689294a78SLandon J. Fuller 	default:
59789294a78SLandon J. Fuller 		panic("invalid width %u", width);
59889294a78SLandon J. Fuller 	}
59989294a78SLandon J. Fuller }
600