]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/bhnd/cores/chipc/chipc_subr.c
Update to bmake-20171028
[FreeBSD/FreeBSD.git] / sys / dev / bhnd / cores / chipc / chipc_subr.c
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  */
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  * 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  */
188 int
189 chipc_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  */
234 void
235 chipc_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  | PwrCtrl: %s\n",
252             caps->extbus_type, CC_TFS(pwr_ctrl));
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  */
273 struct chipc_region *
274 chipc_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
312 failed:
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  */
325 void
326 chipc_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  */
349 struct chipc_region *
350 chipc_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  */
376 struct chipc_region *
377 chipc_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  */
405 int
406 chipc_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  */
478 int
479 chipc_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
522 done:
523         CHIPC_UNLOCK(sc);
524         return (error);
525 }