1d6b3aaf8SOleksandr Tymoshenko /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3718cf2ccSPedro F. Giffuni * 4d6b3aaf8SOleksandr Tymoshenko * Copyright (c) 2008 Alexander Motin <mav@FreeBSD.org> 5d6b3aaf8SOleksandr Tymoshenko * All rights reserved. 6d6b3aaf8SOleksandr Tymoshenko * 7d6b3aaf8SOleksandr Tymoshenko * Redistribution and use in source and binary forms, with or without 8d6b3aaf8SOleksandr Tymoshenko * modification, are permitted provided that the following conditions 9d6b3aaf8SOleksandr Tymoshenko * are met: 10d6b3aaf8SOleksandr Tymoshenko * 1. Redistributions of source code must retain the above copyright 11d6b3aaf8SOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer. 12d6b3aaf8SOleksandr Tymoshenko * 2. Redistributions in binary form must reproduce the above copyright 13d6b3aaf8SOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer in the 14d6b3aaf8SOleksandr Tymoshenko * documentation and/or other materials provided with the distribution. 15d6b3aaf8SOleksandr Tymoshenko * 16d6b3aaf8SOleksandr Tymoshenko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17d6b3aaf8SOleksandr Tymoshenko * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18d6b3aaf8SOleksandr Tymoshenko * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19d6b3aaf8SOleksandr Tymoshenko * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20d6b3aaf8SOleksandr Tymoshenko * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21d6b3aaf8SOleksandr Tymoshenko * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22d6b3aaf8SOleksandr Tymoshenko * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23d6b3aaf8SOleksandr Tymoshenko * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24d6b3aaf8SOleksandr Tymoshenko * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25d6b3aaf8SOleksandr Tymoshenko * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26d6b3aaf8SOleksandr Tymoshenko */ 27d6b3aaf8SOleksandr Tymoshenko 28d6b3aaf8SOleksandr Tymoshenko #include <sys/cdefs.h> 29d6b3aaf8SOleksandr Tymoshenko __FBSDID("$FreeBSD$"); 30d6b3aaf8SOleksandr Tymoshenko 31a94a63f0SWarner Losh #include "opt_mmccam.h" 32a94a63f0SWarner Losh 33d6b3aaf8SOleksandr Tymoshenko #include <sys/param.h> 34d6b3aaf8SOleksandr Tymoshenko #include <sys/systm.h> 35d6b3aaf8SOleksandr Tymoshenko #include <sys/bus.h> 36d6b3aaf8SOleksandr Tymoshenko #include <sys/kernel.h> 37d6b3aaf8SOleksandr Tymoshenko #include <sys/lock.h> 38d6b3aaf8SOleksandr Tymoshenko #include <sys/module.h> 39d6b3aaf8SOleksandr Tymoshenko #include <sys/mutex.h> 40d6b3aaf8SOleksandr Tymoshenko #include <sys/resource.h> 41d6b3aaf8SOleksandr Tymoshenko #include <sys/rman.h> 42d6b3aaf8SOleksandr Tymoshenko #include <sys/sysctl.h> 43d6b3aaf8SOleksandr Tymoshenko #include <sys/taskqueue.h> 44d6b3aaf8SOleksandr Tymoshenko 45d6b3aaf8SOleksandr Tymoshenko #include <dev/pci/pcireg.h> 46d6b3aaf8SOleksandr Tymoshenko #include <dev/pci/pcivar.h> 47d6b3aaf8SOleksandr Tymoshenko 48d6b3aaf8SOleksandr Tymoshenko #include <machine/bus.h> 49d6b3aaf8SOleksandr Tymoshenko #include <machine/resource.h> 50d6b3aaf8SOleksandr Tymoshenko 51d6b3aaf8SOleksandr Tymoshenko #include <dev/mmc/bridge.h> 52d6b3aaf8SOleksandr Tymoshenko 53b440e965SMarius Strobl #include <dev/sdhci/sdhci.h> 54b440e965SMarius Strobl 55d6b3aaf8SOleksandr Tymoshenko #include "mmcbr_if.h" 56d6b3aaf8SOleksandr Tymoshenko #include "sdhci_if.h" 57d6b3aaf8SOleksandr Tymoshenko 58d6b3aaf8SOleksandr Tymoshenko /* 59d6b3aaf8SOleksandr Tymoshenko * PCI registers 60d6b3aaf8SOleksandr Tymoshenko */ 61d6b3aaf8SOleksandr Tymoshenko #define PCI_SDHCI_IFPIO 0x00 62d6b3aaf8SOleksandr Tymoshenko #define PCI_SDHCI_IFDMA 0x01 63d6b3aaf8SOleksandr Tymoshenko #define PCI_SDHCI_IFVENDOR 0x02 64d6b3aaf8SOleksandr Tymoshenko 65d6b3aaf8SOleksandr Tymoshenko #define PCI_SLOT_INFO 0x40 /* 8 bits */ 66d6b3aaf8SOleksandr Tymoshenko #define PCI_SLOT_INFO_SLOTS(x) (((x >> 4) & 7) + 1) 67d6b3aaf8SOleksandr Tymoshenko #define PCI_SLOT_INFO_FIRST_BAR(x) ((x) & 7) 68d6b3aaf8SOleksandr Tymoshenko 69d6b3aaf8SOleksandr Tymoshenko /* 70d6b3aaf8SOleksandr Tymoshenko * RICOH specific PCI registers 71d6b3aaf8SOleksandr Tymoshenko */ 72d6b3aaf8SOleksandr Tymoshenko #define SDHC_PCI_MODE_KEY 0xf9 73d6b3aaf8SOleksandr Tymoshenko #define SDHC_PCI_MODE 0x150 74d6b3aaf8SOleksandr Tymoshenko #define SDHC_PCI_MODE_SD20 0x10 75d6b3aaf8SOleksandr Tymoshenko #define SDHC_PCI_BASE_FREQ_KEY 0xfc 76d6b3aaf8SOleksandr Tymoshenko #define SDHC_PCI_BASE_FREQ 0xe1 77d6b3aaf8SOleksandr Tymoshenko 78d6b3aaf8SOleksandr Tymoshenko static const struct sdhci_device { 79d6b3aaf8SOleksandr Tymoshenko uint32_t model; 80d6b3aaf8SOleksandr Tymoshenko uint16_t subvendor; 81f0d2731dSMarius Strobl const char *desc; 82d6b3aaf8SOleksandr Tymoshenko u_int quirks; 83d6b3aaf8SOleksandr Tymoshenko } sdhci_devices[] = { 84d6b3aaf8SOleksandr Tymoshenko { 0x08221180, 0xffff, "RICOH R5C822 SD", 85d6b3aaf8SOleksandr Tymoshenko SDHCI_QUIRK_FORCE_DMA }, 86c2262647SMarius Strobl { 0xe8221180, 0xffff, "RICOH R5CE822 SD", 87c2262647SMarius Strobl SDHCI_QUIRK_FORCE_DMA | 88c2262647SMarius Strobl SDHCI_QUIRK_LOWER_FREQUENCY }, 89d6b3aaf8SOleksandr Tymoshenko { 0xe8231180, 0xffff, "RICOH R5CE823 SD", 90d6b3aaf8SOleksandr Tymoshenko SDHCI_QUIRK_LOWER_FREQUENCY }, 91d6b3aaf8SOleksandr Tymoshenko { 0x8034104c, 0xffff, "TI XX21/XX11 SD", 92d6b3aaf8SOleksandr Tymoshenko SDHCI_QUIRK_FORCE_DMA }, 93d6b3aaf8SOleksandr Tymoshenko { 0x05501524, 0xffff, "ENE CB712 SD", 94d6b3aaf8SOleksandr Tymoshenko SDHCI_QUIRK_BROKEN_TIMINGS }, 95d6b3aaf8SOleksandr Tymoshenko { 0x05511524, 0xffff, "ENE CB712 SD 2", 96d6b3aaf8SOleksandr Tymoshenko SDHCI_QUIRK_BROKEN_TIMINGS }, 97d6b3aaf8SOleksandr Tymoshenko { 0x07501524, 0xffff, "ENE CB714 SD", 98d6b3aaf8SOleksandr Tymoshenko SDHCI_QUIRK_RESET_ON_IOS | 99d6b3aaf8SOleksandr Tymoshenko SDHCI_QUIRK_BROKEN_TIMINGS }, 100d6b3aaf8SOleksandr Tymoshenko { 0x07511524, 0xffff, "ENE CB714 SD 2", 101d6b3aaf8SOleksandr Tymoshenko SDHCI_QUIRK_RESET_ON_IOS | 102d6b3aaf8SOleksandr Tymoshenko SDHCI_QUIRK_BROKEN_TIMINGS }, 103d6b3aaf8SOleksandr Tymoshenko { 0x410111ab, 0xffff, "Marvell CaFe SD", 104d6b3aaf8SOleksandr Tymoshenko SDHCI_QUIRK_INCR_TIMEOUT_CONTROL }, 105d6b3aaf8SOleksandr Tymoshenko { 0x2381197B, 0xffff, "JMicron JMB38X SD", 106d6b3aaf8SOleksandr Tymoshenko SDHCI_QUIRK_32BIT_DMA_SIZE | 107d6b3aaf8SOleksandr Tymoshenko SDHCI_QUIRK_RESET_AFTER_REQUEST }, 10893efdc63SAdrian Chadd { 0x16bc14e4, 0xffff, "Broadcom BCM577xx SDXC/MMC Card Reader", 10993efdc63SAdrian Chadd SDHCI_QUIRK_BCM577XX_400KHZ_CLKSRC }, 110a2832f9fSMarius Strobl { 0x0f148086, 0xffff, "Intel Bay Trail eMMC 4.5 Controller", 111*aafdd1d6SMarius Strobl /* DDR52 is supported but affected by the VLI54 erratum */ 11272dec079SMarius Strobl SDHCI_QUIRK_INTEL_POWER_UP_RESET | 1130f34084fSMarius Strobl SDHCI_QUIRK_WAIT_WHILE_BUSY | 1140f34084fSMarius Strobl SDHCI_QUIRK_CAPS_BIT63_FOR_MMC_HS400 | 1150f34084fSMarius Strobl SDHCI_QUIRK_PRESET_VALUE_BROKEN}, 11672dec079SMarius Strobl { 0x0f158086, 0xffff, "Intel Bay Trail SDXC Controller", 1170f34084fSMarius Strobl SDHCI_QUIRK_WAIT_WHILE_BUSY | 1180f34084fSMarius Strobl SDHCI_QUIRK_PRESET_VALUE_BROKEN }, 119a2832f9fSMarius Strobl { 0x0f508086, 0xffff, "Intel Bay Trail eMMC 4.5 Controller", 120*aafdd1d6SMarius Strobl /* DDR52 is supported but affected by the VLI54 erratum */ 12172dec079SMarius Strobl SDHCI_QUIRK_INTEL_POWER_UP_RESET | 1220f34084fSMarius Strobl SDHCI_QUIRK_WAIT_WHILE_BUSY | 1230f34084fSMarius Strobl SDHCI_QUIRK_CAPS_BIT63_FOR_MMC_HS400 | 1240f34084fSMarius Strobl SDHCI_QUIRK_PRESET_VALUE_BROKEN }, 12554ef33c2SMarius Strobl { 0x19db8086, 0xffff, "Intel Denverton eMMC 5.0 Controller", 12654ef33c2SMarius Strobl SDHCI_QUIRK_INTEL_POWER_UP_RESET | 12754ef33c2SMarius Strobl SDHCI_QUIRK_WAIT_WHILE_BUSY | 12854ef33c2SMarius Strobl SDHCI_QUIRK_MMC_DDR52 | 12954ef33c2SMarius Strobl SDHCI_QUIRK_CAPS_BIT63_FOR_MMC_HS400 | 13054ef33c2SMarius Strobl SDHCI_QUIRK_PRESET_VALUE_BROKEN }, 131a2832f9fSMarius Strobl { 0x22948086, 0xffff, "Intel Braswell eMMC 4.5.1 Controller", 132a2832f9fSMarius Strobl SDHCI_QUIRK_DATA_TIMEOUT_1MHZ | 13372dec079SMarius Strobl SDHCI_QUIRK_INTEL_POWER_UP_RESET | 1340f34084fSMarius Strobl SDHCI_QUIRK_WAIT_WHILE_BUSY | 1350f34084fSMarius Strobl SDHCI_QUIRK_MMC_DDR52 | 1360f34084fSMarius Strobl SDHCI_QUIRK_CAPS_BIT63_FOR_MMC_HS400 | 1370f34084fSMarius Strobl SDHCI_QUIRK_PRESET_VALUE_BROKEN }, 13872dec079SMarius Strobl { 0x22968086, 0xffff, "Intel Braswell SDXC Controller", 1390f34084fSMarius Strobl SDHCI_QUIRK_WAIT_WHILE_BUSY | 1400f34084fSMarius Strobl SDHCI_QUIRK_PRESET_VALUE_BROKEN }, 14172dec079SMarius Strobl { 0x5aca8086, 0xffff, "Intel Apollo Lake SDXC Controller", 142806202b5SMarius Strobl SDHCI_QUIRK_BROKEN_DMA | /* APL18 erratum */ 1430f34084fSMarius Strobl SDHCI_QUIRK_WAIT_WHILE_BUSY | 1440f34084fSMarius Strobl SDHCI_QUIRK_PRESET_VALUE_BROKEN }, 145a2832f9fSMarius Strobl { 0x5acc8086, 0xffff, "Intel Apollo Lake eMMC 5.0 Controller", 146806202b5SMarius Strobl SDHCI_QUIRK_BROKEN_DMA | /* APL18 erratum */ 14772dec079SMarius Strobl SDHCI_QUIRK_INTEL_POWER_UP_RESET | 1480f34084fSMarius Strobl SDHCI_QUIRK_WAIT_WHILE_BUSY | 1490f34084fSMarius Strobl SDHCI_QUIRK_MMC_DDR52 | 1500f34084fSMarius Strobl SDHCI_QUIRK_CAPS_BIT63_FOR_MMC_HS400 | 1510f34084fSMarius Strobl SDHCI_QUIRK_PRESET_VALUE_BROKEN }, 152d6b3aaf8SOleksandr Tymoshenko { 0, 0xffff, NULL, 153d6b3aaf8SOleksandr Tymoshenko 0 } 154d6b3aaf8SOleksandr Tymoshenko }; 155d6b3aaf8SOleksandr Tymoshenko 156d6b3aaf8SOleksandr Tymoshenko struct sdhci_pci_softc { 157d6b3aaf8SOleksandr Tymoshenko u_int quirks; /* Chip specific quirks */ 158d6b3aaf8SOleksandr Tymoshenko struct resource *irq_res; /* IRQ resource */ 159d6b3aaf8SOleksandr Tymoshenko void *intrhand; /* Interrupt handle */ 160d6b3aaf8SOleksandr Tymoshenko 161d6b3aaf8SOleksandr Tymoshenko int num_slots; /* Number of slots on this controller */ 162d6b3aaf8SOleksandr Tymoshenko struct sdhci_slot slots[6]; 163d6b3aaf8SOleksandr Tymoshenko struct resource *mem_res[6]; /* Memory resource */ 164a2832f9fSMarius Strobl uint8_t cfg_freq; /* Saved frequency */ 165a2832f9fSMarius Strobl uint8_t cfg_mode; /* Saved mode */ 166d6b3aaf8SOleksandr Tymoshenko }; 167d6b3aaf8SOleksandr Tymoshenko 168f0d2731dSMarius Strobl static int sdhci_enable_msi = 1; 169f0d2731dSMarius Strobl SYSCTL_INT(_hw_sdhci, OID_AUTO, enable_msi, CTLFLAG_RDTUN, &sdhci_enable_msi, 170f0d2731dSMarius Strobl 0, "Enable MSI interrupts"); 171d6b3aaf8SOleksandr Tymoshenko 172d6b3aaf8SOleksandr Tymoshenko static uint8_t 173c11bbc7dSMarius Strobl sdhci_pci_read_1(device_t dev, struct sdhci_slot *slot __unused, bus_size_t off) 174d6b3aaf8SOleksandr Tymoshenko { 175d6b3aaf8SOleksandr Tymoshenko struct sdhci_pci_softc *sc = device_get_softc(dev); 176d6b3aaf8SOleksandr Tymoshenko 177d6b3aaf8SOleksandr Tymoshenko bus_barrier(sc->mem_res[slot->num], 0, 0xFF, 178d6b3aaf8SOleksandr Tymoshenko BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 179d6b3aaf8SOleksandr Tymoshenko return bus_read_1(sc->mem_res[slot->num], off); 180d6b3aaf8SOleksandr Tymoshenko } 181d6b3aaf8SOleksandr Tymoshenko 182d6b3aaf8SOleksandr Tymoshenko static void 183c11bbc7dSMarius Strobl sdhci_pci_write_1(device_t dev, struct sdhci_slot *slot __unused, 184c11bbc7dSMarius Strobl bus_size_t off, uint8_t val) 185d6b3aaf8SOleksandr Tymoshenko { 186d6b3aaf8SOleksandr Tymoshenko struct sdhci_pci_softc *sc = device_get_softc(dev); 187d6b3aaf8SOleksandr Tymoshenko 188d6b3aaf8SOleksandr Tymoshenko bus_barrier(sc->mem_res[slot->num], 0, 0xFF, 189d6b3aaf8SOleksandr Tymoshenko BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 190d6b3aaf8SOleksandr Tymoshenko bus_write_1(sc->mem_res[slot->num], off, val); 191d6b3aaf8SOleksandr Tymoshenko } 192d6b3aaf8SOleksandr Tymoshenko 193d6b3aaf8SOleksandr Tymoshenko static uint16_t 194c11bbc7dSMarius Strobl sdhci_pci_read_2(device_t dev, struct sdhci_slot *slot __unused, bus_size_t off) 195d6b3aaf8SOleksandr Tymoshenko { 196d6b3aaf8SOleksandr Tymoshenko struct sdhci_pci_softc *sc = device_get_softc(dev); 197d6b3aaf8SOleksandr Tymoshenko 198d6b3aaf8SOleksandr Tymoshenko bus_barrier(sc->mem_res[slot->num], 0, 0xFF, 199d6b3aaf8SOleksandr Tymoshenko BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 200d6b3aaf8SOleksandr Tymoshenko return bus_read_2(sc->mem_res[slot->num], off); 201d6b3aaf8SOleksandr Tymoshenko } 202d6b3aaf8SOleksandr Tymoshenko 203d6b3aaf8SOleksandr Tymoshenko static void 204c11bbc7dSMarius Strobl sdhci_pci_write_2(device_t dev, struct sdhci_slot *slot __unused, 205c11bbc7dSMarius Strobl bus_size_t off, uint16_t val) 206d6b3aaf8SOleksandr Tymoshenko { 207d6b3aaf8SOleksandr Tymoshenko struct sdhci_pci_softc *sc = device_get_softc(dev); 208d6b3aaf8SOleksandr Tymoshenko 209d6b3aaf8SOleksandr Tymoshenko bus_barrier(sc->mem_res[slot->num], 0, 0xFF, 210d6b3aaf8SOleksandr Tymoshenko BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 211d6b3aaf8SOleksandr Tymoshenko bus_write_2(sc->mem_res[slot->num], off, val); 212d6b3aaf8SOleksandr Tymoshenko } 213d6b3aaf8SOleksandr Tymoshenko 214d6b3aaf8SOleksandr Tymoshenko static uint32_t 215c11bbc7dSMarius Strobl sdhci_pci_read_4(device_t dev, struct sdhci_slot *slot __unused, bus_size_t off) 216d6b3aaf8SOleksandr Tymoshenko { 217d6b3aaf8SOleksandr Tymoshenko struct sdhci_pci_softc *sc = device_get_softc(dev); 218d6b3aaf8SOleksandr Tymoshenko 219d6b3aaf8SOleksandr Tymoshenko bus_barrier(sc->mem_res[slot->num], 0, 0xFF, 220d6b3aaf8SOleksandr Tymoshenko BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 221d6b3aaf8SOleksandr Tymoshenko return bus_read_4(sc->mem_res[slot->num], off); 222d6b3aaf8SOleksandr Tymoshenko } 223d6b3aaf8SOleksandr Tymoshenko 224d6b3aaf8SOleksandr Tymoshenko static void 225c11bbc7dSMarius Strobl sdhci_pci_write_4(device_t dev, struct sdhci_slot *slot __unused, 226c11bbc7dSMarius Strobl bus_size_t off, uint32_t val) 227d6b3aaf8SOleksandr Tymoshenko { 228d6b3aaf8SOleksandr Tymoshenko struct sdhci_pci_softc *sc = device_get_softc(dev); 229d6b3aaf8SOleksandr Tymoshenko 230d6b3aaf8SOleksandr Tymoshenko bus_barrier(sc->mem_res[slot->num], 0, 0xFF, 231d6b3aaf8SOleksandr Tymoshenko BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 232d6b3aaf8SOleksandr Tymoshenko bus_write_4(sc->mem_res[slot->num], off, val); 233d6b3aaf8SOleksandr Tymoshenko } 234d6b3aaf8SOleksandr Tymoshenko 235d6b3aaf8SOleksandr Tymoshenko static void 236c11bbc7dSMarius Strobl sdhci_pci_read_multi_4(device_t dev, struct sdhci_slot *slot __unused, 237d6b3aaf8SOleksandr Tymoshenko bus_size_t off, uint32_t *data, bus_size_t count) 238d6b3aaf8SOleksandr Tymoshenko { 239d6b3aaf8SOleksandr Tymoshenko struct sdhci_pci_softc *sc = device_get_softc(dev); 240d6b3aaf8SOleksandr Tymoshenko 241d6b3aaf8SOleksandr Tymoshenko bus_read_multi_stream_4(sc->mem_res[slot->num], off, data, count); 242d6b3aaf8SOleksandr Tymoshenko } 243d6b3aaf8SOleksandr Tymoshenko 244d6b3aaf8SOleksandr Tymoshenko static void 245c11bbc7dSMarius Strobl sdhci_pci_write_multi_4(device_t dev, struct sdhci_slot *slot __unused, 246d6b3aaf8SOleksandr Tymoshenko bus_size_t off, uint32_t *data, bus_size_t count) 247d6b3aaf8SOleksandr Tymoshenko { 248d6b3aaf8SOleksandr Tymoshenko struct sdhci_pci_softc *sc = device_get_softc(dev); 249d6b3aaf8SOleksandr Tymoshenko 250d6b3aaf8SOleksandr Tymoshenko bus_write_multi_stream_4(sc->mem_res[slot->num], off, data, count); 251d6b3aaf8SOleksandr Tymoshenko } 252d6b3aaf8SOleksandr Tymoshenko 253d6b3aaf8SOleksandr Tymoshenko static void sdhci_pci_intr(void *arg); 254d6b3aaf8SOleksandr Tymoshenko 255d6b3aaf8SOleksandr Tymoshenko static void 256d6b3aaf8SOleksandr Tymoshenko sdhci_lower_frequency(device_t dev) 257d6b3aaf8SOleksandr Tymoshenko { 258c2262647SMarius Strobl struct sdhci_pci_softc *sc = device_get_softc(dev); 259d6b3aaf8SOleksandr Tymoshenko 260c2262647SMarius Strobl /* 261c2262647SMarius Strobl * Enable SD2.0 mode. 262c2262647SMarius Strobl * NB: for RICOH R5CE823, this changes the PCI device ID to 0xe822. 263c2262647SMarius Strobl */ 264d6b3aaf8SOleksandr Tymoshenko pci_write_config(dev, SDHC_PCI_MODE_KEY, 0xfc, 1); 265c2262647SMarius Strobl sc->cfg_mode = pci_read_config(dev, SDHC_PCI_MODE, 1); 266d6b3aaf8SOleksandr Tymoshenko pci_write_config(dev, SDHC_PCI_MODE, SDHC_PCI_MODE_SD20, 1); 267d6b3aaf8SOleksandr Tymoshenko pci_write_config(dev, SDHC_PCI_MODE_KEY, 0x00, 1); 268d6b3aaf8SOleksandr Tymoshenko 269d6b3aaf8SOleksandr Tymoshenko /* 270d6b3aaf8SOleksandr Tymoshenko * Some SD/MMC cards don't work with the default base 271c2262647SMarius Strobl * clock frequency of 200 MHz. Lower it to 50 MHz. 272d6b3aaf8SOleksandr Tymoshenko */ 273d6b3aaf8SOleksandr Tymoshenko pci_write_config(dev, SDHC_PCI_BASE_FREQ_KEY, 0x01, 1); 274c2262647SMarius Strobl sc->cfg_freq = pci_read_config(dev, SDHC_PCI_BASE_FREQ, 1); 275d6b3aaf8SOleksandr Tymoshenko pci_write_config(dev, SDHC_PCI_BASE_FREQ, 50, 1); 276d6b3aaf8SOleksandr Tymoshenko pci_write_config(dev, SDHC_PCI_BASE_FREQ_KEY, 0x00, 1); 277d6b3aaf8SOleksandr Tymoshenko } 278d6b3aaf8SOleksandr Tymoshenko 279c2262647SMarius Strobl static void 280c2262647SMarius Strobl sdhci_restore_frequency(device_t dev) 281c2262647SMarius Strobl { 282c2262647SMarius Strobl struct sdhci_pci_softc *sc = device_get_softc(dev); 283c2262647SMarius Strobl 284c2262647SMarius Strobl /* Restore mode. */ 285c2262647SMarius Strobl pci_write_config(dev, SDHC_PCI_MODE_KEY, 0xfc, 1); 286c2262647SMarius Strobl pci_write_config(dev, SDHC_PCI_MODE, sc->cfg_mode, 1); 287c2262647SMarius Strobl pci_write_config(dev, SDHC_PCI_MODE_KEY, 0x00, 1); 288c2262647SMarius Strobl 289c2262647SMarius Strobl /* Restore frequency. */ 290c2262647SMarius Strobl pci_write_config(dev, SDHC_PCI_BASE_FREQ_KEY, 0x01, 1); 291c2262647SMarius Strobl pci_write_config(dev, SDHC_PCI_BASE_FREQ, sc->cfg_freq, 1); 292c2262647SMarius Strobl pci_write_config(dev, SDHC_PCI_BASE_FREQ_KEY, 0x00, 1); 293c2262647SMarius Strobl } 294c2262647SMarius Strobl 295d6b3aaf8SOleksandr Tymoshenko static int 296d6b3aaf8SOleksandr Tymoshenko sdhci_pci_probe(device_t dev) 297d6b3aaf8SOleksandr Tymoshenko { 298d6b3aaf8SOleksandr Tymoshenko uint32_t model; 299d6b3aaf8SOleksandr Tymoshenko uint16_t subvendor; 300d6b3aaf8SOleksandr Tymoshenko uint8_t class, subclass; 301d6b3aaf8SOleksandr Tymoshenko int i, result; 302d6b3aaf8SOleksandr Tymoshenko 303d6b3aaf8SOleksandr Tymoshenko model = (uint32_t)pci_get_device(dev) << 16; 304d6b3aaf8SOleksandr Tymoshenko model |= (uint32_t)pci_get_vendor(dev) & 0x0000ffff; 305d6b3aaf8SOleksandr Tymoshenko subvendor = pci_get_subvendor(dev); 306d6b3aaf8SOleksandr Tymoshenko class = pci_get_class(dev); 307d6b3aaf8SOleksandr Tymoshenko subclass = pci_get_subclass(dev); 308d6b3aaf8SOleksandr Tymoshenko 309d6b3aaf8SOleksandr Tymoshenko result = ENXIO; 310d6b3aaf8SOleksandr Tymoshenko for (i = 0; sdhci_devices[i].model != 0; i++) { 311d6b3aaf8SOleksandr Tymoshenko if (sdhci_devices[i].model == model && 312d6b3aaf8SOleksandr Tymoshenko (sdhci_devices[i].subvendor == 0xffff || 313d6b3aaf8SOleksandr Tymoshenko sdhci_devices[i].subvendor == subvendor)) { 314d6b3aaf8SOleksandr Tymoshenko device_set_desc(dev, sdhci_devices[i].desc); 315d6b3aaf8SOleksandr Tymoshenko result = BUS_PROBE_DEFAULT; 316d6b3aaf8SOleksandr Tymoshenko break; 317d6b3aaf8SOleksandr Tymoshenko } 318d6b3aaf8SOleksandr Tymoshenko } 319d6b3aaf8SOleksandr Tymoshenko if (result == ENXIO && class == PCIC_BASEPERIPH && 320d6b3aaf8SOleksandr Tymoshenko subclass == PCIS_BASEPERIPH_SDHC) { 321d6b3aaf8SOleksandr Tymoshenko device_set_desc(dev, "Generic SD HCI"); 322d6b3aaf8SOleksandr Tymoshenko result = BUS_PROBE_GENERIC; 323d6b3aaf8SOleksandr Tymoshenko } 324d6b3aaf8SOleksandr Tymoshenko 325d6b3aaf8SOleksandr Tymoshenko return (result); 326d6b3aaf8SOleksandr Tymoshenko } 327d6b3aaf8SOleksandr Tymoshenko 328d6b3aaf8SOleksandr Tymoshenko static int 329d6b3aaf8SOleksandr Tymoshenko sdhci_pci_attach(device_t dev) 330d6b3aaf8SOleksandr Tymoshenko { 331d6b3aaf8SOleksandr Tymoshenko struct sdhci_pci_softc *sc = device_get_softc(dev); 3327e6ccea3SMarius Strobl struct sdhci_slot *slot; 333d6b3aaf8SOleksandr Tymoshenko uint32_t model; 334d6b3aaf8SOleksandr Tymoshenko uint16_t subvendor; 335f0d2731dSMarius Strobl int bar, err, rid, slots, i; 336d6b3aaf8SOleksandr Tymoshenko 337d6b3aaf8SOleksandr Tymoshenko model = (uint32_t)pci_get_device(dev) << 16; 338d6b3aaf8SOleksandr Tymoshenko model |= (uint32_t)pci_get_vendor(dev) & 0x0000ffff; 339d6b3aaf8SOleksandr Tymoshenko subvendor = pci_get_subvendor(dev); 340d6b3aaf8SOleksandr Tymoshenko /* Apply chip specific quirks. */ 341d6b3aaf8SOleksandr Tymoshenko for (i = 0; sdhci_devices[i].model != 0; i++) { 342d6b3aaf8SOleksandr Tymoshenko if (sdhci_devices[i].model == model && 343d6b3aaf8SOleksandr Tymoshenko (sdhci_devices[i].subvendor == 0xffff || 344d6b3aaf8SOleksandr Tymoshenko sdhci_devices[i].subvendor == subvendor)) { 345d6b3aaf8SOleksandr Tymoshenko sc->quirks = sdhci_devices[i].quirks; 346d6b3aaf8SOleksandr Tymoshenko break; 347d6b3aaf8SOleksandr Tymoshenko } 348d6b3aaf8SOleksandr Tymoshenko } 3490f34084fSMarius Strobl sc->quirks &= ~sdhci_quirk_clear; 3500f34084fSMarius Strobl sc->quirks |= sdhci_quirk_set; 351806202b5SMarius Strobl 352d6b3aaf8SOleksandr Tymoshenko /* Some controllers need to be bumped into the right mode. */ 353d6b3aaf8SOleksandr Tymoshenko if (sc->quirks & SDHCI_QUIRK_LOWER_FREQUENCY) 354d6b3aaf8SOleksandr Tymoshenko sdhci_lower_frequency(dev); 355d6b3aaf8SOleksandr Tymoshenko /* Read slots info from PCI registers. */ 356d6b3aaf8SOleksandr Tymoshenko slots = pci_read_config(dev, PCI_SLOT_INFO, 1); 357d6b3aaf8SOleksandr Tymoshenko bar = PCI_SLOT_INFO_FIRST_BAR(slots); 358d6b3aaf8SOleksandr Tymoshenko slots = PCI_SLOT_INFO_SLOTS(slots); 359d6b3aaf8SOleksandr Tymoshenko if (slots > 6 || bar > 5) { 360d6b3aaf8SOleksandr Tymoshenko device_printf(dev, "Incorrect slots information (%d, %d).\n", 361d6b3aaf8SOleksandr Tymoshenko slots, bar); 362d6b3aaf8SOleksandr Tymoshenko return (EINVAL); 363d6b3aaf8SOleksandr Tymoshenko } 364d6b3aaf8SOleksandr Tymoshenko /* Allocate IRQ. */ 365f0d2731dSMarius Strobl i = 1; 366f0d2731dSMarius Strobl rid = 0; 367f0d2731dSMarius Strobl if (sdhci_enable_msi != 0 && pci_alloc_msi(dev, &i) == 0) 368f0d2731dSMarius Strobl rid = 1; 369f0d2731dSMarius Strobl sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 370f0d2731dSMarius Strobl RF_ACTIVE | (rid != 0 ? 0 : RF_SHAREABLE)); 371d6b3aaf8SOleksandr Tymoshenko if (sc->irq_res == NULL) { 372d6b3aaf8SOleksandr Tymoshenko device_printf(dev, "Can't allocate IRQ\n"); 373f0d2731dSMarius Strobl pci_release_msi(dev); 374d6b3aaf8SOleksandr Tymoshenko return (ENOMEM); 375d6b3aaf8SOleksandr Tymoshenko } 376d6b3aaf8SOleksandr Tymoshenko /* Scan all slots. */ 377d6b3aaf8SOleksandr Tymoshenko for (i = 0; i < slots; i++) { 3787e6ccea3SMarius Strobl slot = &sc->slots[sc->num_slots]; 379d6b3aaf8SOleksandr Tymoshenko 380d6b3aaf8SOleksandr Tymoshenko /* Allocate memory. */ 381f0d2731dSMarius Strobl rid = PCIR_BAR(bar + i); 382eff83876SJustin Hibbits sc->mem_res[i] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 383eff83876SJustin Hibbits &rid, RF_ACTIVE); 384d6b3aaf8SOleksandr Tymoshenko if (sc->mem_res[i] == NULL) { 3851bacf3beSMarius Strobl device_printf(dev, 3861bacf3beSMarius Strobl "Can't allocate memory for slot %d\n", i); 387d6b3aaf8SOleksandr Tymoshenko continue; 388d6b3aaf8SOleksandr Tymoshenko } 389d6b3aaf8SOleksandr Tymoshenko 39093efdc63SAdrian Chadd slot->quirks = sc->quirks; 39193efdc63SAdrian Chadd 392d6b3aaf8SOleksandr Tymoshenko if (sdhci_init_slot(dev, slot, i) != 0) 393d6b3aaf8SOleksandr Tymoshenko continue; 394d6b3aaf8SOleksandr Tymoshenko 395d6b3aaf8SOleksandr Tymoshenko sc->num_slots++; 396d6b3aaf8SOleksandr Tymoshenko } 397d6b3aaf8SOleksandr Tymoshenko device_printf(dev, "%d slot(s) allocated\n", sc->num_slots); 398d6b3aaf8SOleksandr Tymoshenko /* Activate the interrupt */ 399d6b3aaf8SOleksandr Tymoshenko err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 400d6b3aaf8SOleksandr Tymoshenko NULL, sdhci_pci_intr, sc, &sc->intrhand); 401d6b3aaf8SOleksandr Tymoshenko if (err) 402d6b3aaf8SOleksandr Tymoshenko device_printf(dev, "Can't setup IRQ\n"); 403d6b3aaf8SOleksandr Tymoshenko pci_enable_busmaster(dev); 404d6b3aaf8SOleksandr Tymoshenko /* Process cards detection. */ 405a94a63f0SWarner Losh for (i = 0; i < sc->num_slots; i++) { 4067e6ccea3SMarius Strobl sdhci_start_slot(&sc->slots[i]); 407a94a63f0SWarner Losh } 408d6b3aaf8SOleksandr Tymoshenko 409d6b3aaf8SOleksandr Tymoshenko return (0); 410d6b3aaf8SOleksandr Tymoshenko } 411d6b3aaf8SOleksandr Tymoshenko 412d6b3aaf8SOleksandr Tymoshenko static int 413d6b3aaf8SOleksandr Tymoshenko sdhci_pci_detach(device_t dev) 414d6b3aaf8SOleksandr Tymoshenko { 415d6b3aaf8SOleksandr Tymoshenko struct sdhci_pci_softc *sc = device_get_softc(dev); 416d6b3aaf8SOleksandr Tymoshenko int i; 417d6b3aaf8SOleksandr Tymoshenko 418d6b3aaf8SOleksandr Tymoshenko bus_teardown_intr(dev, sc->irq_res, sc->intrhand); 419d6b3aaf8SOleksandr Tymoshenko bus_release_resource(dev, SYS_RES_IRQ, 420f0d2731dSMarius Strobl rman_get_rid(sc->irq_res), sc->irq_res); 421f0d2731dSMarius Strobl pci_release_msi(dev); 422d6b3aaf8SOleksandr Tymoshenko 423d6b3aaf8SOleksandr Tymoshenko for (i = 0; i < sc->num_slots; i++) { 4247e6ccea3SMarius Strobl sdhci_cleanup_slot(&sc->slots[i]); 425d6b3aaf8SOleksandr Tymoshenko bus_release_resource(dev, SYS_RES_MEMORY, 426f0d2731dSMarius Strobl rman_get_rid(sc->mem_res[i]), sc->mem_res[i]); 427d6b3aaf8SOleksandr Tymoshenko } 428c2262647SMarius Strobl if (sc->quirks & SDHCI_QUIRK_LOWER_FREQUENCY) 429c2262647SMarius Strobl sdhci_restore_frequency(dev); 430c2262647SMarius Strobl return (0); 431c2262647SMarius Strobl } 432c2262647SMarius Strobl 433c2262647SMarius Strobl static int 434c2262647SMarius Strobl sdhci_pci_shutdown(device_t dev) 435c2262647SMarius Strobl { 436c2262647SMarius Strobl struct sdhci_pci_softc *sc = device_get_softc(dev); 437c2262647SMarius Strobl 438c2262647SMarius Strobl if (sc->quirks & SDHCI_QUIRK_LOWER_FREQUENCY) 439c2262647SMarius Strobl sdhci_restore_frequency(dev); 440d6b3aaf8SOleksandr Tymoshenko return (0); 441d6b3aaf8SOleksandr Tymoshenko } 442d6b3aaf8SOleksandr Tymoshenko 443d6b3aaf8SOleksandr Tymoshenko static int 444d6b3aaf8SOleksandr Tymoshenko sdhci_pci_suspend(device_t dev) 445d6b3aaf8SOleksandr Tymoshenko { 446d6b3aaf8SOleksandr Tymoshenko struct sdhci_pci_softc *sc = device_get_softc(dev); 447d6b3aaf8SOleksandr Tymoshenko int i, err; 448d6b3aaf8SOleksandr Tymoshenko 449d6b3aaf8SOleksandr Tymoshenko err = bus_generic_suspend(dev); 450d6b3aaf8SOleksandr Tymoshenko if (err) 451d6b3aaf8SOleksandr Tymoshenko return (err); 452d6b3aaf8SOleksandr Tymoshenko for (i = 0; i < sc->num_slots; i++) 453d6b3aaf8SOleksandr Tymoshenko sdhci_generic_suspend(&sc->slots[i]); 454d6b3aaf8SOleksandr Tymoshenko return (0); 455d6b3aaf8SOleksandr Tymoshenko } 456d6b3aaf8SOleksandr Tymoshenko 457d6b3aaf8SOleksandr Tymoshenko static int 458d6b3aaf8SOleksandr Tymoshenko sdhci_pci_resume(device_t dev) 459d6b3aaf8SOleksandr Tymoshenko { 460d6b3aaf8SOleksandr Tymoshenko struct sdhci_pci_softc *sc = device_get_softc(dev); 461220adb04SEdward Tomasz Napierala int i, err; 462d6b3aaf8SOleksandr Tymoshenko 463d6b3aaf8SOleksandr Tymoshenko for (i = 0; i < sc->num_slots; i++) 464d6b3aaf8SOleksandr Tymoshenko sdhci_generic_resume(&sc->slots[i]); 465220adb04SEdward Tomasz Napierala err = bus_generic_resume(dev); 466220adb04SEdward Tomasz Napierala if (err) 467220adb04SEdward Tomasz Napierala return (err); 468220adb04SEdward Tomasz Napierala if (sc->quirks & SDHCI_QUIRK_LOWER_FREQUENCY) 469220adb04SEdward Tomasz Napierala sdhci_lower_frequency(dev); 470220adb04SEdward Tomasz Napierala return (0); 471d6b3aaf8SOleksandr Tymoshenko } 472d6b3aaf8SOleksandr Tymoshenko 473d6b3aaf8SOleksandr Tymoshenko static void 474d6b3aaf8SOleksandr Tymoshenko sdhci_pci_intr(void *arg) 475d6b3aaf8SOleksandr Tymoshenko { 476d6b3aaf8SOleksandr Tymoshenko struct sdhci_pci_softc *sc = (struct sdhci_pci_softc *)arg; 477d6b3aaf8SOleksandr Tymoshenko int i; 478d6b3aaf8SOleksandr Tymoshenko 4797e6ccea3SMarius Strobl for (i = 0; i < sc->num_slots; i++) 4807e6ccea3SMarius Strobl sdhci_generic_intr(&sc->slots[i]); 481d6b3aaf8SOleksandr Tymoshenko } 482d6b3aaf8SOleksandr Tymoshenko 483d6b3aaf8SOleksandr Tymoshenko static device_method_t sdhci_methods[] = { 484d6b3aaf8SOleksandr Tymoshenko /* device_if */ 485d6b3aaf8SOleksandr Tymoshenko DEVMETHOD(device_probe, sdhci_pci_probe), 486d6b3aaf8SOleksandr Tymoshenko DEVMETHOD(device_attach, sdhci_pci_attach), 487d6b3aaf8SOleksandr Tymoshenko DEVMETHOD(device_detach, sdhci_pci_detach), 488c2262647SMarius Strobl DEVMETHOD(device_shutdown, sdhci_pci_shutdown), 489d6b3aaf8SOleksandr Tymoshenko DEVMETHOD(device_suspend, sdhci_pci_suspend), 490d6b3aaf8SOleksandr Tymoshenko DEVMETHOD(device_resume, sdhci_pci_resume), 491d6b3aaf8SOleksandr Tymoshenko 492d6b3aaf8SOleksandr Tymoshenko /* Bus interface */ 493d6b3aaf8SOleksandr Tymoshenko DEVMETHOD(bus_read_ivar, sdhci_generic_read_ivar), 494d6b3aaf8SOleksandr Tymoshenko DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar), 495d6b3aaf8SOleksandr Tymoshenko 496d6b3aaf8SOleksandr Tymoshenko /* mmcbr_if */ 497d6b3aaf8SOleksandr Tymoshenko DEVMETHOD(mmcbr_update_ios, sdhci_generic_update_ios), 4980f34084fSMarius Strobl DEVMETHOD(mmcbr_switch_vccq, sdhci_generic_switch_vccq), 499aca38eabSMarius Strobl DEVMETHOD(mmcbr_tune, sdhci_generic_tune), 500aca38eabSMarius Strobl DEVMETHOD(mmcbr_retune, sdhci_generic_retune), 501d6b3aaf8SOleksandr Tymoshenko DEVMETHOD(mmcbr_request, sdhci_generic_request), 502d6b3aaf8SOleksandr Tymoshenko DEVMETHOD(mmcbr_get_ro, sdhci_generic_get_ro), 503d6b3aaf8SOleksandr Tymoshenko DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host), 504d6b3aaf8SOleksandr Tymoshenko DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host), 505d6b3aaf8SOleksandr Tymoshenko 5060f34084fSMarius Strobl /* SDHCI accessors */ 507d6b3aaf8SOleksandr Tymoshenko DEVMETHOD(sdhci_read_1, sdhci_pci_read_1), 508d6b3aaf8SOleksandr Tymoshenko DEVMETHOD(sdhci_read_2, sdhci_pci_read_2), 509d6b3aaf8SOleksandr Tymoshenko DEVMETHOD(sdhci_read_4, sdhci_pci_read_4), 510d6b3aaf8SOleksandr Tymoshenko DEVMETHOD(sdhci_read_multi_4, sdhci_pci_read_multi_4), 511d6b3aaf8SOleksandr Tymoshenko DEVMETHOD(sdhci_write_1, sdhci_pci_write_1), 512d6b3aaf8SOleksandr Tymoshenko DEVMETHOD(sdhci_write_2, sdhci_pci_write_2), 513d6b3aaf8SOleksandr Tymoshenko DEVMETHOD(sdhci_write_4, sdhci_pci_write_4), 514d6b3aaf8SOleksandr Tymoshenko DEVMETHOD(sdhci_write_multi_4, sdhci_pci_write_multi_4), 5150f34084fSMarius Strobl DEVMETHOD(sdhci_set_uhs_timing, sdhci_generic_set_uhs_timing), 516d6b3aaf8SOleksandr Tymoshenko 51761bfd867SSofian Brabez DEVMETHOD_END 518d6b3aaf8SOleksandr Tymoshenko }; 519d6b3aaf8SOleksandr Tymoshenko 520d6b3aaf8SOleksandr Tymoshenko static driver_t sdhci_pci_driver = { 521d6b3aaf8SOleksandr Tymoshenko "sdhci_pci", 522d6b3aaf8SOleksandr Tymoshenko sdhci_methods, 523d6b3aaf8SOleksandr Tymoshenko sizeof(struct sdhci_pci_softc), 524d6b3aaf8SOleksandr Tymoshenko }; 525d6b3aaf8SOleksandr Tymoshenko static devclass_t sdhci_pci_devclass; 526d6b3aaf8SOleksandr Tymoshenko 527f0d2731dSMarius Strobl DRIVER_MODULE(sdhci_pci, pci, sdhci_pci_driver, sdhci_pci_devclass, NULL, 528f0d2731dSMarius Strobl NULL); 529d6b3aaf8SOleksandr Tymoshenko MODULE_DEPEND(sdhci_pci, sdhci, 1, 1, 1); 530a94a63f0SWarner Losh 531a94a63f0SWarner Losh #ifndef MMCCAM 53255dae242SMarius Strobl MMC_DECLARE_BRIDGE(sdhci_pci); 533a94a63f0SWarner Losh #endif 534