1 | /* $NetBSD: sdhc_pci.c,v 1.12 2015/08/09 13:27:48 mlelstv Exp $ */ |
2 | /* $OpenBSD: sdhc_pci.c,v 1.7 2007/10/30 18:13:45 chl Exp $ */ |
3 | |
4 | /* |
5 | * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> |
6 | * |
7 | * Permission to use, copy, modify, and distribute this software for any |
8 | * purpose with or without fee is hereby granted, provided that the above |
9 | * copyright notice and this permission notice appear in all copies. |
10 | * |
11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
18 | */ |
19 | |
20 | #include <sys/cdefs.h> |
21 | __KERNEL_RCSID(0, "$NetBSD: sdhc_pci.c,v 1.12 2015/08/09 13:27:48 mlelstv Exp $" ); |
22 | |
23 | #ifdef _KERNEL_OPT |
24 | #include "opt_sdmmc.h" |
25 | #endif |
26 | |
27 | #include <sys/param.h> |
28 | #include <sys/device.h> |
29 | #include <sys/systm.h> |
30 | #include <sys/malloc.h> |
31 | #include <sys/pmf.h> |
32 | |
33 | #include <dev/pci/pcivar.h> |
34 | #include <dev/pci/pcidevs.h> |
35 | |
36 | #include <dev/sdmmc/sdhcreg.h> |
37 | #include <dev/sdmmc/sdhcvar.h> |
38 | #include <dev/sdmmc/sdmmcvar.h> |
39 | |
40 | /* PCI base address registers */ |
41 | #define SDHC_PCI_BAR_START PCI_MAPREG_START |
42 | #define SDHC_PCI_BAR_END PCI_MAPREG_END |
43 | |
44 | /* PCI interface classes */ |
45 | #define SDHC_PCI_INTERFACE_NO_DMA 0x00 |
46 | #define SDHC_PCI_INTERFACE_DMA 0x01 |
47 | #define SDHC_PCI_INTERFACE_VENDOR 0x02 |
48 | |
49 | /* |
50 | * 8-bit PCI configuration register that tells us how many slots there |
51 | * are and which BAR entry corresponds to the first slot. |
52 | */ |
53 | #define SDHC_PCI_CONF_SLOT_INFO 0x40 |
54 | #define SDHC_PCI_NUM_SLOTS(info) ((((info) >> 4) & 0x7) + 1) |
55 | #define SDHC_PCI_FIRST_BAR(info) ((info) & 0x7) |
56 | |
57 | struct sdhc_pci_softc { |
58 | struct sdhc_softc sc; |
59 | pci_chipset_tag_t sc_pc; |
60 | void *sc_ih; |
61 | }; |
62 | |
63 | static int sdhc_pci_match(device_t, cfdata_t, void *); |
64 | static void sdhc_pci_attach(device_t, device_t, void *); |
65 | static int sdhc_pci_detach(device_t, int); |
66 | |
67 | CFATTACH_DECL_NEW(sdhc_pci, sizeof(struct sdhc_pci_softc), |
68 | sdhc_pci_match, sdhc_pci_attach, sdhc_pci_detach, NULL); |
69 | |
70 | #ifdef SDHC_DEBUG |
71 | #define DPRINTF(s) printf s |
72 | #else |
73 | #define DPRINTF(s) /**/ |
74 | #endif |
75 | |
76 | static const struct sdhc_pci_quirk { |
77 | pci_vendor_id_t vendor; |
78 | pci_product_id_t product; |
79 | pci_vendor_id_t subvendor; |
80 | pci_product_id_t subproduct; |
81 | u_int function; |
82 | |
83 | uint32_t flags; |
84 | #define SDHC_PCI_QUIRK_FORCE_DMA (1U << 0) |
85 | #define SDHC_PCI_QUIRK_TI_HACK (1U << 1) |
86 | #define SDHC_PCI_QUIRK_NO_PWR0 (1U << 2) |
87 | #define SDHC_PCI_QUIRK_RICOH_LOWER_FREQ_HACK (1U << 3) |
88 | #define SDHC_PCI_QUIRK_RICOH_SLOW_SDR50_HACK (1U << 4) |
89 | } sdhc_pci_quirk_table[] = { |
90 | { |
91 | PCI_VENDOR_TI, |
92 | PCI_PRODUCT_TI_PCI72111SD, |
93 | 0xffff, |
94 | 0xffff, |
95 | 4, |
96 | SDHC_PCI_QUIRK_TI_HACK |
97 | }, |
98 | |
99 | { |
100 | PCI_VENDOR_TI, |
101 | PCI_PRODUCT_TI_PCIXX12SD, |
102 | 0xffff, |
103 | 0xffff, |
104 | 3, |
105 | SDHC_PCI_QUIRK_TI_HACK |
106 | }, |
107 | |
108 | { |
109 | PCI_VENDOR_ENE, |
110 | PCI_PRODUCT_ENE_CB712, |
111 | 0xffff, |
112 | 0xffff, |
113 | 0, |
114 | SDHC_PCI_QUIRK_NO_PWR0 |
115 | }, |
116 | { |
117 | PCI_VENDOR_RICOH, |
118 | PCI_PRODUCT_RICOH_Rx5U823, |
119 | 0xffff, |
120 | 0xffff, |
121 | 0, |
122 | SDHC_PCI_QUIRK_RICOH_SLOW_SDR50_HACK |
123 | }, |
124 | { |
125 | PCI_VENDOR_RICOH, |
126 | PCI_PRODUCT_RICOH_Rx5C822, |
127 | 0xffff, |
128 | 0xffff, |
129 | ~0, |
130 | SDHC_PCI_QUIRK_FORCE_DMA |
131 | }, |
132 | |
133 | { |
134 | PCI_VENDOR_RICOH, |
135 | PCI_PRODUCT_RICOH_Rx5U822, |
136 | 0xffff, |
137 | 0xffff, |
138 | ~0, |
139 | SDHC_PCI_QUIRK_FORCE_DMA |
140 | }, |
141 | }; |
142 | |
143 | static void sdhc_pci_quirk_ti_hack(struct pci_attach_args *); |
144 | static void sdhc_pci_quirk_ricoh_lower_freq_hack(struct pci_attach_args *); |
145 | |
146 | static uint32_t |
147 | sdhc_pci_lookup_quirk_flags(struct pci_attach_args *pa) |
148 | { |
149 | const struct sdhc_pci_quirk *q; |
150 | pcireg_t id; |
151 | pci_vendor_id_t vendor; |
152 | pci_product_id_t product; |
153 | int i; |
154 | |
155 | for (i = 0; i < __arraycount(sdhc_pci_quirk_table); i++) { |
156 | q = &sdhc_pci_quirk_table[i]; |
157 | |
158 | if ((PCI_VENDOR(pa->pa_id) == q->vendor) |
159 | && (PCI_PRODUCT(pa->pa_id) == q->product)) { |
160 | if ((q->function != ~0) |
161 | && (pa->pa_function != q->function)) |
162 | continue; |
163 | |
164 | if ((q->subvendor == 0xffff) |
165 | && (q->subproduct == 0xffff)) |
166 | return (q->flags); |
167 | |
168 | id = pci_conf_read(pa->pa_pc, pa->pa_tag, |
169 | PCI_SUBSYS_ID_REG); |
170 | vendor = PCI_VENDOR(id); |
171 | product = PCI_PRODUCT(id); |
172 | |
173 | if ((q->subvendor != 0xffff) |
174 | && (q->subproduct != 0xffff)) { |
175 | if ((vendor == q->subvendor) |
176 | && (product == q->subproduct)) |
177 | return (q->flags); |
178 | } else if (q->subvendor != 0xffff) { |
179 | if (product == q->subproduct) |
180 | return (q->flags); |
181 | } else { |
182 | if (vendor == q->subvendor) |
183 | return (q->flags); |
184 | } |
185 | } |
186 | } |
187 | return (0); |
188 | } |
189 | |
190 | static int |
191 | sdhc_pci_match(device_t parent, cfdata_t cf, void *aux) |
192 | { |
193 | struct pci_attach_args *pa = aux; |
194 | |
195 | if (PCI_CLASS(pa->pa_class) == PCI_CLASS_SYSTEM && |
196 | PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_SYSTEM_SDHC) |
197 | return (1); |
198 | if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_RICOH && |
199 | (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_RICOH_Rx5U822 || |
200 | PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_RICOH_Rx5U823)) |
201 | return (1); |
202 | return (0); |
203 | } |
204 | |
205 | static void |
206 | sdhc_pci_attach(device_t parent, device_t self, void *aux) |
207 | { |
208 | struct sdhc_pci_softc *sc = device_private(self); |
209 | struct pci_attach_args *pa = (struct pci_attach_args *)aux; |
210 | pci_chipset_tag_t pc = pa->pa_pc; |
211 | pcitag_t tag = pa->pa_tag; |
212 | pci_intr_handle_t ih; |
213 | pcireg_t csr; |
214 | pcireg_t slotinfo; |
215 | char const *intrstr; |
216 | int nslots; |
217 | int reg; |
218 | int cnt; |
219 | bus_space_tag_t iot; |
220 | bus_space_handle_t ioh; |
221 | bus_size_t size; |
222 | uint32_t flags; |
223 | char intrbuf[PCI_INTRSTR_LEN]; |
224 | |
225 | sc->sc.sc_dev = self; |
226 | sc->sc.sc_dmat = pa->pa_dmat; |
227 | sc->sc.sc_host = NULL; |
228 | |
229 | sc->sc_pc = pc; |
230 | |
231 | pci_aprint_devinfo(pa, NULL); |
232 | |
233 | /* Some controllers needs special treatment. */ |
234 | flags = sdhc_pci_lookup_quirk_flags(pa); |
235 | if (ISSET(flags, SDHC_PCI_QUIRK_TI_HACK)) |
236 | sdhc_pci_quirk_ti_hack(pa); |
237 | if (ISSET(flags, SDHC_PCI_QUIRK_FORCE_DMA)) |
238 | SET(sc->sc.sc_flags, SDHC_FLAG_FORCE_DMA); |
239 | if (ISSET(flags, SDHC_PCI_QUIRK_NO_PWR0)) |
240 | SET(sc->sc.sc_flags, SDHC_FLAG_NO_PWR0); |
241 | if (ISSET(flags, SDHC_PCI_QUIRK_RICOH_LOWER_FREQ_HACK)) |
242 | sdhc_pci_quirk_ricoh_lower_freq_hack(pa); |
243 | if (ISSET(flags, SDHC_PCI_QUIRK_RICOH_SLOW_SDR50_HACK)) |
244 | SET(sc->sc.sc_flags, SDHC_FLAG_SLOW_SDR50); |
245 | |
246 | /* |
247 | * Map and attach all hosts supported by the host controller. |
248 | */ |
249 | slotinfo = pci_conf_read(pc, tag, SDHC_PCI_CONF_SLOT_INFO); |
250 | nslots = SDHC_PCI_NUM_SLOTS(slotinfo); |
251 | |
252 | /* Allocate an array big enough to hold all the possible hosts */ |
253 | sc->sc.sc_host = malloc(sizeof(struct sdhc_host *) * nslots, |
254 | M_DEVBUF, M_NOWAIT | M_ZERO); |
255 | if (sc->sc.sc_host == NULL) { |
256 | aprint_error_dev(self, "couldn't alloc memory\n" ); |
257 | goto err; |
258 | } |
259 | |
260 | /* Enable the device. */ |
261 | csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); |
262 | pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, |
263 | csr | PCI_COMMAND_MASTER_ENABLE); |
264 | |
265 | /* Map and establish the interrupt. */ |
266 | if (pci_intr_map(pa, &ih)) { |
267 | aprint_error_dev(self, "couldn't map interrupt\n" ); |
268 | goto err; |
269 | } |
270 | |
271 | intrstr = pci_intr_string(pc, ih, intrbuf, sizeof(intrbuf)); |
272 | sc->sc_ih = pci_intr_establish(pc, ih, IPL_SDMMC, sdhc_intr, &sc->sc); |
273 | if (sc->sc_ih == NULL) { |
274 | aprint_error_dev(self, "couldn't establish interrupt\n" ); |
275 | goto err; |
276 | } |
277 | aprint_normal_dev(self, "interrupting at %s\n" , intrstr); |
278 | |
279 | /* Enable use of DMA if supported by the interface. */ |
280 | if ((PCI_INTERFACE(pa->pa_class) == SDHC_PCI_INTERFACE_DMA)) |
281 | SET(sc->sc.sc_flags, SDHC_FLAG_USE_DMA); |
282 | |
283 | /* XXX: handle 64-bit BARs */ |
284 | cnt = 0; |
285 | for (reg = SDHC_PCI_BAR_START + SDHC_PCI_FIRST_BAR(slotinfo) * |
286 | sizeof(uint32_t); |
287 | reg < SDHC_PCI_BAR_END && nslots > 0; |
288 | reg += sizeof(uint32_t), nslots--) { |
289 | if (pci_mapreg_map(pa, reg, PCI_MAPREG_TYPE_MEM, 0, |
290 | &iot, &ioh, NULL, &size)) { |
291 | continue; |
292 | } |
293 | |
294 | cnt++; |
295 | if (sdhc_host_found(&sc->sc, iot, ioh, size) != 0) { |
296 | /* XXX: sc->sc_host leak */ |
297 | aprint_error_dev(self, |
298 | "couldn't initialize host (0x%x)\n" , reg); |
299 | } |
300 | } |
301 | if (cnt == 0) { |
302 | aprint_error_dev(self, "couldn't map register\n" ); |
303 | goto err; |
304 | } |
305 | |
306 | if (!pmf_device_register1(self, sdhc_suspend, sdhc_resume, |
307 | sdhc_shutdown)) { |
308 | aprint_error_dev(self, "couldn't establish powerhook\n" ); |
309 | } |
310 | |
311 | return; |
312 | |
313 | err: |
314 | if (sc->sc.sc_host != NULL) { |
315 | free(sc->sc.sc_host, M_DEVBUF); |
316 | sc->sc.sc_host = NULL; |
317 | } |
318 | } |
319 | |
320 | static int |
321 | sdhc_pci_detach(device_t self, int flags) |
322 | { |
323 | struct sdhc_pci_softc * const sc = device_private(self); |
324 | int rv; |
325 | |
326 | rv = sdhc_detach(&sc->sc, flags); |
327 | if (rv) |
328 | return rv; |
329 | |
330 | if (sc->sc_ih != NULL) { |
331 | pci_intr_disestablish(sc->sc_pc, sc->sc_ih); |
332 | sc->sc_ih = NULL; |
333 | } |
334 | |
335 | if (sc->sc.sc_host != NULL) { |
336 | free(sc->sc.sc_host, M_DEVBUF); |
337 | sc->sc.sc_host = NULL; |
338 | } |
339 | |
340 | return rv; |
341 | } |
342 | |
343 | static void |
344 | sdhc_pci_conf_write(struct pci_attach_args *pa, int reg, uint8_t val) |
345 | { |
346 | pcireg_t r; |
347 | |
348 | r = pci_conf_read(pa->pa_pc, pa->pa_tag, reg & ~0x3); |
349 | r &= ~(0xff << ((reg & 0x3) * 8)); |
350 | r |= (val << ((reg & 0x3) * 8)); |
351 | pci_conf_write(pa->pa_pc, pa->pa_tag, reg & ~0x3, r); |
352 | } |
353 | |
354 | /* TI specific register */ |
355 | #define SDHC_PCI_GENERAL_CTL 0x4c |
356 | #define MMC_SD_DIS 0x02 |
357 | |
358 | static void |
359 | sdhc_pci_quirk_ti_hack(struct pci_attach_args *pa) |
360 | { |
361 | pci_chipset_tag_t pc = pa->pa_pc; |
362 | pcitag_t tag; |
363 | pcireg_t id, reg; |
364 | |
365 | /* Look at func - 1 for the flash device */ |
366 | tag = pci_make_tag(pc, pa->pa_bus, pa->pa_device, pa->pa_function - 1); |
367 | id = pci_conf_read(pc, tag, PCI_ID_REG); |
368 | if (PCI_VENDOR(id) != PCI_VENDOR_TI) { |
369 | return; |
370 | } |
371 | switch (PCI_PRODUCT(id)) { |
372 | case PCI_PRODUCT_TI_PCI72111FM: |
373 | case PCI_PRODUCT_TI_PCIXX12FM: |
374 | break; |
375 | default: |
376 | return; |
377 | } |
378 | |
379 | /* |
380 | * Disable MMC/SD on the flash media controller so the |
381 | * SD host takes over. |
382 | */ |
383 | reg = pci_conf_read(pc, tag, SDHC_PCI_GENERAL_CTL); |
384 | reg |= MMC_SD_DIS; |
385 | pci_conf_write(pc, tag, SDHC_PCI_GENERAL_CTL, reg); |
386 | } |
387 | |
388 | /* Ricoh specific register */ |
389 | #define SDHC_PCI_MODE_KEY 0xf9 |
390 | #define SDHC_PCI_MODE 0x150 |
391 | #define SDHC_PCI_MODE_SD20 0x10 |
392 | #define SDHC_PCI_BASE_FREQ_KEY 0xfc |
393 | #define SDHC_PCI_BASE_FREQ 0xe1 |
394 | |
395 | /* Some RICOH controllers need to be bumped into the right mode. */ |
396 | static void |
397 | sdhc_pci_quirk_ricoh_lower_freq_hack(struct pci_attach_args *pa) |
398 | { |
399 | /* Enable SD2.0 mode. */ |
400 | sdhc_pci_conf_write(pa, SDHC_PCI_MODE_KEY, 0xfc); |
401 | sdhc_pci_conf_write(pa, SDHC_PCI_MODE, SDHC_PCI_MODE_SD20); |
402 | sdhc_pci_conf_write(pa, SDHC_PCI_MODE_KEY, 0x00); |
403 | |
404 | /* |
405 | * Some SD/MMC cards don't work with the default base |
406 | * clock frequency of 200MHz. Lower it to 50Hz. |
407 | */ |
408 | sdhc_pci_conf_write(pa, SDHC_PCI_BASE_FREQ_KEY, 0x01); |
409 | sdhc_pci_conf_write(pa, SDHC_PCI_BASE_FREQ, 50); |
410 | sdhc_pci_conf_write(pa, SDHC_PCI_BASE_FREQ_KEY, 0x00); |
411 | printf("quirked\n" ); |
412 | } |
413 | |