xref: /freebsd/sys/dev/bhnd/bcma/bcma_subr.c (revision 3fc36ee018bb836bd1796067cf4ef8683f166ebc)
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 "bcmavar.h"
45 
46 /* Return the resource ID for a device's agent register allocation */
47 #define	BCMA_AGENT_RID(_dinfo)	\
48     (BCMA_AGENT_RID_BASE + BCMA_DINFO_COREIDX(_dinfo))
49 
50  /**
51  * Allocate and initialize new core config structure.
52  *
53  * @param core_index Core index on the bus.
54  * @param core_unit Core unit number.
55  * @param vendor Core designer.
56  * @param device Core identifier (e.g. part number).
57  * @param hwrev Core revision.
58  */
59 struct bcma_corecfg *
60 bcma_alloc_corecfg(u_int core_index, int core_unit, uint16_t vendor,
61     uint16_t device, uint8_t hwrev)
62 {
63 	struct bcma_corecfg *cfg;
64 
65 	cfg = malloc(sizeof(*cfg), M_BHND, M_NOWAIT);
66 	if (cfg == NULL)
67 		return NULL;
68 
69 	cfg->core_info = (struct bhnd_core_info) {
70 		.vendor = vendor,
71 		.device = device,
72 		.hwrev = hwrev,
73 		.core_idx = core_index,
74 		.unit = core_unit
75 	};
76 
77 	STAILQ_INIT(&cfg->master_ports);
78 	cfg->num_master_ports = 0;
79 
80 	STAILQ_INIT(&cfg->dev_ports);
81 	cfg->num_dev_ports = 0;
82 
83 	STAILQ_INIT(&cfg->bridge_ports);
84 	cfg->num_bridge_ports = 0;
85 
86 	STAILQ_INIT(&cfg->wrapper_ports);
87 	cfg->num_wrapper_ports = 0;
88 
89 	return (cfg);
90 }
91 
92 /**
93  * Deallocate the given core config and any associated resources.
94  *
95  * @param corecfg Core info to be deallocated.
96  */
97 void
98 bcma_free_corecfg(struct bcma_corecfg *corecfg)
99 {
100 	struct bcma_mport *mport, *mnext;
101 	struct bcma_sport *sport, *snext;
102 
103 	STAILQ_FOREACH_SAFE(mport, &corecfg->master_ports, mp_link, mnext) {
104 		free(mport, M_BHND);
105 	}
106 
107 	STAILQ_FOREACH_SAFE(sport, &corecfg->dev_ports, sp_link, snext) {
108 		bcma_free_sport(sport);
109 	}
110 
111 	STAILQ_FOREACH_SAFE(sport, &corecfg->bridge_ports, sp_link, snext) {
112 		bcma_free_sport(sport);
113 	}
114 
115 	STAILQ_FOREACH_SAFE(sport, &corecfg->wrapper_ports, sp_link, snext) {
116 		bcma_free_sport(sport);
117 	}
118 
119 	free(corecfg, M_BHND);
120 }
121 
122 /**
123  * Return the @p cfg port list for @p type.
124  *
125  * @param cfg The core configuration.
126  * @param type The requested port type.
127  */
128 struct bcma_sport_list *
129 bcma_corecfg_get_port_list(struct bcma_corecfg *cfg, bhnd_port_type type)
130 {
131 	switch (type) {
132 	case BHND_PORT_DEVICE:
133 		return (&cfg->dev_ports);
134 		break;
135 	case BHND_PORT_BRIDGE:
136 		return (&cfg->bridge_ports);
137 		break;
138 	case BHND_PORT_AGENT:
139 		return (&cfg->wrapper_ports);
140 		break;
141 	default:
142 		return (NULL);
143 	}
144 }
145 
146 /**
147  * Populate the resource list and bcma_map RIDs using the maps defined on
148  * @p ports.
149  *
150  * @param bus The requesting bus device.
151  * @param dinfo The device info instance to be initialized.
152  * @param ports The set of ports to be enumerated
153  */
154 static void
155 bcma_dinfo_init_resource_info(device_t bus, struct bcma_devinfo *dinfo,
156     struct bcma_sport_list *ports)
157 {
158 	struct bcma_map		*map;
159 	struct bcma_sport	*port;
160 	bhnd_addr_t		 end;
161 
162 	STAILQ_FOREACH(port, ports, sp_link) {
163 		STAILQ_FOREACH(map, &port->sp_maps, m_link) {
164 			/*
165 			 * Create the corresponding device resource list entry.
166 			 *
167 			 * We necessarily skip registration if the region's
168 			 * device memory range is not representable via
169 			 * rman_res_t.
170 			 *
171 			 * When rman_res_t is migrated to uintmax_t, any
172 			 * range should be representable.
173 			 */
174 			end = map->m_base + map->m_size;
175 			if (map->m_base <= RM_MAX_END && end <= RM_MAX_END) {
176 				map->m_rid = resource_list_add_next(
177 				    &dinfo->resources, SYS_RES_MEMORY,
178 				    map->m_base, end, map->m_size);
179 			} else if (bootverbose) {
180 				device_printf(bus,
181 				    "core%u %s%u.%u: region %llx-%llx extends "
182 				        "beyond supported addressable range\n",
183 				    dinfo->corecfg->core_info.core_idx,
184 				    bhnd_port_type_name(port->sp_type),
185 				    port->sp_num, map->m_region_num,
186 				    (unsigned long long) map->m_base,
187 				    (unsigned long long) end);
188 			}
189 		}
190 	}
191 }
192 
193 
194 /**
195  * Allocate and return a new empty device info structure.
196  *
197  * @param bus The requesting bus device.
198  *
199  * @retval NULL if allocation failed.
200  */
201 struct bcma_devinfo *
202 bcma_alloc_dinfo(device_t bus)
203 {
204 	struct bcma_devinfo *dinfo;
205 
206 	dinfo = malloc(sizeof(struct bcma_devinfo), M_BHND, M_NOWAIT|M_ZERO);
207 	if (dinfo == NULL)
208 		return (NULL);
209 
210 	dinfo->corecfg = NULL;
211 	dinfo->res_agent = NULL;
212 	dinfo->rid_agent = -1;
213 
214 	resource_list_init(&dinfo->resources);
215 
216 	return (dinfo);
217 }
218 
219 /**
220  * Initialize a device info structure previously allocated via
221  * bcma_alloc_dinfo, assuming ownership of the provided core
222  * configuration.
223  *
224  * @param bus The requesting bus device.
225  * @param dinfo The device info instance.
226  * @param corecfg Device core configuration; ownership of this value
227  * will be assumed by @p dinfo.
228  *
229  * @retval 0 success
230  * @retval non-zero initialization failed.
231  */
232 int
233 bcma_init_dinfo(device_t bus, struct bcma_devinfo *dinfo,
234     struct bcma_corecfg *corecfg)
235 {
236 	KASSERT(dinfo->corecfg == NULL, ("dinfo previously initialized"));
237 
238 	/* Save core configuration value */
239 	dinfo->corecfg = corecfg;
240 
241 	/* The device ports must always be initialized first to ensure that
242 	 * rid 0 maps to the first device port */
243 	bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->dev_ports);
244 
245 	bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->bridge_ports);
246 	bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->wrapper_ports);
247 
248 	return (0);
249 }
250 
251 
252 /**
253  * Allocate the per-core agent register block for a device info structure
254  * previous initialized via bcma_init_dinfo().
255  *
256  * If an agent0.0 region is not defined on @p dinfo, the device info
257  * agent resource is set to NULL and 0 is returned.
258  *
259  * @param bus The requesting bus device.
260  * @param child The bcma child device.
261  * @param dinfo The device info associated with @p child
262  *
263  * @retval 0 success
264  * @retval non-zero resource allocation failed.
265  */
266 int
267 bcma_dinfo_alloc_agent(device_t bus, device_t child, struct bcma_devinfo *dinfo)
268 {
269 	bhnd_addr_t	addr;
270 	bhnd_size_t	size;
271 	rman_res_t	r_start, r_count, r_end;
272 	int		error;
273 
274 	KASSERT(dinfo->res_agent == NULL, ("double allocation of agent"));
275 
276 	/* Verify that the agent register block exists and is
277 	 * mappable */
278 	if (bhnd_get_port_rid(child, BHND_PORT_AGENT, 0, 0) == -1)
279 		return (0);	/* nothing to do */
280 
281 	/* Fetch the address of the agent register block */
282 	error = bhnd_get_region_addr(child, BHND_PORT_AGENT, 0, 0,
283 	    &addr, &size);
284 	if (error) {
285 		device_printf(bus, "failed fetching agent register block "
286 		    "address for core %u\n", BCMA_DINFO_COREIDX(dinfo));
287 		return (error);
288 	}
289 
290 	/* Allocate the resource */
291 	r_start = addr;
292 	r_count = size;
293 	r_end = r_start + r_count - 1;
294 
295 	dinfo->rid_agent = BCMA_AGENT_RID(dinfo);
296 	dinfo->res_agent = BHND_BUS_ALLOC_RESOURCE(bus, bus, SYS_RES_MEMORY,
297 	    &dinfo->rid_agent, r_start, r_end, r_count, RF_ACTIVE);
298 	if (dinfo->res_agent == NULL) {
299 		device_printf(bus, "failed allocating agent register block for "
300 		    "core %u\n", BCMA_DINFO_COREIDX(dinfo));
301 		return (ENXIO);
302 	}
303 
304 	return (0);
305 }
306 
307 
308 /**
309  * Deallocate the given device info structure and any associated resources.
310  *
311  * @param bus The requesting bus device.
312  * @param dinfo Device info to be deallocated.
313  */
314 void
315 bcma_free_dinfo(device_t bus, struct bcma_devinfo *dinfo)
316 {
317 	resource_list_free(&dinfo->resources);
318 
319 	if (dinfo->corecfg != NULL)
320 		bcma_free_corecfg(dinfo->corecfg);
321 
322 	/* Release agent resource, if any */
323 	if (dinfo->res_agent != NULL) {
324 		bhnd_release_resource(bus, SYS_RES_MEMORY, dinfo->rid_agent,
325 		    dinfo->res_agent);
326 	}
327 
328 	free(dinfo, M_BHND);
329 }
330 
331 
332 /**
333  * Allocate and initialize new slave port descriptor.
334  *
335  * @param port_num Per-core port number.
336  * @param port_type Port type.
337  */
338 struct bcma_sport *
339 bcma_alloc_sport(bcma_pid_t port_num, bhnd_port_type port_type)
340 {
341 	struct bcma_sport *sport;
342 
343 	sport = malloc(sizeof(struct bcma_sport), M_BHND, M_NOWAIT);
344 	if (sport == NULL)
345 		return NULL;
346 
347 	sport->sp_num = port_num;
348 	sport->sp_type = port_type;
349 	sport->sp_num_maps = 0;
350 	STAILQ_INIT(&sport->sp_maps);
351 
352 	return sport;
353 }
354 
355 /**
356  * Deallocate all resources associated with the given port descriptor.
357  *
358  * @param sport Port descriptor to be deallocated.
359  */
360 void
361 bcma_free_sport(struct bcma_sport *sport) {
362 	struct bcma_map *map, *mapnext;
363 
364 	STAILQ_FOREACH_SAFE(map, &sport->sp_maps, m_link, mapnext) {
365 		free(map, M_BHND);
366 	}
367 
368 	free(sport, M_BHND);
369 }
370 
371