xref: /netbsd-src/sys/dev/cardbus/sdhc_cardbus.c (revision d47bcd296c8b39243dd81e9cc75ea86330d4eeaf)
1 /*	$NetBSD: sdhc_cardbus.c,v 1.6 2019/11/10 21:16:34 chs Exp $	*/
2 
3 /*
4  * Copyright (c) 2010 NONAKA Kimihiro <nonaka@netbsd.org>
5  * 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 ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __KERNEL_RCSID(0, "$NetBSD: sdhc_cardbus.c,v 1.6 2019/11/10 21:16:34 chs Exp $");
30 
31 #ifdef _KERNEL_OPT
32 #include "opt_sdmmc.h"
33 #endif
34 
35 #include <sys/param.h>
36 #include <sys/device.h>
37 #include <sys/systm.h>
38 #include <sys/malloc.h>
39 #include <sys/pmf.h>
40 
41 #include <dev/cardbus/cardbusvar.h>
42 #include <dev/pci/pcidevs.h>
43 
44 #include <dev/sdmmc/sdhcreg.h>
45 #include <dev/sdmmc/sdhcvar.h>
46 #include <dev/sdmmc/sdmmcvar.h>
47 
48 /* PCI interface classes */
49 #define SDHC_PCI_INTERFACE_NO_DMA	0x00
50 #define SDHC_PCI_INTERFACE_DMA		0x01
51 #define SDHC_PCI_INTERFACE_VENDOR	0x02
52 
53 /*
54  * 8-bit PCI configuration register that tells us how many slots there
55  * are and which BAR entry corresponds to the first slot.
56  */
57 #define SDHC_PCI_CONF_SLOT_INFO		0x40
58 #define SDHC_PCI_NUM_SLOTS(info)	((((info) >> 4) & 0x7) + 1)
59 #define SDHC_PCI_FIRST_BAR(info)	((info) & 0x7)
60 
61 struct sdhc_cardbus_softc {
62 	struct sdhc_softc sc;
63 	cardbus_chipset_tag_t sc_cc;
64 	cardbus_function_tag_t sc_cf;
65 	cardbus_devfunc_t sc_ct;
66 	pcitag_t sc_tag;
67 	bus_space_tag_t sc_iot;		/* CardBus I/O space tag */
68 	bus_space_tag_t sc_memt;	/* CardBus MEM space tag */
69 
70 	void *sc_ih;
71 };
72 
73 static int sdhc_cardbus_match(device_t, cfdata_t, void *);
74 static void sdhc_cardbus_attach(device_t, device_t, void *);
75 static int sdhc_cardbus_detach(device_t, int);
76 
77 CFATTACH_DECL_NEW(sdhc_cardbus, sizeof(struct sdhc_cardbus_softc),
78     sdhc_cardbus_match, sdhc_cardbus_attach, sdhc_cardbus_detach, NULL);
79 
80 #ifdef SDHC_DEBUG
81 #define	DPRINTF(s)	printf s
82 #else
83 #define	DPRINTF(s)	/**/
84 #endif
85 
86 static int
sdhc_cardbus_match(device_t parent,cfdata_t cf,void * aux)87 sdhc_cardbus_match(device_t parent, cfdata_t cf, void *aux)
88 {
89 	struct cardbus_attach_args *ca = aux;
90 
91 	if (PCI_CLASS(ca->ca_class) == PCI_CLASS_SYSTEM &&
92 	    PCI_SUBCLASS(ca->ca_class) == PCI_SUBCLASS_SYSTEM_SDHC)
93 		return 3;
94 
95 	return 0;
96 }
97 
98 static void
sdhc_cardbus_attach(device_t parent,device_t self,void * aux)99 sdhc_cardbus_attach(device_t parent, device_t self, void *aux)
100 {
101 	struct sdhc_cardbus_softc *sc = device_private(self);
102 	struct cardbus_attach_args *ca = aux;
103 	cardbus_devfunc_t ct = ca->ca_ct;
104 	cardbus_chipset_tag_t cc = ct->ct_cc;
105 	cardbus_function_tag_t cf = ct->ct_cf;
106 	pcireg_t csr;
107 	pcireg_t slotinfo;
108 	char devinfo[256];
109 	int nslots;
110 	bus_space_tag_t iot;
111 	bus_space_handle_t ioh;
112 	bus_size_t size;
113 
114 	sc->sc.sc_dev = self;
115 	sc->sc.sc_dmat = ca->ca_dmat;
116 	sc->sc.sc_host = NULL;
117 
118 	sc->sc_cc = cc;
119 	sc->sc_cf = cf;
120 	sc->sc_ct = ct;
121 	sc->sc_tag = ca->ca_tag;
122 
123 	sc->sc_iot = ca->ca_iot;
124 	sc->sc_memt = ca->ca_memt;
125 
126 	pci_devinfo(ca->ca_id, ca->ca_class, 0, devinfo, sizeof(devinfo));
127 	aprint_normal(": %s (rev. 0x%02x)\n", devinfo,
128 	    PCI_REVISION(ca->ca_class));
129 	aprint_naive("\n");
130 
131 	/*
132 	 * Map and attach all hosts supported by the host controller.
133 	 */
134 	slotinfo = Cardbus_conf_read(ct, ca->ca_tag, SDHC_PCI_CONF_SLOT_INFO);
135 	nslots = SDHC_PCI_NUM_SLOTS(slotinfo);
136 	KASSERT(nslots == 1);
137 
138 	/* Allocate an array big enough to hold all the possible hosts */
139 	sc->sc.sc_host = malloc(sizeof(struct sdhc_host *) * nslots,
140 	    M_DEVBUF, M_WAITOK | M_ZERO);
141 
142 	/* Enable the device. */
143 	csr = Cardbus_conf_read(ct, ca->ca_tag, PCI_COMMAND_STATUS_REG);
144 	Cardbus_conf_write(ct, ca->ca_tag, PCI_COMMAND_STATUS_REG,
145 	    csr | PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_MEM_ENABLE);
146 
147 	/* Establish the interrupt. */
148 	sc->sc_ih = Cardbus_intr_establish(ct, IPL_SDMMC, sdhc_intr, &sc->sc);
149 	if (sc->sc_ih == NULL) {
150 		aprint_error_dev(self, "couldn't establish interrupt\n");
151 		goto err;
152 	}
153 
154 	/* Enable use of DMA if supported by the interface. */
155 	if ((PCI_INTERFACE(ca->ca_class) == SDHC_PCI_INTERFACE_DMA))
156 		SET(sc->sc.sc_flags, SDHC_FLAG_USE_DMA);
157 
158 	if (Cardbus_mapreg_map(ct, PCI_MAPREG_START, PCI_MAPREG_TYPE_MEM, 0,
159 	    &iot, &ioh, NULL, &size)) {
160 		aprint_error_dev(self, "couldn't map register\n");
161 		goto err;
162 	}
163 
164 	if (sdhc_host_found(&sc->sc, iot, ioh, size) != 0) {
165 		aprint_error_dev(self, "couldn't initialize host\n");
166 		goto err;
167 	}
168 
169 	if (!pmf_device_register1(self, sdhc_suspend, sdhc_resume,
170 	    sdhc_shutdown)) {
171 		aprint_error_dev(self, "couldn't establish powerhook\n");
172 	}
173 
174 	return;
175 
176 err:
177 	if (sc->sc_ih != NULL)
178 		Cardbus_intr_disestablish(ct, sc->sc_ih);
179 	if (sc->sc.sc_host != NULL)
180 		free(sc->sc.sc_host, M_DEVBUF);
181 }
182 
183 static int
sdhc_cardbus_detach(device_t self,int flags)184 sdhc_cardbus_detach(device_t self, int flags)
185 {
186 	struct sdhc_cardbus_softc *sc = device_private(self);
187 	struct cardbus_devfunc *ct = sc->sc_ct;
188 	int rv;
189 
190 	rv = sdhc_detach(&sc->sc, flags);
191 	if (rv)
192 		return rv;
193 	if (sc->sc_ih != NULL) {
194 		Cardbus_intr_disestablish(ct, sc->sc_ih);
195 		sc->sc_ih = NULL;
196 	}
197 	if (sc->sc.sc_host != NULL) {
198 		free(sc->sc.sc_host, M_DEVBUF);
199 		sc->sc.sc_host = NULL;
200 	}
201 	return 0;
202 }
203