1/*-
2 * Copyright (c) 2016 Michael Zhilin <mizhka@gmail.com>
3 * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer,
11 *    without modification.
12 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
13 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
14 *    redistribution must be conditioned upon including a substantially
15 *    similar Disclaimer requirement for further binary redistribution.
16 *
17 * NO WARRANTY
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
21 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
23 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
26 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28 * THE POSSIBILITY OF SUCH DAMAGES.
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD$");
33
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 */
43const char *
44chipc_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 */
72const char *
73chipc_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 */
103const char *
104chipc_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 */
137int
138chipc_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 * Associate a resource with a given resource ID, relative to the given
166 * port and region.
167 *
168 * This function behaves identically to bus_set_resource() for all resource
169 * types other than SYS_RES_MEMORY.
170 *
171 * For SYS_RES_MEMORY resources, the specified @p region's address and size
172 * will be fetched from the bhnd(4) bus, and bus_set_resource() will be called
173 * with @p start added the region's actual base address.
174 *
175 * To use the default region values for @p start and @p count, specify
176 * a @p start value of 0ul, and an end value of RMAN_MAX_END
177 *
178 * @param sc chipc driver state.
179 * @param child The device to set the resource on.
180 * @param type The resource type.
181 * @param rid The resource ID.
182 * @param start The resource start address (if SYS_RES_MEMORY, this is
183 * relative to @p region's base address).
184 * @param count The length of the resource.
185 * @param port The mapping port number (ignored if not SYS_RES_MEMORY).
186 * @param region The mapping region number (ignored if not SYS_RES_MEMORY).
187 */
188int
189chipc_set_resource(struct chipc_softc *sc, device_t child, int type, int rid,
190    rman_res_t start, rman_res_t count, u_int port, u_int region)
191{
192	bhnd_addr_t	region_addr;
193	bhnd_size_t	region_size;
194	bool		isdefault;
195	int		error;
196
197	if (type != SYS_RES_MEMORY)
198		return (bus_set_resource(child, type, rid, start, count));
199
200	isdefault = RMAN_IS_DEFAULT_RANGE(start, count);
201
202	/* Fetch region address and size */
203	error = bhnd_get_region_addr(sc->dev, BHND_PORT_DEVICE, port,
204	    region, &region_addr, &region_size);
205	if (error) {
206		device_printf(sc->dev,
207		    "lookup of %s%u.%u failed: %d\n",
208		    bhnd_port_type_name(BHND_PORT_DEVICE), port, region, error);
209		return (error);
210	}
211
212	/* Populate defaults */
213	if (isdefault) {
214		start = 0;
215		count = region_size;
216	}
217
218	/* Verify requested range is mappable */
219	if (start > region_size || region_size - start < count) {
220		device_printf(sc->dev,
221		    "%s%u.%u region cannot map requested range %#jx+%#jx\n",
222		    bhnd_port_type_name(BHND_PORT_DEVICE), port, region, start,
223		    count);
224		return (ERANGE);
225	}
226
227	return (bus_set_resource(child, type, rid, region_addr + start, count));
228}
229
230
231/*
232 * Print a capability structure.
233 */
234void
235chipc_print_caps(device_t dev, struct chipc_caps *caps)
236{
237#define CC_TFS(_flag) (caps->_flag ? "yes" : "no")
238
239	device_printf(dev, "MIPSEB:  %-3s   | BP64:  %s\n",
240	    CC_TFS(mipseb), CC_TFS(backplane_64));
241	device_printf(dev, "UARTs:   %-3hhu   | UGPIO: %s\n",
242	    caps->num_uarts, CC_TFS(uart_gpio));
243	// XXX: hitting a kvprintf bug with '%#02x' not prefixing '0x' in
244	// some cases, and not apply the field width in others
245	device_printf(dev, "UARTClk: 0x%02x  | Flash: %u\n",
246	    caps->uart_clock, caps->flash_type);
247	device_printf(dev, "SPROM:   %-3s   | OTP:   %s\n",
248	    CC_TFS(sprom), CC_TFS(otp_size));
249	device_printf(dev, "CFIsz:   0x%02x  | OTPsz: 0x%02x\n",
250	    caps->cfi_width, caps->otp_size);
251	device_printf(dev, "ExtBus:  0x%02x  | PwCtl: %s\n",
252	    caps->extbus_type, CC_TFS(power_control));
253	device_printf(dev, "PLL:     0x%02x  | JTAGM: %s\n",
254	    caps->pll_type, CC_TFS(jtag_master));
255	device_printf(dev, "PMU:     %-3s   | ECI:   %s\n",
256	    CC_TFS(pmu), CC_TFS(eci));
257	device_printf(dev, "SECI:    %-3s   | GSIO:  %s\n",
258	    CC_TFS(seci), CC_TFS(gsio));
259	device_printf(dev, "AOB:     %-3s   | BootROM: %s\n",
260	    CC_TFS(aob), CC_TFS(boot_rom));
261
262#undef CC_TFS
263}
264
265/**
266 * Allocate and initialize new region record.
267 *
268 * @param sc Driver instance state.
269 * @param type The port type to query.
270 * @param port The port number to query.
271 * @param region The region number to query.
272 */
273struct chipc_region *
274chipc_alloc_region(struct chipc_softc *sc, bhnd_port_type type,
275    u_int port, u_int region)
276{
277	struct chipc_region	*cr;
278	int			 error;
279
280	/* Don't bother allocating a chipc_region if init will fail */
281	if (!bhnd_is_region_valid(sc->dev, type, port, region))
282		return (NULL);
283
284	/* Allocate and initialize region info */
285	cr = malloc(sizeof(*cr), M_BHND, M_NOWAIT);
286	if (cr == NULL)
287		return (NULL);
288
289	cr->cr_port_type = type;
290	cr->cr_port_num = port;
291	cr->cr_region_num = region;
292	cr->cr_res = NULL;
293	cr->cr_refs = 0;
294	cr->cr_act_refs = 0;
295
296	error = bhnd_get_region_addr(sc->dev, type, port, region, &cr->cr_addr,
297	    &cr->cr_count);
298	if (error) {
299		device_printf(sc->dev,
300		    "fetching chipc region address failed: %d\n", error);
301		goto failed;
302	}
303
304	cr->cr_end = cr->cr_addr + cr->cr_count - 1;
305
306	/* Fetch default resource ID for this region. Not all regions have an
307	 * assigned rid, in which case this will return -1 */
308	cr->cr_rid = bhnd_get_port_rid(sc->dev, type, port, region);
309
310	return (cr);
311
312failed:
313	device_printf(sc->dev, "chipc region alloc failed for %s%u.%u\n",
314	    bhnd_port_type_name(type), port, region);
315	free(cr, M_BHND);
316	return (NULL);
317}
318
319/**
320 * Deallocate the given region record and its associated resource, if any.
321 *
322 * @param sc Driver instance state.
323 * @param cr Region record to be deallocated.
324 */
325void
326chipc_free_region(struct chipc_softc *sc, struct chipc_region *cr)
327{
328	KASSERT(cr->cr_refs == 0,
329	    ("chipc %s%u.%u region has %u active references",
330	     bhnd_port_type_name(cr->cr_port_type), cr->cr_port_num,
331	     cr->cr_region_num, cr->cr_refs));
332
333	if (cr->cr_res != NULL) {
334		bhnd_release_resource(sc->dev, SYS_RES_MEMORY, cr->cr_res_rid,
335		    cr->cr_res);
336	}
337
338	free(cr, M_BHND);
339}
340
341/**
342 * Locate the region mapping the given range, if any. Returns NULL if no
343 * valid region is found.
344 *
345 * @param sc Driver instance state.
346 * @param start start of address range.
347 * @param end end of address range.
348 */
349struct chipc_region *
350chipc_find_region(struct chipc_softc *sc, rman_res_t start, rman_res_t end)
351{
352	struct chipc_region *cr;
353
354	if (start > end)
355		return (NULL);
356
357	STAILQ_FOREACH(cr, &sc->mem_regions, cr_link) {
358		if (start < cr->cr_addr || end > cr->cr_end)
359			continue;
360
361		/* Found */
362		return (cr);
363	}
364
365	/* Not found */
366	return (NULL);
367}
368
369/**
370 * Locate a region mapping by its bhnd-assigned resource id (as returned by
371 * bhnd_get_port_rid).
372 *
373 * @param sc Driver instance state.
374 * @param rid Resource ID to query for.
375 */
376struct chipc_region *
377chipc_find_region_by_rid(struct chipc_softc *sc, int rid)
378{
379	struct chipc_region	*cr;
380	int			 port_rid;
381
382	STAILQ_FOREACH(cr, &sc->mem_regions, cr_link) {
383		port_rid = bhnd_get_port_rid(sc->dev, cr->cr_port_type,
384		    cr->cr_port_num, cr->cr_region_num);
385		if (port_rid == -1 || port_rid != rid)
386			continue;
387
388		/* Found */
389		return (cr);
390	}
391
392	/* Not found */
393	return (NULL);
394}
395
396/**
397 * Retain a reference to a chipc_region, allocating and activating the
398 * backing resource as required.
399 *
400 * @param sc chipc driver instance state
401 * @param cr region to retain.
402 * @param flags specify RF_ALLOCATED to retain an allocation reference,
403 * RF_ACTIVE to retain an activation reference.
404 */
405int
406chipc_retain_region(struct chipc_softc *sc, struct chipc_region *cr, int flags)
407{
408	int error;
409
410	KASSERT(!(flags &~ (RF_ACTIVE|RF_ALLOCATED)), ("unsupported flags"));
411
412	CHIPC_LOCK(sc);
413
414	/* Handle allocation */
415	if (flags & RF_ALLOCATED) {
416		/* If this is the first reference, allocate the resource */
417		if (cr->cr_refs == 0) {
418			KASSERT(cr->cr_res == NULL,
419			    ("non-NULL resource has refcount"));
420
421			/* Fetch initial resource ID */
422			if ((cr->cr_res_rid = cr->cr_rid) == -1) {
423				CHIPC_UNLOCK(sc);
424				return (EINVAL);
425			}
426
427			/* Allocate resource */
428			cr->cr_res = bhnd_alloc_resource(sc->dev,
429			    SYS_RES_MEMORY, &cr->cr_res_rid, cr->cr_addr,
430			    cr->cr_end, cr->cr_count, 0);
431			if (cr->cr_res == NULL) {
432				CHIPC_UNLOCK(sc);
433				return (ENXIO);
434			}
435		}
436
437		/* Increment allocation refcount */
438		cr->cr_refs++;
439	}
440
441
442	/* Handle activation */
443	if (flags & RF_ACTIVE) {
444		KASSERT(cr->cr_refs > 0,
445		    ("cannot activate unallocated resource"));
446
447		/* If this is the first reference, activate the resource */
448		if (cr->cr_act_refs == 0) {
449			error = bhnd_activate_resource(sc->dev, SYS_RES_MEMORY,
450			    cr->cr_res_rid, cr->cr_res);
451			if (error) {
452				/* Drop any allocation reference acquired
453				 * above */
454				CHIPC_UNLOCK(sc);
455				chipc_release_region(sc, cr,
456				    flags &~ RF_ACTIVE);
457				return (error);
458			}
459		}
460
461		/* Increment activation refcount */
462		cr->cr_act_refs++;
463	}
464
465	CHIPC_UNLOCK(sc);
466	return (0);
467}
468
469/**
470 * Release a reference to a chipc_region, deactivating and releasing the
471 * backing resource if the reference count hits zero.
472 *
473 * @param sc chipc driver instance state
474 * @param cr region to retain.
475 * @param flags specify RF_ALLOCATED to release an allocation reference,
476 * RF_ACTIVE to release an activation reference.
477 */
478int
479chipc_release_region(struct chipc_softc *sc, struct chipc_region *cr,
480    int flags)
481{
482	int	error;
483
484	CHIPC_LOCK(sc);
485	error = 0;
486
487	KASSERT(cr->cr_res != NULL, ("release on NULL region resource"));
488
489	if (flags & RF_ACTIVE) {
490		KASSERT(cr->cr_act_refs > 0, ("RF_ACTIVE over-released"));
491		KASSERT(cr->cr_act_refs <= cr->cr_refs,
492		     ("RF_ALLOCATED released with RF_ACTIVE held"));
493
494		/* If this is the last reference, deactivate the resource */
495		if (cr->cr_act_refs == 1) {
496			error = bhnd_deactivate_resource(sc->dev,
497			    SYS_RES_MEMORY, cr->cr_res_rid, cr->cr_res);
498			if (error)
499				goto done;
500		}
501
502		/* Drop our activation refcount */
503		cr->cr_act_refs--;
504	}
505
506	if (flags & RF_ALLOCATED) {
507		KASSERT(cr->cr_refs > 0, ("overrelease of refs"));
508		/* If this is the last reference, release the resource */
509		if (cr->cr_refs == 1) {
510			error = bhnd_release_resource(sc->dev, SYS_RES_MEMORY,
511			    cr->cr_res_rid, cr->cr_res);
512			if (error)
513				goto done;
514
515			cr->cr_res = NULL;
516		}
517
518		/* Drop our allocation refcount */
519		cr->cr_refs--;
520	}
521
522done:
523	CHIPC_UNLOCK(sc);
524	return (error);
525}
526