]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/bhnd/bhnd_erom.c
Merge ACPICA 20170929.
[FreeBSD/FreeBSD.git] / sys / dev / bhnd / bhnd_erom.c
1 /*-
2  * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
3  * Copyright (c) 2017 The FreeBSD Foundation
4  * All rights reserved.
5  *
6  * Portions of this software were developed by Landon Fuller
7  * under sponsorship from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer,
14  *    without modification.
15  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
16  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
17  *    redistribution must be conditioned upon including a substantially
18  *    similar Disclaimer requirement for further binary redistribution.
19  *
20  * NO WARRANTY
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
24  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
25  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
26  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
29  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31  * THE POSSIBILITY OF SUCH DAMAGES.
32  */
33
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36
37 #include <sys/param.h>
38 #include <sys/bus.h>
39 #include <sys/kobj.h>
40   
41 #include <machine/bus.h>
42 #include <sys/rman.h>
43 #include <machine/resource.h>
44
45 #include <dev/bhnd/bhndvar.h>
46 #include <dev/bhnd/bhnd_erom.h>
47 #include <dev/bhnd/bhnd_eromvar.h>
48
49 static int      bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
50                     bhnd_size_t size);
51 static uint32_t bhnd_erom_iores_read(struct bhnd_erom_io *eio,
52                     bhnd_size_t offset, u_int width);
53 static void     bhnd_erom_iores_fini(struct bhnd_erom_io *eio);
54
55 static int      bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
56                     bhnd_size_t size);
57 static uint32_t bhnd_erom_iobus_read(struct bhnd_erom_io *eio,
58                     bhnd_size_t offset, u_int width);
59
60 /**
61  * An implementation of bhnd_erom_io that manages mappings via
62  * bhnd_alloc_resource() and bhnd_release_resource().
63  */
64 struct bhnd_erom_iores {
65         struct bhnd_erom_io      eio;
66         device_t                 owner;         /**< device from which we'll allocate resources */
67         int                      owner_rid;     /**< rid to use when allocating new mappings */
68         struct bhnd_resource    *mapped;        /**< current mapping, or NULL */
69         int                      mapped_rid;    /**< resource ID of current mapping, or -1 */
70 };
71
72 /**
73  * Fetch the device enumeration parser class from all bhnd(4)-compatible drivers
74  * registered for @p bus_devclass, probe @p eio for supporting parser classes,
75  * and return the best available supporting enumeration parser class.
76  * 
77  * @param       bus_devclass    The bus device class to be queried for
78  *                              bhnd(4)-compatible drivers.
79  * @param       eio             An erom bus I/O instance, configured with a
80  *                              mapping of the first bus core.
81  * @param       hint            Identification hint used to identify the device.
82  *                              If the chipset supports standard chip
83  *                              identification registers within the first core,
84  *                              this parameter should be NULL.
85  * @param[out]  cid             On success, the probed chip identifier.
86  * 
87  * @retval non-NULL     on success, the best available EROM class.
88  * @retval NULL         if no erom class returned a successful probe result for
89  *                      @p eio.
90  */
91 bhnd_erom_class_t *
92 bhnd_erom_probe_driver_classes(devclass_t bus_devclass,
93     struct bhnd_erom_io *eio, const struct bhnd_chipid *hint,
94     struct bhnd_chipid *cid)
95 {
96         driver_t                **drivers;
97         int                      drv_count;
98         bhnd_erom_class_t       *erom_cls;
99         int                      error, prio, result;
100
101         erom_cls = NULL;
102         prio = 0;
103
104         /* Fetch all available drivers */
105         error = devclass_get_drivers(bus_devclass, &drivers, &drv_count);
106         if (error) {
107                 printf("error fetching bhnd(4) drivers for %s: %d\n",
108                     devclass_get_name(bus_devclass), error);
109                 return (NULL);
110         }
111
112         /* Enumerate the drivers looking for the best available EROM class */
113         for (int i = 0; i < drv_count; i++) {
114                 struct bhnd_chipid       pcid;
115                 bhnd_erom_class_t       *cls;
116
117                 /* The default implementation of BHND_BUS_GET_EROM_CLASS()
118                  * returns NULL if unimplemented; this should always be safe
119                  * to call on arbitrary drivers */
120                 cls = bhnd_driver_get_erom_class(drivers[i]);
121                 if (cls == NULL)
122                         continue;
123
124                 kobj_class_compile(cls);
125
126                 /* Probe the bus */
127                 result = bhnd_erom_probe(cls, eio, hint, &pcid);
128
129                 /* The parser did not match if an error was returned */
130                 if (result > 0)
131                         continue;
132                 
133                 /* Check for a new highest priority match */
134                 if (erom_cls == NULL || result > prio) {
135                         prio = result;
136
137                         *cid = pcid;
138                         erom_cls = cls;
139                 }
140
141                 /* Terminate immediately on BUS_PROBE_SPECIFIC */
142                 if (result == BUS_PROBE_SPECIFIC)
143                         break;
144         }
145
146         return (erom_cls);
147 }
148
149 /**
150  * Allocate and return a new device enumeration table parser.
151  * 
152  * @param cls           The parser class for which an instance will be
153  *                      allocated.
154  * @param eio           The bus I/O callbacks to use when reading the device
155  *                      enumeration table.
156  * @param cid           The device's chip identifier.
157  *
158  * @retval non-NULL     success
159  * @retval NULL         if an error occured allocating or initializing the
160  *                      EROM parser.
161  */
162 bhnd_erom_t *
163 bhnd_erom_alloc(bhnd_erom_class_t *cls, const struct bhnd_chipid *cid,
164     struct bhnd_erom_io *eio)
165 {
166         bhnd_erom_t     *erom;
167         int              error;
168
169         erom = (bhnd_erom_t *)kobj_create((kobj_class_t)cls, M_BHND,
170             M_WAITOK|M_ZERO);
171
172         if ((error = BHND_EROM_INIT(erom, cid, eio))) {
173                 printf("error initializing %s parser at %#jx: %d\n", cls->name,
174                     (uintmax_t)cid->enum_addr, error);
175
176                 kobj_delete((kobj_t)erom, M_BHND);
177                 return (NULL);
178         }
179
180         return (erom);
181 }
182
183 /**
184  * Perform static initialization of a device enumeration table parser.
185  * 
186  * This may be used to initialize a caller-allocated erom instance state
187  * during early boot, prior to malloc availability.
188  * 
189  * @param cls           The parser class for which an instance will be
190  *                      allocated.
191  * @param erom          The erom parser instance to initialize.
192  * @param esize         The total available number of bytes allocated for
193  *                      @p erom. If this is less than is required by @p cls,
194  *                      ENOMEM will be returned.
195  * @param cid           The device's chip identifier.
196  * @param eio           The bus I/O callbacks to use when reading the device
197  *                      enumeration table.
198  *
199  * @retval 0            success
200  * @retval ENOMEM       if @p esize is smaller than required by @p cls.
201  * @retval non-zero     if an error occurs initializing the EROM parser,
202  *                      a regular unix error code will be returned.
203  */
204 int
205 bhnd_erom_init_static(bhnd_erom_class_t *cls, bhnd_erom_t *erom, size_t esize,
206     const struct bhnd_chipid *cid, struct bhnd_erom_io *eio)
207 {
208         kobj_class_t    kcls;
209
210         kcls = (kobj_class_t)cls;
211
212         /* Verify allocation size */
213         if (kcls->size > esize)
214                 return (ENOMEM);
215
216         /* Perform instance initialization */
217         kobj_init_static((kobj_t)erom, kcls);
218         return (BHND_EROM_INIT(erom, cid, eio)); 
219 }
220
221 /**
222  * Release any resources held by a @p erom parser previously
223  * initialized via bhnd_erom_init_static().
224  * 
225  * @param       erom    An erom parser instance previously initialized via
226  *                      bhnd_erom_init_static().
227  */
228 void
229 bhnd_erom_fini_static(bhnd_erom_t *erom)
230 {
231         return (BHND_EROM_FINI(erom));
232 }
233
234 /**
235  * Release all resources held by a @p erom parser previously
236  * allocated via bhnd_erom_alloc().
237  * 
238  * @param       erom    An erom parser instance previously allocated via
239  *                      bhnd_erom_alloc().
240  */
241 void
242 bhnd_erom_free(bhnd_erom_t *erom)
243 {
244         BHND_EROM_FINI(erom);
245         kobj_delete((kobj_t)erom, M_BHND);
246 }
247
248
249 /**
250  * Attempt to map @p size bytes at @p addr, replacing any existing
251  * @p eio mapping.
252  * 
253  * @param eio   I/O instance state.
254  * @param addr  The address to be mapped.
255  * @param size  The number of bytes to be mapped at @p addr.
256  * 
257  * @retval 0            success
258  * @retval non-zero     if mapping @p addr otherwise fails, a regular
259  *                      unix error code should be returned.
260  */
261 int
262 bhnd_erom_io_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, bhnd_size_t size)
263 {
264         return (eio->map(eio, addr, size));
265 }
266
267 /**
268  * Read a 1, 2, or 4 byte data item from @p eio, at the given @p offset
269  * relative to @p eio's current mapping.
270  * 
271  * @param eio           erom I/O callbacks
272  * @param offset        read offset.
273  * @param width         item width (1, 2, or 4 bytes).
274  */
275 uint32_t
276 bhnd_erom_io_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
277 {
278         return (eio->read(eio, offset, width));
279 }
280
281 /**
282  * Free all resources held by @p eio.
283  */
284 void
285 bhnd_erom_io_fini(struct bhnd_erom_io *eio)
286 {
287         if (eio->fini != NULL)
288                 return (eio->fini(eio));
289 }
290
291 /**
292  * Allocate, initialize, and return a new I/O instance that will perform
293  * mapping by allocating SYS_RES_MEMORY resources from @p dev using @p rid.
294  * 
295  * @param dev   The device to pass to bhnd_alloc_resource() and
296  *              bhnd_release_resource() functions.
297  * @param rid   The resource ID to be used when allocating memory resources.
298  */
299 struct bhnd_erom_io *
300 bhnd_erom_iores_new(device_t dev, int rid)
301 {
302         struct bhnd_erom_iores  *iores;
303
304         iores = malloc(sizeof(*iores), M_BHND, M_WAITOK | M_ZERO);
305         iores->eio.map = bhnd_erom_iores_map;
306         iores->eio.read = bhnd_erom_iores_read;
307         iores->eio.fini = bhnd_erom_iores_fini;
308
309         iores->owner = dev;
310         iores->owner_rid = rid;
311         iores->mapped = NULL;
312         iores->mapped_rid = -1;
313
314         return (&iores->eio);
315 }
316
317 static int
318 bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
319     bhnd_size_t size)
320 {
321         struct bhnd_erom_iores *iores;
322
323         iores = (struct bhnd_erom_iores *)eio;
324
325         /* Sanity check the addr/size */
326         if (size == 0)
327                 return (EINVAL);
328
329         if (BHND_ADDR_MAX - size < addr)
330                 return (EINVAL);        /* would overflow */
331
332         /* Check for an existing mapping */
333         if (iores->mapped) {
334                 /* If already mapped, nothing else to do */
335                 if (rman_get_start(iores->mapped->res) == addr &&
336                     rman_get_size(iores->mapped->res) == size)
337                 {
338                         return (0);
339                 }
340
341                 /* Otherwise, we need to drop the existing mapping */
342                 bhnd_release_resource(iores->owner, SYS_RES_MEMORY,
343                     iores->mapped_rid, iores->mapped);
344                 iores->mapped = NULL;
345                 iores->mapped_rid = -1;
346         }
347
348         /* Try to allocate the new mapping */
349         iores->mapped_rid = iores->owner_rid;
350         iores->mapped = bhnd_alloc_resource(iores->owner, SYS_RES_MEMORY,
351             &iores->mapped_rid, addr, addr+size-1, size,
352             RF_ACTIVE|RF_SHAREABLE);
353         if (iores->mapped == NULL) {
354                 iores->mapped_rid = -1;
355                 return (ENXIO);
356         }
357
358         return (0);
359 }
360
361 static uint32_t
362 bhnd_erom_iores_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
363 {
364         struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
365
366         if (iores->mapped == NULL)
367                 panic("read with invalid mapping");
368
369         switch (width) {
370         case 1:
371                 return (bhnd_bus_read_1(iores->mapped, offset));
372         case 2:
373                 return (bhnd_bus_read_2(iores->mapped, offset));
374         case 4:
375                 return (bhnd_bus_read_4(iores->mapped, offset));
376         default:
377                 panic("invalid width %u", width);
378         }
379 }
380
381 static void
382 bhnd_erom_iores_fini(struct bhnd_erom_io *eio)
383 {
384         struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
385
386         /* Release any mapping */
387         if (iores->mapped) {
388                 bhnd_release_resource(iores->owner, SYS_RES_MEMORY,
389                     iores->mapped_rid, iores->mapped);
390                 iores->mapped = NULL;
391                 iores->mapped_rid = -1;
392         }
393
394         free(eio, M_BHND);
395 }
396
397 /**
398  * Initialize an I/O instance that will perform mapping directly from the
399  * given bus space tag and handle.
400  * 
401  * @param addr  The base address mapped by @p bsh.
402  * @param size  The total size mapped by @p bsh.
403  * @param bst   Bus space tag for @p bsh.
404  * @param bsh   Bus space handle mapping the full bus enumeration space.
405  * 
406  * @retval 0            success
407  * @retval non-zero     if initializing @p iobus otherwise fails, a regular
408  *                      unix error code will be returned.
409  */
410 int
411 bhnd_erom_iobus_init(struct bhnd_erom_iobus *iobus, bhnd_addr_t addr,
412     bhnd_size_t size, bus_space_tag_t bst, bus_space_handle_t bsh)
413 {
414         iobus->eio.map = bhnd_erom_iobus_map;
415         iobus->eio.read = bhnd_erom_iobus_read;
416         iobus->eio.fini = NULL;
417
418         iobus->addr = addr;
419         iobus->size = size;
420         iobus->bst = bst;
421         iobus->bsh = bsh;
422         iobus->mapped = false;
423
424         return (0);
425 }
426
427 static int
428 bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
429     bhnd_size_t size)
430 {
431         struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
432
433         /* Sanity check the addr/size */
434         if (size == 0)
435                 return (EINVAL);
436
437         /* addr+size must not overflow */
438         if (BHND_ADDR_MAX - size < addr)
439                 return (EINVAL);
440
441         /* addr/size must fit within our bus tag's mapping */
442         if (addr < iobus->addr || size > iobus->size)
443                 return (ENXIO);
444
445         if (iobus->size - (addr - iobus->addr) < size)
446                 return (ENXIO);
447
448         /* The new addr offset and size must be representible as a bus_size_t */
449         if ((addr - iobus->addr) > BUS_SPACE_MAXSIZE)
450                 return (ENXIO);
451
452         if (size > BUS_SPACE_MAXSIZE)
453                 return (ENXIO);
454
455         iobus->offset = addr - iobus->addr;
456         iobus->limit = size;
457         iobus->mapped = true;
458
459         return (0);
460 }
461
462 static uint32_t
463 bhnd_erom_iobus_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
464 {
465         struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
466
467         if (!iobus->mapped) 
468                 panic("no active mapping");
469
470         if (iobus->limit < width || iobus->limit - width < offset)
471                 panic("invalid offset %#jx", offset);
472
473         switch (width) {
474         case 1:
475                 return (bus_space_read_1(iobus->bst, iobus->bsh,
476                     iobus->offset + offset));
477         case 2:
478                 return (bus_space_read_2(iobus->bst, iobus->bsh,
479                     iobus->offset + offset));
480         case 4:
481                 return (bus_space_read_4(iobus->bst, iobus->bsh,
482                     iobus->offset + offset));
483         default:
484                 panic("invalid width %u", width);
485         }
486 }