pccard.c revision 150391
1321936Shselasky/*	$NetBSD: pcmcia.c,v 1.23 2000/07/28 19:17:02 drochner Exp $	*/
2321936Shselasky
3321936Shselasky/*-
4321936Shselasky * Copyright (c) 1997 Marc Horowitz.  All rights reserved.
5321936Shselasky *
6321936Shselasky * Redistribution and use in source and binary forms, with or without
7321936Shselasky * modification, are permitted provided that the following conditions
8321936Shselasky * are met:
9321936Shselasky * 1. Redistributions of source code must retain the above copyright
10321936Shselasky *    notice, this list of conditions and the following disclaimer.
11321936Shselasky * 2. Redistributions in binary form must reproduce the above copyright
12321936Shselasky *    notice, this list of conditions and the following disclaimer in the
13321936Shselasky *    documentation and/or other materials provided with the distribution.
14321936Shselasky * 3. All advertising materials mentioning features or use of this software
15321936Shselasky *    must display the following acknowledgement:
16321936Shselasky *	This product includes software developed by Marc Horowitz.
17321936Shselasky * 4. The name of the author may not be used to endorse or promote products
18321936Shselasky *    derived from this software without specific prior written permission.
19321936Shselasky *
20321936Shselasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21321936Shselasky * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22321936Shselasky * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23321936Shselasky * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24321936Shselasky * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25321936Shselasky * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26321936Shselasky * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27321936Shselasky * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28321936Shselasky * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29321936Shselasky * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30321936Shselasky */
31321936Shselasky
32321936Shselasky#include <sys/cdefs.h>
33321936Shselasky__FBSDID("$FreeBSD: head/sys/dev/pccard/pccard.c 150391 2005-09-20 19:34:10Z imp $");
34321936Shselasky
35321936Shselasky#include <sys/param.h>
36321936Shselasky#include <sys/systm.h>
37321936Shselasky#include <sys/malloc.h>
38321936Shselasky#include <sys/module.h>
39321936Shselasky#include <sys/kernel.h>
40321936Shselasky#include <sys/queue.h>
41321936Shselasky#include <sys/sysctl.h>
42321936Shselasky#include <sys/types.h>
43321936Shselasky
44321936Shselasky#include <sys/bus.h>
45321936Shselasky#include <machine/bus.h>
46321936Shselasky#include <sys/rman.h>
47321936Shselasky#include <machine/resource.h>
48321936Shselasky
49321936Shselasky#include <net/ethernet.h>
50321936Shselasky
51321936Shselasky#include <dev/pccard/pccardreg.h>
52321936Shselasky#include <dev/pccard/pccardvar.h>
53321936Shselasky#include <dev/pccard/pccardvarp.h>
54321936Shselasky#include <dev/pccard/pccard_cis.h>
55321936Shselasky
56321936Shselasky#include "power_if.h"
57321936Shselasky#include "card_if.h"
58321936Shselasky
59321936Shselasky#define PCCARDDEBUG
60321936Shselasky
61321936Shselasky/* sysctl vars */
62321936ShselaskySYSCTL_NODE(_hw, OID_AUTO, pccard, CTLFLAG_RD, 0, "PCCARD parameters");
63321936Shselasky
64321936Shselaskyint	pccard_debug = 0;
65321936ShselaskyTUNABLE_INT("hw.pccard.debug", &pccard_debug);
66321936ShselaskySYSCTL_INT(_hw_pccard, OID_AUTO, debug, CTLFLAG_RW,
67321936Shselasky    &pccard_debug, 0,
68321936Shselasky  "pccard debug");
69321936Shselasky
70321936Shselaskyint	pccard_cis_debug = 0;
71321936ShselaskyTUNABLE_INT("hw.pccard.cis_debug", &pccard_cis_debug);
72321936ShselaskySYSCTL_INT(_hw_pccard, OID_AUTO, cis_debug, CTLFLAG_RW,
73321936Shselasky    &pccard_cis_debug, 0, "pccard CIS debug");
74321936Shselasky
75321936Shselasky#ifdef PCCARDDEBUG
76321936Shselasky#define	DPRINTF(arg) if (pccard_debug) printf arg
77321936Shselasky#define	DEVPRINTF(arg) if (pccard_debug) device_printf arg
78321936Shselasky#define PRVERBOSE(arg) printf arg
79321936Shselasky#define DEVPRVERBOSE(arg) device_printf arg
80321936Shselasky#else
81321936Shselasky#define	DPRINTF(arg)
82321936Shselasky#define	DEVPRINTF(arg)
83321936Shselasky#define PRVERBOSE(arg) if (bootverbose) printf arg
84321936Shselasky#define DEVPRVERBOSE(arg) if (bootverbose) device_printf arg
85321936Shselasky#endif
86321936Shselasky
87321936Shselaskystatic int	pccard_ccr_read(struct pccard_function *pf, int ccr);
88321936Shselaskystatic void	pccard_ccr_write(struct pccard_function *pf, int ccr, int val);
89321936Shselaskystatic int	pccard_attach_card(device_t dev);
90321936Shselaskystatic int	pccard_detach_card(device_t dev);
91321936Shselaskystatic void	pccard_function_init(struct pccard_function *pf);
92321936Shselaskystatic void	pccard_function_free(struct pccard_function *pf);
93321936Shselaskystatic int	pccard_function_enable(struct pccard_function *pf);
94321936Shselaskystatic void	pccard_function_disable(struct pccard_function *pf);
95321936Shselaskystatic int	pccard_compat_do_probe(device_t bus, device_t dev);
96321936Shselaskystatic int	pccard_compat_do_attach(device_t bus, device_t dev);
97321936Shselaskystatic int	pccard_probe(device_t dev);
98321936Shselaskystatic int	pccard_attach(device_t dev);
99321936Shselaskystatic int	pccard_detach(device_t dev);
100321936Shselaskystatic void	pccard_print_resources(struct resource_list *rl,
101321936Shselasky		    const char *name, int type, int count, const char *format);
102321936Shselaskystatic int	pccard_print_child(device_t dev, device_t child);
103321936Shselaskystatic int	pccard_set_resource(device_t dev, device_t child, int type,
104321936Shselasky		    int rid, u_long start, u_long count);
105321936Shselaskystatic int	pccard_get_resource(device_t dev, device_t child, int type,
106321936Shselasky		    int rid, u_long *startp, u_long *countp);
107321936Shselaskystatic void	pccard_delete_resource(device_t dev, device_t child, int type,
108321936Shselasky		    int rid);
109321936Shselaskystatic int	pccard_set_res_flags(device_t dev, device_t child, int type,
110321936Shselasky		    int rid, uint32_t flags);
111321936Shselaskystatic int	pccard_set_memory_offset(device_t dev, device_t child, int rid,
112321936Shselasky		    uint32_t offset, uint32_t *deltap);
113321936Shselaskystatic void	pccard_probe_nomatch(device_t cbdev, device_t child);
114321936Shselaskystatic int	pccard_read_ivar(device_t bus, device_t child, int which,
115321936Shselasky		    u_char *result);
116321936Shselaskystatic void	pccard_driver_added(device_t dev, driver_t *driver);
117321936Shselaskystatic struct resource *pccard_alloc_resource(device_t dev,
118321936Shselasky		    device_t child, int type, int *rid, u_long start,
119321936Shselasky		    u_long end, u_long count, u_int flags);
120321936Shselaskystatic int	pccard_release_resource(device_t dev, device_t child, int type,
121321936Shselasky		    int rid, struct resource *r);
122321936Shselaskystatic void	pccard_child_detached(device_t parent, device_t dev);
123321936Shselaskystatic void	pccard_intr(void *arg);
124321936Shselaskystatic int	pccard_setup_intr(device_t dev, device_t child,
125321936Shselasky		    struct resource *irq, int flags, driver_intr_t *intr,
126321936Shselasky		    void *arg, void **cookiep);
127321936Shselaskystatic int	pccard_teardown_intr(device_t dev, device_t child,
128321936Shselasky		    struct resource *r, void *cookie);
129321936Shselasky
130321936Shselaskystatic const struct pccard_product *
131321936Shselaskypccard_do_product_lookup(device_t bus, device_t dev,
132321936Shselasky			 const struct pccard_product *tab, size_t ent_size,
133321936Shselasky			 pccard_product_match_fn matchfn);
134321936Shselasky
135321936Shselasky
136321936Shselaskystatic int
137321936Shselaskypccard_ccr_read(struct pccard_function *pf, int ccr)
138321936Shselasky{
139321936Shselasky	return (bus_space_read_1(pf->pf_ccrt, pf->pf_ccrh,
140321936Shselasky	    pf->pf_ccr_offset + ccr));
141321936Shselasky}
142321936Shselasky
143321936Shselaskystatic void
144321936Shselaskypccard_ccr_write(struct pccard_function *pf, int ccr, int val)
145321936Shselasky{
146321936Shselasky	if ((pf->ccr_mask) & (1 << (ccr / 2))) {
147321936Shselasky		bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh,
148321936Shselasky		    pf->pf_ccr_offset + ccr, val);
149321936Shselasky	}
150321936Shselasky}
151321936Shselasky
152321936Shselaskystatic int
153321936Shselaskypccard_set_default_descr(device_t dev)
154321936Shselasky{
155321936Shselasky	const char *vendorstr, *prodstr;
156321936Shselasky	uint32_t vendor, prod;
157321936Shselasky	char *str;
158321936Shselasky
159321936Shselasky	if (pccard_get_vendor_str(dev, &vendorstr))
160321936Shselasky		return (0);
161321936Shselasky	if (pccard_get_product_str(dev, &prodstr))
162321936Shselasky		return (0);
163321936Shselasky	if (vendorstr != NULL && prodstr != NULL) {
164321936Shselasky		str = malloc(strlen(vendorstr) + strlen(prodstr) + 2, M_DEVBUF,
165321936Shselasky		    M_WAITOK);
166321936Shselasky		sprintf(str, "%s %s", vendorstr, prodstr);
167321936Shselasky		device_set_desc_copy(dev, str);
168321936Shselasky		free(str, M_DEVBUF);
169321936Shselasky	} else {
170321936Shselasky		if (pccard_get_vendor(dev, &vendor))
171321936Shselasky			return (0);
172321936Shselasky		if (pccard_get_product(dev, &prod))
173321936Shselasky			return (0);
174321936Shselasky		str = malloc(100, M_DEVBUF, M_WAITOK);
175321936Shselasky		snprintf(str, 100, "vendor=0x%x product=0x%x", vendor, prod);
176321936Shselasky		device_set_desc_copy(dev, str);
177321936Shselasky		free(str, M_DEVBUF);
178321936Shselasky	}
179321936Shselasky	return (0);
180321936Shselasky}
181321936Shselasky
182321936Shselaskystatic int
183321936Shselaskypccard_attach_card(device_t dev)
184321936Shselasky{
185321936Shselasky	struct pccard_softc *sc = PCCARD_SOFTC(dev);
186321936Shselasky	struct pccard_function *pf;
187321936Shselasky	struct pccard_ivar *ivar;
188321936Shselasky	device_t child;
189321936Shselasky	int i;
190321936Shselasky
191321936Shselasky	/*
192321936Shselasky	 * this is here so that when socket_enable calls gettype, trt happens
193321936Shselasky	 */
194321936Shselasky	STAILQ_INIT(&sc->card.pf_head);
195321936Shselasky
196321936Shselasky	DEVPRINTF((dev, "chip_socket_enable\n"));
197321936Shselasky	POWER_ENABLE_SOCKET(device_get_parent(dev), dev);
198321936Shselasky
199321936Shselasky	DEVPRINTF((dev, "read_cis\n"));
200321936Shselasky	pccard_read_cis(sc);
201321936Shselasky
202321936Shselasky	DEVPRINTF((dev, "check_cis_quirks\n"));
203321936Shselasky	pccard_check_cis_quirks(dev);
204321936Shselasky
205321936Shselasky	/*
206321936Shselasky	 * bail now if the card has no functions, or if there was an error in
207321936Shselasky	 * the cis.
208321936Shselasky	 */
209321936Shselasky
210321936Shselasky	if (sc->card.error) {
211321936Shselasky		device_printf(dev, "CARD ERROR!\n");
212321936Shselasky		return (1);
213321936Shselasky	}
214321936Shselasky	if (STAILQ_EMPTY(&sc->card.pf_head)) {
215321936Shselasky		device_printf(dev, "Card has no functions!\n");
216321936Shselasky		return (1);
217321936Shselasky	}
218321936Shselasky
219321936Shselasky	if (bootverbose || pccard_debug)
220321936Shselasky		pccard_print_cis(dev);
221321936Shselasky
222321936Shselasky	DEVPRINTF((dev, "functions scanning\n"));
223321936Shselasky	i = -1;
224321936Shselasky	STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
225321936Shselasky		i++;
226321936Shselasky		if (STAILQ_EMPTY(&pf->cfe_head)) {
227321936Shselasky			device_printf(dev,
228321936Shselasky			    "Function %d has no config entries.!\n", i);
229321936Shselasky			continue;
230321936Shselasky		}
231321936Shselasky		pf->sc = sc;
232321936Shselasky		pf->cfe = NULL;
233321936Shselasky		pf->dev = NULL;
234321936Shselasky	}
235321936Shselasky	DEVPRINTF((dev, "Card has %d functions. pccard_mfc is %d\n", i + 1,
236321936Shselasky	    pccard_mfc(sc)));
237321936Shselasky
238321936Shselasky	STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
239321936Shselasky		if (STAILQ_EMPTY(&pf->cfe_head))
240321936Shselasky			continue;
241321936Shselasky		/*
242321936Shselasky		 * In NetBSD, the drivers are responsible for activating
243321936Shselasky		 * each function of a card.  I think that in FreeBSD we
244321936Shselasky		 * want to activate them enough for the usual bus_*_resource
245321936Shselasky		 * routines will do the right thing.  This many mean a
246321936Shselasky		 * departure from the current NetBSD model.
247321936Shselasky		 *
248321936Shselasky		 * This seems to work well in practice for most cards.
249321936Shselasky		 * However, there are two cases that are problematic.
250321936Shselasky		 * If a driver wishes to pick and chose which config
251321936Shselasky		 * entry to use, then this method falls down.  These
252321936Shselasky		 * are usually older cards.  In addition, there are
253321936Shselasky		 * some cards that have multiple hardware units on the
254321936Shselasky		 * cards, but presents only one CIS chain.  These cards
255321936Shselasky		 * are combination cards, but only one of these units
256321936Shselasky		 * can be on at a time.
257321936Shselasky		 */
258321936Shselasky		ivar = malloc(sizeof(struct pccard_ivar), M_DEVBUF,
259321936Shselasky		    M_WAITOK | M_ZERO);
260321936Shselasky		resource_list_init(&ivar->resources);
261321936Shselasky		child = device_add_child(dev, NULL, -1);
262321936Shselasky		device_set_ivars(child, ivar);
263321936Shselasky		ivar->pf = pf;
264321936Shselasky		pf->dev = child;
265321936Shselasky		/*
266321936Shselasky		 * XXX We might want to move the next three lines into
267321936Shselasky		 * XXX the pccard interface layer.  For the moment, this
268321936Shselasky		 * XXX is OK, but some drivers want to pick the config
269321936Shselasky		 * XXX entry to use as well as some address tweaks (mostly
270321936Shselasky		 * XXX due to bugs in decode logic that makes some
271321936Shselasky		 * XXX addresses illegal or broken).
272321936Shselasky		 */
273321936Shselasky		pccard_function_init(pf);
274321936Shselasky		if (sc->sc_enabled_count == 0)
275321936Shselasky			POWER_ENABLE_SOCKET(device_get_parent(dev), dev);
276321936Shselasky		if (pccard_function_enable(pf) == 0 &&
277321936Shselasky		    pccard_set_default_descr(child) == 0 &&
278321936Shselasky		    device_probe_and_attach(child) == 0) {
279321936Shselasky			DEVPRINTF((sc->dev, "function %d CCR at %d "
280321936Shselasky			    "offset %x mask %x: "
281321936Shselasky			    "%x %x %x %x, %x %x %x %x, %x\n",
282321936Shselasky			    pf->number, pf->pf_ccr_window, pf->pf_ccr_offset,
283321936Shselasky			    pf->ccr_mask, pccard_ccr_read(pf, 0x00),
284321936Shselasky			pccard_ccr_read(pf, 0x02), pccard_ccr_read(pf, 0x04),
285321936Shselasky			pccard_ccr_read(pf, 0x06), pccard_ccr_read(pf, 0x0A),
286321936Shselasky			pccard_ccr_read(pf, 0x0C), pccard_ccr_read(pf, 0x0E),
287321936Shselasky			pccard_ccr_read(pf, 0x10), pccard_ccr_read(pf, 0x12)));
288321936Shselasky		} else {
289321936Shselasky			if (pf->cfe != NULL)
290321936Shselasky				pccard_function_disable(pf);
291321936Shselasky		}
292321936Shselasky	}
293321936Shselasky	return (0);
294321936Shselasky}
295321936Shselasky
296321936Shselaskystatic int
297321936Shselaskypccard_detach_card(device_t dev)
298321936Shselasky{
299321936Shselasky	struct pccard_softc *sc = PCCARD_SOFTC(dev);
300321936Shselasky	struct pccard_function *pf;
301321936Shselasky	struct pccard_config_entry *cfe;
302321936Shselasky	int state;
303321936Shselasky
304321936Shselasky	/*
305321936Shselasky	 * We are running on either the PCCARD socket's event thread
306321936Shselasky	 * or in user context detaching a device by user request.
307321936Shselasky	 */
308321936Shselasky	STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
309321936Shselasky		if (pf->dev == NULL)
310321936Shselasky			continue;
311321936Shselasky		state = device_get_state(pf->dev);
312321936Shselasky		if (state == DS_ATTACHED || state == DS_BUSY)
313321936Shselasky			device_detach(pf->dev);
314321936Shselasky		if (pf->cfe != NULL)
315321936Shselasky			pccard_function_disable(pf);
316321936Shselasky		pccard_function_free(pf);
317321936Shselasky		device_delete_child(dev, pf->dev);
318321936Shselasky	}
319321936Shselasky	if (sc->sc_enabled_count == 0)
320321936Shselasky		POWER_DISABLE_SOCKET(device_get_parent(dev), dev);
321321936Shselasky
322321936Shselasky	while (NULL != (pf = STAILQ_FIRST(&sc->card.pf_head))) {
323321936Shselasky		while (NULL != (cfe = STAILQ_FIRST(&pf->cfe_head))) {
324321936Shselasky			STAILQ_REMOVE_HEAD(&pf->cfe_head, cfe_list);
325321936Shselasky			free(cfe, M_DEVBUF);
326321936Shselasky		}
327321936Shselasky		STAILQ_REMOVE_HEAD(&sc->card.pf_head, pf_list);
328321936Shselasky		free(pf, M_DEVBUF);
329321936Shselasky	}
330321936Shselasky	return (0);
331321936Shselasky}
332321936Shselasky
333321936Shselaskystatic const struct pccard_product *
334321936Shselaskypccard_do_product_lookup(device_t bus, device_t dev,
335321936Shselasky    const struct pccard_product *tab, size_t ent_size,
336321936Shselasky    pccard_product_match_fn matchfn)
337321936Shselasky{
338321936Shselasky	const struct pccard_product *ent;
339321936Shselasky	int matches;
340321936Shselasky	uint32_t vendor;
341321936Shselasky	uint32_t prod;
342321936Shselasky	const char *vendorstr;
343321936Shselasky	const char *prodstr;
344321936Shselasky	const char *cis3str;
345321936Shselasky	const char *cis4str;
346321936Shselasky
347321936Shselasky#ifdef DIAGNOSTIC
348321936Shselasky	if (sizeof *ent > ent_size)
349321936Shselasky		panic("pccard_product_lookup: bogus ent_size %jd",
350321936Shselasky		    (intmax_t) ent_size);
351321936Shselasky#endif
352321936Shselasky	if (pccard_get_vendor(dev, &vendor))
353321936Shselasky		return (NULL);
354321936Shselasky	if (pccard_get_product(dev, &prod))
355321936Shselasky		return (NULL);
356321936Shselasky	if (pccard_get_vendor_str(dev, &vendorstr))
357321936Shselasky		return (NULL);
358321936Shselasky	if (pccard_get_product_str(dev, &prodstr))
359321936Shselasky		return (NULL);
360321936Shselasky	if (pccard_get_cis3_str(dev, &cis3str))
361321936Shselasky		return (NULL);
362321936Shselasky	if (pccard_get_cis4_str(dev, &cis4str))
363321936Shselasky		return (NULL);
364321936Shselasky	for (ent = tab; ent->pp_vendor != 0; ent =
365321936Shselasky	    (const struct pccard_product *) ((const char *) ent + ent_size)) {
366321936Shselasky		matches = 1;
367321936Shselasky		if (ent->pp_vendor == PCCARD_VENDOR_ANY &&
368321936Shselasky		    ent->pp_product == PCCARD_PRODUCT_ANY &&
369321936Shselasky		    ent->pp_cis[0] == NULL &&
370321936Shselasky		    ent->pp_cis[1] == NULL) {
371321936Shselasky			if (ent->pp_name)
372321936Shselasky				device_printf(dev,
373321936Shselasky				    "Total wildcard entry ignored for %s\n",
374321936Shselasky				    ent->pp_name);
375321936Shselasky			continue;
376321936Shselasky		}
377321936Shselasky		if (matches && ent->pp_vendor != PCCARD_VENDOR_ANY &&
378321936Shselasky		    vendor != ent->pp_vendor)
379321936Shselasky			matches = 0;
380321936Shselasky		if (matches && ent->pp_product != PCCARD_PRODUCT_ANY &&
381321936Shselasky		    prod != ent->pp_product)
382321936Shselasky			matches = 0;
383321936Shselasky		if (matches && ent->pp_cis[0] &&
384321936Shselasky		    (vendorstr == NULL ||
385321936Shselasky		    strcmp(ent->pp_cis[0], vendorstr) != 0))
386321936Shselasky			matches = 0;
387321936Shselasky		if (matches && ent->pp_cis[1] &&
388321936Shselasky		    (prodstr == NULL ||
389321936Shselasky		    strcmp(ent->pp_cis[1], prodstr) != 0))
390321936Shselasky			matches = 0;
391321936Shselasky		if (matches && ent->pp_cis[2] &&
392321936Shselasky		    (cis3str == NULL ||
393321936Shselasky		    strcmp(ent->pp_cis[2], cis3str) != 0))
394321936Shselasky			matches = 0;
395321936Shselasky		if (matches && ent->pp_cis[3] &&
396321936Shselasky		    (cis4str == NULL ||
397321936Shselasky		    strcmp(ent->pp_cis[3], cis4str) != 0))
398321936Shselasky			matches = 0;
399321936Shselasky		if (matchfn != NULL)
400321936Shselasky			matches = (*matchfn)(dev, ent, matches);
401321936Shselasky		if (matches)
402321936Shselasky			return (ent);
403321936Shselasky	}
404321936Shselasky	return (NULL);
405321936Shselasky}
406321936Shselasky
407321936Shselasky/*
408321936Shselasky * Initialize a PCCARD function.  May be called as long as the function is
409321936Shselasky * disabled.
410321936Shselasky *
411321936Shselasky * Note: pccard_function_init should not keep resources allocated.  It should
412321936Shselasky * only set them up ala isa pnp, set the values in the rl lists, and return.
413321936Shselasky * Any resource held after pccard_function_init is called is a bug.  However,
414321936Shselasky * the bus routines to get the resources also assume that pccard_function_init
415321936Shselasky * does this, so they need to be fixed too.
416321936Shselasky */
417321936Shselaskystatic void
418321936Shselaskypccard_function_init(struct pccard_function *pf)
419321936Shselasky{
420321936Shselasky	struct pccard_config_entry *cfe;
421321936Shselasky	struct pccard_ivar *devi = PCCARD_IVAR(pf->dev);
422321936Shselasky	struct resource_list *rl = &devi->resources;
423321936Shselasky	struct resource_list_entry *rle;
424321936Shselasky	struct resource *r = 0;
425321936Shselasky	device_t bus;
426321936Shselasky	u_long start, end, len;
427321936Shselasky	int i, rid, spaces;
428321936Shselasky
429321936Shselasky	if (pf->pf_flags & PFF_ENABLED) {
430321936Shselasky		printf("pccard_function_init: function is enabled");
431321936Shselasky		return;
432321936Shselasky	}
433321936Shselasky	bus = device_get_parent(pf->dev);
434321936Shselasky	/* Remember which configuration entry we are using. */
435321936Shselasky	STAILQ_FOREACH(cfe, &pf->cfe_head, cfe_list) {
436321936Shselasky		if (cfe->iftype != PCCARD_IFTYPE_IO)
437321936Shselasky			continue;
438321936Shselasky		spaces = 0;
439321936Shselasky		for (i = 0; i < cfe->num_iospace; i++) {
440321936Shselasky			start = cfe->iospace[i].start;
441321936Shselasky			if (start)
442321936Shselasky				end = start + cfe->iospace[i].length - 1;
443321936Shselasky			else
444321936Shselasky				end = ~0UL;
445321936Shselasky			DEVPRINTF((bus, "I/O rid %d start %lx end %lx\n",
446321936Shselasky			    i, start, end));
447321936Shselasky			rid = i;
448321936Shselasky			len = cfe->iospace[i].length;
449321936Shselasky			r = bus_alloc_resource(bus, SYS_RES_IOPORT, &rid,
450321936Shselasky			    start, end, len, rman_make_alignment_flags(len));
451321936Shselasky			if (r == NULL)
452321936Shselasky				goto not_this_one;
453321936Shselasky			rle = resource_list_add(rl, SYS_RES_IOPORT,
454321936Shselasky			    rid, rman_get_start(r), rman_get_end(r),
455321936Shselasky			    cfe->iospace[i].length);
456321936Shselasky			if (rle == NULL)
457321936Shselasky				panic("Cannot add resource rid %d IOPORT", rid);
458321936Shselasky			rle->res = r;
459321936Shselasky			spaces++;
460321936Shselasky		}
461321936Shselasky		for (i = 0; i < cfe->num_memspace; i++) {
462321936Shselasky			start = cfe->memspace[i].hostaddr;
463321936Shselasky			if (start)
464321936Shselasky				end = start + cfe->memspace[i].length - 1;
465321936Shselasky			else
466321936Shselasky				end = ~0UL;
467321936Shselasky			DEVPRINTF((bus, "Memory rid %d start %lx end %lx\n",
468321936Shselasky			    i, start, end));
469321936Shselasky			rid = i;
470321936Shselasky			len = cfe->memspace[i].length;
471321936Shselasky			r = bus_alloc_resource(bus, SYS_RES_MEMORY, &rid,
472321936Shselasky			    start, end, len, rman_make_alignment_flags(len));
473321936Shselasky			if (r == NULL)
474321936Shselasky				goto not_this_one;
475321936Shselasky			rle = resource_list_add(rl, SYS_RES_MEMORY,
476321936Shselasky			    rid, rman_get_start(r), rman_get_end(r),
477321936Shselasky			    cfe->memspace[i].length);
478321936Shselasky			if (rle == NULL)
479321936Shselasky				panic("Cannot add resource rid %d MEM", rid);
480321936Shselasky			rle->res = r;
481321936Shselasky			spaces++;
482321936Shselasky		}
483321936Shselasky		if (spaces == 0) {
484321936Shselasky			DEVPRINTF((bus, "Neither memory nor I/O mapped\n"));
485321936Shselasky			goto not_this_one;
486321936Shselasky		}
487321936Shselasky		if (cfe->irqmask) {
488321936Shselasky			rid = 0;
489321936Shselasky			r = bus_alloc_resource_any(bus, SYS_RES_IRQ, &rid,
490321936Shselasky			    RF_SHAREABLE);
491321936Shselasky			if (r == NULL)
492321936Shselasky				goto not_this_one;
493321936Shselasky			rle = resource_list_add(rl, SYS_RES_IRQ, rid,
494321936Shselasky			    rman_get_start(r), rman_get_end(r), 1);
495321936Shselasky			if (rle == NULL)
496321936Shselasky				panic("Cannot add resource rid %d IRQ", rid);
497321936Shselasky			rle->res = r;
498321936Shselasky		}
499321936Shselasky		/* If we get to here, we've allocated all we need */
500321936Shselasky		pf->cfe = cfe;
501321936Shselasky		break;
502321936Shselasky	    not_this_one:;
503321936Shselasky		DEVPRVERBOSE((bus, "Allocation failed for cfe %d\n",
504321936Shselasky		    cfe->number));
505321936Shselasky		resource_list_purge(rl);
506321936Shselasky	}
507321936Shselasky}
508321936Shselasky
509321936Shselasky/*
510321936Shselasky * Free resources allocated by pccard_function_init(), May be called as long
511321936Shselasky * as the function is disabled.
512321936Shselasky *
513321936Shselasky * NOTE: This function should be unnecessary.  pccard_function_init should
514321936Shselasky * never keep resources initialized.
515321936Shselasky */
516321936Shselaskystatic void
517321936Shselaskypccard_function_free(struct pccard_function *pf)
518321936Shselasky{
519321936Shselasky	struct pccard_ivar *devi = PCCARD_IVAR(pf->dev);
520321936Shselasky	struct resource_list_entry *rle;
521321936Shselasky
522321936Shselasky	if (pf->pf_flags & PFF_ENABLED) {
523321936Shselasky		printf("pccard_function_init: function is enabled");
524321936Shselasky		return;
525321936Shselasky	}
526321936Shselasky
527321936Shselasky	STAILQ_FOREACH(rle, &devi->resources, link) {
528321936Shselasky		if (rle->res) {
529321936Shselasky			if (rman_get_device(rle->res) != pf->sc->dev)
530321936Shselasky				device_printf(pf->sc->dev,
531321936Shselasky				    "function_free: Resource still owned by "
532321936Shselasky				    "child, oops. "
533321936Shselasky				    "(type=%d, rid=%d, addr=%lx)\n",
534321936Shselasky				    rle->type, rle->rid,
535321936Shselasky				    rman_get_start(rle->res));
536321936Shselasky			BUS_RELEASE_RESOURCE(device_get_parent(pf->sc->dev),
537321936Shselasky			    pf->sc->dev, rle->type, rle->rid, rle->res);
538321936Shselasky			rle->res = NULL;
539321936Shselasky		}
540321936Shselasky	}
541321936Shselasky	resource_list_free(&devi->resources);
542321936Shselasky}
543321936Shselasky
544321936Shselaskystatic void
545321936Shselaskypccard_mfc_adjust_iobase(struct pccard_function *pf, bus_addr_t addr,
546321936Shselasky    bus_addr_t offset, bus_size_t size)
547321936Shselasky{
548321936Shselasky	bus_size_t iosize, tmp;
549321936Shselasky
550321936Shselasky	if (addr != 0) {
551321936Shselasky		if (pf->pf_mfc_iomax == 0) {
552321936Shselasky			pf->pf_mfc_iobase = addr + offset;
553321936Shselasky			pf->pf_mfc_iomax = pf->pf_mfc_iobase + size;
554321936Shselasky		} else {
555321936Shselasky			/* this makes the assumption that nothing overlaps */
556321936Shselasky			if (pf->pf_mfc_iobase > addr + offset)
557321936Shselasky				pf->pf_mfc_iobase = addr + offset;
558321936Shselasky			if (pf->pf_mfc_iomax < addr + offset + size)
559321936Shselasky				pf->pf_mfc_iomax = addr + offset + size;
560321936Shselasky		}
561321936Shselasky	}
562321936Shselasky
563321936Shselasky	tmp = pf->pf_mfc_iomax - pf->pf_mfc_iobase;
564321936Shselasky	/* round up to nearest (2^n)-1 */
565321936Shselasky	for (iosize = 1; iosize < tmp; iosize <<= 1)
566321936Shselasky		;
567321936Shselasky	iosize--;
568321936Shselasky
569321936Shselasky	DEVPRINTF((pf->dev, "MFC: I/O base %#jx IOSIZE %#jx\n",
570321936Shselasky	    (uintmax_t)pf->pf_mfc_iobase, (uintmax_t)(iosize + 1)));
571321936Shselasky	pccard_ccr_write(pf, PCCARD_CCR_IOBASE0,
572321936Shselasky	    pf->pf_mfc_iobase & 0xff);
573321936Shselasky	pccard_ccr_write(pf, PCCARD_CCR_IOBASE1,
574321936Shselasky	    (pf->pf_mfc_iobase >> 8) & 0xff);
575321936Shselasky	pccard_ccr_write(pf, PCCARD_CCR_IOBASE2, 0);
576321936Shselasky	pccard_ccr_write(pf, PCCARD_CCR_IOBASE3, 0);
577321936Shselasky	pccard_ccr_write(pf, PCCARD_CCR_IOSIZE, iosize);
578321936Shselasky}
579321936Shselasky
580321936Shselasky/* Enable a PCCARD function */
581321936Shselaskystatic int
582321936Shselaskypccard_function_enable(struct pccard_function *pf)
583321936Shselasky{
584321936Shselasky	struct pccard_function *tmp;
585321936Shselasky	int reg;
586321936Shselasky	device_t dev = pf->sc->dev;
587321936Shselasky
588321936Shselasky	if (pf->cfe == NULL) {
589321936Shselasky		DEVPRVERBOSE((dev, "No config entry could be allocated.\n"));
590321936Shselasky		return (ENOMEM);
591321936Shselasky	}
592321936Shselasky
593321936Shselasky	/*
594321936Shselasky	 * Increase the reference count on the socket, enabling power, if
595321936Shselasky	 * necessary.
596321936Shselasky	 */
597321936Shselasky	pf->sc->sc_enabled_count++;
598321936Shselasky
599321936Shselasky	if (pf->pf_flags & PFF_ENABLED) {
600321936Shselasky		/*
601321936Shselasky		 * Don't do anything if we're already enabled.
602321936Shselasky		 */
603321936Shselasky		return (0);
604321936Shselasky	}
605321936Shselasky
606321936Shselasky	/*
607321936Shselasky	 * it's possible for different functions' CCRs to be in the same
608321936Shselasky	 * underlying page.  Check for that.
609321936Shselasky	 */
610321936Shselasky	STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) {
611321936Shselasky		if ((tmp->pf_flags & PFF_ENABLED) &&
612321936Shselasky		    (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) &&
613321936Shselasky		    ((pf->ccr_base + PCCARD_CCR_SIZE) <=
614321936Shselasky		    (tmp->ccr_base - tmp->pf_ccr_offset +
615321936Shselasky		    tmp->pf_ccr_realsize))) {
616321936Shselasky			pf->pf_ccrt = tmp->pf_ccrt;
617321936Shselasky			pf->pf_ccrh = tmp->pf_ccrh;
618321936Shselasky			pf->pf_ccr_realsize = tmp->pf_ccr_realsize;
619321936Shselasky
620321936Shselasky			/*
621321936Shselasky			 * pf->pf_ccr_offset = (tmp->pf_ccr_offset -
622321936Shselasky			 * tmp->ccr_base) + pf->ccr_base;
623321936Shselasky			 */
624321936Shselasky			/* pf->pf_ccr_offset =
625321936Shselasky			    (tmp->pf_ccr_offset + pf->ccr_base) -
626321936Shselasky			    tmp->ccr_base; */
627321936Shselasky			pf->pf_ccr_window = tmp->pf_ccr_window;
628321936Shselasky			break;
629321936Shselasky		}
630321936Shselasky	}
631321936Shselasky	if (tmp == NULL) {
632321936Shselasky		pf->ccr_rid = 0;
633321936Shselasky		pf->ccr_res = bus_alloc_resource(dev, SYS_RES_MEMORY,
634321936Shselasky		    &pf->ccr_rid, 0, ~0, PCCARD_MEM_PAGE_SIZE, RF_ACTIVE);
635321936Shselasky		if (!pf->ccr_res)
636321936Shselasky			goto bad;
637321936Shselasky		DEVPRINTF((dev, "ccr_res == %lx-%lx, base=%x\n",
638321936Shselasky		    rman_get_start(pf->ccr_res), rman_get_end(pf->ccr_res),
639321936Shselasky		    pf->ccr_base));
640321936Shselasky		CARD_SET_RES_FLAGS(device_get_parent(dev), dev, SYS_RES_MEMORY,
641321936Shselasky		    pf->ccr_rid, PCCARD_A_MEM_ATTR);
642321936Shselasky		CARD_SET_MEMORY_OFFSET(device_get_parent(dev), dev,
643321936Shselasky		    pf->ccr_rid, pf->ccr_base, &pf->pf_ccr_offset);
644321936Shselasky		pf->pf_ccrt = rman_get_bustag(pf->ccr_res);
645321936Shselasky		pf->pf_ccrh = rman_get_bushandle(pf->ccr_res);
646321936Shselasky		pf->pf_ccr_realsize = 1;
647321936Shselasky	}
648321936Shselasky
649321936Shselasky	reg = (pf->cfe->number & PCCARD_CCR_OPTION_CFINDEX);
650321936Shselasky	reg |= PCCARD_CCR_OPTION_LEVIREQ;
651321936Shselasky	if (pccard_mfc(pf->sc)) {
652321936Shselasky		reg |= (PCCARD_CCR_OPTION_FUNC_ENABLE |
653321936Shselasky			PCCARD_CCR_OPTION_ADDR_DECODE);
654321936Shselasky		/* PCCARD_CCR_OPTION_IRQ_ENABLE set elsewhere as needed */
655321936Shselasky	}
656321936Shselasky	pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg);
657321936Shselasky
658321936Shselasky	reg = 0;
659321936Shselasky	if ((pf->cfe->flags & PCCARD_CFE_IO16) == 0)
660321936Shselasky		reg |= PCCARD_CCR_STATUS_IOIS8;
661321936Shselasky	if (pf->cfe->flags & PCCARD_CFE_AUDIO)
662321936Shselasky		reg |= PCCARD_CCR_STATUS_AUDIO;
663321936Shselasky	pccard_ccr_write(pf, PCCARD_CCR_STATUS, reg);
664321936Shselasky
665321936Shselasky	pccard_ccr_write(pf, PCCARD_CCR_SOCKETCOPY, 0);
666321936Shselasky
667321936Shselasky	if (pccard_mfc(pf->sc))
668321936Shselasky		pccard_mfc_adjust_iobase(pf, 0, 0, 0);
669321936Shselasky
670321936Shselasky#ifdef PCCARDDEBUG
671321936Shselasky	if (pccard_debug) {
672321936Shselasky		STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) {
673321936Shselasky			device_printf(tmp->sc->dev,
674321936Shselasky			    "function %d CCR at %d offset %x: "
675321936Shselasky			    "%x %x %x %x, %x %x %x %x, %x\n",
676321936Shselasky			    tmp->number, tmp->pf_ccr_window,
677321936Shselasky			    tmp->pf_ccr_offset,
678321936Shselasky			    pccard_ccr_read(tmp, 0x00),
679321936Shselasky			    pccard_ccr_read(tmp, 0x02),
680321936Shselasky			    pccard_ccr_read(tmp, 0x04),
681321936Shselasky			    pccard_ccr_read(tmp, 0x06),
682321936Shselasky			    pccard_ccr_read(tmp, 0x0A),
683321936Shselasky			    pccard_ccr_read(tmp, 0x0C),
684321936Shselasky			    pccard_ccr_read(tmp, 0x0E),
685321936Shselasky			    pccard_ccr_read(tmp, 0x10),
686321936Shselasky			    pccard_ccr_read(tmp, 0x12));
687321936Shselasky		}
688321936Shselasky	}
689321936Shselasky#endif
690321936Shselasky	pf->pf_flags |= PFF_ENABLED;
691321936Shselasky	return (0);
692321936Shselasky
693321936Shselasky bad:
694321936Shselasky	/*
695321936Shselasky	 * Decrement the reference count, and power down the socket, if
696321936Shselasky	 * necessary.
697321936Shselasky	 */
698321936Shselasky	pf->sc->sc_enabled_count--;
699321936Shselasky	DEVPRINTF((dev, "bad --enabled_count = %d\n", pf->sc->sc_enabled_count));
700321936Shselasky
701321936Shselasky	return (1);
702321936Shselasky}
703321936Shselasky
704321936Shselasky/* Disable PCCARD function. */
705321936Shselaskystatic void
706321936Shselaskypccard_function_disable(struct pccard_function *pf)
707321936Shselasky{
708321936Shselasky	struct pccard_function *tmp;
709321936Shselasky	device_t dev = pf->sc->dev;
710321936Shselasky
711321936Shselasky	if (pf->cfe == NULL)
712321936Shselasky		panic("pccard_function_disable: function not initialized");
713321936Shselasky
714321936Shselasky	if ((pf->pf_flags & PFF_ENABLED) == 0) {
715321936Shselasky		/*
716321936Shselasky		 * Don't do anything if we're already disabled.
717321936Shselasky		 */
718321936Shselasky		return;
719321936Shselasky	}
720321936Shselasky
721321936Shselasky	if (pf->intr_handler != NULL) {
722321936Shselasky		struct pccard_ivar *devi = PCCARD_IVAR(pf->dev);
723321936Shselasky		struct resource_list_entry *rle =
724321936Shselasky		    resource_list_find(&devi->resources, SYS_RES_IRQ, 0);
725321936Shselasky		if (rle == NULL)
726321936Shselasky			panic("Can't disable an interrupt with no IRQ res\n");
727321936Shselasky		BUS_TEARDOWN_INTR(dev, pf->dev, rle->res,
728321936Shselasky		    pf->intr_handler_cookie);
729321936Shselasky	}
730321936Shselasky
731321936Shselasky	/*
732321936Shselasky	 * it's possible for different functions' CCRs to be in the same
733321936Shselasky	 * underlying page.  Check for that.  Note we mark us as disabled
734321936Shselasky	 * first to avoid matching ourself.
735321936Shselasky	 */
736321936Shselasky
737321936Shselasky	pf->pf_flags &= ~PFF_ENABLED;
738321936Shselasky	STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) {
739321936Shselasky		if ((tmp->pf_flags & PFF_ENABLED) &&
740321936Shselasky		    (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) &&
741321936Shselasky		    ((pf->ccr_base + PCCARD_CCR_SIZE) <=
742321936Shselasky		    (tmp->ccr_base - tmp->pf_ccr_offset +
743321936Shselasky		    tmp->pf_ccr_realsize)))
744321936Shselasky			break;
745321936Shselasky	}
746321936Shselasky
747321936Shselasky	/* Not used by anyone else; unmap the CCR. */
748321936Shselasky	if (tmp == NULL) {
749321936Shselasky		bus_release_resource(dev, SYS_RES_MEMORY, pf->ccr_rid,
750321936Shselasky		    pf->ccr_res);
751321936Shselasky		pf->ccr_res = NULL;
752321936Shselasky	}
753321936Shselasky
754321936Shselasky	/*
755321936Shselasky	 * Decrement the reference count, and power down the socket, if
756321936Shselasky	 * necessary.
757321936Shselasky	 */
758321936Shselasky	pf->sc->sc_enabled_count--;
759321936Shselasky}
760321936Shselasky
761321936Shselasky/*
762321936Shselasky * simulate the old "probe" routine.  In the new world order, the driver
763321936Shselasky * needs to grab devices while in the old they were assigned to the device by
764321936Shselasky * the pccardd process.  These symbols are exported to the upper layers.
765321936Shselasky */
766321936Shselaskystatic int
767321936Shselaskypccard_compat_do_probe(device_t bus, device_t dev)
768321936Shselasky{
769321936Shselasky	return (CARD_COMPAT_MATCH(dev));
770321936Shselasky}
771321936Shselasky
772321936Shselaskystatic int
773321936Shselaskypccard_compat_do_attach(device_t bus, device_t dev)
774321936Shselasky{
775321936Shselasky	int err;
776321936Shselasky
777321936Shselasky	err = CARD_COMPAT_PROBE(dev);
778321936Shselasky	if (err <= 0)
779321936Shselasky		err = CARD_COMPAT_ATTACH(dev);
780321936Shselasky	return (err);
781321936Shselasky}
782321936Shselasky
783321936Shselasky#define PCCARD_NPORT	2
784321936Shselasky#define PCCARD_NMEM	5
785321936Shselasky#define PCCARD_NIRQ	1
786321936Shselasky#define PCCARD_NDRQ	0
787321936Shselasky
788321936Shselaskystatic int
789321936Shselaskypccard_probe(device_t dev)
790321936Shselasky{
791321936Shselasky	device_set_desc(dev, "16-bit PCCard bus");
792321936Shselasky	return (0);
793321936Shselasky}
794321936Shselasky
795321936Shselaskystatic int
796321936Shselaskypccard_attach(device_t dev)
797321936Shselasky{
798321936Shselasky	struct pccard_softc *sc = PCCARD_SOFTC(dev);
799321936Shselasky	int err;
800321936Shselasky
801321936Shselasky	sc->dev = dev;
802321936Shselasky	sc->sc_enabled_count = 0;
803321936Shselasky	if ((err = pccard_device_create(sc)) != 0)
804321936Shselasky		return  (err);
805321936Shselasky	return (bus_generic_attach(dev));
806321936Shselasky}
807321936Shselasky
808321936Shselaskystatic int
809321936Shselaskypccard_detach(device_t dev)
810321936Shselasky{
811321936Shselasky	pccard_detach_card(dev);
812321936Shselasky	pccard_device_destroy(device_get_softc(dev));
813321936Shselasky	return (0);
814321936Shselasky}
815321936Shselasky
816321936Shselaskystatic int
817321936Shselaskypccard_suspend(device_t self)
818321936Shselasky{
819321936Shselasky	pccard_detach_card(self);
820321936Shselasky	return (0);
821321936Shselasky}
822321936Shselasky
823321936Shselaskystatic
824321936Shselaskyint
825321936Shselaskypccard_resume(device_t self)
826321936Shselasky{
827321936Shselasky	return (0);
828321936Shselasky}
829321936Shselasky
830321936Shselaskystatic void
831321936Shselaskypccard_print_resources(struct resource_list *rl, const char *name, int type,
832321936Shselasky    int count, const char *format)
833321936Shselasky{
834321936Shselasky	struct resource_list_entry *rle;
835321936Shselasky	int printed;
836321936Shselasky	int i;
837321936Shselasky
838321936Shselasky	printed = 0;
839321936Shselasky	for (i = 0; i < count; i++) {
840321936Shselasky		rle = resource_list_find(rl, type, i);
841321936Shselasky		if (rle != NULL) {
842321936Shselasky			if (printed == 0)
843321936Shselasky				printf(" %s ", name);
844321936Shselasky			else if (printed > 0)
845321936Shselasky				printf(",");
846321936Shselasky			printed++;
847321936Shselasky			printf(format, rle->start);
848321936Shselasky			if (rle->count > 1) {
849321936Shselasky				printf("-");
850321936Shselasky				printf(format, rle->start + rle->count - 1);
851321936Shselasky			}
852321936Shselasky		} else if (i > 3) {
853321936Shselasky			/* check the first few regardless */
854321936Shselasky			break;
855321936Shselasky		}
856321936Shselasky	}
857321936Shselasky}
858321936Shselasky
859321936Shselaskystatic int
860321936Shselaskypccard_print_child(device_t dev, device_t child)
861321936Shselasky{
862321936Shselasky	struct pccard_ivar *devi = PCCARD_IVAR(child);
863321936Shselasky	struct resource_list *rl = &devi->resources;
864321936Shselasky	int retval = 0;
865321936Shselasky
866321936Shselasky	retval += bus_print_child_header(dev, child);
867321936Shselasky	retval += printf(" at");
868321936Shselasky
869321936Shselasky	if (devi != NULL) {
870321936Shselasky		pccard_print_resources(rl, "port", SYS_RES_IOPORT,
871321936Shselasky		    PCCARD_NPORT, "%#lx");
872321936Shselasky		pccard_print_resources(rl, "iomem", SYS_RES_MEMORY,
873321936Shselasky		    PCCARD_NMEM, "%#lx");
874321936Shselasky		pccard_print_resources(rl, "irq", SYS_RES_IRQ, PCCARD_NIRQ,
875321936Shselasky		    "%ld");
876321936Shselasky		pccard_print_resources(rl, "drq", SYS_RES_DRQ, PCCARD_NDRQ,
877321936Shselasky		    "%ld");
878321936Shselasky		retval += printf(" function %d config %d", devi->pf->number,
879321936Shselasky		    devi->pf->cfe->number);
880321936Shselasky	}
881321936Shselasky
882321936Shselasky	retval += bus_print_child_footer(dev, child);
883321936Shselasky
884321936Shselasky	return (retval);
885321936Shselasky}
886321936Shselasky
887321936Shselaskystatic int
888321936Shselaskypccard_set_resource(device_t dev, device_t child, int type, int rid,
889321936Shselasky    u_long start, u_long count)
890321936Shselasky{
891321936Shselasky	struct pccard_ivar *devi = PCCARD_IVAR(child);
892321936Shselasky	struct resource_list *rl = &devi->resources;
893321936Shselasky
894321936Shselasky	if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY
895321936Shselasky	    && type != SYS_RES_IRQ && type != SYS_RES_DRQ)
896321936Shselasky		return (EINVAL);
897321936Shselasky	if (rid < 0)
898321936Shselasky		return (EINVAL);
899321936Shselasky	if (type == SYS_RES_IOPORT && rid >= PCCARD_NPORT)
900321936Shselasky		return (EINVAL);
901321936Shselasky	if (type == SYS_RES_MEMORY && rid >= PCCARD_NMEM)
902321936Shselasky		return (EINVAL);
903321936Shselasky	if (type == SYS_RES_IRQ && rid >= PCCARD_NIRQ)
904321936Shselasky		return (EINVAL);
905321936Shselasky	if (type == SYS_RES_DRQ && rid >= PCCARD_NDRQ)
906321936Shselasky		return (EINVAL);
907321936Shselasky
908321936Shselasky	resource_list_add(rl, type, rid, start, start + count - 1, count);
909321936Shselasky	if (NULL != resource_list_alloc(rl, device_get_parent(dev), dev,
910321936Shselasky	    type, &rid, start, start + count - 1, count, 0))
911321936Shselasky		return 0;
912321936Shselasky	else
913321936Shselasky		return ENOMEM;
914321936Shselasky}
915321936Shselasky
916321936Shselaskystatic int
917321936Shselaskypccard_get_resource(device_t dev, device_t child, int type, int rid,
918321936Shselasky    u_long *startp, u_long *countp)
919321936Shselasky{
920321936Shselasky	struct pccard_ivar *devi = PCCARD_IVAR(child);
921321936Shselasky	struct resource_list *rl = &devi->resources;
922321936Shselasky	struct resource_list_entry *rle;
923321936Shselasky
924321936Shselasky	rle = resource_list_find(rl, type, rid);
925321936Shselasky	if (rle == NULL)
926321936Shselasky		return (ENOENT);
927321936Shselasky
928321936Shselasky	if (startp != NULL)
929321936Shselasky		*startp = rle->start;
930321936Shselasky	if (countp != NULL)
931321936Shselasky		*countp = rle->count;
932321936Shselasky
933321936Shselasky	return (0);
934321936Shselasky}
935321936Shselasky
936321936Shselaskystatic void
937321936Shselaskypccard_delete_resource(device_t dev, device_t child, int type, int rid)
938321936Shselasky{
939321936Shselasky	struct pccard_ivar *devi = PCCARD_IVAR(child);
940321936Shselasky	struct resource_list *rl = &devi->resources;
941321936Shselasky	resource_list_delete(rl, type, rid);
942321936Shselasky}
943321936Shselasky
944321936Shselaskystatic int
945321936Shselaskypccard_set_res_flags(device_t dev, device_t child, int type, int rid,
946321936Shselasky    uint32_t flags)
947321936Shselasky{
948321936Shselasky	return (CARD_SET_RES_FLAGS(device_get_parent(dev), child, type,
949321936Shselasky	    rid, flags));
950321936Shselasky}
951321936Shselasky
952321936Shselaskystatic int
953321936Shselaskypccard_set_memory_offset(device_t dev, device_t child, int rid,
954321936Shselasky    uint32_t offset, uint32_t *deltap)
955321936Shselasky
956321936Shselasky{
957321936Shselasky	return (CARD_SET_MEMORY_OFFSET(device_get_parent(dev), child, rid,
958321936Shselasky	    offset, deltap));
959321936Shselasky}
960321936Shselasky
961321936Shselaskystatic void
962321936Shselaskypccard_probe_nomatch(device_t bus, device_t child)
963321936Shselasky{
964321936Shselasky	struct pccard_ivar *devi = PCCARD_IVAR(child);
965321936Shselasky	struct pccard_function *pf = devi->pf;
966321936Shselasky	struct pccard_softc *sc = PCCARD_SOFTC(bus);
967321936Shselasky	int i;
968321936Shselasky
969321936Shselasky	device_printf(bus, "<unknown card>");
970321936Shselasky	printf(" (manufacturer=0x%04x, product=0x%04x, function_type=%d) "
971321936Shselasky	    "at function %d\n", sc->card.manufacturer, sc->card.product,
972321936Shselasky	    pf->function, pf->number);
973321936Shselasky	device_printf(bus, "   CIS info: ");
974321936Shselasky	for (i = 0; sc->card.cis1_info[i] != NULL && i < 4; i++)
975321936Shselasky		printf("%s%s", i > 0 ? ", " : "", sc->card.cis1_info[i]);
976321936Shselasky	printf("\n");
977321936Shselasky	return;
978321936Shselasky}
979321936Shselasky
980321936Shselaskystatic int
981321936Shselaskypccard_child_location_str(device_t bus, device_t child, char *buf,
982321936Shselasky    size_t buflen)
983321936Shselasky{
984321936Shselasky	struct pccard_ivar *devi = PCCARD_IVAR(child);
985321936Shselasky	struct pccard_function *pf = devi->pf;
986321936Shselasky
987321936Shselasky	snprintf(buf, buflen, "function=%d", pf->number);
988321936Shselasky	return (0);
989321936Shselasky}
990321936Shselasky
991321936Shselasky/* XXX Maybe this should be in subr_bus? */
992321936Shselaskystatic void
993321936Shselaskypccard_safe_quote(char *dst, const char *src, size_t len)
994321936Shselasky{
995321936Shselasky	char *walker = dst, *ep = dst + len - 1;
996321936Shselasky
997321936Shselasky	if (len == 0)
998321936Shselasky		return;
999321936Shselasky	while (walker < ep)
1000321936Shselasky	{
1001321936Shselasky		if (*src == '"') {
1002321936Shselasky			if (ep - walker < 2)
1003321936Shselasky				break;
1004321936Shselasky			*walker++ = '\\';
1005321936Shselasky		}
1006321936Shselasky		*walker++ = *src++;
1007321936Shselasky	}
1008321936Shselasky	*walker = '\0';
1009321936Shselasky}
1010321936Shselasky
1011321936Shselaskystatic int
1012321936Shselaskypccard_child_pnpinfo_str(device_t bus, device_t child, char *buf,
1013321936Shselasky    size_t buflen)
1014321936Shselasky{
1015321936Shselasky	struct pccard_ivar *devi = PCCARD_IVAR(child);
1016321936Shselasky	struct pccard_function *pf = devi->pf;
1017321936Shselasky	struct pccard_softc *sc = PCCARD_SOFTC(bus);
1018321936Shselasky	char cis0[128], cis1[128];
1019321936Shselasky
1020321936Shselasky	pccard_safe_quote(cis0, sc->card.cis1_info[0], sizeof(cis0));
1021321936Shselasky	pccard_safe_quote(cis1, sc->card.cis1_info[1], sizeof(cis1));
1022321936Shselasky	snprintf(buf, buflen, "manufacturer=0x%04x product=0x%04x "
1023321936Shselasky	    "cisvendor=\"%s\" cisproduct=\"%s\" function_type=%d",
1024321936Shselasky	    sc->card.manufacturer, sc->card.product, cis0, cis1, pf->function);
1025321936Shselasky	return (0);
1026321936Shselasky}
1027321936Shselasky
1028321936Shselaskystatic int
1029321936Shselaskypccard_read_ivar(device_t bus, device_t child, int which, u_char *result)
1030321936Shselasky{
1031321936Shselasky	struct pccard_ivar *devi = PCCARD_IVAR(child);
1032321936Shselasky	struct pccard_function *pf = devi->pf;
1033321936Shselasky	struct pccard_softc *sc = PCCARD_SOFTC(bus);
1034321936Shselasky
1035321936Shselasky	if (!pf)
1036321936Shselasky		panic("No pccard function pointer");
1037321936Shselasky	switch (which) {
1038321936Shselasky	default:
1039321936Shselasky		return (EINVAL);
1040321936Shselasky	case PCCARD_IVAR_ETHADDR:
1041321936Shselasky		bcopy(pf->pf_funce_lan_nid, result, ETHER_ADDR_LEN);
1042321936Shselasky		break;
1043321936Shselasky	case PCCARD_IVAR_VENDOR:
1044321936Shselasky		*(uint32_t *)result = sc->card.manufacturer;
1045321936Shselasky		break;
1046321936Shselasky	case PCCARD_IVAR_PRODUCT:
1047321936Shselasky		*(uint32_t *)result = sc->card.product;
1048321936Shselasky		break;
1049321936Shselasky	case PCCARD_IVAR_PRODEXT:
1050321936Shselasky		*(uint16_t *)result = sc->card.prodext;
1051321936Shselasky		break;
1052321936Shselasky	case PCCARD_IVAR_FUNCTION:
1053321936Shselasky		*(uint32_t *)result = pf->function;
1054321936Shselasky		break;
1055321936Shselasky	case PCCARD_IVAR_FUNCTION_NUMBER:
1056321936Shselasky		*(uint32_t *)result = pf->number;
1057321936Shselasky		break;
1058321936Shselasky	case PCCARD_IVAR_VENDOR_STR:
1059321936Shselasky		*(const char **)result = sc->card.cis1_info[0];
1060321936Shselasky		break;
1061321936Shselasky	case PCCARD_IVAR_PRODUCT_STR:
1062321936Shselasky		*(const char **)result = sc->card.cis1_info[1];
1063321936Shselasky		break;
1064321936Shselasky	case PCCARD_IVAR_CIS3_STR:
1065321936Shselasky		*(const char **)result = sc->card.cis1_info[2];
1066321936Shselasky		break;
1067321936Shselasky	case PCCARD_IVAR_CIS4_STR:
1068321936Shselasky		*(const char **)result = sc->card.cis1_info[3];
1069321936Shselasky		break;
1070321936Shselasky	}
1071321936Shselasky	return (0);
1072321936Shselasky}
1073321936Shselasky
1074321936Shselaskystatic void
1075321936Shselaskypccard_driver_added(device_t dev, driver_t *driver)
1076321936Shselasky{
1077321936Shselasky	struct pccard_softc *sc = PCCARD_SOFTC(dev);
1078321936Shselasky	struct pccard_function *pf;
1079321936Shselasky	device_t child;
1080321936Shselasky
1081321936Shselasky	STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
1082321936Shselasky		if (STAILQ_EMPTY(&pf->cfe_head))
1083321936Shselasky			continue;
1084321936Shselasky		child = pf->dev;
1085321936Shselasky		if (device_get_state(child) != DS_NOTPRESENT)
1086321936Shselasky			continue;
1087321936Shselasky		if (pccard_function_enable(pf) == 0 &&
1088321936Shselasky		    device_probe_and_attach(child) == 0) {
1089321936Shselasky			DEVPRINTF((sc->dev, "function %d CCR at %d "
1090321936Shselasky			    "offset %x: %x %x %x %x, %x %x %x %x, %x\n",
1091321936Shselasky			    pf->number, pf->pf_ccr_window, pf->pf_ccr_offset,
1092321936Shselasky			    pccard_ccr_read(pf, 0x00),
1093321936Shselasky			pccard_ccr_read(pf, 0x02), pccard_ccr_read(pf, 0x04),
1094321936Shselasky			pccard_ccr_read(pf, 0x06), pccard_ccr_read(pf, 0x0A),
1095321936Shselasky			pccard_ccr_read(pf, 0x0C), pccard_ccr_read(pf, 0x0E),
1096321936Shselasky			pccard_ccr_read(pf, 0x10), pccard_ccr_read(pf, 0x12)));
1097321936Shselasky		} else {
1098321936Shselasky			if (pf->cfe != NULL)
1099321936Shselasky				pccard_function_disable(pf);
1100321936Shselasky		}
1101321936Shselasky	}
1102321936Shselasky	return;
1103321936Shselasky}
1104321936Shselasky
1105321936Shselaskystatic struct resource *
1106321936Shselaskypccard_alloc_resource(device_t dev, device_t child, int type, int *rid,
1107321936Shselasky    u_long start, u_long end, u_long count, u_int flags)
1108321936Shselasky{
1109321936Shselasky	struct pccard_ivar *dinfo;
1110321936Shselasky	struct resource_list_entry *rle = 0;
1111321936Shselasky	int passthrough = (device_get_parent(child) != dev);
1112321936Shselasky	int isdefault = (start == 0 && end == ~0UL && count == 1);
1113321936Shselasky	struct resource *r = NULL;
1114321936Shselasky
1115321936Shselasky	/* XXX I'm no longer sure this is right */
1116321936Shselasky	if (passthrough) {
1117321936Shselasky		return (BUS_ALLOC_RESOURCE(device_get_parent(dev), child,
1118321936Shselasky		    type, rid, start, end, count, flags));
1119321936Shselasky	}
1120321936Shselasky
1121321936Shselasky	dinfo = device_get_ivars(child);
1122321936Shselasky	rle = resource_list_find(&dinfo->resources, type, *rid);
1123321936Shselasky
1124321936Shselasky	if (rle == NULL && isdefault)
1125321936Shselasky		return (NULL);	/* no resource of that type/rid */
1126321936Shselasky	if (rle == NULL || rle->res == NULL) {
1127321936Shselasky		/* XXX Need to adjust flags */
1128321936Shselasky		r = bus_alloc_resource(dev, type, rid, start, end,
1129321936Shselasky		  count, flags);
1130321936Shselasky		if (r == NULL)
1131321936Shselasky		    goto bad;
1132321936Shselasky		resource_list_add(&dinfo->resources, type, *rid,
1133321936Shselasky		  rman_get_start(r), rman_get_end(r), count);
1134321936Shselasky		rle = resource_list_find(&dinfo->resources, type, *rid);
1135321936Shselasky		if (!rle)
1136321936Shselasky		    goto bad;
1137321936Shselasky		rle->res = r;
1138321936Shselasky	}
1139321936Shselasky	/*
1140321936Shselasky	 * If dev doesn't own the device, then we can't give this device
1141321936Shselasky	 * out.
1142321936Shselasky	 */
1143321936Shselasky	if (rman_get_device(rle->res) != dev)
1144321936Shselasky		return (NULL);
1145321936Shselasky	rman_set_device(rle->res, child);
1146321936Shselasky	if (flags & RF_ACTIVE)
1147321936Shselasky		BUS_ACTIVATE_RESOURCE(dev, child, type, *rid, rle->res);
1148321936Shselasky	return (rle->res);
1149321936Shselaskybad:;
1150321936Shselasky	device_printf(dev, "WARNING: Resource not reserved by pccard\n");
1151321936Shselasky	return (NULL);
1152321936Shselasky}
1153321936Shselasky
1154321936Shselaskystatic int
1155321936Shselaskypccard_release_resource(device_t dev, device_t child, int type, int rid,
1156321936Shselasky    struct resource *r)
1157321936Shselasky{
1158321936Shselasky	struct pccard_ivar *dinfo;
1159321936Shselasky	int passthrough = (device_get_parent(child) != dev);
1160321936Shselasky	struct resource_list_entry *rle = 0;
1161321936Shselasky
1162321936Shselasky	if (passthrough)
1163321936Shselasky		return BUS_RELEASE_RESOURCE(device_get_parent(dev), child,
1164321936Shselasky		    type, rid, r);
1165321936Shselasky
1166321936Shselasky	dinfo = device_get_ivars(child);
1167321936Shselasky
1168321936Shselasky	rle = resource_list_find(&dinfo->resources, type, rid);
1169321936Shselasky
1170321936Shselasky	if (!rle) {
1171321936Shselasky		device_printf(dev, "Allocated resource not found, "
1172321936Shselasky		    "%d %x %lx %lx\n",
1173321936Shselasky		    type, rid, rman_get_start(r), rman_get_size(r));
1174321936Shselasky		return ENOENT;
1175321936Shselasky	}
1176321936Shselasky	if (!rle->res) {
1177321936Shselasky		device_printf(dev, "Allocated resource not recorded\n");
1178321936Shselasky		return ENOENT;
1179321936Shselasky	}
1180321936Shselasky	/*
1181321936Shselasky	 * Deactivate the resource (since it is being released), and
1182321936Shselasky	 * assign it to the bus.
1183321936Shselasky	 */
1184321936Shselasky	BUS_DEACTIVATE_RESOURCE(dev, child, type, rid, rle->res);
1185321936Shselasky	rman_set_device(rle->res, dev);
1186321936Shselasky	return (0);
1187321936Shselasky}
1188321936Shselasky
1189321936Shselaskystatic void
1190321936Shselaskypccard_child_detached(device_t parent, device_t dev)
1191321936Shselasky{
1192321936Shselasky	struct pccard_ivar *ivar = PCCARD_IVAR(dev);
1193321936Shselasky	struct pccard_function *pf = ivar->pf;
1194321936Shselasky
1195321936Shselasky	pccard_function_disable(pf);
1196321936Shselasky}
1197321936Shselasky
1198321936Shselaskystatic void
1199321936Shselaskypccard_intr(void *arg)
1200321936Shselasky{
1201321936Shselasky	struct pccard_function *pf = (struct pccard_function*) arg;
1202321936Shselasky	int reg;
1203321936Shselasky	int doisr = 1;
1204321936Shselasky
1205321936Shselasky	/*
1206321936Shselasky	 * MFC cards know if they interrupted, so we have to ack the
1207321936Shselasky	 * interrupt and call the ISR.  Non-MFC cards don't have these
1208321936Shselasky	 * bits, so they always get called.  Many non-MFC cards have
1209321936Shselasky	 * this bit set always upon read, but some do not.
1210321936Shselasky	 *
1211321936Shselasky	 * We always ack the interrupt, even if there's no ISR
1212321936Shselasky	 * for the card.  This is done on the theory that acking
1213321936Shselasky	 * the interrupt will pacify the card enough to keep an
1214321936Shselasky	 * interrupt storm from happening.  Of course this won't
1215321936Shselasky	 * help in the non-MFC case.
1216321936Shselasky	 *
1217321936Shselasky	 * This has no impact for MPSAFEness of the client drivers.
1218321936Shselasky	 * We register this with whatever flags the intr_handler
1219321936Shselasky	 * was registered with.  All these functions are MPSAFE.
1220321936Shselasky	 */
1221321936Shselasky	if (pccard_mfc(pf->sc)) {
1222321936Shselasky		reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS);
1223321936Shselasky		if (reg & PCCARD_CCR_STATUS_INTR)
1224321936Shselasky			pccard_ccr_write(pf, PCCARD_CCR_STATUS,
1225321936Shselasky			    reg & ~PCCARD_CCR_STATUS_INTR);
1226321936Shselasky		else
1227321936Shselasky			doisr = 0;
1228321936Shselasky	}
1229321936Shselasky	if (pf->intr_handler != NULL && doisr)
1230321936Shselasky		pf->intr_handler(pf->intr_handler_arg);
1231321936Shselasky}
1232321936Shselasky
1233321936Shselaskystatic int
1234321936Shselaskypccard_setup_intr(device_t dev, device_t child, struct resource *irq,
1235321936Shselasky    int flags, driver_intr_t *intr, void *arg, void **cookiep)
1236321936Shselasky{
1237321936Shselasky	struct pccard_softc *sc = PCCARD_SOFTC(dev);
1238321936Shselasky	struct pccard_ivar *ivar = PCCARD_IVAR(child);
1239321936Shselasky	struct pccard_function *pf = ivar->pf;
1240321936Shselasky	int err;
1241321936Shselasky
1242321936Shselasky	if (pf->intr_handler != NULL)
1243321936Shselasky		panic("Only one interrupt handler per function allowed");
1244321936Shselasky	err = bus_generic_setup_intr(dev, child, irq, flags, pccard_intr,
1245321936Shselasky	    pf, cookiep);
1246321936Shselasky	if (err != 0)
1247321936Shselasky		return (err);
1248321936Shselasky	pf->intr_handler = intr;
1249321936Shselasky	pf->intr_handler_arg = arg;
1250321936Shselasky	pf->intr_handler_cookie = *cookiep;
1251321936Shselasky	if (pccard_mfc(sc)) {
1252321936Shselasky		pccard_ccr_write(pf, PCCARD_CCR_OPTION,
1253321936Shselasky		    pccard_ccr_read(pf, PCCARD_CCR_OPTION) |
1254321936Shselasky		    PCCARD_CCR_OPTION_IREQ_ENABLE);
1255321936Shselasky	}
1256321936Shselasky	return (0);
1257321936Shselasky}
1258321936Shselasky
1259321936Shselaskystatic int
1260321936Shselaskypccard_teardown_intr(device_t dev, device_t child, struct resource *r,
1261321936Shselasky    void *cookie)
1262321936Shselasky{
1263321936Shselasky	struct pccard_softc *sc = PCCARD_SOFTC(dev);
1264321936Shselasky	struct pccard_ivar *ivar = PCCARD_IVAR(child);
1265321936Shselasky	struct pccard_function *pf = ivar->pf;
1266321936Shselasky	int ret;
1267321936Shselasky
1268321936Shselasky	if (pccard_mfc(sc)) {
1269321936Shselasky		pccard_ccr_write(pf, PCCARD_CCR_OPTION,
1270321936Shselasky		    pccard_ccr_read(pf, PCCARD_CCR_OPTION) &
1271321936Shselasky		    ~PCCARD_CCR_OPTION_IREQ_ENABLE);
1272321936Shselasky	}
1273321936Shselasky	ret = bus_generic_teardown_intr(dev, child, r, cookie);
1274321936Shselasky	if (ret == 0) {
1275321936Shselasky		pf->intr_handler = NULL;
1276321936Shselasky		pf->intr_handler_arg = NULL;
1277321936Shselasky		pf->intr_handler_cookie = NULL;
1278321936Shselasky	}
1279321936Shselasky
1280321936Shselasky	return (ret);
1281321936Shselasky}
1282321936Shselasky
1283321936Shselaskystatic int
1284321936Shselaskypccard_activate_resource(device_t brdev, device_t child, int type, int rid,
1285321936Shselasky    struct resource *r)
1286321936Shselasky{
1287321936Shselasky	struct pccard_ivar *ivar = PCCARD_IVAR(child);
1288321936Shselasky	struct pccard_function *pf = ivar->pf;
1289321936Shselasky
1290321936Shselasky	switch(type) {
1291321936Shselasky	case SYS_RES_IOPORT:
1292321936Shselasky		/*
1293321936Shselasky		 * We need to adjust IOBASE[01] and IOSIZE if we're an MFC
1294321936Shselasky		 * card.
1295321936Shselasky		 */
1296321936Shselasky		if (pccard_mfc(pf->sc))
1297321936Shselasky			pccard_mfc_adjust_iobase(pf, rman_get_start(r), 0,
1298321936Shselasky			    rman_get_size(r));
1299321936Shselasky		break;
1300321936Shselasky	default:
1301321936Shselasky		break;
1302321936Shselasky	}
1303321936Shselasky	return (bus_generic_activate_resource(brdev, child, type, rid, r));
1304321936Shselasky}
1305321936Shselasky
1306321936Shselaskystatic int
1307321936Shselaskypccard_deactivate_resource(device_t brdev, device_t child, int type,
1308321936Shselasky    int rid, struct resource *r)
1309321936Shselasky{
1310321936Shselasky	/* XXX undo pccard_activate_resource? XXX */
1311321936Shselasky	return (bus_generic_deactivate_resource(brdev, child, type, rid, r));
1312321936Shselasky}
1313321936Shselasky
1314321936Shselaskystatic int
1315321936Shselaskypccard_attr_read_impl(device_t brdev, device_t child, uint32_t offset,
1316321936Shselasky    uint8_t *val)
1317321936Shselasky{
1318321936Shselasky	struct pccard_ivar *devi = PCCARD_IVAR(child);
1319321936Shselasky	struct pccard_function *pf = devi->pf;
1320321936Shselasky
1321321936Shselasky	/*
1322321936Shselasky	 * Optimization.  Most of the time, devices want to access
1323321936Shselasky	 * the same page of the attribute memory that the CCR is in.
1324321936Shselasky	 * We take advantage of this fact here.
1325321936Shselasky	 */
1326321936Shselasky	if (offset / PCCARD_MEM_PAGE_SIZE ==
1327321936Shselasky	    pf->ccr_base / PCCARD_MEM_PAGE_SIZE)
1328321936Shselasky		*val = bus_space_read_1(pf->pf_ccrt, pf->pf_ccrh,
1329321936Shselasky		    offset % PCCARD_MEM_PAGE_SIZE);
1330321936Shselasky	else {
1331321936Shselasky		CARD_SET_MEMORY_OFFSET(brdev, child, pf->ccr_rid, offset,
1332321936Shselasky		    &offset);
1333321936Shselasky		*val = bus_space_read_1(pf->pf_ccrt, pf->pf_ccrh, offset);
1334321936Shselasky		CARD_SET_MEMORY_OFFSET(brdev, child, pf->ccr_rid, pf->ccr_base,
1335321936Shselasky		    &offset);
1336321936Shselasky	}
1337321936Shselasky	return 0;
1338321936Shselasky}
1339321936Shselasky
1340321936Shselaskystatic int
1341321936Shselaskypccard_attr_write_impl(device_t brdev, device_t child, uint32_t offset,
1342321936Shselasky    uint8_t val)
1343321936Shselasky{
1344321936Shselasky	struct pccard_ivar *devi = PCCARD_IVAR(child);
1345321936Shselasky	struct pccard_function *pf = devi->pf;
1346321936Shselasky
1347321936Shselasky	/*
1348321936Shselasky	 * Optimization.  Most of the time, devices want to access
1349321936Shselasky	 * the same page of the attribute memory that the CCR is in.
1350321936Shselasky	 * We take advantage of this fact here.
1351321936Shselasky	 */
1352321936Shselasky	if (offset / PCCARD_MEM_PAGE_SIZE ==
1353321936Shselasky	    pf->ccr_base / PCCARD_MEM_PAGE_SIZE)
1354321936Shselasky		bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh,
1355321936Shselasky		    offset % PCCARD_MEM_PAGE_SIZE, val);
1356321936Shselasky	else {
1357321936Shselasky		CARD_SET_MEMORY_OFFSET(brdev, child, pf->ccr_rid, offset,
1358321936Shselasky		    &offset);
1359321936Shselasky		bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh, offset, val);
1360321936Shselasky		CARD_SET_MEMORY_OFFSET(brdev, child, pf->ccr_rid, pf->ccr_base,
1361321936Shselasky		    &offset);
1362321936Shselasky	}
1363321936Shselasky
1364321936Shselasky	return 0;
1365321936Shselasky}
1366321936Shselasky
1367321936Shselaskystatic int
1368321936Shselaskypccard_ccr_read_impl(device_t brdev, device_t child, uint32_t offset,
1369321936Shselasky    uint8_t *val)
1370321936Shselasky{
1371321936Shselasky	struct pccard_ivar *devi = PCCARD_IVAR(child);
1372321936Shselasky
1373321936Shselasky	*val = pccard_ccr_read(devi->pf, offset);
1374321936Shselasky	device_printf(child, "ccr_read of %#x (%#x) is %#x\n", offset,
1375321936Shselasky	  devi->pf->pf_ccr_offset, *val);
1376321936Shselasky	return 0;
1377321936Shselasky}
1378321936Shselasky
1379321936Shselaskystatic int
1380321936Shselaskypccard_ccr_write_impl(device_t brdev, device_t child, uint32_t offset,
1381321936Shselasky    uint8_t val)
1382321936Shselasky{
1383321936Shselasky	struct pccard_ivar *devi = PCCARD_IVAR(child);
1384321936Shselasky	struct pccard_function *pf = devi->pf;
1385321936Shselasky
1386321936Shselasky	/*
1387321936Shselasky	 * Can't use pccard_ccr_write since client drivers may access
1388321936Shselasky	 * registers not contained in the 'mask' if they are non-standard.
1389321936Shselasky	 */
1390321936Shselasky	device_printf(child, "ccr_write of %#x to %#x (%#x)\n", val, offset,
1391321936Shselasky	  devi->pf->pf_ccr_offset);
1392321936Shselasky	bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh, pf->pf_ccr_offset + offset,
1393321936Shselasky	    val);
1394321936Shselasky	return 0;
1395321936Shselasky}
1396321936Shselasky
1397321936Shselasky
1398321936Shselaskystatic device_method_t pccard_methods[] = {
1399321936Shselasky	/* Device interface */
1400321936Shselasky	DEVMETHOD(device_probe,		pccard_probe),
1401321936Shselasky	DEVMETHOD(device_attach,	pccard_attach),
1402321936Shselasky	DEVMETHOD(device_detach,	pccard_detach),
1403321936Shselasky	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
1404321936Shselasky	DEVMETHOD(device_suspend,	pccard_suspend),
1405321936Shselasky	DEVMETHOD(device_resume,	pccard_resume),
1406321936Shselasky
1407321936Shselasky	/* Bus interface */
1408321936Shselasky	DEVMETHOD(bus_print_child,	pccard_print_child),
1409321936Shselasky	DEVMETHOD(bus_driver_added,	pccard_driver_added),
1410321936Shselasky	DEVMETHOD(bus_child_detached,	pccard_child_detached),
1411321936Shselasky	DEVMETHOD(bus_alloc_resource,	pccard_alloc_resource),
1412321936Shselasky	DEVMETHOD(bus_release_resource,	pccard_release_resource),
1413321936Shselasky	DEVMETHOD(bus_activate_resource, pccard_activate_resource),
1414321936Shselasky	DEVMETHOD(bus_deactivate_resource, pccard_deactivate_resource),
1415321936Shselasky	DEVMETHOD(bus_setup_intr,	pccard_setup_intr),
1416321936Shselasky	DEVMETHOD(bus_teardown_intr,	pccard_teardown_intr),
1417321936Shselasky	DEVMETHOD(bus_set_resource,	pccard_set_resource),
1418321936Shselasky	DEVMETHOD(bus_get_resource,	pccard_get_resource),
1419321936Shselasky	DEVMETHOD(bus_delete_resource,	pccard_delete_resource),
1420321936Shselasky	DEVMETHOD(bus_probe_nomatch,	pccard_probe_nomatch),
1421321936Shselasky	DEVMETHOD(bus_read_ivar,	pccard_read_ivar),
1422321936Shselasky	DEVMETHOD(bus_child_pnpinfo_str, pccard_child_pnpinfo_str),
1423321936Shselasky	DEVMETHOD(bus_child_location_str, pccard_child_location_str),
1424321936Shselasky
1425321936Shselasky	/* Card Interface */
1426321936Shselasky	DEVMETHOD(card_set_res_flags,	pccard_set_res_flags),
1427321936Shselasky	DEVMETHOD(card_set_memory_offset, pccard_set_memory_offset),
1428321936Shselasky	DEVMETHOD(card_attach_card,	pccard_attach_card),
1429321936Shselasky	DEVMETHOD(card_detach_card,	pccard_detach_card),
1430321936Shselasky	DEVMETHOD(card_do_product_lookup, pccard_do_product_lookup),
1431321936Shselasky	DEVMETHOD(card_compat_do_probe, pccard_compat_do_probe),
1432321936Shselasky	DEVMETHOD(card_compat_do_attach, pccard_compat_do_attach),
1433321936Shselasky	DEVMETHOD(card_cis_scan,	pccard_scan_cis),
1434321936Shselasky	DEVMETHOD(card_attr_read,	pccard_attr_read_impl),
1435321936Shselasky	DEVMETHOD(card_attr_write,	pccard_attr_write_impl),
1436321936Shselasky	DEVMETHOD(card_ccr_read,	pccard_ccr_read_impl),
1437321936Shselasky	DEVMETHOD(card_ccr_write,	pccard_ccr_write_impl),
1438321936Shselasky
1439321936Shselasky	{ 0, 0 }
1440321936Shselasky};
1441321936Shselasky
1442321936Shselaskystatic driver_t pccard_driver = {
1443321936Shselasky	"pccard",
1444321936Shselasky	pccard_methods,
1445321936Shselasky	sizeof(struct pccard_softc)
1446321936Shselasky};
1447321936Shselasky
1448321936Shselaskydevclass_t	pccard_devclass;
1449321936Shselasky
1450321936Shselasky/* Maybe we need to have a slot device? */
1451321936ShselaskyDRIVER_MODULE(pccard, pcic, pccard_driver, pccard_devclass, 0, 0);
1452321936ShselaskyDRIVER_MODULE(pccard, cbb, pccard_driver, pccard_devclass, 0, 0);
1453321936ShselaskyMODULE_VERSION(pccard, 1);
1454321936Shselasky