1/*-
2 * Copyright (c) 2009-2010 Weongyo Jeong <weongyo@freebsd.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/*
34 * Sonics Silicon Backplane front-end for bwn(4).
35 */
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/module.h>
40#include <sys/kernel.h>
41#include <sys/lock.h>
42#include <sys/mutex.h>
43#include <sys/errno.h>
44#include <machine/bus.h>
45#include <machine/resource.h>
46#include <sys/bus.h>
47#include <sys/rman.h>
48#include <sys/socket.h>
49
50#include <net/if.h>
51#include <net/if_media.h>
52#include <net/if_arp.h>
53
54#include <dev/pci/pcivar.h>
55#include <dev/pci/pcireg.h>
56
57#include <dev/siba/siba_ids.h>
58#include <dev/siba/sibareg.h>
59#include <dev/siba/sibavar.h>
60
61/*
62 * PCI glue.
63 */
64
65struct siba_bwn_softc {
66	/* Child driver using MSI. */
67	device_t			ssc_msi_child;
68	struct siba_softc		ssc_siba;
69};
70
71#define	BS_BAR				0x10
72#define	PCI_VENDOR_BROADCOM		0x14e4
73#define	N(a)				(sizeof(a) / sizeof(a[0]))
74
75static const struct siba_dev {
76	uint16_t	vid;
77	uint16_t	did;
78	const char	*desc;
79} siba_devices[] = {
80	{ PCI_VENDOR_BROADCOM, 0x4301, "Broadcom BCM4301 802.11b Wireless" },
81	{ PCI_VENDOR_BROADCOM, 0x4306, "Unknown" },
82	{ PCI_VENDOR_BROADCOM, 0x4307, "Broadcom BCM4307 802.11b Wireless" },
83	{ PCI_VENDOR_BROADCOM, 0x4311, "Broadcom BCM4311 802.11b/g Wireless" },
84	{ PCI_VENDOR_BROADCOM, 0x4312,
85	  "Broadcom BCM4312 802.11a/b/g Wireless" },
86	{ PCI_VENDOR_BROADCOM, 0x4315, "Broadcom BCM4312 802.11b/g Wireless" },
87	{ PCI_VENDOR_BROADCOM, 0x4318, "Broadcom BCM4318 802.11b/g Wireless" },
88	{ PCI_VENDOR_BROADCOM, 0x4319,
89	  "Broadcom BCM4318 802.11a/b/g Wireless" },
90	{ PCI_VENDOR_BROADCOM, 0x4320, "Broadcom BCM4306 802.11b/g Wireless" },
91	{ PCI_VENDOR_BROADCOM, 0x4321, "Broadcom BCM4306 802.11a Wireless" },
92	{ PCI_VENDOR_BROADCOM, 0x4324,
93	  "Broadcom BCM4309 802.11a/b/g Wireless" },
94	{ PCI_VENDOR_BROADCOM, 0x4325, "Broadcom BCM4306 802.11b/g Wireless" },
95	{ PCI_VENDOR_BROADCOM, 0x4328, "Unknown" },
96	{ PCI_VENDOR_BROADCOM, 0x4329, "Unknown" },
97	{ PCI_VENDOR_BROADCOM, 0x432b, "Unknown" }
98};
99
100int		siba_core_attach(struct siba_softc *);
101int		siba_core_detach(struct siba_softc *);
102int		siba_core_suspend(struct siba_softc *);
103int		siba_core_resume(struct siba_softc *);
104
105static int
106siba_bwn_probe(device_t dev)
107{
108	int i;
109	uint16_t did, vid;
110
111	did = pci_get_device(dev);
112	vid = pci_get_vendor(dev);
113
114	for (i = 0; i < N(siba_devices); i++) {
115		if (siba_devices[i].did == did && siba_devices[i].vid == vid) {
116			device_set_desc(dev, siba_devices[i].desc);
117			return (BUS_PROBE_DEFAULT);
118		}
119	}
120	return (ENXIO);
121}
122
123static int
124siba_bwn_attach(device_t dev)
125{
126	struct siba_bwn_softc *ssc = device_get_softc(dev);
127	struct siba_softc *siba = &ssc->ssc_siba;
128
129	siba->siba_dev = dev;
130	siba->siba_type = SIBA_TYPE_PCI;
131
132	/*
133	 * Enable bus mastering.
134	 */
135	pci_enable_busmaster(dev);
136
137	/*
138	 * Setup memory-mapping of PCI registers.
139	 */
140	siba->siba_mem_rid = SIBA_PCIR_BAR;
141	siba->siba_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
142		&siba->siba_mem_rid, RF_ACTIVE);
143	if (siba->siba_mem_res == NULL) {
144		device_printf(dev, "cannot map register space\n");
145		return (ENXIO);
146	}
147	siba->siba_mem_bt = rman_get_bustag(siba->siba_mem_res);
148	siba->siba_mem_bh = rman_get_bushandle(siba->siba_mem_res);
149
150	/* Get more PCI information */
151	siba->siba_pci_did = pci_get_device(dev);
152	siba->siba_pci_vid = pci_get_vendor(dev);
153	siba->siba_pci_subvid = pci_get_subvendor(dev);
154	siba->siba_pci_subdid = pci_get_subdevice(dev);
155	siba->siba_pci_revid = pci_get_revid(dev);
156
157	return (siba_core_attach(siba));
158}
159
160static int
161siba_bwn_detach(device_t dev)
162{
163	struct siba_bwn_softc *ssc = device_get_softc(dev);
164	struct siba_softc *siba = &ssc->ssc_siba;
165
166	/* check if device was removed */
167	siba->siba_invalid = !bus_child_present(dev);
168
169	pci_disable_busmaster(dev);
170	bus_generic_detach(dev);
171	siba_core_detach(siba);
172
173	bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR, siba->siba_mem_res);
174
175	return (0);
176}
177
178static int
179siba_bwn_shutdown(device_t dev)
180{
181	device_t *devlistp;
182	int devcnt, error = 0, i;
183
184	error = device_get_children(dev, &devlistp, &devcnt);
185	if (error != 0)
186		return (error);
187
188	for (i = 0 ; i < devcnt ; i++)
189		device_shutdown(devlistp[i]);
190	free(devlistp, M_TEMP);
191	return (0);
192}
193
194static int
195siba_bwn_suspend(device_t dev)
196{
197	struct siba_bwn_softc *ssc = device_get_softc(dev);
198	struct siba_softc *siba = &ssc->ssc_siba;
199	device_t *devlistp;
200	int devcnt, error = 0, i, j;
201
202	error = device_get_children(dev, &devlistp, &devcnt);
203	if (error != 0)
204		return (error);
205
206	for (i = 0 ; i < devcnt ; i++) {
207		error = DEVICE_SUSPEND(devlistp[i]);
208		if (error) {
209			for (j = 0; j < i; j++)
210				DEVICE_RESUME(devlistp[j]);
211			free(devlistp, M_TEMP);
212			return (error);
213		}
214	}
215	free(devlistp, M_TEMP);
216	return (siba_core_suspend(siba));
217}
218
219static int
220siba_bwn_resume(device_t dev)
221{
222	struct siba_bwn_softc *ssc = device_get_softc(dev);
223	struct siba_softc *siba = &ssc->ssc_siba;
224	device_t *devlistp;
225	int devcnt, error = 0, i;
226
227	error = siba_core_resume(siba);
228	if (error != 0)
229		return (error);
230
231	error = device_get_children(dev, &devlistp, &devcnt);
232	if (error != 0)
233		return (error);
234
235	for (i = 0 ; i < devcnt ; i++)
236		DEVICE_RESUME(devlistp[i]);
237	free(devlistp, M_TEMP);
238	return (0);
239}
240
241/* proxying to the parent */
242static struct resource *
243siba_bwn_alloc_resource(device_t dev, device_t child, int type, int *rid,
244    u_long start, u_long end, u_long count, u_int flags)
245{
246
247	return (BUS_ALLOC_RESOURCE(device_get_parent(dev), dev,
248	    type, rid, start, end, count, flags));
249}
250
251/* proxying to the parent */
252static int
253siba_bwn_release_resource(device_t dev, device_t child, int type,
254    int rid, struct resource *r)
255{
256
257	return (BUS_RELEASE_RESOURCE(device_get_parent(dev), dev, type,
258	    rid, r));
259}
260
261/* proxying to the parent */
262static int
263siba_bwn_setup_intr(device_t dev, device_t child, struct resource *irq,
264    int flags, driver_filter_t *filter, driver_intr_t *intr, void *arg,
265    void **cookiep)
266{
267
268	return (BUS_SETUP_INTR(device_get_parent(dev), dev, irq, flags,
269	    filter, intr, arg, cookiep));
270}
271
272/* proxying to the parent */
273static int
274siba_bwn_teardown_intr(device_t dev, device_t child, struct resource *irq,
275    void *cookie)
276{
277
278	return (BUS_TEARDOWN_INTR(device_get_parent(dev), dev, irq, cookie));
279}
280
281static int
282siba_bwn_find_cap(device_t dev, device_t child, int capability,
283    int *capreg)
284{
285
286	return (pci_find_cap(dev, capability, capreg));
287}
288
289static int
290siba_bwn_find_extcap(device_t dev, device_t child, int capability,
291    int *capreg)
292{
293
294	return (pci_find_extcap(dev, capability, capreg));
295}
296
297static int
298siba_bwn_find_htcap(device_t dev, device_t child, int capability,
299    int *capreg)
300{
301
302	return (pci_find_htcap(dev, capability, capreg));
303}
304
305static int
306siba_bwn_alloc_msi(device_t dev, device_t child, int *count)
307{
308	struct siba_bwn_softc *ssc;
309	int error;
310
311	ssc = device_get_softc(dev);
312	if (ssc->ssc_msi_child != NULL)
313		return (EBUSY);
314	error = pci_alloc_msi(dev, count);
315	if (error == 0)
316		ssc->ssc_msi_child = child;
317	return (error);
318}
319
320static int
321siba_bwn_release_msi(device_t dev, device_t child)
322{
323	struct siba_bwn_softc *ssc;
324	int error;
325
326	ssc = device_get_softc(dev);
327	if (ssc->ssc_msi_child != child)
328		return (ENXIO);
329	error = pci_release_msi(dev);
330	if (error == 0)
331		ssc->ssc_msi_child = NULL;
332	return (error);
333}
334
335static int
336siba_bwn_msi_count(device_t dev, device_t child)
337{
338
339	return (pci_msi_count(dev));
340}
341
342static int
343siba_bwn_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
344{
345	struct siba_dev_softc *sd;
346	struct siba_softc *siba;
347
348	sd = device_get_ivars(child);
349	siba = sd->sd_bus;
350
351	switch (which) {
352	case SIBA_IVAR_VENDOR:
353		*result = sd->sd_id.sd_vendor;
354		break;
355	case SIBA_IVAR_DEVICE:
356		*result = sd->sd_id.sd_device;
357		break;
358	case SIBA_IVAR_REVID:
359		*result = sd->sd_id.sd_rev;
360		break;
361	case SIBA_IVAR_PCI_VENDOR:
362		*result = siba->siba_pci_vid;
363		break;
364	case SIBA_IVAR_PCI_DEVICE:
365		*result = siba->siba_pci_did;
366		break;
367	case SIBA_IVAR_PCI_SUBVENDOR:
368		*result = siba->siba_pci_subvid;
369		break;
370	case SIBA_IVAR_PCI_SUBDEVICE:
371		*result = siba->siba_pci_subdid;
372		break;
373	case SIBA_IVAR_PCI_REVID:
374		*result = siba->siba_pci_revid;
375		break;
376	case SIBA_IVAR_CHIPID:
377		*result = siba->siba_chipid;
378		break;
379	case SIBA_IVAR_CHIPREV:
380		*result = siba->siba_chiprev;
381		break;
382	case SIBA_IVAR_CHIPPKG:
383		*result = siba->siba_chippkg;
384		break;
385	case SIBA_IVAR_TYPE:
386		*result = siba->siba_type;
387		break;
388	case SIBA_IVAR_CC_PMUFREQ:
389		*result = siba->siba_cc.scc_pmu.freq;
390		break;
391	case SIBA_IVAR_CC_CAPS:
392		*result = siba->siba_cc.scc_caps;
393		break;
394	case SIBA_IVAR_CC_POWERDELAY:
395		*result = siba->siba_cc.scc_powerup_delay;
396		break;
397	case SIBA_IVAR_PCICORE_REVID:
398		*result = siba->siba_pci.spc_dev->sd_id.sd_rev;
399		break;
400	default:
401		return (ENOENT);
402	}
403
404	return (0);
405}
406
407static device_method_t siba_bwn_methods[] = {
408	/* Device interface */
409	DEVMETHOD(device_probe,		siba_bwn_probe),
410	DEVMETHOD(device_attach,	siba_bwn_attach),
411	DEVMETHOD(device_detach,	siba_bwn_detach),
412	DEVMETHOD(device_shutdown,	siba_bwn_shutdown),
413	DEVMETHOD(device_suspend,	siba_bwn_suspend),
414	DEVMETHOD(device_resume,	siba_bwn_resume),
415
416	/* Bus interface */
417	DEVMETHOD(bus_alloc_resource,   siba_bwn_alloc_resource),
418	DEVMETHOD(bus_release_resource, siba_bwn_release_resource),
419	DEVMETHOD(bus_read_ivar,	siba_bwn_read_ivar),
420	DEVMETHOD(bus_setup_intr,       siba_bwn_setup_intr),
421	DEVMETHOD(bus_teardown_intr,    siba_bwn_teardown_intr),
422
423	/* PCI interface */
424	DEVMETHOD(pci_find_cap,		siba_bwn_find_cap),
425	DEVMETHOD(pci_find_extcap,	siba_bwn_find_extcap),
426	DEVMETHOD(pci_find_htcap,	siba_bwn_find_htcap),
427	DEVMETHOD(pci_alloc_msi,	siba_bwn_alloc_msi),
428	DEVMETHOD(pci_release_msi,	siba_bwn_release_msi),
429	DEVMETHOD(pci_msi_count,	siba_bwn_msi_count),
430
431	DEVMETHOD_END
432};
433static driver_t siba_bwn_driver = {
434	"siba_bwn",
435	siba_bwn_methods,
436	sizeof(struct siba_bwn_softc)
437};
438static devclass_t siba_bwn_devclass;
439DRIVER_MODULE(siba_bwn, pci, siba_bwn_driver, siba_bwn_devclass, 0, 0);
440MODULE_VERSION(siba_bwn, 1);
441