]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/bhnd/cores/chipc/chipc_subr.c
MFV r330973: 9164 assert: newds == os->os_dsl_dataset
[FreeBSD/FreeBSD.git] / sys / dev / bhnd / cores / chipc / chipc_subr.c
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 }