xref: /freebsd/sys/dev/bhnd/siba/siba_subr.c (revision a321cc5dc908a14d42e57e2468923937f18c21fc)
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 	}
191 }
192 
193 
194 /**
195  * Find an address space with @p sid on @p port.
196  *
197  * @param port The port to search for a matching address space.
198  * @param sid The siba-assigned address space ID to search for.
199  */
200 struct siba_addrspace *
201 siba_find_port_addrspace(struct siba_port *port, uint8_t sid)
202 {
203 	struct siba_addrspace	*addrspace;
204 
205 	STAILQ_FOREACH(addrspace, &port->sp_addrs, sa_link) {
206 		if (addrspace->sa_sid == sid)
207 			return (addrspace);
208 	}
209 
210 	/* not found */
211 	return (NULL);
212 }
213 
214 /**
215  * Append a new address space entry to @p port_num of type @p port_type
216  * in @p dinfo.
217  *
218  * The range will also be registered in @p dinfo resource list.
219  *
220  * @param dinfo The device info entry to update.
221  * @param port_type The port type.
222  * @param port_num The port number.
223  * @param region_num The region index number.
224  * @param sid The siba-assigned core-unique address space identifier.
225  * @param base The mapping's base address.
226  * @param size The mapping size.
227  * @param bus_reserved Number of bytes to reserve in @p size for bus use
228  * when registering the resource list entry. This is used to reserve bus
229  * access to the core's SIBA_CFG* register blocks.
230  *
231  * @retval 0 success
232  * @retval non-zero An error occurred appending the entry.
233  */
234 int
235 siba_append_dinfo_region(struct siba_devinfo *dinfo, bhnd_port_type port_type,
236     u_int port_num, u_int region_num, uint8_t sid, uint32_t base, uint32_t size,
237     uint32_t bus_reserved)
238 {
239 	struct siba_addrspace	*sa;
240 	struct siba_port	*port;
241 
242 	/* Verify that base + size will not overflow */
243 	if (UINT32_MAX - size < base)
244 		return (ERANGE);
245 
246 	/* Must not be 0-length */
247 	if (size == 0)
248 		return (EINVAL);
249 
250 	/* Determine target port */
251 	port = siba_dinfo_get_port(dinfo, port_type, port_num);
252 	if (port == NULL)
253 		return (EINVAL);
254 
255 	/* Allocate new addrspace entry */
256 	sa = malloc(sizeof(*sa), M_BHND, M_NOWAIT|M_ZERO);
257 	if (sa == NULL)
258 		return (ENOMEM);
259 
260 	sa->sa_base = base;
261 	sa->sa_size = size;
262 	sa->sa_sid = sid;
263 	sa->sa_region_num = region_num;
264 
265 	/* Populate the resource list */
266 	size -= bus_reserved;
267 	sa->sa_rid = resource_list_add_next(&dinfo->resources, SYS_RES_MEMORY,
268 	    base, base + size - 1, size);
269 
270 	/* Append to target port */
271 	STAILQ_INSERT_TAIL(&port->sp_addrs, sa, sa_link);
272 	port->sp_num_addrs++;
273 
274 	return (0);
275 }
276 
277 /**
278  * Deallocate the given device info structure and any associated resources.
279  *
280  * @param dev The requesting bus device.
281  * @param dinfo Device info to be deallocated.
282  */
283 void
284 siba_free_dinfo(device_t dev, struct siba_devinfo *dinfo)
285 {
286 	siba_release_port(&dinfo->device_port);
287 
288 	resource_list_free(&dinfo->resources);
289 
290 	/* Free all mapped configuration blocks */
291 	for (u_int i = 0; i < nitems(dinfo->cfg); i++) {
292 		if (dinfo->cfg[i] == NULL)
293 			continue;
294 
295 		bhnd_release_resource(dev, SYS_RES_MEMORY, dinfo->cfg_rid[i],
296 		    dinfo->cfg[i]);
297 
298 		dinfo->cfg[i] = NULL;
299 		dinfo->cfg_rid[i] = -1;
300 	}
301 
302 	free(dinfo, M_BHND);
303 }
304 
305 /**
306  * Return the core-enumeration-relative offset for the @p addrspace
307  * SIBA_R0_ADMATCH* register.
308  *
309  * @param addrspace The address space index.
310  *
311  * @retval non-zero success
312  * @retval 0 the given @p addrspace index is not supported.
313  */
314 u_int
315 siba_admatch_offset(uint8_t addrspace)
316 {
317 	switch (addrspace) {
318 	case 0:
319 		return SB0_REG_ABS(SIBA_CFG0_ADMATCH0);
320 	case 1:
321 		return SB0_REG_ABS(SIBA_CFG0_ADMATCH1);
322 	case 2:
323 		return SB0_REG_ABS(SIBA_CFG0_ADMATCH2);
324 	case 3:
325 		return SB0_REG_ABS(SIBA_CFG0_ADMATCH3);
326 	default:
327 		return (0);
328 	}
329 }
330 
331 /**
332  * Parse a SIBA_R0_ADMATCH* register.
333  *
334  * @param addrspace The address space index.
335  * @param am The address match register value to be parsed.
336  * @param[out] addr The parsed address.
337  * @param[out] size The parsed size.
338  *
339  * @retval 0 success
340  * @retval non-zero a parse error occured.
341  */
342 int
343 siba_parse_admatch(uint32_t am, uint32_t *addr, uint32_t *size)
344 {
345 	u_int		am_type;
346 
347 	/* Negative encoding is not supported. This is not used on any
348 	 * currently known devices*/
349 	if (am & SIBA_AM_ADNEG)
350 		return (EINVAL);
351 
352 	/* Extract the base address and size */
353 	am_type = SIBA_REG_GET(am, AM_TYPE);
354 	switch (am_type) {
355 	case 0:
356 		*addr = am & SIBA_AM_BASE0_MASK;
357 		*size = 1 << (SIBA_REG_GET(am, AM_ADINT0) + 1);
358 		break;
359 	case 1:
360 		*addr = am & SIBA_AM_BASE1_MASK;
361 		*size = 1 << (SIBA_REG_GET(am, AM_ADINT1) + 1);
362 		break;
363 	case 2:
364 		*addr = am & SIBA_AM_BASE2_MASK;
365 		*size = 1 << (SIBA_REG_GET(am, AM_ADINT2) + 1);
366 		break;
367 	default:
368 		return (EINVAL);
369 	}
370 
371 	return (0);
372 }