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