xref: /dragonfly/sys/dev/disk/sdhci/sdhci_acpi.c (revision 70a02aad)
1 /*-
2  * Copyright (c) 2008 Alexander Motin <mav@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include <sys/param.h>
27 #include <sys/systm.h>
28 #include <sys/bus.h>
29 #include <sys/conf.h>
30 #include <sys/kernel.h>
31 #include <sys/module.h>
32 #include <sys/resource.h>
33 #include <sys/rman.h>
34 #include <sys/taskqueue.h>
35 
36 #include "acpi.h"
37 #include "opt_acpi.h"
38 #include <dev/acpica/acpivar.h>
39 
40 #include <bus/pci/pcivar.h>
41 
42 #include <machine/stdarg.h>
43 
44 #include <bus/mmc/bridge.h>
45 #include <bus/mmc/mmcreg.h>
46 #include <bus/mmc/mmcbrvar.h>
47 
48 #include "sdhci.h"
49 #include "mmcbr_if.h"
50 #include "sdhci_if.h"
51 
52 ACPI_MODULE_NAME("sdhci_acpi");
53 
54 struct sdhci_acpi_softc {
55 	device_t	dev;		/* Controller device */
56 	ACPI_HANDLE	handle;
57 	struct resource *irq_res;	/* IRQ resource */
58 	void 		*intrhand;	/* Interrupt handle */
59 
60 	struct sdhci_slot slot;
61 	struct resource	*mem_res;	/* Memory resource */
62 };
63 
64 static uint8_t
65 sdhci_acpi_read_1(device_t dev, struct sdhci_slot *slot __unused,
66     bus_size_t off)
67 {
68 	struct sdhci_acpi_softc *sc = device_get_softc(dev);
69 
70 	bus_barrier(sc->mem_res, 0, 0xFF,
71 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
72 	return bus_read_1(sc->mem_res, off);
73 }
74 
75 static void
76 sdhci_acpi_write_1(device_t dev, struct sdhci_slot *slot __unused,
77     bus_size_t off, uint8_t val)
78 {
79 	struct sdhci_acpi_softc *sc = device_get_softc(dev);
80 
81 	bus_barrier(sc->mem_res, 0, 0xFF,
82 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
83 	bus_write_1(sc->mem_res, off, val);
84 }
85 
86 static uint16_t
87 sdhci_acpi_read_2(device_t dev, struct sdhci_slot *slot __unused,
88     bus_size_t off)
89 {
90 	struct sdhci_acpi_softc *sc = device_get_softc(dev);
91 
92 	bus_barrier(sc->mem_res, 0, 0xFF,
93 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
94 	return bus_read_2(sc->mem_res, off);
95 }
96 
97 static void
98 sdhci_acpi_write_2(device_t dev, struct sdhci_slot *slot __unused,
99     bus_size_t off, uint16_t val)
100 {
101 	struct sdhci_acpi_softc *sc = device_get_softc(dev);
102 
103 	bus_barrier(sc->mem_res, 0, 0xFF,
104 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
105 	bus_write_2(sc->mem_res, off, val);
106 }
107 
108 static uint32_t
109 sdhci_acpi_read_4(device_t dev, struct sdhci_slot *slot __unused,
110     bus_size_t off)
111 {
112 	struct sdhci_acpi_softc *sc = device_get_softc(dev);
113 
114 	bus_barrier(sc->mem_res, 0, 0xFF,
115 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
116 	return bus_read_4(sc->mem_res, off);
117 }
118 
119 static void
120 sdhci_acpi_write_4(device_t dev, struct sdhci_slot *slot __unused,
121     bus_size_t off, uint32_t val)
122 {
123 	struct sdhci_acpi_softc *sc = device_get_softc(dev);
124 
125 	bus_barrier(sc->mem_res, 0, 0xFF,
126 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
127 	bus_write_4(sc->mem_res, off, val);
128 }
129 
130 static void
131 sdhci_acpi_read_multi_4(device_t dev, struct sdhci_slot *slot __unused,
132     bus_size_t off, uint32_t *data, bus_size_t count)
133 {
134 	struct sdhci_acpi_softc *sc = device_get_softc(dev);
135 
136 	bus_read_multi_stream_4(sc->mem_res, off, data, count);
137 }
138 
139 static void
140 sdhci_acpi_write_multi_4(device_t dev, struct sdhci_slot *slot __unused,
141     bus_size_t off, uint32_t *data, bus_size_t count)
142 {
143 	struct sdhci_acpi_softc *sc = device_get_softc(dev);
144 
145 	bus_write_multi_stream_4(sc->mem_res, off, data, count);
146 }
147 
148 static void sdhci_acpi_intr(void *arg);
149 
150 static int
151 sdhci_acpi_probe(device_t dev)
152 {
153 	static char *sdhci_ids[] = { "80860F14", "80860F16", NULL };
154 
155 	if (acpi_disabled("sdhci") ||
156 	    ACPI_ID_PROBE(device_get_parent(dev), dev, sdhci_ids) == NULL)
157 		return (ENXIO);
158 
159 	device_set_desc(dev, "SDHCI controller");
160 	return (0);
161 }
162 
163 static int
164 sdhci_acpi_attach(device_t dev)
165 {
166 	struct sdhci_acpi_softc *sc = device_get_softc(dev);
167 	int err, rid;
168 
169 	sc->dev = dev;
170 	sc->handle = acpi_get_handle(dev);
171 
172 	/* Allocate IRQ. */
173 	rid = 0;
174 	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
175 		RF_SHAREABLE);
176 	if (sc->irq_res == NULL) {
177 		device_printf(dev, "Can't allocate IRQ\n");
178 		err = ENOMEM;
179 		goto error;
180 	}
181 
182 	/* Allocate memory. */
183 	rid = 0;
184 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
185 	    RF_ACTIVE);
186 	if (sc->mem_res == NULL) {
187 		device_printf(dev, "Can't allocate memory for slot %d\n", 0);
188 		err = ENOMEM;
189 		goto error;
190 	}
191 
192 	pci_set_powerstate(dev, PCI_POWERSTATE_D0);
193 	/* The Intel sdhci controllers all work fine with ADMA2. */
194 	sc->slot.quirks = SDHCI_QUIRK_WHITELIST_ADMA2;
195 	if (sdhci_init_slot(dev, &sc->slot, 0) != 0) {
196 		device_printf(dev, "sdhci initialization failed\n");
197 		pci_set_powerstate(dev, PCI_POWERSTATE_D3);
198 		err = ENXIO;
199 		goto error;
200 	}
201 
202 	device_printf(dev, "%d slot(s) allocated\n", 1);
203 	/* Activate the interrupt */
204 	err = bus_setup_intr(dev, sc->irq_res, INTR_MPSAFE,
205 	    sdhci_acpi_intr, sc, &sc->intrhand, NULL);
206 	if (err)
207 		device_printf(dev, "Can't setup IRQ\n");
208 
209 	/* Process cards detection. */
210 	sdhci_start_slot(&sc->slot);
211 
212 	return (0);
213 
214 error:
215 	if (sc->irq_res != NULL) {
216 		bus_release_resource(dev, SYS_RES_IRQ,
217 		    rman_get_rid(sc->irq_res), sc->irq_res);
218 	}
219 	if (sc->mem_res != NULL) {
220 		bus_release_resource(dev, SYS_RES_MEMORY,
221 		    rman_get_rid(sc->mem_res), sc->mem_res);
222 	}
223 	return (err);
224 }
225 
226 static int
227 sdhci_acpi_detach(device_t dev)
228 {
229 	struct sdhci_acpi_softc *sc = device_get_softc(dev);
230 
231 	bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
232 	bus_release_resource(dev, SYS_RES_IRQ,
233 	    rman_get_rid(sc->irq_res), sc->irq_res);
234 
235 	sdhci_cleanup_slot(&sc->slot);
236 	bus_release_resource(dev, SYS_RES_MEMORY,
237 	    rman_get_rid(sc->mem_res), sc->mem_res);
238 	pci_set_powerstate(dev, PCI_POWERSTATE_D3);
239 	return (0);
240 }
241 
242 static int
243 sdhci_acpi_suspend(device_t dev)
244 {
245 	struct sdhci_acpi_softc *sc = device_get_softc(dev);
246 	int err;
247 
248 	err = bus_generic_suspend(dev);
249 	if (err)
250 		return (err);
251 	sdhci_generic_suspend(&sc->slot);
252 	return (0);
253 }
254 
255 static int
256 sdhci_acpi_resume(device_t dev)
257 {
258 	struct sdhci_acpi_softc *sc = device_get_softc(dev);
259 
260 	sdhci_generic_resume(&sc->slot);
261 	return (bus_generic_resume(dev));
262 }
263 
264 static void
265 sdhci_acpi_intr(void *arg)
266 {
267 	struct sdhci_acpi_softc *sc = (struct sdhci_acpi_softc *)arg;
268 
269 	sdhci_generic_intr(&sc->slot);
270 }
271 
272 static device_method_t sdhci_methods[] = {
273 	/* device_if */
274 	DEVMETHOD(device_probe, sdhci_acpi_probe),
275 	DEVMETHOD(device_attach, sdhci_acpi_attach),
276 	DEVMETHOD(device_detach, sdhci_acpi_detach),
277 	DEVMETHOD(device_suspend, sdhci_acpi_suspend),
278 	DEVMETHOD(device_resume, sdhci_acpi_resume),
279 
280 	/* Bus interface */
281 	DEVMETHOD(bus_read_ivar,	sdhci_generic_read_ivar),
282 	DEVMETHOD(bus_write_ivar,	sdhci_generic_write_ivar),
283 
284 	/* mmcbr_if */
285 	DEVMETHOD(mmcbr_update_ios, sdhci_generic_update_ios),
286 	DEVMETHOD(mmcbr_request, sdhci_generic_request),
287 	DEVMETHOD(mmcbr_get_ro, sdhci_generic_get_ro),
288 	DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host),
289 	DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host),
290 
291 	/* SDHCI registers accessors */
292 	DEVMETHOD(sdhci_read_1,		sdhci_acpi_read_1),
293 	DEVMETHOD(sdhci_read_2,		sdhci_acpi_read_2),
294 	DEVMETHOD(sdhci_read_4,		sdhci_acpi_read_4),
295 	DEVMETHOD(sdhci_read_multi_4,	sdhci_acpi_read_multi_4),
296 	DEVMETHOD(sdhci_write_1,	sdhci_acpi_write_1),
297 	DEVMETHOD(sdhci_write_2,	sdhci_acpi_write_2),
298 	DEVMETHOD(sdhci_write_4,	sdhci_acpi_write_4),
299 	DEVMETHOD(sdhci_write_multi_4,	sdhci_acpi_write_multi_4),
300 
301 	DEVMETHOD_END
302 };
303 
304 static driver_t sdhci_acpi_driver = {
305 	"sdhci_acpi",
306 	sdhci_methods,
307 	sizeof(struct sdhci_acpi_softc),
308 };
309 static devclass_t sdhci_acpi_devclass;
310 
311 DRIVER_MODULE(sdhci_acpi, acpi, sdhci_acpi_driver, sdhci_acpi_devclass, NULL,
312     NULL);
313 MODULE_DEPEND(sdhci_acpi, sdhci, 1, 1, 1);
314