xref: /freebsd/sys/dev/bhnd/bcma/bcma_subr.c (revision 076ad2f8)
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 }
432