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