1300548Sadrian/*-
2300548Sadrian * Copyright (c) 2016 Michael Zhilin <mizhka@gmail.com>
3300548Sadrian * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
4300548Sadrian * All rights reserved.
5300548Sadrian *
6300548Sadrian * Redistribution and use in source and binary forms, with or without
7300548Sadrian * modification, are permitted provided that the following conditions
8300548Sadrian * are met:
9300548Sadrian * 1. Redistributions of source code must retain the above copyright
10300548Sadrian *    notice, this list of conditions and the following disclaimer,
11300548Sadrian *    without modification.
12300548Sadrian * 2. Redistributions in binary form must reproduce at minimum a disclaimer
13300548Sadrian *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
14300548Sadrian *    redistribution must be conditioned upon including a substantially
15300548Sadrian *    similar Disclaimer requirement for further binary redistribution.
16300548Sadrian *
17300548Sadrian * NO WARRANTY
18300548Sadrian * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19300548Sadrian * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20300548Sadrian * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
21300548Sadrian * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22300548Sadrian * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
23300548Sadrian * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24300548Sadrian * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25300548Sadrian * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
26300548Sadrian * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27300548Sadrian * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28300548Sadrian * THE POSSIBILITY OF SUCH DAMAGES.
29300548Sadrian */
30300548Sadrian
31300548Sadrian#include <sys/cdefs.h>
32300548Sadrian__FBSDID("$FreeBSD$");
33300548Sadrian
34300548Sadrian#include <sys/param.h>
35300548Sadrian#include <sys/kernel.h>
36300548Sadrian
37300548Sadrian#include "chipc_private.h"
38300548Sadrian#include "chipcvar.h"
39300548Sadrian
40300548Sadrian/**
41302189Slandonf * Return a human-readable name for the given flash @p type.
42302189Slandonf */
43302189Slandonfconst char *
44302189Slandonfchipc_flash_name(chipc_flash type)
45302189Slandonf{
46302189Slandonf	switch (type) {
47302189Slandonf	case CHIPC_PFLASH_CFI:
48302189Slandonf		return ("CFI Flash");
49302189Slandonf
50302189Slandonf	case CHIPC_SFLASH_ST:
51302189Slandonf	case CHIPC_SFLASH_AT:
52302189Slandonf		return ("SPI Flash");
53302189Slandonf
54302189Slandonf	case CHIPC_QSFLASH_ST:
55302189Slandonf	case CHIPC_QSFLASH_AT:
56302189Slandonf		return ("QSPI Flash");
57302189Slandonf
58302189Slandonf	case CHIPC_NFLASH:
59302189Slandonf	case CHIPC_NFLASH_4706:
60302189Slandonf		return ("NAND");
61302189Slandonf
62302189Slandonf	case CHIPC_FLASH_NONE:
63302189Slandonf	default:
64302189Slandonf		return ("unknown");
65302189Slandonf	}
66302189Slandonf}
67302189Slandonf
68302189Slandonf/**
69302189Slandonf * Return the name of the bus device class used by flash @p type,
70302189Slandonf * or NULL if @p type is unsupported.
71302189Slandonf */
72302189Slandonfconst char *
73302189Slandonfchipc_flash_bus_name(chipc_flash type)
74302189Slandonf{
75302189Slandonf	switch (type) {
76302189Slandonf	case CHIPC_PFLASH_CFI:
77302189Slandonf		return ("cfi");
78302189Slandonf
79302189Slandonf	case CHIPC_SFLASH_ST:
80302189Slandonf	case CHIPC_SFLASH_AT:
81302189Slandonf		return ("spi");
82302189Slandonf
83302189Slandonf	case CHIPC_QSFLASH_ST:
84302189Slandonf	case CHIPC_QSFLASH_AT:
85302189Slandonf		/* unimplemented; spi? */
86302189Slandonf		return (NULL);
87302189Slandonf
88302189Slandonf	case CHIPC_NFLASH:
89302189Slandonf	case CHIPC_NFLASH_4706:
90302189Slandonf		/* unimplemented; nandbus? */
91302189Slandonf		return (NULL);
92302189Slandonf
93302189Slandonf	case CHIPC_FLASH_NONE:
94302189Slandonf	default:
95302189Slandonf		return (NULL);
96302189Slandonf	}
97302189Slandonf}
98302189Slandonf
99302189Slandonf/**
100302189Slandonf * Return the name of the flash device class for SPI flash @p type,
101302189Slandonf * or NULL if @p type does not use SPI, or is unsupported.
102302189Slandonf */
103302189Slandonfconst char *
104302189Slandonfchipc_sflash_device_name(chipc_flash type)
105302189Slandonf{
106302189Slandonf	switch (type) {
107302189Slandonf	case CHIPC_SFLASH_ST:
108302189Slandonf		return ("mx25l");
109302189Slandonf
110302189Slandonf	case CHIPC_SFLASH_AT:
111302189Slandonf		return ("at45d");
112302189Slandonf
113302189Slandonf	case CHIPC_QSFLASH_ST:
114302189Slandonf	case CHIPC_QSFLASH_AT:
115302189Slandonf		/* unimplemented */
116302189Slandonf		return (NULL);
117302189Slandonf
118302189Slandonf	case CHIPC_PFLASH_CFI:
119302189Slandonf	case CHIPC_NFLASH:
120302189Slandonf	case CHIPC_NFLASH_4706:
121302189Slandonf	case CHIPC_FLASH_NONE:
122302189Slandonf	default:
123302189Slandonf		return (NULL);
124302189Slandonf	}
125302189Slandonf}
126302189Slandonf
127302189Slandonf/**
128300548Sadrian * Initialize child resource @p r with a virtual address, tag, and handle
129300548Sadrian * copied from @p parent, adjusted to contain only the range defined by
130300548Sadrian * @p offsize and @p size.
131300548Sadrian *
132300548Sadrian * @param r The register to be initialized.
133300548Sadrian * @param parent The parent bus resource that fully contains the subregion.
134300548Sadrian * @param offset The subregion offset within @p parent.
135300548Sadrian * @param size The subregion size.
136300548Sadrian */
137300548Sadrianint
138300548Sadrianchipc_init_child_resource(struct resource *r,
139300548Sadrian    struct resource *parent, bhnd_size_t offset, bhnd_size_t size)
140300548Sadrian{
141300548Sadrian	bus_space_handle_t	bh, child_bh;
142300548Sadrian	bus_space_tag_t		bt;
143300548Sadrian	uintptr_t		vaddr;
144300548Sadrian	int			error;
145300548Sadrian
146300548Sadrian	/* Fetch the parent resource's bus values */
147300548Sadrian	vaddr = (uintptr_t) rman_get_virtual(parent);
148300548Sadrian	bt = rman_get_bustag(parent);
149300548Sadrian	bh = rman_get_bushandle(parent);
150300548Sadrian
151300548Sadrian	/* Configure child resource with offset-adjusted values */
152300548Sadrian	vaddr += offset;
153300548Sadrian	error = bus_space_subregion(bt, bh, offset, size, &child_bh);
154300548Sadrian	if (error)
155300548Sadrian		return (error);
156300548Sadrian
157300548Sadrian	rman_set_virtual(r, (void *) vaddr);
158300548Sadrian	rman_set_bustag(r, bt);
159300548Sadrian	rman_set_bushandle(r, child_bh);
160300548Sadrian
161300548Sadrian	return (0);
162300548Sadrian}
163300548Sadrian
164302189Slandonf/**
165302189Slandonf * Associate a resource with a given resource ID, relative to the given
166302189Slandonf * port and region.
167302189Slandonf *
168302189Slandonf * This function behaves identically to bus_set_resource() for all resource
169302189Slandonf * types other than SYS_RES_MEMORY.
170302189Slandonf *
171302189Slandonf * For SYS_RES_MEMORY resources, the specified @p region's address and size
172302189Slandonf * will be fetched from the bhnd(4) bus, and bus_set_resource() will be called
173302189Slandonf * with @p start added the region's actual base address.
174302189Slandonf *
175302189Slandonf * To use the default region values for @p start and @p count, specify
176302189Slandonf * a @p start value of 0ul, and an end value of RMAN_MAX_END
177302189Slandonf *
178302189Slandonf * @param sc chipc driver state.
179302189Slandonf * @param child The device to set the resource on.
180302189Slandonf * @param type The resource type.
181302189Slandonf * @param rid The resource ID.
182302189Slandonf * @param start The resource start address (if SYS_RES_MEMORY, this is
183302189Slandonf * relative to @p region's base address).
184302189Slandonf * @param count The length of the resource.
185302189Slandonf * @param port The mapping port number (ignored if not SYS_RES_MEMORY).
186302189Slandonf * @param region The mapping region number (ignored if not SYS_RES_MEMORY).
187302189Slandonf */
188302189Slandonfint
189302189Slandonfchipc_set_resource(struct chipc_softc *sc, device_t child, int type, int rid,
190302189Slandonf    rman_res_t start, rman_res_t count, u_int port, u_int region)
191302189Slandonf{
192302189Slandonf	bhnd_addr_t	region_addr;
193302189Slandonf	bhnd_size_t	region_size;
194302189Slandonf	bool		isdefault;
195302189Slandonf	int		error;
196300548Sadrian
197302189Slandonf	if (type != SYS_RES_MEMORY)
198302189Slandonf		return (bus_set_resource(child, type, rid, start, count));
199302189Slandonf
200302189Slandonf	isdefault = RMAN_IS_DEFAULT_RANGE(start, count);
201302189Slandonf
202302189Slandonf	/* Fetch region address and size */
203302189Slandonf	error = bhnd_get_region_addr(sc->dev, BHND_PORT_DEVICE, port,
204302189Slandonf	    region, &region_addr, &region_size);
205302189Slandonf	if (error) {
206302189Slandonf		device_printf(sc->dev,
207302189Slandonf		    "lookup of %s%u.%u failed: %d\n",
208302189Slandonf		    bhnd_port_type_name(BHND_PORT_DEVICE), port, region, error);
209302189Slandonf		return (error);
210302189Slandonf	}
211302189Slandonf
212302189Slandonf	/* Populate defaults */
213302189Slandonf	if (isdefault) {
214302189Slandonf		start = 0;
215302189Slandonf		count = region_size;
216302189Slandonf	}
217302189Slandonf
218302189Slandonf	/* Verify requested range is mappable */
219302189Slandonf	if (start > region_size || region_size - start < count) {
220302189Slandonf		device_printf(sc->dev,
221302189Slandonf		    "%s%u.%u region cannot map requested range %#jx+%#jx\n",
222302189Slandonf		    bhnd_port_type_name(BHND_PORT_DEVICE), port, region, start,
223302189Slandonf		    count);
224302189Slandonf		return (ERANGE);
225302189Slandonf	}
226302189Slandonf
227302189Slandonf	return (bus_set_resource(child, type, rid, region_addr + start, count));
228302189Slandonf}
229302189Slandonf
230302189Slandonf
231300548Sadrian/*
232300548Sadrian * Print a capability structure.
233300548Sadrian */
234300548Sadrianvoid
235300548Sadrianchipc_print_caps(device_t dev, struct chipc_caps *caps)
236300548Sadrian{
237300548Sadrian#define CC_TFS(_flag) (caps->_flag ? "yes" : "no")
238300548Sadrian
239300548Sadrian	device_printf(dev, "MIPSEB:  %-3s   | BP64:  %s\n",
240300548Sadrian	    CC_TFS(mipseb), CC_TFS(backplane_64));
241300548Sadrian	device_printf(dev, "UARTs:   %-3hhu   | UGPIO: %s\n",
242300548Sadrian	    caps->num_uarts, CC_TFS(uart_gpio));
243300548Sadrian	// XXX: hitting a kvprintf bug with '%#02x' not prefixing '0x' in
244300548Sadrian	// some cases, and not apply the field width in others
245300548Sadrian	device_printf(dev, "UARTClk: 0x%02x  | Flash: %u\n",
246300548Sadrian	    caps->uart_clock, caps->flash_type);
247300548Sadrian	device_printf(dev, "SPROM:   %-3s   | OTP:   %s\n",
248300548Sadrian	    CC_TFS(sprom), CC_TFS(otp_size));
249300548Sadrian	device_printf(dev, "CFIsz:   0x%02x  | OTPsz: 0x%02x\n",
250300548Sadrian	    caps->cfi_width, caps->otp_size);
251300548Sadrian	device_printf(dev, "ExtBus:  0x%02x  | PwCtl: %s\n",
252300548Sadrian	    caps->extbus_type, CC_TFS(power_control));
253300548Sadrian	device_printf(dev, "PLL:     0x%02x  | JTAGM: %s\n",
254300548Sadrian	    caps->pll_type, CC_TFS(jtag_master));
255300548Sadrian	device_printf(dev, "PMU:     %-3s   | ECI:   %s\n",
256300548Sadrian	    CC_TFS(pmu), CC_TFS(eci));
257300548Sadrian	device_printf(dev, "SECI:    %-3s   | GSIO:  %s\n",
258300548Sadrian	    CC_TFS(seci), CC_TFS(gsio));
259300548Sadrian	device_printf(dev, "AOB:     %-3s   | BootROM: %s\n",
260300548Sadrian	    CC_TFS(aob), CC_TFS(boot_rom));
261300548Sadrian
262300548Sadrian#undef CC_TFS
263300548Sadrian}
264300548Sadrian
265300548Sadrian/**
266300548Sadrian * Allocate and initialize new region record.
267300548Sadrian *
268300548Sadrian * @param sc Driver instance state.
269300548Sadrian * @param type The port type to query.
270300548Sadrian * @param port The port number to query.
271300548Sadrian * @param region The region number to query.
272300548Sadrian */
273300548Sadrianstruct chipc_region *
274300548Sadrianchipc_alloc_region(struct chipc_softc *sc, bhnd_port_type type,
275300548Sadrian    u_int port, u_int region)
276300548Sadrian{
277300548Sadrian	struct chipc_region	*cr;
278300548Sadrian	int			 error;
279300548Sadrian
280300548Sadrian	/* Don't bother allocating a chipc_region if init will fail */
281300548Sadrian	if (!bhnd_is_region_valid(sc->dev, type, port, region))
282300548Sadrian		return (NULL);
283300548Sadrian
284300548Sadrian	/* Allocate and initialize region info */
285300548Sadrian	cr = malloc(sizeof(*cr), M_BHND, M_NOWAIT);
286300548Sadrian	if (cr == NULL)
287300548Sadrian		return (NULL);
288300548Sadrian
289300548Sadrian	cr->cr_port_type = type;
290300548Sadrian	cr->cr_port_num = port;
291300548Sadrian	cr->cr_region_num = region;
292300548Sadrian	cr->cr_res = NULL;
293300548Sadrian	cr->cr_refs = 0;
294300548Sadrian	cr->cr_act_refs = 0;
295300548Sadrian
296300548Sadrian	error = bhnd_get_region_addr(sc->dev, type, port, region, &cr->cr_addr,
297300548Sadrian	    &cr->cr_count);
298300548Sadrian	if (error) {
299300548Sadrian		device_printf(sc->dev,
300300548Sadrian		    "fetching chipc region address failed: %d\n", error);
301300548Sadrian		goto failed;
302300548Sadrian	}
303300548Sadrian
304300548Sadrian	cr->cr_end = cr->cr_addr + cr->cr_count - 1;
305300548Sadrian
306301971Slandonf	/* Fetch default resource ID for this region. Not all regions have an
307301971Slandonf	 * assigned rid, in which case this will return -1 */
308300548Sadrian	cr->cr_rid = bhnd_get_port_rid(sc->dev, type, port, region);
309301971Slandonf
310300548Sadrian	return (cr);
311300548Sadrian
312300548Sadrianfailed:
313300548Sadrian	device_printf(sc->dev, "chipc region alloc failed for %s%u.%u\n",
314300548Sadrian	    bhnd_port_type_name(type), port, region);
315300548Sadrian	free(cr, M_BHND);
316300548Sadrian	return (NULL);
317300548Sadrian}
318300548Sadrian
319300548Sadrian/**
320300548Sadrian * Deallocate the given region record and its associated resource, if any.
321300548Sadrian *
322300548Sadrian * @param sc Driver instance state.
323300548Sadrian * @param cr Region record to be deallocated.
324300548Sadrian */
325300548Sadrianvoid
326300548Sadrianchipc_free_region(struct chipc_softc *sc, struct chipc_region *cr)
327300548Sadrian{
328300548Sadrian	KASSERT(cr->cr_refs == 0,
329300548Sadrian	    ("chipc %s%u.%u region has %u active references",
330300548Sadrian	     bhnd_port_type_name(cr->cr_port_type), cr->cr_port_num,
331300548Sadrian	     cr->cr_region_num, cr->cr_refs));
332300548Sadrian
333300548Sadrian	if (cr->cr_res != NULL) {
334301971Slandonf		bhnd_release_resource(sc->dev, SYS_RES_MEMORY, cr->cr_res_rid,
335300548Sadrian		    cr->cr_res);
336300548Sadrian	}
337300548Sadrian
338300548Sadrian	free(cr, M_BHND);
339300548Sadrian}
340300548Sadrian
341300548Sadrian/**
342300548Sadrian * Locate the region mapping the given range, if any. Returns NULL if no
343300548Sadrian * valid region is found.
344300548Sadrian *
345300548Sadrian * @param sc Driver instance state.
346300548Sadrian * @param start start of address range.
347300548Sadrian * @param end end of address range.
348300548Sadrian */
349300548Sadrianstruct chipc_region *
350300548Sadrianchipc_find_region(struct chipc_softc *sc, rman_res_t start, rman_res_t end)
351300548Sadrian{
352300548Sadrian	struct chipc_region *cr;
353300548Sadrian
354300548Sadrian	if (start > end)
355300548Sadrian		return (NULL);
356300548Sadrian
357300548Sadrian	STAILQ_FOREACH(cr, &sc->mem_regions, cr_link) {
358300548Sadrian		if (start < cr->cr_addr || end > cr->cr_end)
359300548Sadrian			continue;
360300548Sadrian
361300548Sadrian		/* Found */
362300548Sadrian		return (cr);
363300548Sadrian	}
364300548Sadrian
365300548Sadrian	/* Not found */
366300548Sadrian	return (NULL);
367300548Sadrian}
368300548Sadrian
369300548Sadrian/**
370300548Sadrian * Locate a region mapping by its bhnd-assigned resource id (as returned by
371300548Sadrian * bhnd_get_port_rid).
372300548Sadrian *
373300548Sadrian * @param sc Driver instance state.
374300548Sadrian * @param rid Resource ID to query for.
375300548Sadrian */
376300548Sadrianstruct chipc_region *
377300548Sadrianchipc_find_region_by_rid(struct chipc_softc *sc, int rid)
378300548Sadrian{
379300548Sadrian	struct chipc_region	*cr;
380300548Sadrian	int			 port_rid;
381300548Sadrian
382300548Sadrian	STAILQ_FOREACH(cr, &sc->mem_regions, cr_link) {
383300548Sadrian		port_rid = bhnd_get_port_rid(sc->dev, cr->cr_port_type,
384300548Sadrian		    cr->cr_port_num, cr->cr_region_num);
385300548Sadrian		if (port_rid == -1 || port_rid != rid)
386300548Sadrian			continue;
387300548Sadrian
388300548Sadrian		/* Found */
389300548Sadrian		return (cr);
390300548Sadrian	}
391300548Sadrian
392300548Sadrian	/* Not found */
393300548Sadrian	return (NULL);
394300548Sadrian}
395300548Sadrian
396300548Sadrian/**
397300548Sadrian * Retain a reference to a chipc_region, allocating and activating the
398300548Sadrian * backing resource as required.
399300548Sadrian *
400300548Sadrian * @param sc chipc driver instance state
401300548Sadrian * @param cr region to retain.
402300548Sadrian * @param flags specify RF_ALLOCATED to retain an allocation reference,
403300548Sadrian * RF_ACTIVE to retain an activation reference.
404300548Sadrian */
405300548Sadrianint
406300548Sadrianchipc_retain_region(struct chipc_softc *sc, struct chipc_region *cr, int flags)
407300548Sadrian{
408300548Sadrian	int error;
409300548Sadrian
410300548Sadrian	KASSERT(!(flags &~ (RF_ACTIVE|RF_ALLOCATED)), ("unsupported flags"));
411300548Sadrian
412300548Sadrian	CHIPC_LOCK(sc);
413300548Sadrian
414300548Sadrian	/* Handle allocation */
415300548Sadrian	if (flags & RF_ALLOCATED) {
416300548Sadrian		/* If this is the first reference, allocate the resource */
417300548Sadrian		if (cr->cr_refs == 0) {
418300548Sadrian			KASSERT(cr->cr_res == NULL,
419300548Sadrian			    ("non-NULL resource has refcount"));
420300548Sadrian
421301971Slandonf			/* Fetch initial resource ID */
422301971Slandonf			if ((cr->cr_res_rid = cr->cr_rid) == -1) {
423301971Slandonf				CHIPC_UNLOCK(sc);
424301971Slandonf				return (EINVAL);
425301971Slandonf			}
426301971Slandonf
427301971Slandonf			/* Allocate resource */
428300548Sadrian			cr->cr_res = bhnd_alloc_resource(sc->dev,
429301971Slandonf			    SYS_RES_MEMORY, &cr->cr_res_rid, cr->cr_addr,
430300548Sadrian			    cr->cr_end, cr->cr_count, 0);
431300548Sadrian			if (cr->cr_res == NULL) {
432300548Sadrian				CHIPC_UNLOCK(sc);
433300548Sadrian				return (ENXIO);
434300548Sadrian			}
435300548Sadrian		}
436300548Sadrian
437300548Sadrian		/* Increment allocation refcount */
438300548Sadrian		cr->cr_refs++;
439300548Sadrian	}
440300548Sadrian
441300548Sadrian
442300548Sadrian	/* Handle activation */
443300548Sadrian	if (flags & RF_ACTIVE) {
444300548Sadrian		KASSERT(cr->cr_refs > 0,
445300548Sadrian		    ("cannot activate unallocated resource"));
446300548Sadrian
447300548Sadrian		/* If this is the first reference, activate the resource */
448300548Sadrian		if (cr->cr_act_refs == 0) {
449300548Sadrian			error = bhnd_activate_resource(sc->dev, SYS_RES_MEMORY,
450301971Slandonf			    cr->cr_res_rid, cr->cr_res);
451300548Sadrian			if (error) {
452300548Sadrian				/* Drop any allocation reference acquired
453300548Sadrian				 * above */
454300548Sadrian				CHIPC_UNLOCK(sc);
455300548Sadrian				chipc_release_region(sc, cr,
456300548Sadrian				    flags &~ RF_ACTIVE);
457300548Sadrian				return (error);
458300548Sadrian			}
459300548Sadrian		}
460300548Sadrian
461300548Sadrian		/* Increment activation refcount */
462300548Sadrian		cr->cr_act_refs++;
463300548Sadrian	}
464300548Sadrian
465300548Sadrian	CHIPC_UNLOCK(sc);
466300548Sadrian	return (0);
467300548Sadrian}
468300548Sadrian
469300548Sadrian/**
470300548Sadrian * Release a reference to a chipc_region, deactivating and releasing the
471300548Sadrian * backing resource if the reference count hits zero.
472300548Sadrian *
473300548Sadrian * @param sc chipc driver instance state
474300548Sadrian * @param cr region to retain.
475300548Sadrian * @param flags specify RF_ALLOCATED to release an allocation reference,
476300548Sadrian * RF_ACTIVE to release an activation reference.
477300548Sadrian */
478300548Sadrianint
479300548Sadrianchipc_release_region(struct chipc_softc *sc, struct chipc_region *cr,
480300548Sadrian    int flags)
481300548Sadrian{
482300548Sadrian	int	error;
483300548Sadrian
484300548Sadrian	CHIPC_LOCK(sc);
485300548Sadrian	error = 0;
486300548Sadrian
487301971Slandonf	KASSERT(cr->cr_res != NULL, ("release on NULL region resource"));
488301971Slandonf
489300548Sadrian	if (flags & RF_ACTIVE) {
490300548Sadrian		KASSERT(cr->cr_act_refs > 0, ("RF_ACTIVE over-released"));
491300548Sadrian		KASSERT(cr->cr_act_refs <= cr->cr_refs,
492300548Sadrian		     ("RF_ALLOCATED released with RF_ACTIVE held"));
493300548Sadrian
494300548Sadrian		/* If this is the last reference, deactivate the resource */
495300548Sadrian		if (cr->cr_act_refs == 1) {
496300548Sadrian			error = bhnd_deactivate_resource(sc->dev,
497301971Slandonf			    SYS_RES_MEMORY, cr->cr_res_rid, cr->cr_res);
498300548Sadrian			if (error)
499300548Sadrian				goto done;
500300548Sadrian		}
501300548Sadrian
502300548Sadrian		/* Drop our activation refcount */
503300548Sadrian		cr->cr_act_refs--;
504300548Sadrian	}
505300548Sadrian
506300548Sadrian	if (flags & RF_ALLOCATED) {
507300548Sadrian		KASSERT(cr->cr_refs > 0, ("overrelease of refs"));
508300548Sadrian		/* If this is the last reference, release the resource */
509300548Sadrian		if (cr->cr_refs == 1) {
510301971Slandonf			error = bhnd_release_resource(sc->dev, SYS_RES_MEMORY,
511301971Slandonf			    cr->cr_res_rid, cr->cr_res);
512300548Sadrian			if (error)
513300548Sadrian				goto done;
514300548Sadrian
515300548Sadrian			cr->cr_res = NULL;
516300548Sadrian		}
517300548Sadrian
518300548Sadrian		/* Drop our allocation refcount */
519300548Sadrian		cr->cr_refs--;
520300548Sadrian	}
521300548Sadrian
522300548Sadriandone:
523300548Sadrian	CHIPC_UNLOCK(sc);
524300548Sadrian	return (error);
525300548Sadrian}
526