xref: /freebsd/sys/dev/bhnd/cores/chipc/chipc_subr.c (revision 4f52dfbb)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2016 Michael Zhilin <mizhka@gmail.com>
5  * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer,
13  *    without modification.
14  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
15  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
16  *    redistribution must be conditioned upon including a substantially
17  *    similar Disclaimer requirement for further binary redistribution.
18  *
19  * NO WARRANTY
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
23  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
24  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
25  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
28  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30  * THE POSSIBILITY OF SUCH DAMAGES.
31  */
32 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 #include <sys/param.h>
37 #include <sys/kernel.h>
38 
39 #include "chipc_private.h"
40 #include "chipcvar.h"
41 
42 /**
43  * Return a human-readable name for the given flash @p type.
44  */
45 const char *
46 chipc_flash_name(chipc_flash type)
47 {
48 	switch (type) {
49 	case CHIPC_PFLASH_CFI:
50 		return ("CFI Flash");
51 
52 	case CHIPC_SFLASH_ST:
53 	case CHIPC_SFLASH_AT:
54 		return ("SPI Flash");
55 
56 	case CHIPC_QSFLASH_ST:
57 	case CHIPC_QSFLASH_AT:
58 		return ("QSPI Flash");
59 
60 	case CHIPC_NFLASH:
61 	case CHIPC_NFLASH_4706:
62 		return ("NAND");
63 
64 	case CHIPC_FLASH_NONE:
65 	default:
66 		return ("unknown");
67 	}
68 }
69 
70 /**
71  * Return the name of the bus device class used by flash @p type,
72  * or NULL if @p type is unsupported.
73  */
74 const char *
75 chipc_flash_bus_name(chipc_flash type)
76 {
77 	switch (type) {
78 	case CHIPC_PFLASH_CFI:
79 		return ("cfi");
80 
81 	case CHIPC_SFLASH_ST:
82 	case CHIPC_SFLASH_AT:
83 		return ("spi");
84 
85 	case CHIPC_QSFLASH_ST:
86 	case CHIPC_QSFLASH_AT:
87 		/* unimplemented; spi? */
88 		return (NULL);
89 
90 	case CHIPC_NFLASH:
91 	case CHIPC_NFLASH_4706:
92 		/* unimplemented; nandbus? */
93 		return (NULL);
94 
95 	case CHIPC_FLASH_NONE:
96 	default:
97 		return (NULL);
98 	}
99 }
100 
101 /**
102  * Return the name of the flash device class for SPI flash @p type,
103  * or NULL if @p type does not use SPI, or is unsupported.
104  */
105 const char *
106 chipc_sflash_device_name(chipc_flash type)
107 {
108 	switch (type) {
109 	case CHIPC_SFLASH_ST:
110 		return ("mx25l");
111 
112 	case CHIPC_SFLASH_AT:
113 		return ("at45d");
114 
115 	case CHIPC_QSFLASH_ST:
116 	case CHIPC_QSFLASH_AT:
117 		/* unimplemented */
118 		return (NULL);
119 
120 	case CHIPC_PFLASH_CFI:
121 	case CHIPC_NFLASH:
122 	case CHIPC_NFLASH_4706:
123 	case CHIPC_FLASH_NONE:
124 	default:
125 		return (NULL);
126 	}
127 }
128 
129 /**
130  * Initialize child resource @p r with a virtual address, tag, and handle
131  * copied from @p parent, adjusted to contain only the range defined by
132  * @p offsize and @p size.
133  *
134  * @param r The register to be initialized.
135  * @param parent The parent bus resource that fully contains the subregion.
136  * @param offset The subregion offset within @p parent.
137  * @param size The subregion size.
138  */
139 int
140 chipc_init_child_resource(struct resource *r,
141     struct resource *parent, bhnd_size_t offset, bhnd_size_t size)
142 {
143 	bus_space_handle_t	bh, child_bh;
144 	bus_space_tag_t		bt;
145 	uintptr_t		vaddr;
146 	int			error;
147 
148 	/* Fetch the parent resource's bus values */
149 	vaddr = (uintptr_t) rman_get_virtual(parent);
150 	bt = rman_get_bustag(parent);
151 	bh = rman_get_bushandle(parent);
152 
153 	/* Configure child resource with offset-adjusted values */
154 	vaddr += offset;
155 	error = bus_space_subregion(bt, bh, offset, size, &child_bh);
156 	if (error)
157 		return (error);
158 
159 	rman_set_virtual(r, (void *) vaddr);
160 	rman_set_bustag(r, bt);
161 	rman_set_bushandle(r, child_bh);
162 
163 	return (0);
164 }
165 
166 /**
167  * Map an interrupt line to an IRQ, and then register a corresponding SYS_RES_IRQ
168  * with @p child's resource list.
169  *
170  * @param sc chipc driver state.
171  * @param child The device to set the resource on.
172  * @param rid The resource ID.
173  * @param intr The interrupt line to be mapped.
174  * @param count The length of the resource.
175  * @param port The mapping port number (ignored if not SYS_RES_MEMORY).
176  * @param region The mapping region number (ignored if not SYS_RES_MEMORY).
177  */
178 int
179 chipc_set_irq_resource(struct chipc_softc *sc, device_t child, int rid,
180     u_int intr)
181 {
182 	struct chipc_devinfo	*dinfo;
183 	int			 error;
184 
185 	KASSERT(device_get_parent(child) == sc->dev, ("not a direct child"));
186 	dinfo = device_get_ivars(child);
187 
188 	/* We currently only support a single IRQ mapping */
189 	if (dinfo->irq_mapped) {
190 		device_printf(sc->dev, "irq already mapped for child\n");
191 		return (ENOMEM);
192 	}
193 
194 	/* Map the IRQ */
195 	if ((error = bhnd_map_intr(sc->dev, intr, &dinfo->irq))) {
196 		device_printf(sc->dev, "failed to map intr %u: %d\n", intr,
197 		    error);
198 		return (error);
199 	}
200 
201 	dinfo->irq_mapped = true;
202 
203 	/* Add to child's resource list */
204 	error = bus_set_resource(child, SYS_RES_IRQ, rid, dinfo->irq, 1);
205 	if (error) {
206 		device_printf(sc->dev, "failed to set child irq resource %d to "
207 		    "%ju: %d\n", rid, dinfo->irq, error);
208 
209 		bhnd_unmap_intr(sc->dev, dinfo->irq);
210 		return (error);
211 	}
212 
213 	return (0);
214 }
215 
216 
217 /**
218  * Add a SYS_RES_MEMORY resource with a given resource ID, relative to the
219  * given port and region, to @p child's resource list.
220  *
221  * The specified @p region's address and size will be fetched from the bhnd(4)
222  * bus, and bus_set_resource() will be called with @p start added the region's
223  * actual base address.
224  *
225  * To use the default region values for @p start and @p count, specify
226  * a @p start value of 0ul, and an end value of RMAN_MAX_END
227  *
228  * @param sc chipc driver state.
229  * @param child The device to set the resource on.
230  * @param rid The resource ID.
231  * @param start The resource start address (if SYS_RES_MEMORY, this is
232  * relative to @p region's base address).
233  * @param count The length of the resource.
234  * @param port The mapping port number (ignored if not SYS_RES_MEMORY).
235  * @param region The mapping region number (ignored if not SYS_RES_MEMORY).
236  */
237 int
238 chipc_set_mem_resource(struct chipc_softc *sc, device_t child, int rid,
239     rman_res_t start, rman_res_t count, u_int port, u_int region)
240 {
241 	bhnd_addr_t	region_addr;
242 	bhnd_size_t	region_size;
243 	bool		isdefault;
244 	int		error;
245 
246 	KASSERT(device_get_parent(child) == sc->dev, ("not a direct child"));
247 	isdefault = RMAN_IS_DEFAULT_RANGE(start, count);
248 
249 	/* Fetch region address and size */
250 	error = bhnd_get_region_addr(sc->dev, BHND_PORT_DEVICE, port,
251 	    region, &region_addr, &region_size);
252 	if (error) {
253 		device_printf(sc->dev,
254 		    "lookup of %s%u.%u failed: %d\n",
255 		    bhnd_port_type_name(BHND_PORT_DEVICE), port, region, error);
256 		return (error);
257 	}
258 
259 	/* Populate defaults */
260 	if (isdefault) {
261 		start = 0;
262 		count = region_size;
263 	}
264 
265 	/* Verify requested range is mappable */
266 	if (start > region_size || region_size - start < count) {
267 		device_printf(sc->dev,
268 		    "%s%u.%u region cannot map requested range %#jx+%#jx\n",
269 		    bhnd_port_type_name(BHND_PORT_DEVICE), port, region, start,
270 		    count);
271 		return (ERANGE);
272 	}
273 
274 	return (bus_set_resource(child, SYS_RES_MEMORY, rid,
275 	    region_addr + start, count));
276 }
277 
278 
279 /*
280  * Print a capability structure.
281  */
282 void
283 chipc_print_caps(device_t dev, struct chipc_caps *caps)
284 {
285 #define CC_TFS(_flag) (caps->_flag ? "yes" : "no")
286 
287 	device_printf(dev, "MIPSEB:  %-3s   | BP64:  %s\n",
288 	    CC_TFS(mipseb), CC_TFS(backplane_64));
289 	device_printf(dev, "UARTs:   %-3hhu   | UGPIO: %s\n",
290 	    caps->num_uarts, CC_TFS(uart_gpio));
291 	// XXX: hitting a kvprintf bug with '%#02x' not prefixing '0x' in
292 	// some cases, and not apply the field width in others
293 	device_printf(dev, "UARTClk: 0x%02x  | Flash: %u\n",
294 	    caps->uart_clock, caps->flash_type);
295 	device_printf(dev, "SPROM:   %-3s   | OTP:   %s\n",
296 	    CC_TFS(sprom), CC_TFS(otp_size));
297 	device_printf(dev, "CFIsz:   0x%02x  | OTPsz: 0x%02x\n",
298 	    caps->cfi_width, caps->otp_size);
299 	device_printf(dev, "ExtBus:  0x%02x  | PwrCtrl: %s\n",
300 	    caps->extbus_type, CC_TFS(pwr_ctrl));
301 	device_printf(dev, "PLL:     0x%02x  | JTAGM: %s\n",
302 	    caps->pll_type, CC_TFS(jtag_master));
303 	device_printf(dev, "PMU:     %-3s   | ECI:   %s\n",
304 	    CC_TFS(pmu), CC_TFS(eci));
305 	device_printf(dev, "SECI:    %-3s   | GSIO:  %s\n",
306 	    CC_TFS(seci), CC_TFS(gsio));
307 	device_printf(dev, "AOB:     %-3s   | BootROM: %s\n",
308 	    CC_TFS(aob), CC_TFS(boot_rom));
309 
310 #undef CC_TFS
311 }
312 
313 /**
314  * Allocate and initialize new region record.
315  *
316  * @param sc Driver instance state.
317  * @param type The port type to query.
318  * @param port The port number to query.
319  * @param region The region number to query.
320  */
321 struct chipc_region *
322 chipc_alloc_region(struct chipc_softc *sc, bhnd_port_type type,
323     u_int port, u_int region)
324 {
325 	struct chipc_region	*cr;
326 	int			 error;
327 
328 	/* Don't bother allocating a chipc_region if init will fail */
329 	if (!bhnd_is_region_valid(sc->dev, type, port, region))
330 		return (NULL);
331 
332 	/* Allocate and initialize region info */
333 	cr = malloc(sizeof(*cr), M_BHND, M_NOWAIT);
334 	if (cr == NULL)
335 		return (NULL);
336 
337 	cr->cr_port_type = type;
338 	cr->cr_port_num = port;
339 	cr->cr_region_num = region;
340 	cr->cr_res = NULL;
341 	cr->cr_refs = 0;
342 	cr->cr_act_refs = 0;
343 
344 	error = bhnd_get_region_addr(sc->dev, type, port, region, &cr->cr_addr,
345 	    &cr->cr_count);
346 	if (error) {
347 		device_printf(sc->dev,
348 		    "fetching chipc region address failed: %d\n", error);
349 		goto failed;
350 	}
351 
352 	cr->cr_end = cr->cr_addr + cr->cr_count - 1;
353 
354 	/* Fetch default resource ID for this region. Not all regions have an
355 	 * assigned rid, in which case this will return -1 */
356 	cr->cr_rid = bhnd_get_port_rid(sc->dev, type, port, region);
357 
358 	return (cr);
359 
360 failed:
361 	device_printf(sc->dev, "chipc region alloc failed for %s%u.%u\n",
362 	    bhnd_port_type_name(type), port, region);
363 	free(cr, M_BHND);
364 	return (NULL);
365 }
366 
367 /**
368  * Deallocate the given region record and its associated resource, if any.
369  *
370  * @param sc Driver instance state.
371  * @param cr Region record to be deallocated.
372  */
373 void
374 chipc_free_region(struct chipc_softc *sc, struct chipc_region *cr)
375 {
376 	KASSERT(cr->cr_refs == 0,
377 	    ("chipc %s%u.%u region has %u active references",
378 	     bhnd_port_type_name(cr->cr_port_type), cr->cr_port_num,
379 	     cr->cr_region_num, cr->cr_refs));
380 
381 	if (cr->cr_res != NULL) {
382 		bhnd_release_resource(sc->dev, SYS_RES_MEMORY, cr->cr_res_rid,
383 		    cr->cr_res);
384 	}
385 
386 	free(cr, M_BHND);
387 }
388 
389 /**
390  * Locate the region mapping the given range, if any. Returns NULL if no
391  * valid region is found.
392  *
393  * @param sc Driver instance state.
394  * @param start start of address range.
395  * @param end end of address range.
396  */
397 struct chipc_region *
398 chipc_find_region(struct chipc_softc *sc, rman_res_t start, rman_res_t end)
399 {
400 	struct chipc_region *cr;
401 
402 	if (start > end)
403 		return (NULL);
404 
405 	STAILQ_FOREACH(cr, &sc->mem_regions, cr_link) {
406 		if (start < cr->cr_addr || end > cr->cr_end)
407 			continue;
408 
409 		/* Found */
410 		return (cr);
411 	}
412 
413 	/* Not found */
414 	return (NULL);
415 }
416 
417 /**
418  * Locate a region mapping by its bhnd-assigned resource id (as returned by
419  * bhnd_get_port_rid).
420  *
421  * @param sc Driver instance state.
422  * @param rid Resource ID to query for.
423  */
424 struct chipc_region *
425 chipc_find_region_by_rid(struct chipc_softc *sc, int rid)
426 {
427 	struct chipc_region	*cr;
428 	int			 port_rid;
429 
430 	STAILQ_FOREACH(cr, &sc->mem_regions, cr_link) {
431 		port_rid = bhnd_get_port_rid(sc->dev, cr->cr_port_type,
432 		    cr->cr_port_num, cr->cr_region_num);
433 		if (port_rid == -1 || port_rid != rid)
434 			continue;
435 
436 		/* Found */
437 		return (cr);
438 	}
439 
440 	/* Not found */
441 	return (NULL);
442 }
443 
444 /**
445  * Retain a reference to a chipc_region, allocating and activating the
446  * backing resource as required.
447  *
448  * @param sc chipc driver instance state
449  * @param cr region to retain.
450  * @param flags specify RF_ALLOCATED to retain an allocation reference,
451  * RF_ACTIVE to retain an activation reference.
452  */
453 int
454 chipc_retain_region(struct chipc_softc *sc, struct chipc_region *cr, int flags)
455 {
456 	int error;
457 
458 	KASSERT(!(flags &~ (RF_ACTIVE|RF_ALLOCATED)), ("unsupported flags"));
459 
460 	CHIPC_LOCK(sc);
461 
462 	/* Handle allocation */
463 	if (flags & RF_ALLOCATED) {
464 		/* If this is the first reference, allocate the resource */
465 		if (cr->cr_refs == 0) {
466 			KASSERT(cr->cr_res == NULL,
467 			    ("non-NULL resource has refcount"));
468 
469 			/* Fetch initial resource ID */
470 			if ((cr->cr_res_rid = cr->cr_rid) == -1) {
471 				CHIPC_UNLOCK(sc);
472 				return (EINVAL);
473 			}
474 
475 			/* Allocate resource */
476 			cr->cr_res = bhnd_alloc_resource(sc->dev,
477 			    SYS_RES_MEMORY, &cr->cr_res_rid, cr->cr_addr,
478 			    cr->cr_end, cr->cr_count, RF_SHAREABLE);
479 			if (cr->cr_res == NULL) {
480 				CHIPC_UNLOCK(sc);
481 				return (ENXIO);
482 			}
483 		}
484 
485 		/* Increment allocation refcount */
486 		cr->cr_refs++;
487 	}
488 
489 
490 	/* Handle activation */
491 	if (flags & RF_ACTIVE) {
492 		KASSERT(cr->cr_refs > 0,
493 		    ("cannot activate unallocated resource"));
494 
495 		/* If this is the first reference, activate the resource */
496 		if (cr->cr_act_refs == 0) {
497 			error = bhnd_activate_resource(sc->dev, SYS_RES_MEMORY,
498 			    cr->cr_res_rid, cr->cr_res);
499 			if (error) {
500 				/* Drop any allocation reference acquired
501 				 * above */
502 				CHIPC_UNLOCK(sc);
503 				chipc_release_region(sc, cr,
504 				    flags &~ RF_ACTIVE);
505 				return (error);
506 			}
507 		}
508 
509 		/* Increment activation refcount */
510 		cr->cr_act_refs++;
511 	}
512 
513 	CHIPC_UNLOCK(sc);
514 	return (0);
515 }
516 
517 /**
518  * Release a reference to a chipc_region, deactivating and releasing the
519  * backing resource if the reference count hits zero.
520  *
521  * @param sc chipc driver instance state
522  * @param cr region to retain.
523  * @param flags specify RF_ALLOCATED to release an allocation reference,
524  * RF_ACTIVE to release an activation reference.
525  */
526 int
527 chipc_release_region(struct chipc_softc *sc, struct chipc_region *cr,
528     int flags)
529 {
530 	int	error;
531 
532 	CHIPC_LOCK(sc);
533 	error = 0;
534 
535 	KASSERT(cr->cr_res != NULL, ("release on NULL region resource"));
536 
537 	if (flags & RF_ACTIVE) {
538 		KASSERT(cr->cr_act_refs > 0, ("RF_ACTIVE over-released"));
539 		KASSERT(cr->cr_act_refs <= cr->cr_refs,
540 		     ("RF_ALLOCATED released with RF_ACTIVE held"));
541 
542 		/* If this is the last reference, deactivate the resource */
543 		if (cr->cr_act_refs == 1) {
544 			error = bhnd_deactivate_resource(sc->dev,
545 			    SYS_RES_MEMORY, cr->cr_res_rid, cr->cr_res);
546 			if (error)
547 				goto done;
548 		}
549 
550 		/* Drop our activation refcount */
551 		cr->cr_act_refs--;
552 	}
553 
554 	if (flags & RF_ALLOCATED) {
555 		KASSERT(cr->cr_refs > 0, ("overrelease of refs"));
556 		/* If this is the last reference, release the resource */
557 		if (cr->cr_refs == 1) {
558 			error = bhnd_release_resource(sc->dev, SYS_RES_MEMORY,
559 			    cr->cr_res_rid, cr->cr_res);
560 			if (error)
561 				goto done;
562 
563 			cr->cr_res = NULL;
564 		}
565 
566 		/* Drop our allocation refcount */
567 		cr->cr_refs--;
568 	}
569 
570 done:
571 	CHIPC_UNLOCK(sc);
572 	return (error);
573 }
574