bhnd(4): Implement common API for IOST/IOCTL register access and core reset
[freebsd.git] / sys / dev / bhnd / bcma / bcma_subr.c
1 /*-
2  * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    without modification.
11  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13  *    redistribution must be conditioned upon including a substantially
14  *    similar Disclaimer requirement for further binary redistribution.
15  *
16  * NO WARRANTY
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27  * THE POSSIBILITY OF SUCH DAMAGES.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/param.h>
34 #include <sys/bus.h>
35 #include <sys/kernel.h>
36 #include <sys/limits.h>
37 #include <sys/systm.h>
38
39 #include <machine/bus.h>
40 #include <machine/resource.h>
41
42 #include <dev/bhnd/bhndvar.h>
43
44 #include "bcma_dmp.h"
45
46 #include "bcmavar.h"
47
48 /* Return the resource ID for a device's agent register allocation */
49 #define BCMA_AGENT_RID(_dinfo)  \
50     (BCMA_AGENT_RID_BASE + BCMA_DINFO_COREIDX(_dinfo))
51
52  /**
53  * Allocate and initialize new core config structure.
54  * 
55  * @param core_index Core index on the bus.
56  * @param core_unit Core unit number.
57  * @param vendor Core designer.
58  * @param device Core identifier (e.g. part number).
59  * @param hwrev Core revision.
60  */
61 struct bcma_corecfg *
62 bcma_alloc_corecfg(u_int core_index, int core_unit, uint16_t vendor,
63     uint16_t device, uint8_t hwrev)
64 {
65         struct bcma_corecfg *cfg;
66
67         cfg = malloc(sizeof(*cfg), M_BHND, M_NOWAIT);
68         if (cfg == NULL)
69                 return NULL;
70
71         cfg->core_info = (struct bhnd_core_info) {
72                 .vendor = vendor,
73                 .device = device,
74                 .hwrev = hwrev,
75                 .core_idx = core_index,
76                 .unit = core_unit
77         };
78         
79         STAILQ_INIT(&cfg->master_ports);
80         cfg->num_master_ports = 0;
81
82         STAILQ_INIT(&cfg->dev_ports);
83         cfg->num_dev_ports = 0;
84
85         STAILQ_INIT(&cfg->bridge_ports);
86         cfg->num_bridge_ports = 0;
87
88         STAILQ_INIT(&cfg->wrapper_ports);
89         cfg->num_wrapper_ports = 0;
90
91         return (cfg);
92 }
93
94 /**
95  * Deallocate the given core config and any associated resources.
96  * 
97  * @param corecfg Core info to be deallocated.
98  */
99 void
100 bcma_free_corecfg(struct bcma_corecfg *corecfg)
101 {
102         struct bcma_mport *mport, *mnext;
103         struct bcma_sport *sport, *snext;
104
105         STAILQ_FOREACH_SAFE(mport, &corecfg->master_ports, mp_link, mnext) {
106                 free(mport, M_BHND);
107         }
108         
109         STAILQ_FOREACH_SAFE(sport, &corecfg->dev_ports, sp_link, snext) {
110                 bcma_free_sport(sport);
111         }
112
113         STAILQ_FOREACH_SAFE(sport, &corecfg->bridge_ports, sp_link, snext) {
114                 bcma_free_sport(sport);
115         }
116         
117         STAILQ_FOREACH_SAFE(sport, &corecfg->wrapper_ports, sp_link, snext) {
118                 bcma_free_sport(sport);
119         }
120
121         free(corecfg, M_BHND);
122 }
123
124 /**
125  * Return the @p cfg port list for @p type.
126  * 
127  * @param cfg The core configuration.
128  * @param type The requested port type.
129  */
130 struct bcma_sport_list *
131 bcma_corecfg_get_port_list(struct bcma_corecfg *cfg, bhnd_port_type type)
132 {
133         switch (type) {
134         case BHND_PORT_DEVICE:
135                 return (&cfg->dev_ports);
136                 break;
137         case BHND_PORT_BRIDGE:
138                 return (&cfg->bridge_ports);
139                 break;
140         case BHND_PORT_AGENT:
141                 return (&cfg->wrapper_ports);
142                 break;
143         default:
144                 return (NULL);
145         }
146 }
147
148 /**
149  * Populate the resource list and bcma_map RIDs using the maps defined on
150  * @p ports.
151  * 
152  * @param bus The requesting bus device.
153  * @param dinfo The device info instance to be initialized.
154  * @param ports The set of ports to be enumerated
155  */
156 static void
157 bcma_dinfo_init_resource_info(device_t bus, struct bcma_devinfo *dinfo,
158     struct bcma_sport_list *ports)
159 {
160         struct bcma_map         *map;
161         struct bcma_sport       *port;
162         bhnd_addr_t              end;
163
164         STAILQ_FOREACH(port, ports, sp_link) {
165                 STAILQ_FOREACH(map, &port->sp_maps, m_link) {
166                         /*
167                          * Create the corresponding device resource list entry.
168                          * 
169                          * We necessarily skip registration if the region's
170                          * device memory range is not representable via
171                          * rman_res_t.
172                          * 
173                          * When rman_res_t is migrated to uintmax_t, any
174                          * range should be representable.
175                          */
176                         end = map->m_base + map->m_size;
177                         if (map->m_base <= RM_MAX_END && end <= RM_MAX_END) {
178                                 map->m_rid = resource_list_add_next(
179                                     &dinfo->resources, SYS_RES_MEMORY,
180                                     map->m_base, end, map->m_size);
181                         } else if (bootverbose) {
182                                 device_printf(bus,
183                                     "core%u %s%u.%u: region %llx-%llx extends "
184                                         "beyond supported addressable range\n",
185                                     dinfo->corecfg->core_info.core_idx,
186                                     bhnd_port_type_name(port->sp_type),
187                                     port->sp_num, map->m_region_num,
188                                     (unsigned long long) map->m_base,
189                                     (unsigned long long) end);
190                         }
191                 }
192         }
193 }
194
195
196 /**
197  * Allocate and return a new empty device info structure.
198  * 
199  * @param bus The requesting bus device.
200  * 
201  * @retval NULL if allocation failed.
202  */
203 struct bcma_devinfo *
204 bcma_alloc_dinfo(device_t bus)
205 {
206         struct bcma_devinfo *dinfo;
207         
208         dinfo = malloc(sizeof(struct bcma_devinfo), M_BHND, M_NOWAIT|M_ZERO);
209         if (dinfo == NULL)
210                 return (NULL);
211
212         dinfo->corecfg = NULL;
213         dinfo->res_agent = NULL;
214         dinfo->rid_agent = -1;
215
216         resource_list_init(&dinfo->resources);
217
218         return (dinfo);
219 }
220
221 /**
222  * Initialize a device info structure previously allocated via
223  * bcma_alloc_dinfo, assuming ownership of the provided core
224  * configuration.
225  * 
226  * @param bus The requesting bus device.
227  * @param dinfo The device info instance.
228  * @param corecfg Device core configuration; ownership of this value
229  * will be assumed by @p dinfo.
230  * 
231  * @retval 0 success
232  * @retval non-zero initialization failed.
233  */
234 int
235 bcma_init_dinfo(device_t bus, struct bcma_devinfo *dinfo,
236     struct bcma_corecfg *corecfg)
237 {
238         KASSERT(dinfo->corecfg == NULL, ("dinfo previously initialized"));
239
240         /* Save core configuration value */
241         dinfo->corecfg = corecfg;
242
243         /* The device ports must always be initialized first to ensure that
244          * rid 0 maps to the first device port */
245         bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->dev_ports);
246
247         bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->bridge_ports);
248         bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->wrapper_ports);
249
250         return (0);
251 }
252
253
254 /**
255  * Allocate the per-core agent register block for a device info structure
256  * previous initialized via bcma_init_dinfo().
257  * 
258  * If an agent0.0 region is not defined on @p dinfo, the device info
259  * agent resource is set to NULL and 0 is returned.
260  * 
261  * @param bus The requesting bus device.
262  * @param child The bcma child device.
263  * @param dinfo The device info associated with @p child
264  * 
265  * @retval 0 success
266  * @retval non-zero resource allocation failed.
267  */
268 int
269 bcma_dinfo_alloc_agent(device_t bus, device_t child, struct bcma_devinfo *dinfo)
270 {
271         bhnd_addr_t     addr;
272         bhnd_size_t     size;
273         rman_res_t      r_start, r_count, r_end;
274         int             error;
275
276         KASSERT(dinfo->res_agent == NULL, ("double allocation of agent"));
277         
278         /* Verify that the agent register block exists and is
279          * mappable */
280         if (bhnd_get_port_rid(child, BHND_PORT_AGENT, 0, 0) == -1)
281                 return (0);     /* nothing to do */
282
283         /* Fetch the address of the agent register block */
284         error = bhnd_get_region_addr(child, BHND_PORT_AGENT, 0, 0,
285             &addr, &size);
286         if (error) {
287                 device_printf(bus, "failed fetching agent register block "
288                     "address for core %u\n", BCMA_DINFO_COREIDX(dinfo));
289                 return (error);
290         }
291
292         /* Allocate the resource */
293         r_start = addr;
294         r_count = size;
295         r_end = r_start + r_count - 1;
296
297         dinfo->rid_agent = BCMA_AGENT_RID(dinfo);
298         dinfo->res_agent = BHND_BUS_ALLOC_RESOURCE(bus, bus, SYS_RES_MEMORY,
299             &dinfo->rid_agent, r_start, r_end, r_count, RF_ACTIVE);
300         if (dinfo->res_agent == NULL) {
301                 device_printf(bus, "failed allocating agent register block for "
302                     "core %u\n", BCMA_DINFO_COREIDX(dinfo));
303                 return (ENXIO);
304         }
305
306         return (0);
307 }
308
309
310 /**
311  * Deallocate the given device info structure and any associated resources.
312  * 
313  * @param bus The requesting bus device.
314  * @param dinfo Device info to be deallocated.
315  */
316 void
317 bcma_free_dinfo(device_t bus, struct bcma_devinfo *dinfo)
318 {
319         resource_list_free(&dinfo->resources);
320
321         if (dinfo->corecfg != NULL)
322                 bcma_free_corecfg(dinfo->corecfg);
323
324         /* Release agent resource, if any */
325         if (dinfo->res_agent != NULL) {
326                 bhnd_release_resource(bus, SYS_RES_MEMORY, dinfo->rid_agent,
327                     dinfo->res_agent);
328         }
329
330         free(dinfo, M_BHND);
331 }
332
333
334 /**
335  * Allocate and initialize new slave port descriptor.
336  * 
337  * @param port_num Per-core port number.
338  * @param port_type Port type.
339  */
340 struct bcma_sport *
341 bcma_alloc_sport(bcma_pid_t port_num, bhnd_port_type port_type)
342 {
343         struct bcma_sport *sport;
344         
345         sport = malloc(sizeof(struct bcma_sport), M_BHND, M_NOWAIT);
346         if (sport == NULL)
347                 return NULL;
348         
349         sport->sp_num = port_num;
350         sport->sp_type = port_type;
351         sport->sp_num_maps = 0;
352         STAILQ_INIT(&sport->sp_maps);
353
354         return sport;
355 }
356
357 /**
358  * Deallocate all resources associated with the given port descriptor.
359  * 
360  * @param sport Port descriptor to be deallocated.
361  */
362 void
363 bcma_free_sport(struct bcma_sport *sport) {
364         struct bcma_map *map, *mapnext;
365
366         STAILQ_FOREACH_SAFE(map, &sport->sp_maps, m_link, mapnext) {
367                 free(map, M_BHND);
368         }
369
370         free(sport, M_BHND);
371 }
372
373
374 /**
375  * Given a bcma(4) child's device info, spin waiting for the device's DMP
376  * resetstatus register to clear.
377  * 
378  * @param child The bcma(4) child device.
379  * @param dinfo The @p child device info.
380  * 
381  * @retval 0 success
382  * @retval ENODEV if @p dinfo does not map an agent register resource.
383  * @retval ETIMEDOUT if timeout occurs
384  */
385 int
386 bcma_dmp_wait_reset(device_t child, struct bcma_devinfo *dinfo)
387 {
388         uint32_t rst;
389
390         if (dinfo->res_agent == NULL)
391                 return (ENODEV);
392
393         /* 300us should be long enough, but there are references to this
394          * requiring up to 10ms when performing reset of an 80211 core
395          * after a MAC PSM microcode watchdog event. */
396         for (int i = 0; i < 10000; i += 10) {
397                 rst = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_RESETSTATUS);
398                 if (rst == 0)
399                         return (0);
400
401                 DELAY(10);
402         }
403
404         device_printf(child, "BCMA_DMP_RESETSTATUS timeout\n");
405         return (ETIMEDOUT);
406 }
407
408 /**
409  * Set the bcma(4) child's DMP resetctrl register value, and then wait
410  * for all backplane operations to complete.
411  * 
412  * @param child The bcma(4) child device.
413  * @param dinfo The @p child device info.
414  * @param value The new ioctrl value to set.
415  * 
416  * @retval 0 success
417  * @retval ENODEV if @p dinfo does not map an agent register resource.
418  * @retval ETIMEDOUT if timeout occurs waiting for reset completion
419  */
420 int
421 bcma_dmp_write_reset(device_t child, struct bcma_devinfo *dinfo, uint32_t value)
422 {
423         if (dinfo->res_agent == NULL)
424                 return (ENODEV);
425
426         bhnd_bus_write_4(dinfo->res_agent, BCMA_DMP_RESETCTRL, value);
427         bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_RESETCTRL); /* read-back */
428         DELAY(10);
429
430         return (bcma_dmp_wait_reset(child, dinfo));
431 }