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