| 1 |
/*- |
| 2 |
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD |
| 3 |
* |
| 4 |
* Copyright (c) 2008 Alexander Motin <mav@FreeBSD.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 |
__FBSDID("$FreeBSD$"); |
| 30 |
|
| 31 |
#include "opt_mmccam.h" |
| 32 |
|
| 33 |
#include <sys/param.h> |
| 34 |
#include <sys/systm.h> |
| 35 |
#include <sys/bus.h> |
| 36 |
#include <sys/kernel.h> |
| 37 |
#include <sys/lock.h> |
| 38 |
#include <sys/module.h> |
| 39 |
#include <sys/mutex.h> |
| 40 |
#include <sys/resource.h> |
| 41 |
#include <sys/rman.h> |
| 42 |
#include <sys/sysctl.h> |
| 43 |
#include <sys/taskqueue.h> |
| 44 |
|
| 45 |
#include <dev/pci/pcireg.h> |
| 46 |
#include <dev/pci/pcivar.h> |
| 47 |
|
| 48 |
#include <machine/bus.h> |
| 49 |
#include <machine/resource.h> |
| 50 |
|
| 51 |
#include <dev/mmc/bridge.h> |
| 52 |
|
| 53 |
#include <dev/sdhci/sdhci.h> |
| 54 |
|
| 55 |
#include "mmcbr_if.h" |
| 56 |
#include "sdhci_if.h" |
| 57 |
|
| 58 |
/* |
| 59 |
* PCI registers |
| 60 |
*/ |
| 61 |
#define PCI_SDHCI_IFPIO 0x00 |
| 62 |
#define PCI_SDHCI_IFDMA 0x01 |
| 63 |
#define PCI_SDHCI_IFVENDOR 0x02 |
| 64 |
|
| 65 |
#define PCI_SLOT_INFO 0x40 /* 8 bits */ |
| 66 |
#define PCI_SLOT_INFO_SLOTS(x) (((x >> 4) & 7) + 1) |
| 67 |
#define PCI_SLOT_INFO_FIRST_BAR(x) ((x) & 7) |
| 68 |
|
| 69 |
/* |
| 70 |
* RICOH specific PCI registers |
| 71 |
*/ |
| 72 |
#define SDHC_PCI_MODE_KEY 0xf9 |
| 73 |
#define SDHC_PCI_MODE 0x150 |
| 74 |
#define SDHC_PCI_MODE_SD20 0x10 |
| 75 |
#define SDHC_PCI_BASE_FREQ_KEY 0xfc |
| 76 |
#define SDHC_PCI_BASE_FREQ 0xe1 |
| 77 |
|
| 78 |
static const struct sdhci_device { |
| 79 |
uint32_t model; |
| 80 |
uint16_t subvendor; |
| 81 |
const char *desc; |
| 82 |
u_int quirks; |
| 83 |
} sdhci_devices[] = { |
| 84 |
{ 0x08221180, 0xffff, "RICOH R5C822 SD", |
| 85 |
SDHCI_QUIRK_FORCE_DMA }, |
| 86 |
{ 0xe8221180, 0xffff, "RICOH R5CE822 SD", |
| 87 |
SDHCI_QUIRK_FORCE_DMA | |
| 88 |
SDHCI_QUIRK_LOWER_FREQUENCY }, |
| 89 |
{ 0xe8231180, 0xffff, "RICOH R5CE823 SD", |
| 90 |
SDHCI_QUIRK_LOWER_FREQUENCY }, |
| 91 |
{ 0x8034104c, 0xffff, "TI XX21/XX11 SD", |
| 92 |
SDHCI_QUIRK_FORCE_DMA }, |
| 93 |
{ 0x05501524, 0xffff, "ENE CB712 SD", |
| 94 |
SDHCI_QUIRK_BROKEN_TIMINGS }, |
| 95 |
{ 0x05511524, 0xffff, "ENE CB712 SD 2", |
| 96 |
SDHCI_QUIRK_BROKEN_TIMINGS }, |
| 97 |
{ 0x07501524, 0xffff, "ENE CB714 SD", |
| 98 |
SDHCI_QUIRK_RESET_ON_IOS | |
| 99 |
SDHCI_QUIRK_BROKEN_TIMINGS }, |
| 100 |
{ 0x07511524, 0xffff, "ENE CB714 SD 2", |
| 101 |
SDHCI_QUIRK_RESET_ON_IOS | |
| 102 |
SDHCI_QUIRK_BROKEN_TIMINGS }, |
| 103 |
{ 0x410111ab, 0xffff, "Marvell CaFe SD", |
| 104 |
SDHCI_QUIRK_INCR_TIMEOUT_CONTROL }, |
| 105 |
{ 0x2381197B, 0xffff, "JMicron JMB38X SD", |
| 106 |
SDHCI_QUIRK_32BIT_DMA_SIZE | |
| 107 |
SDHCI_QUIRK_RESET_AFTER_REQUEST }, |
| 108 |
{ 0x16bc14e4, 0xffff, "Broadcom BCM577xx SDXC/MMC Card Reader", |
| 109 |
SDHCI_QUIRK_BCM577XX_400KHZ_CLKSRC }, |
| 110 |
{ 0x0f148086, 0xffff, "Intel Bay Trail eMMC 4.5 Controller", |
| 111 |
SDHCI_QUIRK_INTEL_POWER_UP_RESET | |
| 112 |
SDHCI_QUIRK_WAIT_WHILE_BUSY | |
| 113 |
SDHCI_QUIRK_MMC_DDR52 | |
| 114 |
SDHCI_QUIRK_CAPS_BIT63_FOR_MMC_HS400 | |
| 115 |
SDHCI_QUIRK_PRESET_VALUE_BROKEN}, |
| 116 |
{ 0x0f158086, 0xffff, "Intel Bay Trail SDXC Controller", |
| 117 |
SDHCI_QUIRK_WAIT_WHILE_BUSY | |
| 118 |
SDHCI_QUIRK_PRESET_VALUE_BROKEN }, |
| 119 |
{ 0x0f508086, 0xffff, "Intel Bay Trail eMMC 4.5 Controller", |
| 120 |
SDHCI_QUIRK_INTEL_POWER_UP_RESET | |
| 121 |
SDHCI_QUIRK_WAIT_WHILE_BUSY | |
| 122 |
SDHCI_QUIRK_MMC_DDR52 | |
| 123 |
SDHCI_QUIRK_CAPS_BIT63_FOR_MMC_HS400 | |
| 124 |
SDHCI_QUIRK_PRESET_VALUE_BROKEN }, |
| 125 |
{ 0x19db8086, 0xffff, "Intel Denverton eMMC 5.0 Controller", |
| 126 |
SDHCI_QUIRK_INTEL_POWER_UP_RESET | |
| 127 |
SDHCI_QUIRK_WAIT_WHILE_BUSY | |
| 128 |
SDHCI_QUIRK_MMC_DDR52 | |
| 129 |
SDHCI_QUIRK_CAPS_BIT63_FOR_MMC_HS400 | |
| 130 |
SDHCI_QUIRK_PRESET_VALUE_BROKEN }, |
| 131 |
{ 0x22948086, 0xffff, "Intel Braswell eMMC 4.5.1 Controller", |
| 132 |
SDHCI_QUIRK_DATA_TIMEOUT_1MHZ | |
| 133 |
SDHCI_QUIRK_INTEL_POWER_UP_RESET | |
| 134 |
SDHCI_QUIRK_WAIT_WHILE_BUSY | |
| 135 |
SDHCI_QUIRK_MMC_DDR52 | |
| 136 |
SDHCI_QUIRK_CAPS_BIT63_FOR_MMC_HS400 | |
| 137 |
SDHCI_QUIRK_PRESET_VALUE_BROKEN }, |
| 138 |
{ 0x22968086, 0xffff, "Intel Braswell SDXC Controller", |
| 139 |
SDHCI_QUIRK_WAIT_WHILE_BUSY | |
| 140 |
SDHCI_QUIRK_PRESET_VALUE_BROKEN }, |
| 141 |
{ 0x5aca8086, 0xffff, "Intel Apollo Lake SDXC Controller", |
| 142 |
SDHCI_QUIRK_BROKEN_DMA | /* APL18 erratum */ |
| 143 |
SDHCI_QUIRK_WAIT_WHILE_BUSY | |
| 144 |
SDHCI_QUIRK_PRESET_VALUE_BROKEN }, |
| 145 |
{ 0x5acc8086, 0xffff, "Intel Apollo Lake eMMC 5.0 Controller", |
| 146 |
SDHCI_QUIRK_BROKEN_DMA | /* APL18 erratum */ |
| 147 |
SDHCI_QUIRK_INTEL_POWER_UP_RESET | |
| 148 |
SDHCI_QUIRK_WAIT_WHILE_BUSY | |
| 149 |
SDHCI_QUIRK_MMC_DDR52 | |
| 150 |
SDHCI_QUIRK_CAPS_BIT63_FOR_MMC_HS400 | |
| 151 |
SDHCI_QUIRK_PRESET_VALUE_BROKEN }, |
| 152 |
{ 0, 0xffff, NULL, |
| 153 |
0 } |
| 154 |
}; |
| 155 |
|
| 156 |
struct sdhci_pci_softc { |
| 157 |
u_int quirks; /* Chip specific quirks */ |
| 158 |
struct resource *irq_res; /* IRQ resource */ |
| 159 |
void *intrhand; /* Interrupt handle */ |
| 160 |
|
| 161 |
int num_slots; /* Number of slots on this controller */ |
| 162 |
struct sdhci_slot slots[6]; |
| 163 |
struct resource *mem_res[6]; /* Memory resource */ |
| 164 |
uint8_t cfg_freq; /* Saved frequency */ |
| 165 |
uint8_t cfg_mode; /* Saved mode */ |
| 166 |
}; |
| 167 |
|
| 168 |
static int sdhci_enable_msi = 1; |
| 169 |
SYSCTL_INT(_hw_sdhci, OID_AUTO, enable_msi, CTLFLAG_RDTUN, &sdhci_enable_msi, |
| 170 |
0, "Enable MSI interrupts"); |
| 171 |
|
| 172 |
static uint8_t |
| 173 |
sdhci_pci_read_1(device_t dev, struct sdhci_slot *slot __unused, bus_size_t off) |
| 174 |
{ |
| 175 |
struct sdhci_pci_softc *sc = device_get_softc(dev); |
| 176 |
|
| 177 |
bus_barrier(sc->mem_res[slot->num], 0, 0xFF, |
| 178 |
BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); |
| 179 |
return bus_read_1(sc->mem_res[slot->num], off); |
| 180 |
} |
| 181 |
|
| 182 |
static void |
| 183 |
sdhci_pci_write_1(device_t dev, struct sdhci_slot *slot __unused, |
| 184 |
bus_size_t off, uint8_t val) |
| 185 |
{ |
| 186 |
struct sdhci_pci_softc *sc = device_get_softc(dev); |
| 187 |
|
| 188 |
bus_barrier(sc->mem_res[slot->num], 0, 0xFF, |
| 189 |
BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); |
| 190 |
bus_write_1(sc->mem_res[slot->num], off, val); |
| 191 |
} |
| 192 |
|
| 193 |
static uint16_t |
| 194 |
sdhci_pci_read_2(device_t dev, struct sdhci_slot *slot __unused, bus_size_t off) |
| 195 |
{ |
| 196 |
struct sdhci_pci_softc *sc = device_get_softc(dev); |
| 197 |
|
| 198 |
bus_barrier(sc->mem_res[slot->num], 0, 0xFF, |
| 199 |
BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); |
| 200 |
return bus_read_2(sc->mem_res[slot->num], off); |
| 201 |
} |
| 202 |
|
| 203 |
static void |
| 204 |
sdhci_pci_write_2(device_t dev, struct sdhci_slot *slot __unused, |
| 205 |
bus_size_t off, uint16_t val) |
| 206 |
{ |
| 207 |
struct sdhci_pci_softc *sc = device_get_softc(dev); |
| 208 |
|
| 209 |
bus_barrier(sc->mem_res[slot->num], 0, 0xFF, |
| 210 |
BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); |
| 211 |
bus_write_2(sc->mem_res[slot->num], off, val); |
| 212 |
} |
| 213 |
|
| 214 |
static uint32_t |
| 215 |
sdhci_pci_read_4(device_t dev, struct sdhci_slot *slot __unused, bus_size_t off) |
| 216 |
{ |
| 217 |
struct sdhci_pci_softc *sc = device_get_softc(dev); |
| 218 |
|
| 219 |
bus_barrier(sc->mem_res[slot->num], 0, 0xFF, |
| 220 |
BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); |
| 221 |
return bus_read_4(sc->mem_res[slot->num], off); |
| 222 |
} |
| 223 |
|
| 224 |
static void |
| 225 |
sdhci_pci_write_4(device_t dev, struct sdhci_slot *slot __unused, |
| 226 |
bus_size_t off, uint32_t val) |
| 227 |
{ |
| 228 |
struct sdhci_pci_softc *sc = device_get_softc(dev); |
| 229 |
|
| 230 |
bus_barrier(sc->mem_res[slot->num], 0, 0xFF, |
| 231 |
BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); |
| 232 |
bus_write_4(sc->mem_res[slot->num], off, val); |
| 233 |
} |
| 234 |
|
| 235 |
static void |
| 236 |
sdhci_pci_read_multi_4(device_t dev, struct sdhci_slot *slot __unused, |
| 237 |
bus_size_t off, uint32_t *data, bus_size_t count) |
| 238 |
{ |
| 239 |
struct sdhci_pci_softc *sc = device_get_softc(dev); |
| 240 |
|
| 241 |
bus_read_multi_stream_4(sc->mem_res[slot->num], off, data, count); |
| 242 |
} |
| 243 |
|
| 244 |
static void |
| 245 |
sdhci_pci_write_multi_4(device_t dev, struct sdhci_slot *slot __unused, |
| 246 |
bus_size_t off, uint32_t *data, bus_size_t count) |
| 247 |
{ |
| 248 |
struct sdhci_pci_softc *sc = device_get_softc(dev); |
| 249 |
|
| 250 |
bus_write_multi_stream_4(sc->mem_res[slot->num], off, data, count); |
| 251 |
} |
| 252 |
|
| 253 |
static void sdhci_pci_intr(void *arg); |
| 254 |
|
| 255 |
static void |
| 256 |
sdhci_lower_frequency(device_t dev) |
| 257 |
{ |
| 258 |
struct sdhci_pci_softc *sc = device_get_softc(dev); |
| 259 |
|
| 260 |
/* |
| 261 |
* Enable SD2.0 mode. |
| 262 |
* NB: for RICOH R5CE823, this changes the PCI device ID to 0xe822. |
| 263 |
*/ |
| 264 |
pci_write_config(dev, SDHC_PCI_MODE_KEY, 0xfc, 1); |
| 265 |
sc->cfg_mode = pci_read_config(dev, SDHC_PCI_MODE, 1); |
| 266 |
pci_write_config(dev, SDHC_PCI_MODE, SDHC_PCI_MODE_SD20, 1); |
| 267 |
pci_write_config(dev, SDHC_PCI_MODE_KEY, 0x00, 1); |
| 268 |
|
| 269 |
/* |
| 270 |
* Some SD/MMC cards don't work with the default base |
| 271 |
* clock frequency of 200 MHz. Lower it to 50 MHz. |
| 272 |
*/ |
| 273 |
pci_write_config(dev, SDHC_PCI_BASE_FREQ_KEY, 0x01, 1); |
| 274 |
sc->cfg_freq = pci_read_config(dev, SDHC_PCI_BASE_FREQ, 1); |
| 275 |
pci_write_config(dev, SDHC_PCI_BASE_FREQ, 50, 1); |
| 276 |
pci_write_config(dev, SDHC_PCI_BASE_FREQ_KEY, 0x00, 1); |
| 277 |
} |
| 278 |
|
| 279 |
static void |
| 280 |
sdhci_restore_frequency(device_t dev) |
| 281 |
{ |
| 282 |
struct sdhci_pci_softc *sc = device_get_softc(dev); |
| 283 |
|
| 284 |
/* Restore mode. */ |
| 285 |
pci_write_config(dev, SDHC_PCI_MODE_KEY, 0xfc, 1); |
| 286 |
pci_write_config(dev, SDHC_PCI_MODE, sc->cfg_mode, 1); |
| 287 |
pci_write_config(dev, SDHC_PCI_MODE_KEY, 0x00, 1); |
| 288 |
|
| 289 |
/* Restore frequency. */ |
| 290 |
pci_write_config(dev, SDHC_PCI_BASE_FREQ_KEY, 0x01, 1); |
| 291 |
pci_write_config(dev, SDHC_PCI_BASE_FREQ, sc->cfg_freq, 1); |
| 292 |
pci_write_config(dev, SDHC_PCI_BASE_FREQ_KEY, 0x00, 1); |
| 293 |
} |
| 294 |
|
| 295 |
static int |
| 296 |
sdhci_pci_probe(device_t dev) |
| 297 |
{ |
| 298 |
uint32_t model; |
| 299 |
uint16_t subvendor; |
| 300 |
uint8_t class, subclass; |
| 301 |
int i, result; |
| 302 |
|
| 303 |
model = (uint32_t)pci_get_device(dev) << 16; |
| 304 |
model |= (uint32_t)pci_get_vendor(dev) & 0x0000ffff; |
| 305 |
subvendor = pci_get_subvendor(dev); |
| 306 |
class = pci_get_class(dev); |
| 307 |
subclass = pci_get_subclass(dev); |
| 308 |
|
| 309 |
result = ENXIO; |
| 310 |
for (i = 0; sdhci_devices[i].model != 0; i++) { |
| 311 |
if (sdhci_devices[i].model == model && |
| 312 |
(sdhci_devices[i].subvendor == 0xffff || |
| 313 |
sdhci_devices[i].subvendor == subvendor)) { |
| 314 |
device_set_desc(dev, sdhci_devices[i].desc); |
| 315 |
result = BUS_PROBE_DEFAULT; |
| 316 |
break; |
| 317 |
} |
| 318 |
} |
| 319 |
if (result == ENXIO && class == PCIC_BASEPERIPH && |
| 320 |
subclass == PCIS_BASEPERIPH_SDHC) { |
| 321 |
device_set_desc(dev, "Generic SD HCI"); |
| 322 |
result = BUS_PROBE_GENERIC; |
| 323 |
} |
| 324 |
|
| 325 |
return (result); |
| 326 |
} |
| 327 |
|
| 328 |
static int |
| 329 |
sdhci_pci_attach(device_t dev) |
| 330 |
{ |
| 331 |
struct sdhci_pci_softc *sc = device_get_softc(dev); |
| 332 |
struct sdhci_slot *slot; |
| 333 |
uint32_t model; |
| 334 |
uint16_t subvendor; |
| 335 |
int bar, err, rid, slots, i; |
| 336 |
|
| 337 |
model = (uint32_t)pci_get_device(dev) << 16; |
| 338 |
model |= (uint32_t)pci_get_vendor(dev) & 0x0000ffff; |
| 339 |
subvendor = pci_get_subvendor(dev); |
| 340 |
/* Apply chip specific quirks. */ |
| 341 |
for (i = 0; sdhci_devices[i].model != 0; i++) { |
| 342 |
if (sdhci_devices[i].model == model && |
| 343 |
(sdhci_devices[i].subvendor == 0xffff || |
| 344 |
sdhci_devices[i].subvendor == subvendor)) { |
| 345 |
sc->quirks = sdhci_devices[i].quirks; |
| 346 |
break; |
| 347 |
} |
| 348 |
} |
| 349 |
sc->quirks &= ~sdhci_quirk_clear; |
| 350 |
sc->quirks |= sdhci_quirk_set; |
| 351 |
|
| 352 |
/* Some controllers need to be bumped into the right mode. */ |
| 353 |
if (sc->quirks & SDHCI_QUIRK_LOWER_FREQUENCY) |
| 354 |
sdhci_lower_frequency(dev); |
| 355 |
/* Read slots info from PCI registers. */ |
| 356 |
slots = pci_read_config(dev, PCI_SLOT_INFO, 1); |
| 357 |
bar = PCI_SLOT_INFO_FIRST_BAR(slots); |
| 358 |
slots = PCI_SLOT_INFO_SLOTS(slots); |
| 359 |
if (slots > 6 || bar > 5) { |
| 360 |
device_printf(dev, "Incorrect slots information (%d, %d).\n", |
| 361 |
slots, bar); |
| 362 |
return (EINVAL); |
| 363 |
} |
| 364 |
/* Allocate IRQ. */ |
| 365 |
i = 1; |
| 366 |
rid = 0; |
| 367 |
if (sdhci_enable_msi != 0 && pci_alloc_msi(dev, &i) == 0) |
| 368 |
rid = 1; |
| 369 |
sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, |
| 370 |
RF_ACTIVE | (rid != 0 ? 0 : RF_SHAREABLE)); |
| 371 |
if (sc->irq_res == NULL) { |
| 372 |
device_printf(dev, "Can't allocate IRQ\n"); |
| 373 |
pci_release_msi(dev); |
| 374 |
return (ENOMEM); |
| 375 |
} |
| 376 |
/* Scan all slots. */ |
| 377 |
for (i = 0; i < slots; i++) { |
| 378 |
slot = &sc->slots[sc->num_slots]; |
| 379 |
|
| 380 |
/* Allocate memory. */ |
| 381 |
rid = PCIR_BAR(bar + i); |
| 382 |
sc->mem_res[i] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, |
| 383 |
&rid, RF_ACTIVE); |
| 384 |
if (sc->mem_res[i] == NULL) { |
| 385 |
device_printf(dev, |
| 386 |
"Can't allocate memory for slot %d\n", i); |
| 387 |
continue; |
| 388 |
} |
| 389 |
|
| 390 |
slot->quirks = sc->quirks; |
| 391 |
|
| 392 |
if (sdhci_init_slot(dev, slot, i) != 0) |
| 393 |
continue; |
| 394 |
|
| 395 |
sc->num_slots++; |
| 396 |
} |
| 397 |
device_printf(dev, "%d slot(s) allocated\n", sc->num_slots); |
| 398 |
/* Activate the interrupt */ |
| 399 |
err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, |
| 400 |
NULL, sdhci_pci_intr, sc, &sc->intrhand); |
| 401 |
if (err) |
| 402 |
device_printf(dev, "Can't setup IRQ\n"); |
| 403 |
pci_enable_busmaster(dev); |
| 404 |
/* Process cards detection. */ |
| 405 |
for (i = 0; i < sc->num_slots; i++) { |
| 406 |
sdhci_start_slot(&sc->slots[i]); |
| 407 |
} |
| 408 |
|
| 409 |
return (0); |
| 410 |
} |
| 411 |
|
| 412 |
static int |
| 413 |
sdhci_pci_detach(device_t dev) |
| 414 |
{ |
| 415 |
struct sdhci_pci_softc *sc = device_get_softc(dev); |
| 416 |
int i; |
| 417 |
|
| 418 |
bus_teardown_intr(dev, sc->irq_res, sc->intrhand); |
| 419 |
bus_release_resource(dev, SYS_RES_IRQ, |
| 420 |
rman_get_rid(sc->irq_res), sc->irq_res); |
| 421 |
pci_release_msi(dev); |
| 422 |
|
| 423 |
for (i = 0; i < sc->num_slots; i++) { |
| 424 |
sdhci_cleanup_slot(&sc->slots[i]); |
| 425 |
bus_release_resource(dev, SYS_RES_MEMORY, |
| 426 |
rman_get_rid(sc->mem_res[i]), sc->mem_res[i]); |
| 427 |
} |
| 428 |
if (sc->quirks & SDHCI_QUIRK_LOWER_FREQUENCY) |
| 429 |
sdhci_restore_frequency(dev); |
| 430 |
return (0); |
| 431 |
} |
| 432 |
|
| 433 |
static int |
| 434 |
sdhci_pci_shutdown(device_t dev) |
| 435 |
{ |
| 436 |
struct sdhci_pci_softc *sc = device_get_softc(dev); |
| 437 |
|
| 438 |
if (sc->quirks & SDHCI_QUIRK_LOWER_FREQUENCY) |
| 439 |
sdhci_restore_frequency(dev); |
| 440 |
return (0); |
| 441 |
} |
| 442 |
|
| 443 |
static int |
| 444 |
sdhci_pci_suspend(device_t dev) |
| 445 |
{ |
| 446 |
struct sdhci_pci_softc *sc = device_get_softc(dev); |
| 447 |
int i, err; |
| 448 |
|
| 449 |
err = bus_generic_suspend(dev); |
| 450 |
if (err) |
| 451 |
return (err); |
| 452 |
for (i = 0; i < sc->num_slots; i++) |
| 453 |
sdhci_generic_suspend(&sc->slots[i]); |
| 454 |
return (0); |
| 455 |
} |
| 456 |
|
| 457 |
static int |
| 458 |
sdhci_pci_resume(device_t dev) |
| 459 |
{ |
| 460 |
struct sdhci_pci_softc *sc = device_get_softc(dev); |
| 461 |
int i, err; |
| 462 |
|
| 463 |
for (i = 0; i < sc->num_slots; i++) |
| 464 |
sdhci_generic_resume(&sc->slots[i]); |
| 465 |
err = bus_generic_resume(dev); |
| 466 |
if (err) |
| 467 |
return (err); |
| 468 |
if (sc->quirks & SDHCI_QUIRK_LOWER_FREQUENCY) |
| 469 |
sdhci_lower_frequency(dev); |
| 470 |
return (0); |
| 471 |
} |
| 472 |
|
| 473 |
static void |
| 474 |
sdhci_pci_intr(void *arg) |
| 475 |
{ |
| 476 |
struct sdhci_pci_softc *sc = (struct sdhci_pci_softc *)arg; |
| 477 |
int i; |
| 478 |
|
| 479 |
for (i = 0; i < sc->num_slots; i++) |
| 480 |
sdhci_generic_intr(&sc->slots[i]); |
| 481 |
} |
| 482 |
|
| 483 |
static device_method_t sdhci_methods[] = { |
| 484 |
/* device_if */ |
| 485 |
DEVMETHOD(device_probe, sdhci_pci_probe), |
| 486 |
DEVMETHOD(device_attach, sdhci_pci_attach), |
| 487 |
DEVMETHOD(device_detach, sdhci_pci_detach), |
| 488 |
DEVMETHOD(device_shutdown, sdhci_pci_shutdown), |
| 489 |
DEVMETHOD(device_suspend, sdhci_pci_suspend), |
| 490 |
DEVMETHOD(device_resume, sdhci_pci_resume), |
| 491 |
|
| 492 |
/* Bus interface */ |
| 493 |
DEVMETHOD(bus_read_ivar, sdhci_generic_read_ivar), |
| 494 |
DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar), |
| 495 |
|
| 496 |
/* mmcbr_if */ |
| 497 |
DEVMETHOD(mmcbr_update_ios, sdhci_generic_update_ios), |
| 498 |
DEVMETHOD(mmcbr_switch_vccq, sdhci_generic_switch_vccq), |
| 499 |
DEVMETHOD(mmcbr_tune, sdhci_generic_tune), |
| 500 |
DEVMETHOD(mmcbr_retune, sdhci_generic_retune), |
| 501 |
DEVMETHOD(mmcbr_request, sdhci_generic_request), |
| 502 |
DEVMETHOD(mmcbr_get_ro, sdhci_generic_get_ro), |
| 503 |
DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host), |
| 504 |
DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host), |
| 505 |
|
| 506 |
/* SDHCI accessors */ |
| 507 |
DEVMETHOD(sdhci_read_1, sdhci_pci_read_1), |
| 508 |
DEVMETHOD(sdhci_read_2, sdhci_pci_read_2), |
| 509 |
DEVMETHOD(sdhci_read_4, sdhci_pci_read_4), |
| 510 |
DEVMETHOD(sdhci_read_multi_4, sdhci_pci_read_multi_4), |
| 511 |
DEVMETHOD(sdhci_write_1, sdhci_pci_write_1), |
| 512 |
DEVMETHOD(sdhci_write_2, sdhci_pci_write_2), |
| 513 |
DEVMETHOD(sdhci_write_4, sdhci_pci_write_4), |
| 514 |
DEVMETHOD(sdhci_write_multi_4, sdhci_pci_write_multi_4), |
| 515 |
DEVMETHOD(sdhci_set_uhs_timing, sdhci_generic_set_uhs_timing), |
| 516 |
|
| 517 |
DEVMETHOD_END |
| 518 |
}; |
| 519 |
|
| 520 |
static driver_t sdhci_pci_driver = { |
| 521 |
"sdhci_pci", |
| 522 |
sdhci_methods, |
| 523 |
sizeof(struct sdhci_pci_softc), |
| 524 |
}; |
| 525 |
static devclass_t sdhci_pci_devclass; |
| 526 |
|
| 527 |
DRIVER_MODULE(sdhci_pci, pci, sdhci_pci_driver, sdhci_pci_devclass, NULL, |
| 528 |
NULL); |
| 529 |
MODULE_DEPEND(sdhci_pci, sdhci, 1, 1, 1); |
| 530 |
|
| 531 |
#ifndef MMCCAM |
| 532 |
MMC_DECLARE_BRIDGE(sdhci_pci); |
| 533 |
#endif |