1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
5 * Copyright (c) 2017 The FreeBSD Foundation
6 * All rights reserved.
7 *
8 * Portions of this software were developed by Landon Fuller
9 * under sponsorship from the FreeBSD Foundation.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer,
16 *    without modification.
17 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
18 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
19 *    redistribution must be conditioned upon including a substantially
20 *    similar Disclaimer requirement for further binary redistribution.
21 *
22 * NO WARRANTY
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
26 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
27 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
28 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
31 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
33 * THE POSSIBILITY OF SUCH DAMAGES.
34 */
35
36#include <sys/cdefs.h>
37__FBSDID("$FreeBSD$");
38
39#include <sys/param.h>
40#include <sys/bus.h>
41#include <sys/kernel.h>
42#include <sys/malloc.h>
43#include <sys/module.h>
44#include <sys/systm.h>
45
46#include <machine/bus.h>
47
48#include <dev/bhnd/cores/pmu/bhnd_pmu.h>
49
50#include "bcma_dmp.h"
51
52#include "bcma_eromreg.h"
53#include "bcma_eromvar.h"
54
55#include "bcmavar.h"
56
57/* RID used when allocating EROM table */
58#define	BCMA_EROM_RID	0
59
60static bhnd_erom_class_t *
61bcma_get_erom_class(driver_t *driver)
62{
63	return (&bcma_erom_parser);
64}
65
66int
67bcma_probe(device_t dev)
68{
69	device_set_desc(dev, "BCMA BHND bus");
70	return (BUS_PROBE_DEFAULT);
71}
72
73/**
74 * Default bcma(4) bus driver implementation of DEVICE_ATTACH().
75 *
76 * This implementation initializes internal bcma(4) state and performs
77 * bus enumeration, and must be called by subclassing drivers in
78 * DEVICE_ATTACH() before any other bus methods.
79 */
80int
81bcma_attach(device_t dev)
82{
83	int error;
84
85	/* Enumerate children */
86	if ((error = bcma_add_children(dev))) {
87		device_delete_children(dev);
88		return (error);
89	}
90
91	return (0);
92}
93
94int
95bcma_detach(device_t dev)
96{
97	return (bhnd_generic_detach(dev));
98}
99
100static device_t
101bcma_add_child(device_t dev, u_int order, const char *name, int unit)
102{
103	struct bcma_devinfo	*dinfo;
104	device_t		 child;
105
106	child = device_add_child_ordered(dev, order, name, unit);
107	if (child == NULL)
108		return (NULL);
109
110	if ((dinfo = bcma_alloc_dinfo(dev)) == NULL) {
111		device_delete_child(dev, child);
112		return (NULL);
113	}
114
115	device_set_ivars(child, dinfo);
116
117	return (child);
118}
119
120static void
121bcma_child_deleted(device_t dev, device_t child)
122{
123	struct bhnd_softc	*sc;
124	struct bcma_devinfo	*dinfo;
125
126	sc = device_get_softc(dev);
127
128	/* Call required bhnd(4) implementation */
129	bhnd_generic_child_deleted(dev, child);
130
131	/* Free bcma device info */
132	if ((dinfo = device_get_ivars(child)) != NULL)
133		bcma_free_dinfo(dev, child, dinfo);
134
135	device_set_ivars(child, NULL);
136}
137
138static int
139bcma_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
140{
141	const struct bcma_devinfo *dinfo;
142	const struct bhnd_core_info *ci;
143
144	dinfo = device_get_ivars(child);
145	ci = &dinfo->corecfg->core_info;
146
147	switch (index) {
148	case BHND_IVAR_VENDOR:
149		*result = ci->vendor;
150		return (0);
151	case BHND_IVAR_DEVICE:
152		*result = ci->device;
153		return (0);
154	case BHND_IVAR_HWREV:
155		*result = ci->hwrev;
156		return (0);
157	case BHND_IVAR_DEVICE_CLASS:
158		*result = bhnd_core_class(ci);
159		return (0);
160	case BHND_IVAR_VENDOR_NAME:
161		*result = (uintptr_t) bhnd_vendor_name(ci->vendor);
162		return (0);
163	case BHND_IVAR_DEVICE_NAME:
164		*result = (uintptr_t) bhnd_core_name(ci);
165		return (0);
166	case BHND_IVAR_CORE_INDEX:
167		*result = ci->core_idx;
168		return (0);
169	case BHND_IVAR_CORE_UNIT:
170		*result = ci->unit;
171		return (0);
172	case BHND_IVAR_PMU_INFO:
173		*result = (uintptr_t) dinfo->pmu_info;
174		return (0);
175	default:
176		return (ENOENT);
177	}
178}
179
180static int
181bcma_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
182{
183	struct bcma_devinfo *dinfo;
184
185	dinfo = device_get_ivars(child);
186
187	switch (index) {
188	case BHND_IVAR_VENDOR:
189	case BHND_IVAR_DEVICE:
190	case BHND_IVAR_HWREV:
191	case BHND_IVAR_DEVICE_CLASS:
192	case BHND_IVAR_VENDOR_NAME:
193	case BHND_IVAR_DEVICE_NAME:
194	case BHND_IVAR_CORE_INDEX:
195	case BHND_IVAR_CORE_UNIT:
196		return (EINVAL);
197	case BHND_IVAR_PMU_INFO:
198		dinfo->pmu_info = (void *)value;
199		return (0);
200	default:
201		return (ENOENT);
202	}
203}
204
205static struct resource_list *
206bcma_get_resource_list(device_t dev, device_t child)
207{
208	struct bcma_devinfo *dinfo = device_get_ivars(child);
209	return (&dinfo->resources);
210}
211
212static int
213bcma_read_iost(device_t dev, device_t child, uint16_t *iost)
214{
215	uint32_t	value;
216	int		error;
217
218	if ((error = bhnd_read_config(child, BCMA_DMP_IOSTATUS, &value, 4)))
219		return (error);
220
221	/* Return only the bottom 16 bits */
222	*iost = (value & BCMA_DMP_IOST_MASK);
223	return (0);
224}
225
226static int
227bcma_read_ioctl(device_t dev, device_t child, uint16_t *ioctl)
228{
229	uint32_t	value;
230	int		error;
231
232	if ((error = bhnd_read_config(child, BCMA_DMP_IOCTRL, &value, 4)))
233		return (error);
234
235	/* Return only the bottom 16 bits */
236	*ioctl = (value & BCMA_DMP_IOCTRL_MASK);
237	return (0);
238}
239
240static int
241bcma_write_ioctl(device_t dev, device_t child, uint16_t value, uint16_t mask)
242{
243	struct bcma_devinfo	*dinfo;
244	struct bhnd_resource	*r;
245	uint32_t		 ioctl;
246
247	if (device_get_parent(child) != dev)
248		return (EINVAL);
249
250	dinfo = device_get_ivars(child);
251	if ((r = dinfo->res_agent) == NULL)
252		return (ENODEV);
253
254	/* Write new value */
255	ioctl = bhnd_bus_read_4(r, BCMA_DMP_IOCTRL);
256	ioctl &= ~(BCMA_DMP_IOCTRL_MASK & mask);
257	ioctl |= (value & mask);
258
259	bhnd_bus_write_4(r, BCMA_DMP_IOCTRL, ioctl);
260
261	/* Perform read-back and wait for completion */
262	bhnd_bus_read_4(r, BCMA_DMP_IOCTRL);
263	DELAY(10);
264
265	return (0);
266}
267
268static bool
269bcma_is_hw_suspended(device_t dev, device_t child)
270{
271	uint32_t	rst;
272	uint16_t	ioctl;
273	int		error;
274
275	/* Is core held in RESET? */
276	error = bhnd_read_config(child, BCMA_DMP_RESETCTRL, &rst, 4);
277	if (error) {
278		device_printf(child, "error reading HW reset state: %d\n",
279		    error);
280		return (true);
281	}
282
283	if (rst & BCMA_DMP_RC_RESET)
284		return (true);
285
286	/* Is core clocked? */
287	error = bhnd_read_ioctl(child, &ioctl);
288	if (error) {
289		device_printf(child, "error reading HW ioctl register: %d\n",
290		    error);
291		return (true);
292	}
293
294	if (!(ioctl & BHND_IOCTL_CLK_EN))
295		return (true);
296
297	return (false);
298}
299
300static int
301bcma_reset_hw(device_t dev, device_t child, uint16_t ioctl,
302    uint16_t reset_ioctl)
303{
304	struct bcma_devinfo	*dinfo;
305	struct bhnd_resource	*r;
306	uint16_t		 clkflags;
307	int			 error;
308
309	if (device_get_parent(child) != dev)
310		return (EINVAL);
311
312	dinfo = device_get_ivars(child);
313
314	/* We require exclusive control over BHND_IOCTL_CLK_(EN|FORCE) */
315	clkflags = BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE;
316	if (ioctl & clkflags)
317		return (EINVAL);
318
319	/* Can't suspend the core without access to the agent registers */
320	if ((r = dinfo->res_agent) == NULL)
321		return (ENODEV);
322
323	/* Place core into known RESET state */
324	if ((error = bhnd_suspend_hw(child, reset_ioctl)))
325		return (error);
326
327	/*
328	 * Leaving the core in reset:
329	 * - Set the caller's IOCTL flags
330	 * - Enable clocks
331	 * - Force clock distribution to ensure propagation throughout the
332	 *   core.
333	 */
334	if ((error = bhnd_write_ioctl(child, ioctl | clkflags, UINT16_MAX)))
335		return (error);
336
337	/* Bring the core out of reset */
338	if ((error = bcma_dmp_write_reset(child, dinfo, 0x0)))
339		return (error);
340
341	/* Disable forced clock gating (leaving clock enabled) */
342	error = bhnd_write_ioctl(child, 0x0, BHND_IOCTL_CLK_FORCE);
343	if (error)
344		return (error);
345
346	return (0);
347}
348
349static int
350bcma_suspend_hw(device_t dev, device_t child, uint16_t ioctl)
351{
352	struct bcma_devinfo	*dinfo;
353	struct bhnd_resource	*r;
354	uint16_t		 clkflags;
355	int			 error;
356
357	if (device_get_parent(child) != dev)
358		return (EINVAL);
359
360	dinfo = device_get_ivars(child);
361
362	/* We require exclusive control over BHND_IOCTL_CLK_(EN|FORCE) */
363	clkflags = BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE;
364	if (ioctl & clkflags)
365		return (EINVAL);
366
367	/* Can't suspend the core without access to the agent registers */
368	if ((r = dinfo->res_agent) == NULL)
369		return (ENODEV);
370
371	/* Wait for any pending reset operations to clear */
372	if ((error = bcma_dmp_wait_reset(child, dinfo)))
373		return (error);
374
375	/* Put core into reset (if not already in reset) */
376	if ((error = bcma_dmp_write_reset(child, dinfo, BCMA_DMP_RC_RESET)))
377		return (error);
378
379	/* Write core flags (and clear CLK_EN/CLK_FORCE) */
380	if ((error = bhnd_write_ioctl(child, ioctl, ~clkflags)))
381		return (error);
382
383	return (0);
384}
385
386static int
387bcma_read_config(device_t dev, device_t child, bus_size_t offset, void *value,
388    u_int width)
389{
390	struct bcma_devinfo	*dinfo;
391	struct bhnd_resource	*r;
392
393	/* Must be a directly attached child core */
394	if (device_get_parent(child) != dev)
395		return (EINVAL);
396
397	/* Fetch the agent registers */
398	dinfo = device_get_ivars(child);
399	if ((r = dinfo->res_agent) == NULL)
400		return (ENODEV);
401
402	/* Verify bounds */
403	if (offset > rman_get_size(r->res))
404		return (EFAULT);
405
406	if (rman_get_size(r->res) - offset < width)
407		return (EFAULT);
408
409	switch (width) {
410	case 1:
411		*((uint8_t *)value) = bhnd_bus_read_1(r, offset);
412		return (0);
413	case 2:
414		*((uint16_t *)value) = bhnd_bus_read_2(r, offset);
415		return (0);
416	case 4:
417		*((uint32_t *)value) = bhnd_bus_read_4(r, offset);
418		return (0);
419	default:
420		return (EINVAL);
421	}
422}
423
424static int
425bcma_write_config(device_t dev, device_t child, bus_size_t offset,
426    const void *value, u_int width)
427{
428	struct bcma_devinfo	*dinfo;
429	struct bhnd_resource	*r;
430
431	/* Must be a directly attached child core */
432	if (device_get_parent(child) != dev)
433		return (EINVAL);
434
435	/* Fetch the agent registers */
436	dinfo = device_get_ivars(child);
437	if ((r = dinfo->res_agent) == NULL)
438		return (ENODEV);
439
440	/* Verify bounds */
441	if (offset > rman_get_size(r->res))
442		return (EFAULT);
443
444	if (rman_get_size(r->res) - offset < width)
445		return (EFAULT);
446
447	switch (width) {
448	case 1:
449		bhnd_bus_write_1(r, offset, *(const uint8_t *)value);
450		return (0);
451	case 2:
452		bhnd_bus_write_2(r, offset, *(const uint16_t *)value);
453		return (0);
454	case 4:
455		bhnd_bus_write_4(r, offset, *(const uint32_t *)value);
456		return (0);
457	default:
458		return (EINVAL);
459	}
460}
461
462static u_int
463bcma_get_port_count(device_t dev, device_t child, bhnd_port_type type)
464{
465	struct bcma_devinfo *dinfo;
466
467	/* delegate non-bus-attached devices to our parent */
468	if (device_get_parent(child) != dev)
469		return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), child,
470		    type));
471
472	dinfo = device_get_ivars(child);
473	switch (type) {
474	case BHND_PORT_DEVICE:
475		return (dinfo->corecfg->num_dev_ports);
476	case BHND_PORT_BRIDGE:
477		return (dinfo->corecfg->num_bridge_ports);
478	case BHND_PORT_AGENT:
479		return (dinfo->corecfg->num_wrapper_ports);
480	default:
481		device_printf(dev, "%s: unknown type (%d)\n",
482		    __func__,
483		    type);
484		return (0);
485	}
486}
487
488static u_int
489bcma_get_region_count(device_t dev, device_t child, bhnd_port_type type,
490    u_int port_num)
491{
492	struct bcma_devinfo	*dinfo;
493	struct bcma_sport_list	*ports;
494	struct bcma_sport	*port;
495
496	/* delegate non-bus-attached devices to our parent */
497	if (device_get_parent(child) != dev)
498		return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), child,
499		    type, port_num));
500
501	dinfo = device_get_ivars(child);
502	ports = bcma_corecfg_get_port_list(dinfo->corecfg, type);
503
504	STAILQ_FOREACH(port, ports, sp_link) {
505		if (port->sp_num == port_num)
506			return (port->sp_num_maps);
507	}
508
509	/* not found */
510	return (0);
511}
512
513static int
514bcma_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type,
515    u_int port_num, u_int region_num)
516{
517	struct bcma_devinfo	*dinfo;
518	struct bcma_map		*map;
519	struct bcma_sport_list	*ports;
520	struct bcma_sport	*port;
521
522	dinfo = device_get_ivars(child);
523	ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
524
525	STAILQ_FOREACH(port, ports, sp_link) {
526		if (port->sp_num != port_num)
527			continue;
528
529		STAILQ_FOREACH(map, &port->sp_maps, m_link)
530			if (map->m_region_num == region_num)
531				return map->m_rid;
532	}
533
534	return -1;
535}
536
537static int
538bcma_decode_port_rid(device_t dev, device_t child, int type, int rid,
539    bhnd_port_type *port_type, u_int *port_num, u_int *region_num)
540{
541	struct bcma_devinfo	*dinfo;
542	struct bcma_map		*map;
543	struct bcma_sport_list	*ports;
544	struct bcma_sport	*port;
545
546	dinfo = device_get_ivars(child);
547
548	/* Ports are always memory mapped */
549	if (type != SYS_RES_MEMORY)
550		return (EINVAL);
551
552	/* Starting with the most likely device list, search all three port
553	 * lists */
554	bhnd_port_type types[] = {
555	    BHND_PORT_DEVICE,
556	    BHND_PORT_AGENT,
557	    BHND_PORT_BRIDGE
558	};
559
560	for (int i = 0; i < nitems(types); i++) {
561		ports = bcma_corecfg_get_port_list(dinfo->corecfg, types[i]);
562
563		STAILQ_FOREACH(port, ports, sp_link) {
564			STAILQ_FOREACH(map, &port->sp_maps, m_link) {
565				if (map->m_rid != rid)
566					continue;
567
568				*port_type = port->sp_type;
569				*port_num = port->sp_num;
570				*region_num = map->m_region_num;
571				return (0);
572			}
573		}
574	}
575
576	return (ENOENT);
577}
578
579static int
580bcma_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type,
581    u_int port_num, u_int region_num, bhnd_addr_t *addr, bhnd_size_t *size)
582{
583	struct bcma_devinfo	*dinfo;
584	struct bcma_map		*map;
585	struct bcma_sport_list	*ports;
586	struct bcma_sport	*port;
587
588	dinfo = device_get_ivars(child);
589	ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
590
591	/* Search the port list */
592	STAILQ_FOREACH(port, ports, sp_link) {
593		if (port->sp_num != port_num)
594			continue;
595
596		STAILQ_FOREACH(map, &port->sp_maps, m_link) {
597			if (map->m_region_num != region_num)
598				continue;
599
600			/* Found! */
601			*addr = map->m_base;
602			*size = map->m_size;
603			return (0);
604		}
605	}
606
607	return (ENOENT);
608}
609
610/**
611 * Default bcma(4) bus driver implementation of BHND_BUS_GET_INTR_COUNT().
612 */
613u_int
614bcma_get_intr_count(device_t dev, device_t child)
615{
616	struct bcma_devinfo *dinfo;
617
618	/* delegate non-bus-attached devices to our parent */
619	if (device_get_parent(child) != dev)
620		return (BHND_BUS_GET_INTR_COUNT(device_get_parent(dev), child));
621
622	dinfo = device_get_ivars(child);
623	return (dinfo->num_intrs);
624}
625
626/**
627 * Default bcma(4) bus driver implementation of BHND_BUS_GET_INTR_IVEC().
628 */
629int
630bcma_get_intr_ivec(device_t dev, device_t child, u_int intr, u_int *ivec)
631{
632	struct bcma_devinfo	*dinfo;
633	struct bcma_intr	*desc;
634
635	/* delegate non-bus-attached devices to our parent */
636	if (device_get_parent(child) != dev) {
637		return (BHND_BUS_GET_INTR_IVEC(device_get_parent(dev), child,
638		    intr, ivec));
639	}
640
641	dinfo = device_get_ivars(child);
642
643	STAILQ_FOREACH(desc, &dinfo->intrs, i_link) {
644		if (desc->i_sel == intr) {
645			*ivec = desc->i_busline;
646			return (0);
647		}
648	}
649
650	/* Not found */
651	return (ENXIO);
652}
653
654/**
655 * Scan the device enumeration ROM table, adding all valid discovered cores to
656 * the bus.
657 *
658 * @param bus The bcma bus.
659 */
660int
661bcma_add_children(device_t bus)
662{
663	bhnd_erom_t			*erom;
664	struct bcma_erom		*bcma_erom;
665	struct bhnd_erom_io		*eio;
666	const struct bhnd_chipid	*cid;
667	struct bcma_corecfg		*corecfg;
668	struct bcma_devinfo		*dinfo;
669	device_t			 child;
670	int				 error;
671
672	cid = BHND_BUS_GET_CHIPID(bus, bus);
673	corecfg = NULL;
674
675	/* Allocate our EROM parser */
676	eio = bhnd_erom_iores_new(bus, BCMA_EROM_RID);
677	erom = bhnd_erom_alloc(&bcma_erom_parser, cid, eio);
678	if (erom == NULL) {
679		bhnd_erom_io_fini(eio);
680		return (ENODEV);
681	}
682
683	/* Add all cores. */
684	bcma_erom = (struct bcma_erom *)erom;
685	while ((error = bcma_erom_next_corecfg(bcma_erom, &corecfg)) == 0) {
686		/* Add the child device */
687		child = BUS_ADD_CHILD(bus, 0, NULL, -1);
688		if (child == NULL) {
689			error = ENXIO;
690			goto cleanup;
691		}
692
693		/* Initialize device ivars */
694		dinfo = device_get_ivars(child);
695		if ((error = bcma_init_dinfo(bus, child, dinfo, corecfg)))
696			goto cleanup;
697
698		/* The dinfo instance now owns the corecfg value */
699		corecfg = NULL;
700
701		/* If pins are floating or the hardware is otherwise
702		 * unpopulated, the device shouldn't be used. */
703		if (bhnd_is_hw_disabled(child))
704			device_disable(child);
705
706		/* Issue bus callback for fully initialized child. */
707		BHND_BUS_CHILD_ADDED(bus, child);
708	}
709
710	/* EOF while parsing cores is expected */
711	if (error == ENOENT)
712		error = 0;
713
714cleanup:
715	bhnd_erom_free(erom);
716
717	if (corecfg != NULL)
718		bcma_free_corecfg(corecfg);
719
720	if (error)
721		device_delete_children(bus);
722
723	return (error);
724}
725
726
727static device_method_t bcma_methods[] = {
728	/* Device interface */
729	DEVMETHOD(device_probe,			bcma_probe),
730	DEVMETHOD(device_attach,		bcma_attach),
731	DEVMETHOD(device_detach,		bcma_detach),
732
733	/* Bus interface */
734	DEVMETHOD(bus_add_child,		bcma_add_child),
735	DEVMETHOD(bus_child_deleted,		bcma_child_deleted),
736	DEVMETHOD(bus_read_ivar,		bcma_read_ivar),
737	DEVMETHOD(bus_write_ivar,		bcma_write_ivar),
738	DEVMETHOD(bus_get_resource_list,	bcma_get_resource_list),
739
740	/* BHND interface */
741	DEVMETHOD(bhnd_bus_get_erom_class,	bcma_get_erom_class),
742	DEVMETHOD(bhnd_bus_read_ioctl,		bcma_read_ioctl),
743	DEVMETHOD(bhnd_bus_write_ioctl,		bcma_write_ioctl),
744	DEVMETHOD(bhnd_bus_read_iost,		bcma_read_iost),
745	DEVMETHOD(bhnd_bus_is_hw_suspended,	bcma_is_hw_suspended),
746	DEVMETHOD(bhnd_bus_reset_hw,		bcma_reset_hw),
747	DEVMETHOD(bhnd_bus_suspend_hw,		bcma_suspend_hw),
748	DEVMETHOD(bhnd_bus_read_config,		bcma_read_config),
749	DEVMETHOD(bhnd_bus_write_config,	bcma_write_config),
750	DEVMETHOD(bhnd_bus_get_port_count,	bcma_get_port_count),
751	DEVMETHOD(bhnd_bus_get_region_count,	bcma_get_region_count),
752	DEVMETHOD(bhnd_bus_get_port_rid,	bcma_get_port_rid),
753	DEVMETHOD(bhnd_bus_decode_port_rid,	bcma_decode_port_rid),
754	DEVMETHOD(bhnd_bus_get_region_addr,	bcma_get_region_addr),
755	DEVMETHOD(bhnd_bus_get_intr_count,	bcma_get_intr_count),
756	DEVMETHOD(bhnd_bus_get_intr_ivec,	bcma_get_intr_ivec),
757
758	DEVMETHOD_END
759};
760
761DEFINE_CLASS_1(bhnd, bcma_driver, bcma_methods, sizeof(struct bcma_softc), bhnd_driver);
762MODULE_VERSION(bcma, 1);
763MODULE_DEPEND(bcma, bhnd, 1, 1, 1);
764