xref: /freebsd/sys/dev/bhnd/siba/siba_subr.c (revision 8ef24a0d4b28fe230e20637f56869cc4148cd2ca)
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 "sibareg.h"
45 #include "sibavar.h"
46 
47 /**
48  * Map a siba(4) OCP vendor code to its corresponding JEDEC JEP-106 vendor
49  * code.
50  *
51  * @param ocp_vendor An OCP vendor code.
52  * @return The BHND_MFGID constant corresponding to @p ocp_vendor, or
53  * BHND_MFGID_INVALID if the OCP vendor is unknown.
54  */
55 uint16_t
56 siba_get_bhnd_mfgid(uint16_t ocp_vendor)
57 {
58 	switch (ocp_vendor) {
59 	case OCP_VENDOR_BCM:
60 		return (BHND_MFGID_BCM);
61 	default:
62 		return (BHND_MFGID_INVALID);
63 	}
64 }
65 
66 /**
67  * Parse the SIBA_IDH_* fields from the per-core identification
68  * registers, returning a siba_core_id representation.
69  *
70  * @param idhigh The SIBA_R0_IDHIGH register.
71  * @param idlow The SIBA_R0_IDLOW register.
72  * @param core_id The core id (index) to include in the result.
73  * @param unit The unit number to include in the result.
74  */
75 struct siba_core_id
76 siba_parse_core_id(uint32_t idhigh, uint32_t idlow, u_int core_idx, int unit)
77 {
78 
79 	uint16_t	ocp_vendor;
80 	uint8_t		sonics_rev;
81 	uint8_t		num_addrspace;
82 	uint8_t		num_cfg;
83 
84 	ocp_vendor = SIBA_REG_GET(idhigh, IDH_VENDOR);
85 	sonics_rev = SIBA_REG_GET(idlow, IDL_SBREV);
86 	num_addrspace = SIBA_REG_GET(idlow, IDL_NRADDR) + 1 /* + enum block */;
87 
88 	/* Determine the number of sonics config register blocks */
89 	num_cfg = SIBA_CFG_NUM_2_2;
90 	if (sonics_rev >= SIBA_IDL_SBREV_2_3)
91 		num_cfg = SIBA_CFG_NUM_2_3;
92 
93 	return (struct siba_core_id) {
94 		.core_info	= {
95 			.vendor	= siba_get_bhnd_mfgid(ocp_vendor),
96 			.device	= SIBA_REG_GET(idhigh, IDH_DEVICE),
97 			.hwrev	= SIBA_IDH_CORE_REV(idhigh),
98 			.core_idx = core_idx,
99 			.unit	= unit
100 		},
101 		.sonics_vendor	= ocp_vendor,
102 		.sonics_rev	= sonics_rev,
103 		.num_addrspace	= num_addrspace,
104 		.num_cfg_blocks	= num_cfg
105 	};
106 }
107 
108 /**
109  * Initialize new port descriptor.
110  *
111  * @param port_num Port number.
112  * @param port_type Port type.
113  */
114 static void
115 siba_init_port(struct siba_port *port, bhnd_port_type port_type, u_int port_num)
116 {
117 	port->sp_num = port_num;
118 	port->sp_type = port_type;
119 	port->sp_num_addrs = 0;
120 	STAILQ_INIT(&port->sp_addrs);
121 }
122 
123 /**
124  * Deallocate all resources associated with the given port descriptor.
125  *
126  * @param port Port descriptor to be deallocated.
127  */
128 static void
129 siba_release_port(struct siba_port *port) {
130 	struct siba_addrspace *as, *as_next;
131 
132 	STAILQ_FOREACH_SAFE(as, &port->sp_addrs, sa_link, as_next) {
133 		free(as, M_BHND);
134 	}
135 }
136 
137 /**
138  * Allocate and initialize new device info structure, copying the
139  * provided core id.
140  *
141  * @param dev The requesting bus device.
142  * @param core Device core info.
143  */
144 struct siba_devinfo *
145 siba_alloc_dinfo(device_t bus, const struct siba_core_id *core_id)
146 {
147 	struct siba_devinfo *dinfo;
148 
149 	dinfo = malloc(sizeof(struct siba_devinfo), M_BHND, M_NOWAIT);
150 	if (dinfo == NULL)
151 		return NULL;
152 
153 	dinfo->core_id = *core_id;
154 
155 	for (u_int i = 0; i < nitems(dinfo->cfg); i++) {
156 		dinfo->cfg[i] = NULL;
157 		dinfo->cfg_rid[i] = -1;
158 	}
159 
160 	siba_init_port(&dinfo->device_port, BHND_PORT_DEVICE, 0);
161 	resource_list_init(&dinfo->resources);
162 
163 	return dinfo;
164 }
165 
166 /**
167  * Return the @p dinfo port instance for @p type, or NULL.
168  *
169  * @param dinfo The siba device info.
170  * @param type The requested port type.
171  *
172  * @retval siba_port If @p port_type and @p port_num are defined on @p dinfo.
173  * @retval NULL If the requested port is not defined on @p dinfo.
174  */
175 struct siba_port *
176 siba_dinfo_get_port(struct siba_devinfo *dinfo, bhnd_port_type port_type,
177     u_int port_num)
178 {
179 	/* We only define a single port for any given type. */
180 	if (port_num != 0)
181 		return (NULL);
182 
183 	switch (port_type) {
184 	case BHND_PORT_DEVICE:
185 		return (&dinfo->device_port);
186 	case BHND_PORT_BRIDGE:
187 		return (NULL);
188 	case BHND_PORT_AGENT:
189 		return (NULL);
190 	default:
191 		printf("%s: unknown port_type (%d)\n",
192 		    __func__,
193 		    port_type);
194 		return (NULL);
195 	}
196 }
197 
198 
199 /**
200  * Find an address space with @p sid on @p port.
201  *
202  * @param port The port to search for a matching address space.
203  * @param sid The siba-assigned address space ID to search for.
204  */
205 struct siba_addrspace *
206 siba_find_port_addrspace(struct siba_port *port, uint8_t sid)
207 {
208 	struct siba_addrspace	*addrspace;
209 
210 	STAILQ_FOREACH(addrspace, &port->sp_addrs, sa_link) {
211 		if (addrspace->sa_sid == sid)
212 			return (addrspace);
213 	}
214 
215 	/* not found */
216 	return (NULL);
217 }
218 
219 /**
220  * Append a new address space entry to @p port_num of type @p port_type
221  * in @p dinfo.
222  *
223  * The range will also be registered in @p dinfo resource list.
224  *
225  * @param dinfo The device info entry to update.
226  * @param port_type The port type.
227  * @param port_num The port number.
228  * @param region_num The region index number.
229  * @param sid The siba-assigned core-unique address space identifier.
230  * @param base The mapping's base address.
231  * @param size The mapping size.
232  * @param bus_reserved Number of bytes to reserve in @p size for bus use
233  * when registering the resource list entry. This is used to reserve bus
234  * access to the core's SIBA_CFG* register blocks.
235  *
236  * @retval 0 success
237  * @retval non-zero An error occurred appending the entry.
238  */
239 int
240 siba_append_dinfo_region(struct siba_devinfo *dinfo, bhnd_port_type port_type,
241     u_int port_num, u_int region_num, uint8_t sid, uint32_t base, uint32_t size,
242     uint32_t bus_reserved)
243 {
244 	struct siba_addrspace	*sa;
245 	struct siba_port	*port;
246 
247 	/* Verify that base + size will not overflow */
248 	if (UINT32_MAX - size < base)
249 		return (ERANGE);
250 
251 	/* Must not be 0-length */
252 	if (size == 0)
253 		return (EINVAL);
254 
255 	/* Determine target port */
256 	port = siba_dinfo_get_port(dinfo, port_type, port_num);
257 	if (port == NULL)
258 		return (EINVAL);
259 
260 	/* Allocate new addrspace entry */
261 	sa = malloc(sizeof(*sa), M_BHND, M_NOWAIT|M_ZERO);
262 	if (sa == NULL)
263 		return (ENOMEM);
264 
265 	sa->sa_base = base;
266 	sa->sa_size = size;
267 	sa->sa_sid = sid;
268 	sa->sa_region_num = region_num;
269 
270 	/* Populate the resource list */
271 	size -= bus_reserved;
272 	sa->sa_rid = resource_list_add_next(&dinfo->resources, SYS_RES_MEMORY,
273 	    base, base + size - 1, size);
274 
275 	/* Append to target port */
276 	STAILQ_INSERT_TAIL(&port->sp_addrs, sa, sa_link);
277 	port->sp_num_addrs++;
278 
279 	return (0);
280 }
281 
282 /**
283  * Deallocate the given device info structure and any associated resources.
284  *
285  * @param dev The requesting bus device.
286  * @param dinfo Device info to be deallocated.
287  */
288 void
289 siba_free_dinfo(device_t dev, struct siba_devinfo *dinfo)
290 {
291 	siba_release_port(&dinfo->device_port);
292 
293 	resource_list_free(&dinfo->resources);
294 
295 	/* Free all mapped configuration blocks */
296 	for (u_int i = 0; i < nitems(dinfo->cfg); i++) {
297 		if (dinfo->cfg[i] == NULL)
298 			continue;
299 
300 		bhnd_release_resource(dev, SYS_RES_MEMORY, dinfo->cfg_rid[i],
301 		    dinfo->cfg[i]);
302 
303 		dinfo->cfg[i] = NULL;
304 		dinfo->cfg_rid[i] = -1;
305 	}
306 
307 	free(dinfo, M_BHND);
308 }
309 
310 /**
311  * Return the core-enumeration-relative offset for the @p addrspace
312  * SIBA_R0_ADMATCH* register.
313  *
314  * @param addrspace The address space index.
315  *
316  * @retval non-zero success
317  * @retval 0 the given @p addrspace index is not supported.
318  */
319 u_int
320 siba_admatch_offset(uint8_t addrspace)
321 {
322 	switch (addrspace) {
323 	case 0:
324 		return SB0_REG_ABS(SIBA_CFG0_ADMATCH0);
325 	case 1:
326 		return SB0_REG_ABS(SIBA_CFG0_ADMATCH1);
327 	case 2:
328 		return SB0_REG_ABS(SIBA_CFG0_ADMATCH2);
329 	case 3:
330 		return SB0_REG_ABS(SIBA_CFG0_ADMATCH3);
331 	default:
332 		return (0);
333 	}
334 }
335 
336 /**
337  * Parse a SIBA_R0_ADMATCH* register.
338  *
339  * @param addrspace The address space index.
340  * @param am The address match register value to be parsed.
341  * @param[out] addr The parsed address.
342  * @param[out] size The parsed size.
343  *
344  * @retval 0 success
345  * @retval non-zero a parse error occurred.
346  */
347 int
348 siba_parse_admatch(uint32_t am, uint32_t *addr, uint32_t *size)
349 {
350 	u_int		am_type;
351 
352 	/* Negative encoding is not supported. This is not used on any
353 	 * currently known devices*/
354 	if (am & SIBA_AM_ADNEG)
355 		return (EINVAL);
356 
357 	/* Extract the base address and size */
358 	am_type = SIBA_REG_GET(am, AM_TYPE);
359 	switch (am_type) {
360 	case 0:
361 		*addr = am & SIBA_AM_BASE0_MASK;
362 		*size = 1 << (SIBA_REG_GET(am, AM_ADINT0) + 1);
363 		break;
364 	case 1:
365 		*addr = am & SIBA_AM_BASE1_MASK;
366 		*size = 1 << (SIBA_REG_GET(am, AM_ADINT1) + 1);
367 		break;
368 	case 2:
369 		*addr = am & SIBA_AM_BASE2_MASK;
370 		*size = 1 << (SIBA_REG_GET(am, AM_ADINT2) + 1);
371 		break;
372 	default:
373 		return (EINVAL);
374 	}
375 
376 	return (0);
377 }
378