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 */
45const char *
46chipc_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 */
74const char *
75chipc_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 */
105const char *
106chipc_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 */
139int
140chipc_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 */
178int
179chipc_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 */
237int
238chipc_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 */
282void
283chipc_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 */
321struct chipc_region *
322chipc_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
360failed:
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 */
373void
374chipc_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 */
397struct chipc_region *
398chipc_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 */
424struct chipc_region *
425chipc_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 */
453int
454chipc_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 */
526int
527chipc_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
570done:
571	CHIPC_UNLOCK(sc);
572	return (error);
573}
574