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/malloc.h>
37#include <sys/module.h>
38#include <sys/systm.h>
39
40#include <machine/bus.h>
41
42#include "bcmavar.h"
43
44#include "bcma_eromreg.h"
45#include "bcma_eromvar.h"
46#include <dev/bhnd/bhnd_core.h>
47
48int
49bcma_probe(device_t dev)
50{
51	device_set_desc(dev, "BCMA BHND bus");
52	return (BUS_PROBE_DEFAULT);
53}
54
55int
56bcma_attach(device_t dev)
57{
58	struct bcma_devinfo	*dinfo;
59	device_t		*devs, child;
60	int			 ndevs;
61	int			 error;
62
63
64	if ((error = device_get_children(dev, &devs, &ndevs)))
65		return (error);
66
67	/*
68	 * Map our children's agent register block.
69	 */
70	for (int i = 0; i < ndevs; i++) {
71		bhnd_addr_t	addr;
72		bhnd_size_t	size;
73		rman_res_t	r_start, r_count, r_end;
74
75		child = devs[i];
76		dinfo = device_get_ivars(child);
77
78		KASSERT(!device_is_suspended(child),
79		    ("bcma(4) stateful suspend handling requires that devices "
80		        "not be suspended before bcma_attach()"));
81
82		/* Verify that the agent register block exists and is
83		 * mappable */
84		if (bhnd_get_port_rid(child, BHND_PORT_AGENT, 0, 0) == -1)
85			continue;
86
87		/* Fetch the address of the agent register block */
88		error = bhnd_get_region_addr(child, BHND_PORT_AGENT, 0, 0,
89		    &addr, &size);
90		if (error) {
91			device_printf(dev, "failed fetching agent register "
92			    "block address for core %d\n", i);
93			goto cleanup;
94		}
95
96		/* Allocate the resource */
97		r_start = addr;
98		r_count = size;
99		r_end = r_start + r_count - 1;
100
101		dinfo->rid_agent = i + 1;
102		dinfo->res_agent = BHND_BUS_ALLOC_RESOURCE(dev, dev,
103		    SYS_RES_MEMORY, &dinfo->rid_agent, r_start, r_end, r_count,
104		    RF_ACTIVE);
105		if (dinfo->res_agent == NULL) {
106			device_printf(dev, "failed allocating agent register "
107			    "block for core %d\n", i);
108			error = ENXIO;
109			goto cleanup;
110		}
111	}
112
113cleanup:
114	free(devs, M_BHND);
115	if (error)
116		return (error);
117
118	return (bhnd_generic_attach(dev));
119}
120
121int
122bcma_detach(device_t dev)
123{
124	return (bhnd_generic_detach(dev));
125}
126
127static int
128bcma_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
129{
130	const struct bcma_devinfo *dinfo;
131	const struct bhnd_core_info *ci;
132
133	dinfo = device_get_ivars(child);
134	ci = &dinfo->corecfg->core_info;
135
136	switch (index) {
137	case BHND_IVAR_VENDOR:
138		*result = ci->vendor;
139		return (0);
140	case BHND_IVAR_DEVICE:
141		*result = ci->device;
142		return (0);
143	case BHND_IVAR_HWREV:
144		*result = ci->hwrev;
145		return (0);
146	case BHND_IVAR_DEVICE_CLASS:
147		*result = bhnd_core_class(ci);
148		return (0);
149	case BHND_IVAR_VENDOR_NAME:
150		*result = (uintptr_t) bhnd_vendor_name(ci->vendor);
151		return (0);
152	case BHND_IVAR_DEVICE_NAME:
153		*result = (uintptr_t) bhnd_core_name(ci);
154		return (0);
155	case BHND_IVAR_CORE_INDEX:
156		*result = ci->core_idx;
157		return (0);
158	case BHND_IVAR_CORE_UNIT:
159		*result = ci->unit;
160		return (0);
161	default:
162		return (ENOENT);
163	}
164}
165
166static int
167bcma_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
168{
169	switch (index) {
170	case BHND_IVAR_VENDOR:
171	case BHND_IVAR_DEVICE:
172	case BHND_IVAR_HWREV:
173	case BHND_IVAR_DEVICE_CLASS:
174	case BHND_IVAR_VENDOR_NAME:
175	case BHND_IVAR_DEVICE_NAME:
176	case BHND_IVAR_CORE_INDEX:
177	case BHND_IVAR_CORE_UNIT:
178		return (EINVAL);
179	default:
180		return (ENOENT);
181	}
182}
183
184static struct resource_list *
185bcma_get_resource_list(device_t dev, device_t child)
186{
187	struct bcma_devinfo *dinfo = device_get_ivars(child);
188	return (&dinfo->resources);
189}
190
191static device_t
192bcma_find_hostb_device(device_t dev)
193{
194	struct bcma_softc *sc = device_get_softc(dev);
195
196	/* This is set (or not) by the concrete bcma driver subclass. */
197	return (sc->hostb_dev);
198}
199
200static int
201bcma_reset_core(device_t dev, device_t child, uint16_t flags)
202{
203	struct bcma_devinfo *dinfo;
204
205	if (device_get_parent(child) != dev)
206		BHND_BUS_RESET_CORE(device_get_parent(dev), child, flags);
207
208	dinfo = device_get_ivars(child);
209
210	/* Can't reset the core without access to the agent registers */
211	if (dinfo->res_agent == NULL)
212		return (ENODEV);
213
214	/* Start reset */
215	bhnd_bus_write_4(dinfo->res_agent, BHND_RESET_CF, BHND_RESET_CF_ENABLE);
216	bhnd_bus_read_4(dinfo->res_agent, BHND_RESET_CF);
217	DELAY(10);
218
219	/* Disable clock */
220	bhnd_bus_write_4(dinfo->res_agent, BHND_CF, flags);
221	bhnd_bus_read_4(dinfo->res_agent, BHND_CF);
222	DELAY(10);
223
224	/* Enable clocks & force clock gating */
225	bhnd_bus_write_4(dinfo->res_agent, BHND_CF, BHND_CF_CLOCK_EN |
226	    BHND_CF_FGC | flags);
227	bhnd_bus_read_4(dinfo->res_agent, BHND_CF);
228	DELAY(10);
229
230	/* Complete reset */
231	bhnd_bus_write_4(dinfo->res_agent, BHND_RESET_CF, 0);
232	bhnd_bus_read_4(dinfo->res_agent, BHND_RESET_CF);
233	DELAY(10);
234
235	/* Release force clock gating */
236	bhnd_bus_write_4(dinfo->res_agent, BHND_CF, BHND_CF_CLOCK_EN | flags);
237	bhnd_bus_read_4(dinfo->res_agent, BHND_CF);
238	DELAY(10);
239
240	return (0);
241}
242
243static int
244bcma_suspend_core(device_t dev, device_t child)
245{
246	struct bcma_devinfo *dinfo;
247
248	if (device_get_parent(child) != dev)
249		BHND_BUS_SUSPEND_CORE(device_get_parent(dev), child);
250
251	dinfo = device_get_ivars(child);
252
253	/* Can't suspend the core without access to the agent registers */
254	if (dinfo->res_agent == NULL)
255		return (ENODEV);
256
257	// TODO - perform suspend
258
259	return (ENXIO);
260}
261
262static u_int
263bcma_get_port_count(device_t dev, device_t child, bhnd_port_type type)
264{
265	struct bcma_devinfo *dinfo;
266
267	/* delegate non-bus-attached devices to our parent */
268	if (device_get_parent(child) != dev)
269		return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), child,
270		    type));
271
272	dinfo = device_get_ivars(child);
273	switch (type) {
274	case BHND_PORT_DEVICE:
275		return (dinfo->corecfg->num_dev_ports);
276	case BHND_PORT_BRIDGE:
277		return (dinfo->corecfg->num_bridge_ports);
278	case BHND_PORT_AGENT:
279		return (dinfo->corecfg->num_wrapper_ports);
280	default:
281		device_printf(dev, "%s: unknown type (%d)\n",
282		    __func__,
283		    type);
284		return (0);
285	}
286}
287
288static u_int
289bcma_get_region_count(device_t dev, device_t child, bhnd_port_type type,
290    u_int port_num)
291{
292	struct bcma_devinfo	*dinfo;
293	struct bcma_sport_list	*ports;
294	struct bcma_sport	*port;
295
296	/* delegate non-bus-attached devices to our parent */
297	if (device_get_parent(child) != dev)
298		return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), child,
299		    type, port_num));
300
301	dinfo = device_get_ivars(child);
302	ports = bcma_corecfg_get_port_list(dinfo->corecfg, type);
303
304	STAILQ_FOREACH(port, ports, sp_link) {
305		if (port->sp_num == port_num)
306			return (port->sp_num_maps);
307	}
308
309	/* not found */
310	return (0);
311}
312
313static int
314bcma_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type,
315    u_int port_num, u_int region_num)
316{
317	struct bcma_devinfo	*dinfo;
318	struct bcma_map		*map;
319	struct bcma_sport_list	*ports;
320	struct bcma_sport	*port;
321
322	dinfo = device_get_ivars(child);
323	ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
324
325	STAILQ_FOREACH(port, ports, sp_link) {
326		if (port->sp_num != port_num)
327			continue;
328
329		STAILQ_FOREACH(map, &port->sp_maps, m_link)
330			if (map->m_region_num == region_num)
331				return map->m_rid;
332	}
333
334	return -1;
335}
336
337static int
338bcma_decode_port_rid(device_t dev, device_t child, int type, int rid,
339    bhnd_port_type *port_type, u_int *port_num, u_int *region_num)
340{
341	struct bcma_devinfo	*dinfo;
342	struct bcma_map		*map;
343	struct bcma_sport_list	*ports;
344	struct bcma_sport	*port;
345
346	dinfo = device_get_ivars(child);
347
348	/* Ports are always memory mapped */
349	if (type != SYS_RES_MEMORY)
350		return (EINVAL);
351
352	/* Starting with the most likely device list, search all three port
353	 * lists */
354	bhnd_port_type types[] = {
355	    BHND_PORT_DEVICE,
356	    BHND_PORT_AGENT,
357	    BHND_PORT_BRIDGE
358	};
359
360	for (int i = 0; i < nitems(types); i++) {
361		ports = bcma_corecfg_get_port_list(dinfo->corecfg, types[i]);
362
363		STAILQ_FOREACH(port, ports, sp_link) {
364			STAILQ_FOREACH(map, &port->sp_maps, m_link) {
365				if (map->m_rid != rid)
366					continue;
367
368				*port_type = port->sp_type;
369				*port_num = port->sp_num;
370				*region_num = map->m_region_num;
371				return (0);
372			}
373		}
374	}
375
376	return (ENOENT);
377}
378
379static int
380bcma_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type,
381    u_int port_num, u_int region_num, bhnd_addr_t *addr, bhnd_size_t *size)
382{
383	struct bcma_devinfo	*dinfo;
384	struct bcma_map		*map;
385	struct bcma_sport_list	*ports;
386	struct bcma_sport	*port;
387
388	dinfo = device_get_ivars(child);
389	ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
390
391	/* Search the port list */
392	STAILQ_FOREACH(port, ports, sp_link) {
393		if (port->sp_num != port_num)
394			continue;
395
396		STAILQ_FOREACH(map, &port->sp_maps, m_link) {
397			if (map->m_region_num != region_num)
398				continue;
399
400			/* Found! */
401			*addr = map->m_base;
402			*size = map->m_size;
403			return (0);
404		}
405	}
406
407	return (ENOENT);
408}
409
410static struct bhnd_devinfo *
411bcma_alloc_bhnd_dinfo(device_t dev)
412{
413	struct bcma_devinfo *dinfo = bcma_alloc_dinfo(dev);
414	return ((struct bhnd_devinfo *)dinfo);
415}
416
417static void
418bcma_free_bhnd_dinfo(device_t dev, struct bhnd_devinfo *dinfo)
419{
420	bcma_free_dinfo(dev, (struct bcma_devinfo *)dinfo);
421}
422
423/**
424 * Scan a device enumeration ROM table, adding all valid discovered cores to
425 * the bus.
426 *
427 * @param bus The bcma bus.
428 * @param erom_res An active resource mapping the EROM core.
429 * @param erom_offset Base offset of the EROM core's register mapping.
430 */
431int
432bcma_add_children(device_t bus, struct resource *erom_res, bus_size_t erom_offset)
433{
434	struct bcma_erom	 erom;
435	struct bcma_corecfg	*corecfg;
436	struct bcma_devinfo	*dinfo;
437	device_t		 child;
438	int			 error;
439
440	corecfg = NULL;
441
442	/* Initialize our reader */
443	error = bcma_erom_open(&erom, erom_res, erom_offset);
444	if (error)
445		return (error);
446
447	/* Add all cores. */
448	while (!error) {
449		/* Parse next core */
450		error = bcma_erom_parse_corecfg(&erom, &corecfg);
451		if (error && error == ENOENT) {
452			return (0);
453		} else if (error) {
454			goto failed;
455		}
456
457		/* Add the child device */
458		child = BUS_ADD_CHILD(bus, 0, NULL, -1);
459		if (child == NULL) {
460			error = ENXIO;
461			goto failed;
462		}
463
464		/* Initialize device ivars */
465		dinfo = device_get_ivars(child);
466		if ((error = bcma_init_dinfo(bus, dinfo, corecfg)))
467			goto failed;
468
469		/* The dinfo instance now owns the corecfg value */
470		corecfg = NULL;
471
472		/* If pins are floating or the hardware is otherwise
473		 * unpopulated, the device shouldn't be used. */
474		if (bhnd_is_hw_disabled(child))
475			device_disable(child);
476	}
477
478	/* Hit EOF parsing cores? */
479	if (error == ENOENT)
480		return (0);
481
482failed:
483	if (corecfg != NULL)
484		bcma_free_corecfg(corecfg);
485
486	return (error);
487}
488
489
490static device_method_t bcma_methods[] = {
491	/* Device interface */
492	DEVMETHOD(device_probe,			bcma_probe),
493	DEVMETHOD(device_attach,		bcma_attach),
494	DEVMETHOD(device_detach,		bcma_detach),
495
496	/* Bus interface */
497	DEVMETHOD(bus_read_ivar,		bcma_read_ivar),
498	DEVMETHOD(bus_write_ivar,		bcma_write_ivar),
499	DEVMETHOD(bus_get_resource_list,	bcma_get_resource_list),
500
501	/* BHND interface */
502	DEVMETHOD(bhnd_bus_find_hostb_device,	bcma_find_hostb_device),
503	DEVMETHOD(bhnd_bus_alloc_devinfo,	bcma_alloc_bhnd_dinfo),
504	DEVMETHOD(bhnd_bus_free_devinfo,	bcma_free_bhnd_dinfo),
505	DEVMETHOD(bhnd_bus_reset_core,		bcma_reset_core),
506	DEVMETHOD(bhnd_bus_suspend_core,	bcma_suspend_core),
507	DEVMETHOD(bhnd_bus_get_port_count,	bcma_get_port_count),
508	DEVMETHOD(bhnd_bus_get_region_count,	bcma_get_region_count),
509	DEVMETHOD(bhnd_bus_get_port_rid,	bcma_get_port_rid),
510	DEVMETHOD(bhnd_bus_decode_port_rid,	bcma_decode_port_rid),
511	DEVMETHOD(bhnd_bus_get_region_addr,	bcma_get_region_addr),
512
513	DEVMETHOD_END
514};
515
516DEFINE_CLASS_1(bhnd, bcma_driver, bcma_methods, sizeof(struct bcma_softc), bhnd_driver);
517MODULE_VERSION(bcma, 1);
518MODULE_DEPEND(bcma, bhnd, 1, 1, 1);
519