1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2000,2001 Jonathan Chen.  All rights reserved.
5 * Copyright (c) 2003-2008 M. Warner Losh <imp@FreeBSD.org>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD$");
31
32#include <sys/param.h>
33#include <sys/eventhandler.h>
34#include <sys/systm.h>
35#include <sys/malloc.h>
36#include <sys/module.h>
37#include <sys/kernel.h>
38#include <sys/sysctl.h>
39
40#include <sys/bus.h>
41#include <machine/bus.h>
42#include <sys/rman.h>
43#include <machine/resource.h>
44
45#include <sys/pciio.h>
46#include <dev/pci/pcivar.h>
47#include <dev/pci/pcireg.h>
48#include <dev/pci/pci_private.h>
49
50#include <dev/cardbus/cardbusreg.h>
51#include <dev/cardbus/cardbusvar.h>
52#include <dev/cardbus/cardbus_cis.h>
53#include <dev/pccard/pccard_cis.h>
54#include <dev/pccard/pccardvar.h>
55
56#include "power_if.h"
57#include "pcib_if.h"
58
59/* sysctl vars */
60static SYSCTL_NODE(_hw, OID_AUTO, cardbus, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
61    "CardBus parameters");
62
63int    cardbus_debug = 0;
64SYSCTL_INT(_hw_cardbus, OID_AUTO, debug, CTLFLAG_RWTUN,
65    &cardbus_debug, 0, "CardBus debug");
66
67int    cardbus_cis_debug = 0;
68SYSCTL_INT(_hw_cardbus, OID_AUTO, cis_debug, CTLFLAG_RWTUN,
69    &cardbus_cis_debug, 0, "CardBus CIS debug");
70
71#define	DPRINTF(a) if (cardbus_debug) printf a
72#define	DEVPRINTF(x) if (cardbus_debug) device_printf x
73
74static int	cardbus_attach(device_t cbdev);
75static int	cardbus_attach_card(device_t cbdev);
76static int	cardbus_detach(device_t cbdev);
77static int	cardbus_detach_card(device_t cbdev);
78static void	cardbus_device_setup_regs(pcicfgregs *cfg);
79static void	cardbus_driver_added(device_t cbdev, driver_t *driver);
80static int	cardbus_probe(device_t cbdev);
81static int	cardbus_read_ivar(device_t cbdev, device_t child, int which,
82		    uintptr_t *result);
83
84/************************************************************************/
85/* Probe/Attach								*/
86/************************************************************************/
87
88static int
89cardbus_probe(device_t cbdev)
90{
91	device_set_desc(cbdev, "CardBus bus");
92	return (0);
93}
94
95static int
96cardbus_attach(device_t cbdev)
97{
98	struct cardbus_softc *sc;
99#ifdef PCI_RES_BUS
100	int rid;
101#endif
102
103	sc = device_get_softc(cbdev);
104	sc->sc_dev = cbdev;
105#ifdef PCI_RES_BUS
106	rid = 0;
107	sc->sc_bus = bus_alloc_resource(cbdev, PCI_RES_BUS, &rid,
108	    pcib_get_bus(cbdev), pcib_get_bus(cbdev), 1, 0);
109	if (sc->sc_bus == NULL) {
110		device_printf(cbdev, "failed to allocate bus number\n");
111		return (ENXIO);
112	}
113#else
114	device_printf(cbdev, "Your bus numbers may be AFU\n");
115#endif
116	return (0);
117}
118
119static int
120cardbus_detach(device_t cbdev)
121{
122#ifdef PCI_RES_BUS
123	struct cardbus_softc *sc;
124#endif
125
126	cardbus_detach_card(cbdev);
127#ifdef PCI_RES_BUS
128	sc = device_get_softc(cbdev);
129	device_printf(cbdev, "Freeing up the allocatd bus\n");
130	(void)bus_release_resource(cbdev, PCI_RES_BUS, 0, sc->sc_bus);
131#endif
132	return (0);
133}
134
135static int
136cardbus_suspend(device_t self)
137{
138
139	cardbus_detach_card(self);
140	return (0);
141}
142
143static int
144cardbus_resume(device_t self)
145{
146
147	return (0);
148}
149
150/************************************************************************/
151/* Attach/Detach card							*/
152/************************************************************************/
153
154static void
155cardbus_device_setup_regs(pcicfgregs *cfg)
156{
157	device_t dev = cfg->dev;
158	int i;
159
160	/*
161	 * Some cards power up with garbage in their BARs.  This
162	 * code clears all that junk out.
163	 */
164	for (i = 0; i < PCIR_MAX_BAR_0; i++)
165		pci_write_config(dev, PCIR_BAR(i), 0, 4);
166
167	cfg->intline =
168	    pci_get_irq(device_get_parent(device_get_parent(dev)));
169	pci_write_config(dev, PCIR_INTLINE, cfg->intline, 1);
170	pci_write_config(dev, PCIR_CACHELNSZ, 0x08, 1);
171	pci_write_config(dev, PCIR_LATTIMER, 0xa8, 1);
172	pci_write_config(dev, PCIR_MINGNT, 0x14, 1);
173	pci_write_config(dev, PCIR_MAXLAT, 0x14, 1);
174}
175
176static struct pci_devinfo *
177cardbus_alloc_devinfo(device_t dev)
178{
179	struct cardbus_devinfo *dinfo;
180
181	dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO);
182	return (&dinfo->pci);
183}
184
185static int
186cardbus_attach_card(device_t cbdev)
187{
188	device_t brdev = device_get_parent(cbdev);
189	device_t child;
190	int bus, domain, slot, func;
191	int cardattached = 0;
192	int cardbusfunchigh = 0;
193	struct cardbus_softc *sc;
194
195	sc = device_get_softc(cbdev);
196	cardbus_detach_card(cbdev); /* detach existing cards */
197	POWER_DISABLE_SOCKET(brdev, cbdev); /* Turn the socket off first */
198	POWER_ENABLE_SOCKET(brdev, cbdev);
199	domain = pcib_get_domain(cbdev);
200	bus = pcib_get_bus(cbdev);
201	slot = 0;
202	mtx_lock(&Giant);
203	/* For each function, set it up and try to attach a driver to it */
204	for (func = 0; func <= cardbusfunchigh; func++) {
205		struct cardbus_devinfo *dinfo;
206
207		dinfo = (struct cardbus_devinfo *)
208		    pci_read_device(brdev, cbdev, domain, bus, slot, func);
209		if (dinfo == NULL)
210			continue;
211		if (dinfo->pci.cfg.mfdev)
212			cardbusfunchigh = PCI_FUNCMAX;
213
214		child = device_add_child(cbdev, NULL, -1);
215		if (child == NULL) {
216			DEVPRINTF((cbdev, "Cannot add child!\n"));
217			pci_freecfg((struct pci_devinfo *)dinfo);
218			continue;
219		}
220		dinfo->pci.cfg.dev = child;
221		resource_list_init(&dinfo->pci.resources);
222		device_set_ivars(child, dinfo);
223		cardbus_device_create(sc, dinfo, cbdev, child);
224		if (cardbus_do_cis(cbdev, child) != 0)
225			DEVPRINTF((cbdev, "Warning: Bogus CIS ignored\n"));
226		pci_cfg_save(dinfo->pci.cfg.dev, &dinfo->pci, 0);
227		pci_cfg_restore(dinfo->pci.cfg.dev, &dinfo->pci);
228		cardbus_device_setup_regs(&dinfo->pci.cfg);
229		pci_add_resources(cbdev, child, 1, dinfo->mprefetchable);
230		pci_print_verbose(&dinfo->pci);
231		if (device_probe_and_attach(child) == 0)
232			cardattached++;
233		else
234			pci_cfg_save(dinfo->pci.cfg.dev, &dinfo->pci, 1);
235	}
236	mtx_unlock(&Giant);
237	if (cardattached > 0)
238		return (0);
239/*	POWER_DISABLE_SOCKET(brdev, cbdev); */
240	return (ENOENT);
241}
242
243static void
244cardbus_child_deleted(device_t cbdev, device_t child)
245{
246	struct cardbus_devinfo *dinfo = device_get_ivars(child);
247
248	if (dinfo->pci.cfg.dev != child)
249		device_printf(cbdev, "devinfo dev mismatch\n");
250	cardbus_device_destroy(dinfo);
251	pci_child_deleted(cbdev, child);
252}
253
254static int
255cardbus_detach_card(device_t cbdev)
256{
257	int err = 0;
258
259	mtx_lock(&Giant);
260	err = bus_generic_detach(cbdev);
261	if (err == 0)
262		err = device_delete_children(cbdev);
263	mtx_unlock(&Giant);
264	if (err)
265		return (err);
266
267	POWER_DISABLE_SOCKET(device_get_parent(cbdev), cbdev);
268	return (err);
269}
270
271static void
272cardbus_driver_added(device_t cbdev, driver_t *driver)
273{
274	int numdevs;
275	device_t *devlist;
276	device_t dev;
277	int i;
278	struct cardbus_devinfo *dinfo;
279
280	DEVICE_IDENTIFY(driver, cbdev);
281	if (device_get_children(cbdev, &devlist, &numdevs) != 0)
282		return;
283
284	/*
285	 * If there are no drivers attached, but there are children,
286	 * then power the card up.
287	 */
288	for (i = 0; i < numdevs; i++) {
289		dev = devlist[i];
290		if (device_get_state(dev) != DS_NOTPRESENT)
291		    break;
292	}
293	if (i > 0 && i == numdevs)
294		POWER_ENABLE_SOCKET(device_get_parent(cbdev), cbdev);
295	for (i = 0; i < numdevs; i++) {
296		dev = devlist[i];
297		if (device_get_state(dev) != DS_NOTPRESENT)
298			continue;
299		dinfo = device_get_ivars(dev);
300		pci_print_verbose(&dinfo->pci);
301		if (bootverbose)
302			printf("pci%d:%d:%d:%d: reprobing on driver added\n",
303			    dinfo->pci.cfg.domain, dinfo->pci.cfg.bus,
304			    dinfo->pci.cfg.slot, dinfo->pci.cfg.func);
305		pci_cfg_restore(dinfo->pci.cfg.dev, &dinfo->pci);
306		if (device_probe_and_attach(dev) != 0)
307			pci_cfg_save(dev, &dinfo->pci, 1);
308	}
309	free(devlist, M_TEMP);
310}
311
312/************************************************************************/
313/* Other Bus Methods							*/
314/************************************************************************/
315
316static int
317cardbus_read_ivar(device_t cbdev, device_t child, int which, uintptr_t *result)
318{
319	struct cardbus_devinfo *dinfo;
320	pcicfgregs *cfg;
321
322	dinfo = device_get_ivars(child);
323	cfg = &dinfo->pci.cfg;
324
325	switch (which) {
326	case PCI_IVAR_ETHADDR:
327		/*
328		 * The generic accessor doesn't deal with failure, so
329		 * we set the return value, then return an error.
330		 */
331		if (dinfo->fepresent & (1 << PCCARD_TPLFE_TYPE_LAN_NID)) {
332			*((uint8_t **) result) = dinfo->funce.lan.nid;
333			break;
334		}
335		*((uint8_t **) result) = NULL;
336		return (EINVAL);
337	default:
338		return (pci_read_ivar(cbdev, child, which, result));
339	}
340	return 0;
341}
342
343static device_method_t cardbus_methods[] = {
344	/* Device interface */
345	DEVMETHOD(device_probe,		cardbus_probe),
346	DEVMETHOD(device_attach,	cardbus_attach),
347	DEVMETHOD(device_detach,	cardbus_detach),
348	DEVMETHOD(device_suspend,	cardbus_suspend),
349	DEVMETHOD(device_resume,	cardbus_resume),
350
351	/* Bus interface */
352	DEVMETHOD(bus_child_deleted,	cardbus_child_deleted),
353	DEVMETHOD(bus_get_dma_tag,	bus_generic_get_dma_tag),
354	DEVMETHOD(bus_read_ivar,	cardbus_read_ivar),
355	DEVMETHOD(bus_driver_added,	cardbus_driver_added),
356	DEVMETHOD(bus_rescan,		bus_null_rescan),
357
358	/* Card Interface */
359	DEVMETHOD(card_attach_card,	cardbus_attach_card),
360	DEVMETHOD(card_detach_card,	cardbus_detach_card),
361
362	/* PCI interface */
363	DEVMETHOD(pci_alloc_devinfo,	cardbus_alloc_devinfo),
364	{0,0}
365};
366
367DEFINE_CLASS_1(cardbus, cardbus_driver, cardbus_methods,
368    sizeof(struct cardbus_softc), pci_driver);
369
370static devclass_t cardbus_devclass;
371
372DRIVER_MODULE(cardbus, cbb, cardbus_driver, cardbus_devclass, 0, 0);
373MODULE_VERSION(cardbus, 1);
374