sdhci - Add tunables for disabling SDMA and/or ADMA2, and for testing ADMA2.
[dragonfly.git] / sys / dev / disk / sdhci / sdhci_acpi.c
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, bus_size_t off)
66 {
67         struct sdhci_acpi_softc *sc = device_get_softc(dev);
68
69         bus_barrier(sc->mem_res, 0, 0xFF,
70             BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
71         return bus_read_1(sc->mem_res, off);
72 }
73
74 static void
75 sdhci_acpi_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint8_t val)
76 {
77         struct sdhci_acpi_softc *sc = device_get_softc(dev);
78
79         bus_barrier(sc->mem_res, 0, 0xFF,
80             BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
81         bus_write_1(sc->mem_res, off, val);
82 }
83
84 static uint16_t
85 sdhci_acpi_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off)
86 {
87         struct sdhci_acpi_softc *sc = device_get_softc(dev);
88
89         bus_barrier(sc->mem_res, 0, 0xFF,
90             BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
91         return bus_read_2(sc->mem_res, off);
92 }
93
94 static void
95 sdhci_acpi_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint16_t val)
96 {
97         struct sdhci_acpi_softc *sc = device_get_softc(dev);
98
99         bus_barrier(sc->mem_res, 0, 0xFF,
100             BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
101         bus_write_2(sc->mem_res, off, val);
102 }
103
104 static uint32_t
105 sdhci_acpi_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)
106 {
107         struct sdhci_acpi_softc *sc = device_get_softc(dev);
108
109         bus_barrier(sc->mem_res, 0, 0xFF,
110             BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
111         return bus_read_4(sc->mem_res, off);
112 }
113
114 static void
115 sdhci_acpi_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t val)
116 {
117         struct sdhci_acpi_softc *sc = device_get_softc(dev);
118
119         bus_barrier(sc->mem_res, 0, 0xFF,
120             BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
121         bus_write_4(sc->mem_res, off, val);
122 }
123
124 static void
125 sdhci_acpi_read_multi_4(device_t dev, struct sdhci_slot *slot,
126     bus_size_t off, uint32_t *data, bus_size_t count)
127 {
128         struct sdhci_acpi_softc *sc = device_get_softc(dev);
129
130         bus_read_multi_stream_4(sc->mem_res, off, data, count);
131 }
132
133 static void
134 sdhci_acpi_write_multi_4(device_t dev, struct sdhci_slot *slot,
135     bus_size_t off, uint32_t *data, bus_size_t count)
136 {
137         struct sdhci_acpi_softc *sc = device_get_softc(dev);
138
139         bus_write_multi_stream_4(sc->mem_res, off, data, count);
140 }
141
142 static void sdhci_acpi_intr(void *arg);
143
144 static int
145 sdhci_acpi_probe(device_t dev)
146 {
147         static char *sdhci_ids[] = { "80860F14", "80860F16", NULL };
148
149         if (acpi_disabled("sdhci") ||
150             ACPI_ID_PROBE(device_get_parent(dev), dev, sdhci_ids) == NULL)
151                 return (ENXIO);
152
153         device_set_desc(dev, "SDHCI controller");
154         return (0);
155 }
156
157 static int
158 sdhci_acpi_attach(device_t dev)
159 {
160         struct sdhci_acpi_softc *sc = device_get_softc(dev);
161         int err, rid;
162
163         sc->dev = dev;
164         sc->handle = acpi_get_handle(dev);
165
166         /* Allocate IRQ. */
167         rid = 0;
168         sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
169                 RF_SHAREABLE);
170         if (sc->irq_res == NULL) {
171                 device_printf(dev, "Can't allocate IRQ\n");
172                 err = ENOMEM;
173                 goto error;
174         }
175
176         /* Allocate memory. */
177         rid = 0;
178         sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
179             RF_ACTIVE);
180         if (sc->mem_res == NULL) {
181                 device_printf(dev, "Can't allocate memory for slot %d\n", 0);
182                 err = ENOMEM;
183                 goto error;
184         }
185
186         pci_set_powerstate(dev, PCI_POWERSTATE_D0);
187         /* The Intel sdhci controllers all work fine with ADMA2. */
188         sc->slot.quirks = SDHCI_QUIRK_WHITELIST_ADMA2;
189         if (sdhci_init_slot(dev, &sc->slot, 0) != 0) {
190                 device_printf(dev, "sdhci initialization failed\n");
191                 pci_set_powerstate(dev, PCI_POWERSTATE_D3);
192                 err = ENXIO;
193                 goto error;
194         }
195
196         device_printf(dev, "%d slot(s) allocated\n", 1);
197         /* Activate the interrupt */
198         err = bus_setup_intr(dev, sc->irq_res, INTR_MPSAFE,
199             sdhci_acpi_intr, sc, &sc->intrhand, NULL);
200         if (err)
201                 device_printf(dev, "Can't setup IRQ\n");
202
203         /* Process cards detection. */
204         sdhci_start_slot(&sc->slot);
205
206         return (0);
207
208 error:
209         if (sc->irq_res != NULL) {
210                 bus_release_resource(dev, SYS_RES_IRQ,
211                     rman_get_rid(sc->irq_res), sc->irq_res);
212         }
213         if (sc->mem_res != NULL) {
214                 bus_release_resource(dev, SYS_RES_MEMORY,
215                     rman_get_rid(sc->mem_res), sc->mem_res);
216         }
217         return (err);
218 }
219
220 static int
221 sdhci_acpi_detach(device_t dev)
222 {
223         struct sdhci_acpi_softc *sc = device_get_softc(dev);
224
225         bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
226         bus_release_resource(dev, SYS_RES_IRQ,
227             rman_get_rid(sc->irq_res), sc->irq_res);
228
229         sdhci_cleanup_slot(&sc->slot);
230         bus_release_resource(dev, SYS_RES_MEMORY,
231             rman_get_rid(sc->mem_res), sc->mem_res);
232         pci_set_powerstate(dev, PCI_POWERSTATE_D3);
233         return (0);
234 }
235
236 static int
237 sdhci_acpi_suspend(device_t dev)
238 {
239         struct sdhci_acpi_softc *sc = device_get_softc(dev);
240         int err;
241
242         err = bus_generic_suspend(dev);
243         if (err)
244                 return (err);
245         sdhci_generic_suspend(&sc->slot);
246         return (0);
247 }
248
249 static int
250 sdhci_acpi_resume(device_t dev)
251 {
252         struct sdhci_acpi_softc *sc = device_get_softc(dev);
253
254         sdhci_generic_resume(&sc->slot);
255         return (bus_generic_resume(dev));
256 }
257
258 static void
259 sdhci_acpi_intr(void *arg)
260 {
261         struct sdhci_acpi_softc *sc = (struct sdhci_acpi_softc *)arg;
262
263         sdhci_generic_intr(&sc->slot);
264 }
265
266 static device_method_t sdhci_methods[] = {
267         /* device_if */
268         DEVMETHOD(device_probe, sdhci_acpi_probe),
269         DEVMETHOD(device_attach, sdhci_acpi_attach),
270         DEVMETHOD(device_detach, sdhci_acpi_detach),
271         DEVMETHOD(device_suspend, sdhci_acpi_suspend),
272         DEVMETHOD(device_resume, sdhci_acpi_resume),
273
274         /* Bus interface */
275         DEVMETHOD(bus_read_ivar,        sdhci_generic_read_ivar),
276         DEVMETHOD(bus_write_ivar,       sdhci_generic_write_ivar),
277
278         /* mmcbr_if */
279         DEVMETHOD(mmcbr_update_ios, sdhci_generic_update_ios),
280         DEVMETHOD(mmcbr_request, sdhci_generic_request),
281         DEVMETHOD(mmcbr_get_ro, sdhci_generic_get_ro),
282         DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host),
283         DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host),
284
285         /* SDHCI registers accessors */
286         DEVMETHOD(sdhci_read_1,         sdhci_acpi_read_1),
287         DEVMETHOD(sdhci_read_2,         sdhci_acpi_read_2),
288         DEVMETHOD(sdhci_read_4,         sdhci_acpi_read_4),
289         DEVMETHOD(sdhci_read_multi_4,   sdhci_acpi_read_multi_4),
290         DEVMETHOD(sdhci_write_1,        sdhci_acpi_write_1),
291         DEVMETHOD(sdhci_write_2,        sdhci_acpi_write_2),
292         DEVMETHOD(sdhci_write_4,        sdhci_acpi_write_4),
293         DEVMETHOD(sdhci_write_multi_4,  sdhci_acpi_write_multi_4),
294
295         DEVMETHOD_END
296 };
297
298 static driver_t sdhci_acpi_driver = {
299         "sdhci_acpi",
300         sdhci_methods,
301         sizeof(struct sdhci_acpi_softc),
302 };
303 static devclass_t sdhci_acpi_devclass;
304
305 DRIVER_MODULE(sdhci_acpi, acpi, sdhci_acpi_driver, sdhci_acpi_devclass, NULL,
306     NULL);
307 MODULE_DEPEND(sdhci_acpi, sdhci, 1, 1, 1);