summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorStefan Sperling <stsp@cvs.openbsd.org>2012-11-29 23:36:35 +0000
committerStefan Sperling <stsp@cvs.openbsd.org>2012-11-29 23:36:35 +0000
commite9bd58b2f6eac5915430d66b9e0140f121efde1b (patch)
tree68b0564450aca0c59e743bc2f0776a284697a894 /sys
parent9fd3649d3fe844fed5cb9a0d99e6be49b6a23c54 (diff)
Add rtsx(4), a new driver for the Realtek RTS5209 card reader.
This card reader does not comply to the standard SDHC interface supported by sdhc(4) and hence requires a custom driver. With help from uwe and mikeb. Useful hints were also provided by the author of the corresponding Linux driver (wwang at realsil com cn), thanks a lot! Tested by myself and weerd on i386 and amd64.
Diffstat (limited to 'sys')
-rw-r--r--sys/conf/files6
-rw-r--r--sys/dev/ic/rtsx.c1272
-rw-r--r--sys/dev/ic/rtsxreg.h495
-rw-r--r--sys/dev/ic/rtsxvar.h52
-rw-r--r--sys/dev/pci/files.pci6
-rw-r--r--sys/dev/pci/rtsx_pci.c105
6 files changed, 1934 insertions, 2 deletions
diff --git a/sys/conf/files b/sys/conf/files
index 95159a084e1..5410ee8475b 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1,4 +1,4 @@
-# $OpenBSD: files,v 1.538 2012/08/23 06:12:49 deraadt Exp $
+# $OpenBSD: files,v 1.539 2012/11/29 23:36:34 stsp Exp $
# $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@@ -454,6 +454,10 @@ device wbsd: sdmmcbus
file dev/ic/w83l518d.c wbsd
file dev/ic/w83l518d_sdmmc.c wbsd
+# Realtek RTS5209 Card Reader
+device rtsx: sdmmcbus
+file dev/ic/rtsx.c rtsx needs-flag
+
# AMD 7930 audio/ISDN codec
define am7930
file dev/ic/am7930.c am7930
diff --git a/sys/dev/ic/rtsx.c b/sys/dev/ic/rtsx.c
new file mode 100644
index 00000000000..b431d695fd0
--- /dev/null
+++ b/sys/dev/ic/rtsx.c
@@ -0,0 +1,1272 @@
+/* $OpenBSD: rtsx.c,v 1.1 2012/11/29 23:36:34 stsp Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ * Copyright (c) 2012 Stefan Sperling <stsp@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Realtek RTS5209 Card Reader driver.
+ */
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/systm.h>
+
+#include <dev/ic/rtsxreg.h>
+#include <dev/ic/rtsxvar.h>
+#include <dev/sdmmc/sdmmcvar.h>
+#include <dev/sdmmc/sdmmc_ioreg.h>
+
+/*
+ * We use two DMA buffers, a command buffer and a data buffer.
+ *
+ * The command buffer contains a command queue for the host controller,
+ * which describes SD/MMC commands to run, and other parameters. The chip
+ * runs the command queue when a special bit in the RTSX_HCBAR register is set
+ * and signals completion with the TRANS_OK interrupt.
+ * Each command is encoded as a 4 byte sequence containing command number
+ * (read, write, or check a host controller register), a register address,
+ * and a data bit-mask and value.
+ *
+ * The data buffer is used to transfer data sectors to or from the SD card.
+ * Data transfer is controlled via the RTSX_HDBAR register. Completion is
+ * also signalled by the TRANS_OK interrupt.
+ *
+ * The chip is unable to perform DMA above 4GB.
+ *
+ * SD/MMC commands which do not transfer any data from/to the card only use
+ * the command buffer.
+ */
+
+#define RTSX_DMA_MAX_SEGSIZE 0x80000
+#define RTSX_HOSTCMD_MAX 256
+#define RTSX_HOSTCMD_BUFSIZE (sizeof(u_int32_t) * RTSX_HOSTCMD_MAX)
+#define RTSX_DMA_DATA_BUFSIZE MAXPHYS
+
+#define READ4(sc, reg) \
+ (bus_space_read_4((sc)->iot, (sc)->ioh, (reg)))
+#define WRITE4(sc, reg, val) \
+ bus_space_write_4((sc)->iot, (sc)->ioh, (reg), (val))
+
+#define RTSX_READ(sc, reg, val) \
+ do { \
+ int err = rtsx_read((sc), (reg), (val)); \
+ if (err) \
+ return (err); \
+ } while (0)
+
+#define RTSX_WRITE(sc, reg, val) \
+ do { \
+ int err = rtsx_write((sc), (reg), 0xff, (val)); \
+ if (err) \
+ return (err); \
+ } while (0)
+
+#define RTSX_CLR(sc, reg, bits) \
+ do { \
+ int err = rtsx_write((sc), (reg), (bits), 0); \
+ if (err) \
+ return (err); \
+ } while (0)
+
+#define RTSX_SET(sc, reg, bits) \
+ do { \
+ int err = rtsx_write((sc), (reg), (bits), 0xff);\
+ if (err) \
+ return (err); \
+ } while (0)
+
+int rtsx_host_reset(sdmmc_chipset_handle_t);
+u_int32_t rtsx_host_ocr(sdmmc_chipset_handle_t);
+int rtsx_host_maxblklen(sdmmc_chipset_handle_t);
+int rtsx_card_detect(sdmmc_chipset_handle_t);
+int rtsx_bus_power(sdmmc_chipset_handle_t, u_int32_t);
+int rtsx_bus_clock(sdmmc_chipset_handle_t, int);
+void rtsx_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *);
+int rtsx_init(struct rtsx_softc *, int);
+void rtsx_soft_reset(struct rtsx_softc *);
+int rtsx_bus_power_off(struct rtsx_softc *);
+int rtsx_bus_power_on(struct rtsx_softc *);
+int rtsx_set_bus_width(struct rtsx_softc *, int);
+int rtsx_stop_sd_clock(struct rtsx_softc *);
+int rtsx_switch_sd_clock(struct rtsx_softc *, u_int8_t, int, int);
+int rtsx_wait_intr(struct rtsx_softc *, int, int);
+int rtsx_read(struct rtsx_softc *, u_int16_t, u_int8_t *);
+int rtsx_write(struct rtsx_softc *, u_int16_t, u_int8_t, u_int8_t);
+#ifdef notyet
+int rtsx_read_phy(struct rtsx_softc *, u_int8_t, u_int16_t *);
+#endif
+int rtsx_write_phy(struct rtsx_softc *, u_int8_t, u_int16_t);
+int rtsx_read_cfg(struct rtsx_softc *, u_int8_t, u_int16_t, u_int32_t *);
+#ifdef notyet
+int rtsx_write_cfg(struct rtsx_softc *, u_int8_t, u_int16_t, u_int32_t,
+ u_int32_t);
+#endif
+void rtsx_hostcmd(u_int32_t *, int *, u_int8_t, u_int16_t, u_int8_t,
+ u_int8_t);
+int rtsx_hostcmd_send(struct rtsx_softc *, int, int);
+u_int8_t rtsx_response_type(u_int16_t);
+int rtsx_xfer(struct rtsx_softc *, struct sdmmc_command *, u_int32_t *);
+void rtsx_card_insert(struct rtsx_softc *);
+void rtsx_card_eject(struct rtsx_softc *);
+int rtsx_led_enable(struct rtsx_softc *);
+int rtsx_led_disable(struct rtsx_softc *);
+void rtsx_save_regs(struct rtsx_softc *);
+void rtsx_restore_regs(struct rtsx_softc *);
+
+#ifdef RTSX_DEBUG
+int rtsxdebug = 0;
+#define DPRINTF(n,s) do { if ((n) <= rtsxdebug) printf s; } while (0)
+#else
+#define DPRINTF(n,s) do {} while(0)
+#endif
+
+struct sdmmc_chip_functions rtsx_functions = {
+ /* host controller reset */
+ rtsx_host_reset,
+ /* host controller capabilities */
+ rtsx_host_ocr,
+ rtsx_host_maxblklen,
+ /* card detection */
+ rtsx_card_detect,
+ /* bus power and clock frequency */
+ rtsx_bus_power,
+ rtsx_bus_clock,
+ /* command execution */
+ rtsx_exec_command,
+ /* card interrupt */
+ NULL, NULL
+};
+
+struct cfdriver rtsx_cd = {
+ NULL, "rtsx", DV_DULL
+};
+
+/*
+ * Called by attachment driver.
+ */
+int
+rtsx_attach(struct rtsx_softc *sc, bus_space_tag_t iot,
+ bus_space_handle_t ioh, bus_size_t iosize, bus_dma_tag_t dmat)
+{
+ struct sdmmcbus_attach_args saa;
+ u_int32_t sdio_cfg;
+
+ sc->iot = iot;
+ sc->ioh = ioh;
+ sc->dmat = dmat;
+
+ if (rtsx_init(sc, 1))
+ return 1;
+
+ if (rtsx_read_cfg(sc, 0, RTSX_SDIOCFG_REG, &sdio_cfg)) {
+ if ((sdio_cfg & RTSX_SDIOCFG_SDIO_ONLY) ||
+ (sdio_cfg & RTSX_SDIOCFG_HAVE_SDIO))
+ sc->flags |= RTSX_F_SDIO_SUPPORT;
+ }
+
+ if (bus_dmamap_create(sc->dmat, RTSX_HOSTCMD_BUFSIZE, 1,
+ RTSX_DMA_MAX_SEGSIZE, 0, BUS_DMA_NOWAIT,
+ &sc->dmap_cmd) != 0)
+ return 1;
+ if (bus_dmamap_create(sc->dmat, RTSX_DMA_DATA_BUFSIZE, 1,
+ RTSX_DMA_MAX_SEGSIZE, 0, BUS_DMA_NOWAIT,
+ &sc->dmap_data) != 0)
+ return 1;
+
+ /*
+ * Attach the generic SD/MMC bus driver. (The bus driver must
+ * not invoke any chipset functions before it is attached.)
+ */
+ bzero(&saa, sizeof(saa));
+ saa.saa_busname = "sdmmc";
+ saa.sct = &rtsx_functions;
+ saa.sch = sc;
+ saa.flags = SMF_STOP_AFTER_MULTIPLE;
+
+ sc->sdmmc = config_found(&sc->sc_dev, &saa, NULL);
+ if (sc->sdmmc == NULL)
+ return 1;
+
+ /* Now handle cards discovered during attachment. */
+ if (ISSET(sc->flags, RTSX_F_CARD_PRESENT))
+ rtsx_card_insert(sc);
+
+ return 0;
+}
+
+int
+rtsx_init(struct rtsx_softc *sc, int attaching)
+{
+ u_int32_t status;
+
+ /* Enable interrupts. */
+ WRITE4(sc, RTSX_BIER,
+ RTSX_TRANS_OK_INT_EN | RTSX_TRANS_FAIL_INT_EN | RTSX_SD_INT_EN);
+
+ /* Enable interrupt write-clear (default is read-clear). */
+ RTSX_CLR(sc, RTSX_NFTS_TX_CTRL, RTSX_INT_READ_CLR);
+
+ /* Clear any pending interrupts. */
+ status = READ4(sc, RTSX_BIPR);
+ WRITE4(sc, RTSX_BIPR, status);
+
+ /* Power on SSC clock. */
+ RTSX_CLR(sc, RTSX_FPDCTL, RTSX_SSC_POWER_DOWN);
+ delay(200);
+
+ /* XXX magic numbers from linux driver */
+ if (rtsx_write_phy(sc, 0x00, 0xB966)) {
+ printf("%s: cannot write phy register\n", DEVNAME(sc));
+ return (1);
+ }
+
+ RTSX_SET(sc, RTSX_CLK_DIV, 0x07);
+
+ /* Disable sleep mode. */
+ RTSX_CLR(sc, RTSX_HOST_SLEEP_STATE,
+ RTSX_HOST_ENTER_S1 | RTSX_HOST_ENTER_S3);
+
+ /* Disable card clock. */
+ RTSX_CLR(sc, RTSX_CARD_CLK_EN, RTSX_CARD_CLK_EN_ALL);
+
+ RTSX_CLR(sc, RTSX_CHANGE_LINK_STATE,
+ RTSX_FORCE_RST_CORE_EN | RTSX_NON_STICKY_RST_N_DBG | 0x04);
+ RTSX_WRITE(sc, RTSX_SD30_DRIVE_SEL, RTSX_SD30_DRIVE_SEL_3V3);
+
+ /* Enable SSC clock. */
+ RTSX_WRITE(sc, RTSX_SSC_CTL1, RTSX_SSC_8X_EN | RTSX_SSC_SEL_4M);
+ RTSX_WRITE(sc, RTSX_SSC_CTL2, 0x12);
+
+ RTSX_SET(sc, RTSX_CHANGE_LINK_STATE, RTSX_MAC_PHY_RST_N_DBG);
+ RTSX_SET(sc, RTSX_IRQSTAT0, RTSX_LINK_READY_INT);
+
+ RTSX_WRITE(sc, RTSX_PERST_GLITCH_WIDTH, 0x80);
+
+ /* Set RC oscillator to 400K. */
+ RTSX_CLR(sc, RTSX_RCCTL, RTSX_RCCTL_F_2M);
+
+ /* Request clock by driving CLKREQ pin to zero. */
+ RTSX_SET(sc, RTSX_PETXCFG, RTSX_PETXCFG_CLKREQ_PIN);
+
+ /* Set up LED GPIO. */
+ RTSX_WRITE(sc, RTSX_CARD_GPIO, 0x03);
+ RTSX_WRITE(sc, RTSX_CARD_GPIO_DIR, 0x03);
+
+ /* Check for cards already inserted at attach time. */
+ if (attaching && (status & RTSX_SD_EXIST))
+ sc->flags |= RTSX_F_CARD_PRESENT;
+
+ return (0);
+}
+
+int
+rtsx_activate(struct device *self, int act)
+{
+ struct rtsx_softc *sc = (struct rtsx_softc *)self;
+ int rv = 0;
+
+ switch (act) {
+ case DVACT_QUIESCE:
+ /* XXX abort commands in progress? */
+ rv = config_activate_children(self, act);
+ break;
+ case DVACT_SUSPEND:
+ rtsx_save_regs(sc);
+ rv = config_activate_children(self, act);
+ break;
+ case DVACT_POWERDOWN:
+ rv = config_activate_children(self, act);
+ break;
+ case DVACT_RESUME:
+ rtsx_restore_regs(sc);
+
+ /* Handle cards ejected/inserted during suspend. */
+ if (READ4(sc, RTSX_BIPR) & RTSX_SD_EXIST)
+ rtsx_card_insert(sc);
+ else
+ rtsx_card_eject(sc);
+
+ rv = config_activate_children(self, act);
+ break;
+ }
+ return (rv);
+}
+
+int
+rtsx_led_enable(struct rtsx_softc *sc)
+{
+ RTSX_CLR(sc, RTSX_CARD_GPIO, RTSX_CARD_GPIO_LED_OFF);
+ RTSX_WRITE(sc, RTSX_CARD_AUTO_BLINK,
+ RTSX_LED_BLINK_EN | RTSX_LED_BLINK_SPEED);
+ return 0;
+}
+
+int
+rtsx_led_disable(struct rtsx_softc *sc)
+{
+ RTSX_CLR(sc, RTSX_CARD_AUTO_BLINK, RTSX_LED_BLINK_EN);
+ RTSX_WRITE(sc, RTSX_CARD_GPIO, RTSX_CARD_GPIO_LED_OFF);
+ return 0;
+}
+
+/*
+ * Reset the host controller. Called during initialization, when
+ * cards are removed, upon resume, and during error recovery.
+ */
+int
+rtsx_host_reset(sdmmc_chipset_handle_t sch)
+{
+ struct rtsx_softc *sc = sch;
+ int s;
+
+ DPRINTF(1,("%s: host reset\n", DEVNAME(sc)));
+
+ s = splsdmmc();
+
+ if (ISSET(sc->flags, RTSX_F_CARD_PRESENT))
+ rtsx_soft_reset(sc);
+
+ if (rtsx_init(sc, 0)) {
+ splx(s);
+ return 1;
+ }
+
+ splx(s);
+ return 0;
+}
+
+u_int32_t
+rtsx_host_ocr(sdmmc_chipset_handle_t sch)
+{
+ return RTSX_SUPPORT_VOLTAGE;
+}
+
+int
+rtsx_host_maxblklen(sdmmc_chipset_handle_t sch)
+{
+ return 512;
+}
+
+/*
+ * Return non-zero if the card is currently inserted.
+ */
+int
+rtsx_card_detect(sdmmc_chipset_handle_t sch)
+{
+ struct rtsx_softc *sc = sch;
+
+ return ISSET(sc->flags, RTSX_F_CARD_PRESENT);
+}
+
+int
+rtsx_bus_power_off(struct rtsx_softc *sc)
+{
+ int error;
+
+ error = rtsx_stop_sd_clock(sc);
+ if (error)
+ return error;
+
+ /* Disable SD output. */
+ RTSX_CLR(sc, RTSX_CARD_OE, RTSX_CARD_OUTPUT_EN);
+
+ /* Turn off power. */
+ RTSX_SET(sc, RTSX_PWR_GATE_CTRL, RTSX_LDO3318_OFF);
+ RTSX_SET(sc, RTSX_CARD_PWR_CTL, RTSX_SD_PWR_OFF);
+ RTSX_CLR(sc, RTSX_CARD_PWR_CTL, RTSX_PMOS_STRG_800mA);
+
+ /* Disable pull control. */
+ RTSX_WRITE(sc, RTSX_CARD_PULL_CTL1, RTSX_PULL_CTL_DISABLE12);
+ RTSX_WRITE(sc, RTSX_CARD_PULL_CTL2, RTSX_PULL_CTL_DISABLE12);
+ RTSX_WRITE(sc, RTSX_CARD_PULL_CTL3, RTSX_PULL_CTL_DISABLE3);
+
+ return 0;
+}
+
+int
+rtsx_bus_power_on(struct rtsx_softc *sc)
+{
+ /* Select SD card. */
+ RTSX_WRITE(sc, RTSX_CARD_SELECT, RTSX_SD_MOD_SEL);
+ RTSX_WRITE(sc, RTSX_CARD_SHARE_MODE, RTSX_CARD_SHARE_48_SD);
+ RTSX_SET(sc, RTSX_CARD_CLK_EN, RTSX_SD_CLK_EN);
+
+ /* Enable pull control. */
+ RTSX_WRITE(sc, RTSX_CARD_PULL_CTL1, RTSX_PULL_CTL_ENABLE12);
+ RTSX_WRITE(sc, RTSX_CARD_PULL_CTL2, RTSX_PULL_CTL_ENABLE12);
+ RTSX_WRITE(sc, RTSX_CARD_PULL_CTL3, RTSX_PULL_CTL_ENABLE3);
+
+ /* Enable card power. */
+ RTSX_SET(sc, RTSX_CARD_PWR_CTL, RTSX_SD_PARTIAL_PWR_ON);
+ RTSX_SET(sc, RTSX_PWR_GATE_CTRL, RTSX_LDO3318_SUSPEND);
+ delay(200);
+ RTSX_CLR(sc, RTSX_CARD_PWR_CTL, RTSX_SD_PWR_OFF);
+ RTSX_CLR(sc, RTSX_PWR_GATE_CTRL, RTSX_LDO3318_OFF);
+
+ /* Enable SD card output. */
+ RTSX_WRITE(sc, RTSX_CARD_OE, RTSX_SD_OUTPUT_EN);
+
+ return 0;
+}
+
+int
+rtsx_set_bus_width(struct rtsx_softc *sc, int w)
+{
+ u_int32_t bus_width;
+
+ switch (w) {
+ case 8:
+ bus_width = RTSX_BUS_WIDTH_8;
+ break;
+ case 4:
+ bus_width = RTSX_BUS_WIDTH_4;
+ break;
+ case 1:
+ default:
+ bus_width = RTSX_BUS_WIDTH_1;
+ break;
+ }
+
+ if (bus_width == RTSX_BUS_WIDTH_1)
+ RTSX_CLR(sc, RTSX_SD_CFG1, RTSX_BUS_WIDTH_MASK);
+ else
+ RTSX_SET(sc, RTSX_SD_CFG1, bus_width);
+
+ return 0;
+}
+
+int
+rtsx_stop_sd_clock(struct rtsx_softc *sc)
+{
+ RTSX_CLR(sc, RTSX_CARD_CLK_EN, RTSX_CARD_CLK_EN_ALL);
+ RTSX_SET(sc, RTSX_SD_BUS_STAT, RTSX_SD_CLK_FORCE_STOP);
+
+ return 0;
+}
+
+int
+rtsx_switch_sd_clock(struct rtsx_softc *sc, u_int8_t n, int div, int mcu)
+{
+ /* Enable SD 2.0 mode. */
+ RTSX_CLR(sc, RTSX_SD_CFG1, RTSX_SD_MODE_MASK);
+
+ RTSX_SET(sc, RTSX_CLK_CTL, RTSX_CLK_LOW_FREQ);
+
+ RTSX_WRITE(sc, RTSX_CARD_CLK_SOURCE,
+ RTSX_CRC_FIX_CLK | RTSX_SD30_VAR_CLK0 | RTSX_SAMPLE_VAR_CLK1);
+ RTSX_CLR(sc, RTSX_SD_SAMPLE_POINT_CTL, RTSX_SD20_RX_SEL_MASK);
+ RTSX_WRITE(sc, RTSX_SD_PUSH_POINT_CTL, RTSX_SD20_TX_NEG_EDGE);
+ RTSX_WRITE(sc, RTSX_CLK_DIV, (div << 4) | mcu);
+ RTSX_CLR(sc, RTSX_SSC_CTL1, RTSX_RSTB);
+ RTSX_CLR(sc, RTSX_SSC_CTL2, RTSX_SSC_DEPTH_MASK);
+ RTSX_WRITE(sc, RTSX_SSC_DIV_N_0, n);
+ RTSX_SET(sc, RTSX_SSC_CTL1, RTSX_RSTB);
+ delay(100);
+
+ RTSX_CLR(sc, RTSX_CLK_CTL, RTSX_CLK_LOW_FREQ);
+
+ return 0;
+}
+
+/*
+ * Set or change SD bus voltage and enable or disable SD bus power.
+ * Return zero on success.
+ */
+int
+rtsx_bus_power(sdmmc_chipset_handle_t sch, u_int32_t ocr)
+{
+ struct rtsx_softc *sc = sch;
+ int s, error = 0;
+
+ DPRINTF(1,("%s: voltage change ocr=0x%x\n", DEVNAME(sc), ocr));
+
+ s = splsdmmc();
+
+ /*
+ * Disable bus power before voltage change.
+ */
+ error = rtsx_bus_power_off(sc);
+ if (error)
+ goto ret;
+
+ delay(200);
+
+ /* If power is disabled, reset the host and return now. */
+ if (ocr == 0) {
+ splx(s);
+ (void)rtsx_host_reset(sc);
+ return 0;
+ }
+
+ if (!ISSET(ocr, RTSX_SUPPORT_VOLTAGE)) {
+ /* Unsupported voltage level requested. */
+ DPRINTF(1,("%s: unsupported voltage ocr=0x%x\n",
+ DEVNAME(sc), ocr));
+ error = EINVAL;
+ goto ret;
+ }
+
+ error = rtsx_set_bus_width(sc, 1);
+ if (error)
+ goto ret;
+
+ error = rtsx_bus_power_on(sc);
+ret:
+ splx(s);
+ return error;
+}
+
+/*
+ * Set or change SDCLK frequency or disable the SD clock.
+ * Return zero on success.
+ */
+int
+rtsx_bus_clock(sdmmc_chipset_handle_t sch, int freq)
+{
+ struct rtsx_softc *sc = sch;
+ int s;
+ u_int8_t n;
+ int div;
+ int mcu;
+ int error = 0;
+
+ s = splsdmmc();
+
+ if (freq == SDMMC_SDCLK_OFF) {
+ error = rtsx_stop_sd_clock(sc);
+ goto ret;
+ }
+
+ /*
+ * Configure the clock frequency.
+ */
+ switch (freq) {
+ case SDMMC_SDCLK_400KHZ:
+ n = 80; /* minimum */
+ div = RTSX_CLK_DIV_8;
+ mcu = 7;
+ RTSX_SET(sc, RTSX_SD_CFG1, RTSX_CLK_DIVIDE_128);
+ break;
+ case SDMMC_SDCLK_25MHZ:
+ n = 100;
+ div = RTSX_CLK_DIV_4;
+ mcu = 7;
+ RTSX_CLR(sc, RTSX_SD_CFG1, RTSX_CLK_DIVIDE_MASK);
+ break;
+ default:
+ error = EINVAL;
+ goto ret;
+ }
+
+ /*
+ * Enable SD clock.
+ */
+ error = rtsx_switch_sd_clock(sc, n, div, mcu);
+ret:
+ splx(s);
+ return error;
+}
+
+int
+rtsx_read(struct rtsx_softc *sc, u_int16_t addr, u_int8_t *val)
+{
+ int tries = 1024;
+ u_int32_t reg;
+
+ WRITE4(sc, RTSX_HAIMR, RTSX_HAIMR_BUSY |
+ (u_int32_t)((addr & 0x3FFF) << 16));
+
+ while (tries--) {
+ reg = READ4(sc, RTSX_HAIMR);
+ if (!(reg & RTSX_HAIMR_BUSY))
+ break;
+ }
+
+ *val = (reg & 0xff);
+ return (tries == 0) ? ETIMEDOUT : 0;
+}
+
+int
+rtsx_write(struct rtsx_softc *sc, u_int16_t addr, u_int8_t mask, u_int8_t val)
+{
+ int tries = 1024;
+ u_int32_t reg;
+
+ WRITE4(sc, RTSX_HAIMR,
+ RTSX_HAIMR_BUSY | RTSX_HAIMR_WRITE |
+ (u_int32_t)(((addr & 0x3FFF) << 16) |
+ (mask << 8) | val));
+
+ while (tries--) {
+ reg = READ4(sc, RTSX_HAIMR);
+ if (!(reg & RTSX_HAIMR_BUSY)) {
+ if (val != (reg & 0xff))
+ return EIO;
+ return 0;
+ }
+ }
+
+ return ETIMEDOUT;
+}
+
+#ifdef notyet
+int
+rtsx_read_phy(struct rtsx_softc *sc, u_int8_t addr, u_int16_t *val)
+{
+ int timeout = 100000;
+ u_int8_t data0;
+ u_int8_t data1;
+ u_int8_t rwctl;
+
+ RTSX_WRITE(sc, RTSX_PHY_ADDR, addr);
+ RTSX_WRITE(sc, RTSX_PHY_RWCTL, RTSX_PHY_BUSY|RTSX_PHY_READ);
+
+ while (timeout--) {
+ RTSX_READ(sc, RTSX_PHY_RWCTL, &rwctl);
+ if (!(rwctl & RTSX_PHY_BUSY))
+ break;
+ }
+
+ if (timeout == 0)
+ return ETIMEDOUT;
+
+ RTSX_READ(sc, RTSX_PHY_DATA0, &data0);
+ RTSX_READ(sc, RTSX_PHY_DATA1, &data1);
+ *val = data0 | (data1 << 8);
+
+ return 0;
+}
+#endif
+
+int
+rtsx_write_phy(struct rtsx_softc *sc, u_int8_t addr, u_int16_t val)
+{
+ int timeout = 100000;
+ u_int8_t rwctl;
+
+ RTSX_WRITE(sc, RTSX_PHY_DATA0, val);
+ RTSX_WRITE(sc, RTSX_PHY_DATA1, val >> 8);
+ RTSX_WRITE(sc, RTSX_PHY_ADDR, addr);
+ RTSX_WRITE(sc, RTSX_PHY_RWCTL, RTSX_PHY_BUSY|RTSX_PHY_WRITE);
+
+ while (timeout--) {
+ RTSX_READ(sc, RTSX_PHY_RWCTL, &rwctl);
+ if (!(rwctl & RTSX_PHY_BUSY))
+ break;
+ }
+
+ if (timeout == 0)
+ return ETIMEDOUT;
+
+ return 0;
+}
+
+int
+rtsx_read_cfg(struct rtsx_softc *sc, u_int8_t func, u_int16_t addr,
+ u_int32_t *val)
+{
+ int tries = 1024;
+ u_int8_t data0, data1, data2, data3, rwctl;
+
+ RTSX_WRITE(sc, RTSX_CFGADDR0, addr);
+ RTSX_WRITE(sc, RTSX_CFGADDR1, addr >> 8);
+ RTSX_WRITE(sc, RTSX_CFGRWCTL, RTSX_CFG_BUSY | (func & 0x03 << 4));
+
+ while (tries--) {
+ RTSX_READ(sc, RTSX_CFGRWCTL, &rwctl);
+ if (!(rwctl & RTSX_CFG_BUSY))
+ break;
+ }
+
+ if (tries == 0)
+ return EIO;
+
+ RTSX_READ(sc, RTSX_CFGDATA0, &data0);
+ RTSX_READ(sc, RTSX_CFGDATA1, &data1);
+ RTSX_READ(sc, RTSX_CFGDATA2, &data2);
+ RTSX_READ(sc, RTSX_CFGDATA3, &data3);
+
+ *val = (data3 << 24) | (data2 << 16) | (data1 << 8) | data0;
+
+ return 0;
+}
+
+#ifdef notyet
+int
+rtsx_write_cfg(struct rtsx_softc *sc, u_int8_t func, u_int16_t addr,
+ u_int32_t mask, u_int32_t val)
+{
+ int i, writemask = 0, tries = 1024;
+ u_int8_t rwctl;
+
+ for (i = 0; i < 4; i++) {
+ if (mask & 0xff) {
+ RTSX_WRITE(sc, RTSX_CFGDATA0 + i, val & mask & 0xff);
+ writemask |= (1 << i);
+ }
+ mask >>= 8;
+ val >>= 8;
+ }
+
+ if (writemask) {
+ RTSX_WRITE(sc, RTSX_CFGADDR0, addr);
+ RTSX_WRITE(sc, RTSX_CFGADDR1, addr >> 8);
+ RTSX_WRITE(sc, RTSX_CFGRWCTL,
+ RTSX_CFG_BUSY | writemask | (func & 0x03 << 4));
+ }
+
+ while (tries--) {
+ RTSX_READ(sc, RTSX_CFGRWCTL, &rwctl);
+ if (!(rwctl & RTSX_CFG_BUSY))
+ break;
+ }
+
+ if (tries == 0)
+ return EIO;
+
+ return 0;
+}
+#endif
+
+/* Append a properly encoded host command to the host command buffer. */
+void
+rtsx_hostcmd(u_int32_t *cmdbuf, int *n, u_int8_t cmd, u_int16_t reg,
+ u_int8_t mask, u_int8_t data)
+{
+ KASSERT(*n < RTSX_HOSTCMD_MAX);
+
+ cmdbuf[(*n)++] = htole32((u_int32_t)(cmd & 0x3) << 30) |
+ ((u_int32_t)(reg & 0x3fff) << 16) |
+ ((u_int32_t)(mask) << 8) |
+ ((u_int32_t)data);
+}
+
+void
+rtsx_save_regs(struct rtsx_softc *sc)
+{
+ int s, i;
+ u_int16_t reg;
+
+ s = splsdmmc();
+
+ i = 0;
+ for (reg = 0xFDA0; reg < 0xFDAE; reg++)
+ (void)rtsx_read(sc, reg, &sc->regs[i++]);
+ for (reg = 0xFD52; reg < 0xFD69; reg++)
+ (void)rtsx_read(sc, reg, &sc->regs[i++]);
+ for (reg = 0xFE20; reg < 0xFE34; reg++)
+ (void)rtsx_read(sc, reg, &sc->regs[i++]);
+
+ sc->regs4[0] = READ4(sc, RTSX_HCBAR);
+ sc->regs4[1] = READ4(sc, RTSX_HCBCTLR);
+ sc->regs4[2] = READ4(sc, RTSX_HDBAR);
+ sc->regs4[3] = READ4(sc, RTSX_HDBCTLR);
+ sc->regs4[4] = READ4(sc, RTSX_HAIMR);
+ sc->regs4[5] = READ4(sc, RTSX_BIER);
+ /* Not saving RTSX_BIPR. */
+
+ splx(s);
+}
+
+void
+rtsx_restore_regs(struct rtsx_softc *sc)
+{
+ int s, i;
+ u_int16_t reg;
+
+ s = splsdmmc();
+
+ WRITE4(sc, RTSX_HCBAR, sc->regs4[0]);
+ WRITE4(sc, RTSX_HCBCTLR, sc->regs4[1]);
+ WRITE4(sc, RTSX_HDBAR, sc->regs4[2]);
+ WRITE4(sc, RTSX_HDBCTLR, sc->regs4[3]);
+ WRITE4(sc, RTSX_HAIMR, sc->regs4[4]);
+ WRITE4(sc, RTSX_BIER, sc->regs4[5]);
+ /* Not writing RTSX_BIPR since doing so would clear it. */
+
+ i = 0;
+ for (reg = 0xFDA0; reg < 0xFDAE; reg++)
+ (void)rtsx_write(sc, reg, 0xff, sc->regs[i++]);
+ for (reg = 0xFD52; reg < 0xFD69; reg++)
+ (void)rtsx_write(sc, reg, 0xff, sc->regs[i++]);
+ for (reg = 0xFE20; reg < 0xFE34; reg++)
+ (void)rtsx_write(sc, reg, 0xff, sc->regs[i++]);
+
+ splx(s);
+}
+
+u_int8_t
+rtsx_response_type(u_int16_t sdmmc_rsp)
+{
+ int i;
+ struct rsp_type {
+ u_int16_t sdmmc_rsp;
+ u_int8_t rtsx_rsp;
+ } rsp_types[] = {
+ { SCF_RSP_R0, RTSX_SD_RSP_TYPE_R0 },
+ { SCF_RSP_R1, RTSX_SD_RSP_TYPE_R1 },
+ { SCF_RSP_R1B, RTSX_SD_RSP_TYPE_R1B },
+ { SCF_RSP_R2, RTSX_SD_RSP_TYPE_R2 },
+ { SCF_RSP_R3, RTSX_SD_RSP_TYPE_R3 },
+ { SCF_RSP_R4, RTSX_SD_RSP_TYPE_R4 },
+ { SCF_RSP_R5, RTSX_SD_RSP_TYPE_R5 },
+ { SCF_RSP_R6, RTSX_SD_RSP_TYPE_R6 },
+ { SCF_RSP_R7, RTSX_SD_RSP_TYPE_R7 }
+ };
+
+ for (i = 0; i < nitems(rsp_types); i++) {
+ if (sdmmc_rsp == rsp_types[i].sdmmc_rsp)
+ return rsp_types[i].rtsx_rsp;
+ }
+
+ return 0;
+}
+
+int
+rtsx_hostcmd_send(struct rtsx_softc *sc, int ncmd, int wait)
+{
+ int s, error = 0;
+
+ s = splsdmmc();
+
+ /* Tell the chip where the command buffer is and run the commands. */
+ WRITE4(sc, RTSX_HCBAR, sc->dmap_cmd->dm_segs[0].ds_addr);
+ WRITE4(sc, RTSX_HCBCTLR,
+ ((ncmd * 4) & 0x00ffffff) | RTSX_START_CMD | RTSX_HW_AUTO_RSP);
+
+ splx(s);
+
+ if (wait) {
+ /* Wait for completion. */
+ error = rtsx_wait_intr(sc, wait, hz);
+ }
+
+ return error;
+}
+
+int
+rtsx_xfer(struct rtsx_softc *sc, struct sdmmc_command *cmd, u_int32_t *cmdbuf)
+{
+ caddr_t datakvap;
+ bus_dma_segment_t segs;
+ int ncmd, s, dma_dir, error, rsegs, tmode;
+ int read = ISSET(cmd->c_flags, SCF_CMD_READ);
+ u_int8_t cfg2;
+
+ DPRINTF(3,("%s: %s xfer: %d bytes with block size %d\n", DEVNAME(sc),
+ read ? "read" : "write",
+ cmd->c_datalen, cmd->c_blklen));
+
+ if (cmd->c_datalen > RTSX_DMA_DATA_BUFSIZE) {
+ DPRINTF(3, ("%s: cmd->c_datalen too large: %d > %d\n",
+ DEVNAME(sc), cmd->c_datalen, RTSX_DMA_DATA_BUFSIZE));
+ return ENOMEM;
+ }
+
+ /* Configure DMA transfer mode parameters. */
+ cfg2 = RTSX_SD_NO_CHECK_WAIT_CRC_TO | RTSX_SD_CHECK_CRC16 |
+ RTSX_SD_NO_WAIT_BUSY_END | RTSX_SD_RSP_LEN_0;
+ if (read) {
+ dma_dir = RTSX_DMA_DIR_FROM_CARD;
+ /* Use transfer mode AUTO_READ3, which assumes we've already
+ * sent the read command and gotten the response, and will
+ * send CMD 12 manually after reading multiple blocks. */
+ tmode = RTSX_TM_AUTO_READ3;
+ cfg2 |= RTSX_SD_CALCULATE_CRC7 | RTSX_SD_CHECK_CRC7;
+ } else {
+ dma_dir = RTSX_DMA_DIR_TO_CARD;
+ /* Use transfer mode AUTO_WRITE3, which assumes we've already
+ * sent the write command and gotten the response, and will
+ * send CMD 12 manually after writing multiple blocks. */
+ tmode = RTSX_TM_AUTO_WRITE3;
+ cfg2 |= RTSX_SD_NO_CALCULATE_CRC7 | RTSX_SD_NO_CHECK_CRC7;
+ }
+
+ ncmd = 0;
+
+ rtsx_hostcmd(cmdbuf, &ncmd, RTSX_WRITE_REG_CMD, RTSX_SD_CFG2,
+ 0xff, cfg2);
+
+ /* Queue commands to configure data transfer size. */
+ rtsx_hostcmd(cmdbuf, &ncmd,
+ RTSX_WRITE_REG_CMD, RTSX_SD_BYTE_CNT_L, 0xff, 0);
+ rtsx_hostcmd(cmdbuf, &ncmd,
+ RTSX_WRITE_REG_CMD, RTSX_SD_BYTE_CNT_H, 0xff, 0x02);
+ rtsx_hostcmd(cmdbuf, &ncmd,
+ RTSX_WRITE_REG_CMD, RTSX_SD_BLOCK_CNT_L, 0xff,
+ ((cmd->c_datalen / cmd->c_blklen) & 0xff));
+ rtsx_hostcmd(cmdbuf, &ncmd,
+ RTSX_WRITE_REG_CMD, RTSX_SD_BLOCK_CNT_H, 0xff,
+ ((cmd->c_datalen / cmd->c_blklen) >> 8));
+
+ /* Use the DMA ring buffer for commands which transfer data. */
+ rtsx_hostcmd(cmdbuf, &ncmd,
+ RTSX_WRITE_REG_CMD, RTSX_CARD_DATA_SOURCE, 0x01, RTSX_RING_BUFFER);
+
+ /* Configure DMA controller. */
+ rtsx_hostcmd(cmdbuf, &ncmd, RTSX_WRITE_REG_CMD, RTSX_IRQSTAT0,
+ RTSX_DMA_DONE_INT, RTSX_DMA_DONE_INT);
+ rtsx_hostcmd(cmdbuf, &ncmd,
+ RTSX_WRITE_REG_CMD, RTSX_DMATC3, 0xff, cmd->c_datalen >> 24);
+ rtsx_hostcmd(cmdbuf, &ncmd,
+ RTSX_WRITE_REG_CMD, RTSX_DMATC2, 0xff, cmd->c_datalen >> 16);
+ rtsx_hostcmd(cmdbuf, &ncmd,
+ RTSX_WRITE_REG_CMD, RTSX_DMATC1, 0xff, cmd->c_datalen >> 8);
+ rtsx_hostcmd(cmdbuf, &ncmd,
+ RTSX_WRITE_REG_CMD, RTSX_DMATC0, 0xff, cmd->c_datalen);
+ rtsx_hostcmd(cmdbuf, &ncmd,
+ RTSX_WRITE_REG_CMD, RTSX_DMACTL,
+ 0x03 | RTSX_DMA_PACK_SIZE_MASK,
+ dma_dir | RTSX_DMA_EN | RTSX_DMA_512);
+
+ /* Queue commands to perform SD transfer. */
+ rtsx_hostcmd(cmdbuf, &ncmd,
+ RTSX_WRITE_REG_CMD, RTSX_SD_TRANSFER,
+ 0xff, tmode | RTSX_SD_TRANSFER_START);
+ rtsx_hostcmd(cmdbuf, &ncmd,
+ RTSX_CHECK_REG_CMD, RTSX_SD_TRANSFER,
+ RTSX_SD_TRANSFER_END, RTSX_SD_TRANSFER_END);
+
+ error = rtsx_hostcmd_send(sc, ncmd, 0);
+ if (error)
+ goto ret;
+
+ /* Allocate and map DMA memory for data transfer. */
+ error = bus_dmamem_alloc(sc->dmat, cmd->c_datalen, 0, 0, &segs, 1,
+ &rsegs, BUS_DMA_WAITOK|BUS_DMA_ZERO);
+ if (error) {
+ DPRINTF(3, ("%s: could not allocate %d bytes\n",
+ DEVNAME(sc), cmd->c_datalen));
+ goto ret;
+ }
+ error = bus_dmamem_map(sc->dmat, &segs, rsegs, cmd->c_datalen,
+ &datakvap, BUS_DMA_WAITOK|BUS_DMA_COHERENT);
+ if (error) {
+ DPRINTF(3, ("%s: could not map data buffer\n", DEVNAME(sc)));
+ goto free_databuf;
+ }
+
+ /* If this is a write, copy data from sdmmc-provided buffer. */
+ if (!read)
+ memcpy(datakvap, cmd->c_data, cmd->c_datalen);
+
+ /* Load the data buffer and sync it. */
+ error = bus_dmamap_load(sc->dmat, sc->dmap_data, datakvap,
+ cmd->c_datalen, NULL, BUS_DMA_WAITOK);
+ if (error) {
+ DPRINTF(3, ("%s: could not load DMA map\n", DEVNAME(sc)));
+ goto unmap_databuf;
+ }
+ bus_dmamap_sync(sc->dmat, sc->dmap_data, 0, cmd->c_datalen,
+ BUS_DMASYNC_PREREAD);
+ bus_dmamap_sync(sc->dmat, sc->dmap_data, 0, cmd->c_datalen,
+ BUS_DMASYNC_PREWRITE);
+
+ s = splsdmmc();
+
+ /* Tell the chip where the data buffer is and run the transfer. */
+ WRITE4(sc, RTSX_HDBAR, sc->dmap_data->dm_segs[0].ds_addr);
+ WRITE4(sc, RTSX_HDBCTLR, RTSX_TRIG_DMA | (read ? RTSX_DMA_READ : 0) |
+ (sc->dmap_data->dm_segs[0].ds_len & 0x00ffffff));
+
+ splx(s);
+
+ /* Wait for completion. */
+ error = rtsx_wait_intr(sc, RTSX_TRANS_OK_INT, 10*hz);
+ if (error)
+ goto unload_databuf;
+
+ /* Sync and unload data DMA buffer. */
+ bus_dmamap_sync(sc->dmat, sc->dmap_data, 0, cmd->c_datalen,
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_sync(sc->dmat, sc->dmap_data, 0, cmd->c_datalen,
+ BUS_DMASYNC_POSTWRITE);
+
+unload_databuf:
+ bus_dmamap_unload(sc->dmat, sc->dmap_data);
+
+ /* If this is a read, copy data into sdmmc-provided buffer. */
+ if (error == 0 && read)
+ memcpy(cmd->c_data, datakvap, cmd->c_datalen);
+
+ /* Free DMA data buffer. */
+unmap_databuf:
+ bus_dmamem_unmap(sc->dmat, datakvap, cmd->c_datalen);
+free_databuf:
+ bus_dmamem_free(sc->dmat, &segs, rsegs);
+ret:
+ DPRINTF(3,("%s: xfer done, error=%d\n", DEVNAME(sc), error));
+ return error;
+}
+
+void
+rtsx_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd)
+{
+ struct rtsx_softc *sc = sch;
+ bus_dma_segment_t segs;
+ int rsegs;
+ caddr_t cmdkvap;
+ u_int32_t *cmdbuf;
+ u_int8_t rsp_type;
+ u_int16_t r;
+ int ncmd;
+ int error = 0;
+
+ DPRINTF(3,("%s: executing cmd %hu\n", DEVNAME(sc), cmd->c_opcode));
+
+ /* Refuse SDIO probe if the chip doesn't support SDIO. */
+ if (cmd->c_opcode == SD_IO_SEND_OP_COND &&
+ !ISSET(sc->flags, RTSX_F_SDIO_SUPPORT)) {
+ error = ENOTSUP;
+ goto ret;
+ }
+
+ rsp_type = rtsx_response_type(cmd->c_flags & 0xff00);
+ if (rsp_type == 0) {
+ printf("%s: unknown response type 0x%x\n", DEVNAME(sc),
+ (cmd->c_flags & 0xff00));
+ error = EINVAL;
+ goto ret;
+ }
+
+ /* Allocate and map the host command buffer. */
+ error = bus_dmamem_alloc(sc->dmat, RTSX_HOSTCMD_BUFSIZE, 0, 0, &segs, 1,
+ &rsegs, BUS_DMA_WAITOK|BUS_DMA_ZERO);
+ if (error)
+ goto ret;
+ error = bus_dmamem_map(sc->dmat, &segs, rsegs, RTSX_HOSTCMD_BUFSIZE,
+ &cmdkvap, BUS_DMA_WAITOK|BUS_DMA_COHERENT);
+ if (error)
+ goto free_cmdbuf;
+
+ /* The command buffer queues commands the host controller will
+ * run asynchronously. */
+ cmdbuf = (u_int32_t *)cmdkvap;
+ ncmd = 0;
+
+ /* Queue commands to set SD command index and argument. */
+ rtsx_hostcmd(cmdbuf, &ncmd,
+ RTSX_WRITE_REG_CMD, RTSX_SD_CMD0, 0xff, 0x40 | cmd->c_opcode);
+ rtsx_hostcmd(cmdbuf, &ncmd,
+ RTSX_WRITE_REG_CMD, RTSX_SD_CMD1, 0xff, cmd->c_arg >> 24);
+ rtsx_hostcmd(cmdbuf, &ncmd,
+ RTSX_WRITE_REG_CMD, RTSX_SD_CMD2, 0xff, cmd->c_arg >> 16);
+ rtsx_hostcmd(cmdbuf, &ncmd,
+ RTSX_WRITE_REG_CMD, RTSX_SD_CMD3, 0xff, cmd->c_arg >> 8);
+ rtsx_hostcmd(cmdbuf, &ncmd,
+ RTSX_WRITE_REG_CMD, RTSX_SD_CMD4, 0xff, cmd->c_arg);
+
+ /* Queue command to set response type. */
+ rtsx_hostcmd(cmdbuf, &ncmd,
+ RTSX_WRITE_REG_CMD, RTSX_SD_CFG2, 0xff, rsp_type);
+
+ /* Use the ping-pong buffer for commands which do not transfer data. */
+ rtsx_hostcmd(cmdbuf, &ncmd,
+ RTSX_WRITE_REG_CMD, RTSX_CARD_DATA_SOURCE,
+ 0x01, RTSX_PINGPONG_BUFFER);
+
+ /* Queue commands to perform SD transfer. */
+ rtsx_hostcmd(cmdbuf, &ncmd,
+ RTSX_WRITE_REG_CMD, RTSX_SD_TRANSFER,
+ 0xff, RTSX_TM_CMD_RSP | RTSX_SD_TRANSFER_START);
+ rtsx_hostcmd(cmdbuf, &ncmd,
+ RTSX_CHECK_REG_CMD, RTSX_SD_TRANSFER,
+ RTSX_SD_TRANSFER_END|RTSX_SD_STAT_IDLE,
+ RTSX_SD_TRANSFER_END|RTSX_SD_STAT_IDLE);
+
+ /* Queue commands to read back card status response.*/
+ if (rsp_type == RTSX_SD_RSP_TYPE_R2) {
+ for (r = RTSX_PPBUF_BASE2 + 15; r > RTSX_PPBUF_BASE2; r--)
+ rtsx_hostcmd(cmdbuf, &ncmd, RTSX_READ_REG_CMD, r, 0, 0);
+ rtsx_hostcmd(cmdbuf, &ncmd, RTSX_READ_REG_CMD, RTSX_SD_CMD5,
+ 0, 0);
+ } else if (rsp_type != RTSX_SD_RSP_TYPE_R0) {
+ for (r = RTSX_SD_CMD0; r <= RTSX_SD_CMD4; r++)
+ rtsx_hostcmd(cmdbuf, &ncmd, RTSX_READ_REG_CMD, r, 0, 0);
+ }
+
+ /* Load and sync command DMA buffer. */
+ error = bus_dmamap_load(sc->dmat, sc->dmap_cmd, cmdkvap,
+ RTSX_HOSTCMD_BUFSIZE, NULL, BUS_DMA_WAITOK);
+ if (error)
+ goto unmap_cmdbuf;
+
+ bus_dmamap_sync(sc->dmat, sc->dmap_cmd, 0, RTSX_HOSTCMD_BUFSIZE,
+ BUS_DMASYNC_PREREAD);
+ bus_dmamap_sync(sc->dmat, sc->dmap_cmd, 0, RTSX_HOSTCMD_BUFSIZE,
+ BUS_DMASYNC_PREWRITE);
+
+ /* Run the command queue and wait for completion. */
+ error = rtsx_hostcmd_send(sc, ncmd, RTSX_TRANS_OK_INT);
+ if (error)
+ goto unload_cmdbuf;
+
+ bus_dmamap_sync(sc->dmat, sc->dmap_cmd, 0, RTSX_HOSTCMD_BUFSIZE,
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_sync(sc->dmat, sc->dmap_cmd, 0, RTSX_HOSTCMD_BUFSIZE,
+ BUS_DMASYNC_POSTWRITE);
+
+ /* Copy card response into sdmmc response buffer. */
+ if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
+ /* Copy bytes like sdhc(4), which on little-endian uses
+ * different byte order for short and long responses... */
+ if (ISSET(cmd->c_flags, SCF_RSP_136)) {
+ memcpy(cmd->c_resp, cmdkvap + 1, sizeof(cmd->c_resp));
+ } else {
+ /* First byte is CHECK_REG_CMD return value, second
+ * one is the command op code -- we skip those. */
+ cmd->c_resp[0] =
+ ((betoh32(cmdbuf[0]) & 0x0000ffff) << 16) |
+ ((betoh32(cmdbuf[1]) & 0xffff0000) >> 16);
+ }
+ }
+
+ if (cmd->c_data) {
+ error = rtsx_xfer(sc, cmd, cmdbuf);
+ if (error) {
+ u_int8_t stat1;
+
+ if (rtsx_read(sc, RTSX_SD_STAT1, &stat1) == 0 &&
+ (stat1 & RTSX_SD_CRC_ERR))
+ printf("%s: CRC error\n", DEVNAME(sc));
+ }
+ }
+
+unload_cmdbuf:
+ bus_dmamap_unload(sc->dmat, sc->dmap_cmd);
+unmap_cmdbuf:
+ bus_dmamem_unmap(sc->dmat, cmdkvap, RTSX_HOSTCMD_BUFSIZE);
+free_cmdbuf:
+ bus_dmamem_free(sc->dmat, &segs, rsegs);
+ret:
+ SET(cmd->c_flags, SCF_ITSDONE);
+ cmd->c_error = error;
+}
+
+/* Prepare for another command. */
+void
+rtsx_soft_reset(struct rtsx_softc *sc)
+{
+ DPRINTF(1,("%s: soft reset\n", DEVNAME(sc)));
+
+ /* Stop command transfer. */
+ WRITE4(sc, RTSX_HCBCTLR, RTSX_STOP_CMD);
+
+ (void)rtsx_write(sc, RTSX_CARD_STOP, RTSX_SD_STOP|RTSX_SD_CLR_ERR,
+ RTSX_SD_STOP|RTSX_SD_CLR_ERR);
+
+ /* Stop DMA transfer. */
+ WRITE4(sc, RTSX_HDBCTLR, RTSX_STOP_DMA);
+ (void)rtsx_write(sc, RTSX_DMACTL, RTSX_DMA_RST, RTSX_DMA_RST);
+
+ (void)rtsx_write(sc, RTSX_RBCTL, RTSX_RB_FLUSH, RTSX_RB_FLUSH);
+}
+
+int
+rtsx_wait_intr(struct rtsx_softc *sc, int mask, int timo)
+{
+ int status;
+ int error = 0;
+ int s;
+
+ mask |= RTSX_TRANS_FAIL_INT;
+
+ s = splsdmmc();
+ status = sc->intr_status & mask;
+ while (status == 0) {
+ if (tsleep(&sc->intr_status, PRIBIO, "rtsxintr", timo)
+ == EWOULDBLOCK) {
+ rtsx_soft_reset(sc);
+ error = ETIMEDOUT;
+ break;
+ }
+ status = sc->intr_status & mask;
+ }
+ sc->intr_status &= ~status;
+
+ /* Has the card disappeared? */
+ if (!ISSET(sc->flags, RTSX_F_CARD_PRESENT))
+ error = ENODEV;
+
+ splx(s);
+
+ if (error == 0 && (status & RTSX_TRANS_FAIL_INT))
+ error = EIO;
+
+ return error;
+}
+
+void
+rtsx_card_insert(struct rtsx_softc *sc)
+{
+ DPRINTF(1, ("%s: card inserted\n", DEVNAME(sc)));
+
+ sc->flags |= RTSX_F_CARD_PRESENT;
+ (void)rtsx_led_enable(sc);
+
+ /* Schedule card discovery task. */
+ sdmmc_needs_discover(sc->sdmmc);
+}
+
+void
+rtsx_card_eject(struct rtsx_softc *sc)
+{
+ DPRINTF(1, ("%s: card ejected\n", DEVNAME(sc)));
+
+ sc->flags &= ~RTSX_F_CARD_PRESENT;
+ (void)rtsx_led_disable(sc);
+
+ /* Schedule card discovery task. */
+ sdmmc_needs_discover(sc->sdmmc);
+}
+
+/*
+ * Established by attachment driver at interrupt priority IPL_SDMMC.
+ */
+int
+rtsx_intr(void *arg)
+{
+ struct rtsx_softc *sc = arg;
+ u_int32_t enabled, status;
+
+ enabled = READ4(sc, RTSX_BIER);
+ status = READ4(sc, RTSX_BIPR);
+
+ /* Ack interrupts. */
+ WRITE4(sc, RTSX_BIPR, status);
+
+ if (((enabled & status) == 0) || status == 0xffffffff)
+ return 0;
+
+ if (status & RTSX_SD_INT) {
+ if (status & RTSX_SD_EXIST) {
+ if (!ISSET(sc->flags, RTSX_F_CARD_PRESENT))
+ rtsx_card_insert(sc);
+ } else {
+ rtsx_card_eject(sc);
+ }
+ }
+
+ if (status & (RTSX_TRANS_OK_INT | RTSX_TRANS_FAIL_INT)) {
+ sc->intr_status |= status;
+ wakeup(&sc->intr_status);
+ }
+
+ return 1;
+}
diff --git a/sys/dev/ic/rtsxreg.h b/sys/dev/ic/rtsxreg.h
new file mode 100644
index 00000000000..b43348a47a0
--- /dev/null
+++ b/sys/dev/ic/rtsxreg.h
@@ -0,0 +1,495 @@
+/* $OpenBSD: rtsxreg.h,v 1.1 2012/11/29 23:36:34 stsp Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ * Copyright (c) 2012 Stefan Sperling <stsp@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _RTSXREG_H_
+#define _RTSXREG_H_
+
+/* Host command buffer control register. */
+#define RTSX_HCBAR 0x00
+#define RTSX_HCBCTLR 0x04
+#define RTSX_START_CMD (1 << 31)
+#define RTSX_HW_AUTO_RSP (1 << 30)
+#define RTSX_STOP_CMD (1 << 28)
+
+/* Host data buffer control register. */
+#define RTSX_HDBAR 0x08
+#define RTSX_HDBCTLR 0x0C
+#define RTSX_TRIG_DMA (1 << 31)
+#define RTSX_DMA_READ (1 << 29)
+#define RTSX_STOP_DMA (1 << 28)
+#define RTSX_ADMA_MODE (2 << 26)
+
+/* Interrupt pending register. */
+#define RTSX_BIPR 0x14
+#define RTSX_CMD_DONE_INT (1 << 31)
+#define RTSX_DATA_DONE_INT (1 << 30)
+#define RTSX_TRANS_OK_INT (1 << 29)
+#define RTSX_TRANS_FAIL_INT (1 << 28)
+#define RTSX_XD_INT (1 << 27)
+#define RTSX_MS_INT (1 << 26)
+#define RTSX_SD_INT (1 << 25)
+#define RTSX_SD_WRITE_PROTECT (1 << 19)
+#define RTSX_XD_EXIST (1 << 18)
+#define RTSX_MS_EXIST (1 << 17)
+#define RTSX_SD_EXIST (1 << 16)
+#define RTSX_CARD_EXIST (RTSX_XD_EXIST|RTSX_MS_EXIST|RTSX_SD_EXIST)
+#define RTSX_CARD_INT (RTSX_XD_INT|RTSX_MS_INT|RTSX_SD_INT)
+
+/* Chip register access. */
+#define RTSX_HAIMR 0x10
+#define RTSX_HAIMR_WRITE 0x40000000
+#define RTSX_HAIMR_BUSY 0x80000000
+
+/* Interrupt enable register. */
+#define RTSX_BIER 0x18
+#define RTSX_CMD_DONE_INT_EN (1 << 31)
+#define RTSX_DATA_DONE_INT_EN (1 << 30)
+#define RTSX_TRANS_OK_INT_EN (1 << 29)
+#define RTSX_TRANS_FAIL_INT_EN (1 << 28)
+#define RTSX_XD_INT_EN (1 << 27)
+#define RTSX_MS_INT_EN (1 << 26)
+#define RTSX_SD_INT_EN (1 << 25)
+#define RTSX_GPIO0_INT_EN (1 << 24)
+#define RTSX_MS_OC_INT_EN (1 << 23)
+#define RTSX_SD_OC_INT_EN (1 << 22)
+
+/* Power on/off. */
+#define RTSX_FPDCTL 0xFC00
+#define RTSX_SSC_POWER_DOWN 0x01
+#define RTSX_SD_OC_POWER_DOWN 0x02
+#define RTSX_MS_OC_POWER_DOWN 0x04
+#define RTSX_ALL_POWER_DOWN 0x07
+#define RTSX_OC_POWER_DOWN 0x06
+
+/* Card power control register. */
+#define RTSX_CARD_PWR_CTL 0xFD50
+#define RTSX_SD_PWR_ON 0x00
+#define RTSX_SD_PARTIAL_PWR_ON 0x01
+#define RTSX_SD_PWR_OFF 0x03
+#define RTSX_SD_PWR_MASK 0x03
+#define RTSX_PMOS_STRG_MASK 0x10
+#define RTSX_PMOS_STRG_400mA 0x00
+#define RTSX_PMOS_STRG_800mA 0x10
+
+#define RTSX_MS_PWR_OFF 0x0C
+#define RTSX_MS_PWR_ON 0x00
+#define RTSX_MS_PARTIAL_PWR_ON 0x04
+
+#define RTSX_CARD_SHARE_MODE 0xFD52
+#define RTSX_CARD_SHARE_48_XD 0x02
+#define RTSX_CARD_SHARE_48_SD 0x04
+#define RTSX_CARD_SHARE_48_MS 0x08
+#define RTSX_CARD_DRIVE_SEL 0xFE53
+
+/* Card clock. */
+#define RTSX_CARD_CLK_EN 0xFD69
+#define RTSX_XD_CLK_EN 0x02
+#define RTSX_SD_CLK_EN 0x04
+#define RTSX_MS_CLK_EN 0x08
+#define RTSX_SPI_CLK_EN 0x10
+#define RTSX_CARD_CLK_EN_ALL \
+ (RTSX_XD_CLK_EN|RTSX_SD_CLK_EN|RTSX_MS_CLK_EN|RTSX_SPI_CLK_EN)
+
+#define RTSX_SDIO_CTRL 0xFD6B
+#define RTSX_SDIO_BUS_CTRL 0x01
+#define RTSX_SDIO_CD_CTRL 0x02
+
+/* Internal clock. */
+#define RTSX_CLK_CTL 0xFC02
+#define RTSX_CLK_LOW_FREQ 0x01
+
+/* Internal clock divisor values. */
+#define RTSX_CLK_DIV 0xFC03
+#define RTSX_CLK_DIV_1 0x01
+#define RTSX_CLK_DIV_2 0x02
+#define RTSX_CLK_DIV_4 0x03
+#define RTSX_CLK_DIV_8 0x04
+
+/* Internal clock selection. */
+#define RTSX_CLK_SEL 0xFC04
+#define RTSX_SSC_80 0
+#define RTSX_SSC_100 1
+#define RTSX_SSC_120 2
+#define RTSX_SSC_150 3
+#define RTSX_SSC_200 4
+
+#define RTSX_SSC_DIV_N_0 0xFC0F
+
+#define RTSX_SSC_CTL1 0xFC11
+#define RTSX_RSTB 0x80
+#define RTSX_SSC_8X_EN 0x40
+#define RTSX_SSC_FIX_FRAC 0x20
+#define RTSX_SSC_SEL_1M 0x00
+#define RTSX_SSC_SEL_2M 0x08
+#define RTSX_SSC_SEL_2M 0x08
+#define RTSX_SSC_SEL_4M 0x10
+#define RTSX_SSC_SEL_8M 0x18
+#define RTSX_SSC_CTL2 0xFC12
+#define RTSX_SSC_DEPTH_MASK 0x07
+
+/* RC oscillator, default is 2M */
+#define RTSX_RCCTL 0xFC14
+#define RTSX_RCCTL_F_400K 0x0
+#define RTSX_RCCTL_F_2M 0x1
+
+/* Host controller commands. */
+#define RTSX_READ_REG_CMD 0
+#define RTSX_WRITE_REG_CMD 1
+#define RTSX_CHECK_REG_CMD 2
+
+
+#define RTSX_OCPCTL 0xFC15
+#define RTSX_OCPSTAT 0xFC16
+#define RTSX_OCPGLITCH 0xFC17
+#define RTSX_OCPPARA1 0xFC18
+#define RTSX_OCPPARA2 0xFC19
+
+/* FPGA */
+#define RTSX_FPGA_PULL_CTL 0xFC1D
+#define RTSX_FPGA_MS_PULL_CTL_BIT 0x10
+#define RTSX_FPGA_SD_PULL_CTL_BIT 0x08
+
+/* Clock source configuration register. */
+#define RTSX_CARD_CLK_SOURCE 0xFC2E
+#define RTSX_CRC_FIX_CLK (0x00 << 0)
+#define RTSX_CRC_VAR_CLK0 (0x01 << 0)
+#define RTSX_CRC_VAR_CLK1 (0x02 << 0)
+#define RTSX_SD30_FIX_CLK (0x00 << 2)
+#define RTSX_SD30_VAR_CLK0 (0x01 << 2)
+#define RTSX_SD30_VAR_CLK1 (0x02 << 2)
+#define RTSX_SAMPLE_FIX_CLK (0x00 << 4)
+#define RTSX_SAMPLE_VAR_CLK0 (0x01 << 4)
+#define RTSX_SAMPLE_VAR_CLK1 (0x02 << 4)
+
+
+/* ASIC */
+#define RTSX_CARD_PULL_CTL1 0xFD60
+#define RTSX_CARD_PULL_CTL2 0xFD61
+#define RTSX_CARD_PULL_CTL3 0xFD62
+
+#define RTSX_PULL_CTL_DISABLE12 0x55
+#define RTSX_PULL_CTL_DISABLE3 0xD5
+#define RTSX_PULL_CTL_ENABLE12 0xAA
+#define RTSX_PULL_CTL_ENABLE3 0xE9
+
+/* SD configuration register 1 (clock divider, bus mode and width). */
+#define RTSX_SD_CFG1 0xFDA0
+#define RTSX_CLK_DIVIDE_0 0x00
+#define RTSX_CLK_DIVIDE_128 0x80
+#define RTSX_CLK_DIVIDE_256 0xC0
+#define RTSX_CLK_DIVIDE_MASK 0xC0
+#define RTSX_SD20_MODE 0x00
+#define RTSX_SDDDR_MODE 0x04
+#define RTSX_SD30_MODE 0x08
+#define RTSX_SD_MODE_MASK 0x0C
+#define RTSX_BUS_WIDTH_1 0x00
+#define RTSX_BUS_WIDTH_4 0x01
+#define RTSX_BUS_WIDTH_8 0x02
+#define RTSX_BUS_WIDTH_MASK 0x03
+
+/* SD configuration register 2 (SD command response flags). */
+#define RTSX_SD_CFG2 0xFDA1
+#define RTSX_SD_CALCULATE_CRC7 0x00
+#define RTSX_SD_NO_CALCULATE_CRC7 0x80
+#define RTSX_SD_CHECK_CRC16 0x00
+#define RTSX_SD_NO_CHECK_CRC16 0x40
+#define RTSX_SD_NO_CHECK_WAIT_CRC_TO 0x20
+#define RTSX_SD_WAIT_BUSY_END 0x08
+#define RTSX_SD_NO_WAIT_BUSY_END 0x00
+#define RTSX_SD_CHECK_CRC7 0x00
+#define RTSX_SD_NO_CHECK_CRC7 0x04
+#define RTSX_SD_RSP_LEN_0 0x00
+#define RTSX_SD_RSP_LEN_6 0x01
+#define RTSX_SD_RSP_LEN_17 0x02
+/* SD command response types. */
+#define RTSX_SD_RSP_TYPE_R0 0x04
+#define RTSX_SD_RSP_TYPE_R1 0x01
+#define RTSX_SD_RSP_TYPE_R1B 0x09
+#define RTSX_SD_RSP_TYPE_R2 0x02
+#define RTSX_SD_RSP_TYPE_R3 0x05
+#define RTSX_SD_RSP_TYPE_R4 0x05
+#define RTSX_SD_RSP_TYPE_R5 0x01
+#define RTSX_SD_RSP_TYPE_R6 0x01
+#define RTSX_SD_RSP_TYPE_R7 0x01
+
+#define RTSX_SD_STAT1 0xFDA3
+#define RTSX_SD_CRC7_ERR 0x80
+#define RTSX_SD_CRC16_ERR 0x40
+#define RTSX_SD_CRC_WRITE_ERR 0x20
+#define RTSX_SD_CRC_WRITE_ERR_MASK 0x1C
+#define RTSX_GET_CRC_TIME_OUT 0x02
+#define RTSX_SD_TUNING_COMPARE_ERR 0x01
+#define RTSX_SD_STAT2 0xFDA4
+#define RTSX_SD_RSP_80CLK_TIMEOUT 0x01
+
+#define RTSX_SD_CRC_ERR (RTSX_SD_CRC7_ERR|RTSX_SD_CRC16_ERR|RTSX_SD_CRC_WRITE_ERR)
+
+/* SD bus status register. */
+#define RTSX_SD_BUS_STAT 0xFDA5
+#define RTSX_SD_CLK_TOGGLE_EN 0x80
+#define RTSX_SD_CLK_FORCE_STOP 0x40
+#define RTSX_SD_DAT3_STATUS 0x10
+#define RTSX_SD_DAT2_STATUS 0x08
+#define RTSX_SD_DAT1_STATUS 0x04
+#define RTSX_SD_DAT0_STATUS 0x02
+#define RTSX_SD_CMD_STATUS 0x01
+
+#define RTSX_SD_PAD_CTL 0xFDA6
+#define RTSX_SD_IO_USING_1V8 0x80
+
+/* Sample point control register. */
+#define RTSX_SD_SAMPLE_POINT_CTL 0xFDA7
+#define RTSX_DDR_FIX_RX_DAT 0x00
+#define RTSX_DDR_VAR_RX_DAT 0x80
+#define RTSX_DDR_FIX_RX_DAT_EDGE 0x00
+#define RTSX_DDR_FIX_RX_DAT_14_DELAY 0x40
+#define RTSX_DDR_FIX_RX_CMD 0x00
+#define RTSX_DDR_VAR_RX_CMD 0x20
+#define RTSX_DDR_FIX_RX_CMD_POS_EDGE 0x00
+#define RTSX_DDR_FIX_RX_CMD_14_DELAY 0x10
+#define RTSX_SD20_RX_POS_EDGE 0x00
+#define RTSX_SD20_RX_14_DELAY 0x08
+#define RTSX_SD20_RX_SEL_MASK 0x08
+
+#define RTSX_SD_PUSH_POINT_CTL 0xFDA8
+#define RTSX_SD20_TX_NEG_EDGE 0x00
+
+#define RTSX_SD_CMD0 0xFDA9
+#define RTSX_SD_CMD1 0xFDAA
+#define RTSX_SD_CMD2 0xFDAB
+#define RTSX_SD_CMD3 0xFDAC
+#define RTSX_SD_CMD4 0xFDAD
+#define RTSX_SD_CMD5 0xFDAE
+#define RTSX_SD_BYTE_CNT_L 0xFDAF
+#define RTSX_SD_BYTE_CNT_H 0xFDB0
+#define RTSX_SD_BLOCK_CNT_L 0xFDB1
+#define RTSX_SD_BLOCK_CNT_H 0xFDB2
+
+/*
+ * Transfer modes.
+ */
+#define RTSX_SD_TRANSFER 0xFDB3
+
+/* Write one or two bytes from SD_CMD2 and SD_CMD3 to the card. */
+#define RTSX_TM_NORMAL_WRITE 0x00
+
+/* Write (SD_BYTE_CNT * SD_BLOCK_COUNTS) bytes from ring buffer to card. */
+#define RTSX_TM_AUTO_WRITE3 0x01
+
+/* Like AUTO_WRITE3, plus automatically send CMD 12 when done.
+ * The response to CMD 12 is written to SD_CMD{0,1,2,3,4}. */
+#define RTSX_TM_AUTO_WRITE4 0x02
+
+/* Read (SD_BYTE_CNT * SD_BLOCK_CNT) bytes from card into ring buffer. */
+#define RTSX_TM_AUTO_READ3 0x05
+
+/* Like AUTO_READ3, plus automatically send CMD 12 when done.
+ * The response to CMD 12 is written to SD_CMD{0,1,2,3,4}. */
+#define RTSX_TM_AUTO_READ4 0x06
+
+/* Send an SD command described in SD_CMD{0,1,2,3,4} to the card and put
+ * the response into SD_CMD{0,1,2,3,4}. Long responses (17 byte) are put
+ * into ping-pong buffer 2 instead. */
+#define RTSX_TM_CMD_RSP 0x08
+
+/* Send write command, get response from the card, write data from ring
+ * buffer to card, and send CMD 12 when done.
+ * The response to CMD 12 is written to SD_CMD{0,1,2,3,4}. */
+#define RTSX_TM_AUTO_WRITE1 0x09
+
+/* Like AUTO_WRITE1 except no CMD 12 is sent. */
+#define RTSX_TM_AUTO_WRITE2 0x0A
+
+/* Send read command, read up to 512 bytes (SD_BYTE_CNT * SD_BLOCK_CNT)
+ * from the card into the ring buffer or ping-pong buffer 2. */
+#define RTSX_TM_NORMAL_READ 0x0C
+
+/* Same as WRITE1, except data is read from the card to the ring buffer. */
+#define RTSX_TM_AUTO_READ1 0x0D
+
+/* Same as WRITE2, except data is read from the card to the ring buffer. */
+#define RTSX_TM_AUTO_READ2 0x0E
+
+/* Send CMD 19 and receive response and tuning pattern from card and
+ * report the result. */
+#define RTSX_TM_AUTO_TUNING 0x0F
+
+/* transfer control */
+#define RTSX_SD_TRANSFER_START 0x80
+#define RTSX_SD_TRANSFER_END 0x40
+#define RTSX_SD_STAT_IDLE 0x20
+#define RTSX_SD_TRANSFER_ERR 0x10
+
+#define RTSX_SD_CMD_STATE 0xFDB5
+#define RTSX_SD_DATA_STATE 0xFDB6
+
+#define RTSX_CARD_STOP 0xFD54
+#define RTSX_SPI_STOP 0x01
+#define RTSX_XD_STOP 0x02
+#define RTSX_SD_STOP 0x04
+#define RTSX_MS_STOP 0x08
+#define RTSX_SPI_CLR_ERR 0x10
+#define RTSX_XD_CLR_ERR 0x20
+#define RTSX_SD_CLR_ERR 0x40
+#define RTSX_MS_CLR_ERR 0x80
+#define RTSX_ALL_STOP 0x0F
+#define RTSX_ALL_CLR_ERR 0xF0
+
+#define RTSX_CARD_OE 0xFD55
+#define RTSX_XD_OUTPUT_EN 0x02
+#define RTSX_SD_OUTPUT_EN 0x04
+#define RTSX_MS_OUTPUT_EN 0x08
+#define RTSX_SPI_OUTPUT_EN 0x10
+#define RTSX_CARD_OUTPUT_EN (RTSX_XD_OUTPUT_EN|RTSX_SD_OUTPUT_EN|\
+ RTSX_MS_OUTPUT_EN)
+
+#define RTSX_CARD_DATA_SOURCE 0xFD5B
+#define RTSX_RING_BUFFER 0x00
+#define RTSX_PINGPONG_BUFFER 0x01
+#define RTSX_CARD_SELECT 0xFD5C
+#define RTSX_XD_MOD_SEL 0x01
+#define RTSX_SD_MOD_SEL 0x02
+#define RTSX_MS_MOD_SEL 0x03
+#define RTSX_SPI_MOD_SEL 0x04
+
+#define RTSX_CARD_GPIO_DIR 0xFD57
+#define RTSX_CARD_GPIO 0xFD58
+#define RTSX_CARD_GPIO_LED_OFF 0x01
+
+/* ping-pong buffer 2 */
+#define RTSX_PPBUF_BASE2 0xFA00
+#define RTSX_PPBUF_SIZE 256
+
+#define RTSX_SUPPORT_VOLTAGE (MMC_OCR_3_3V_3_4V|MMC_OCR_3_2V_3_3V|\
+ MMC_OCR_3_1V_3_2V|MMC_OCR_3_0V_3_1V|\
+ SD_OCR_SDHC_CAP)
+
+#define RTSX_CFG_PCI 0x1C
+#define RTSX_CFG_ASIC 0x10
+
+#define RTSX_IRQEN0 0xFE20
+#define RTSX_LINK_DOWN_INT_EN 0x10
+#define RTSX_LINK_READY_INT_EN 0x20
+#define RTSX_SUSPEND_INT_EN 0x40
+#define RTSX_DMA_DONE_INT_EN 0x80
+#define RTSX_IRQSTAT0 0xFE21
+#define RTSX_LINK_DOWN_INT 0x10
+#define RTSX_LINK_READY_INT 0x20
+#define RTSX_SUSPEND_INT 0x40
+#define RTSX_DMA_DONE_INT 0x80
+
+#define RTSX_DMATC0 0xFE28
+#define RTSX_DMATC1 0xFE29
+#define RTSX_DMATC2 0xFE2A
+#define RTSX_DMATC3 0xFE2B
+
+#define RTSX_DMACTL 0xFE2C
+#define RTSX_DMA_DIR_TO_CARD 0x00
+#define RTSX_DMA_EN 0x01
+#define RTSX_DMA_DIR_FROM_CARD 0x02
+#define RTSX_DMA_BUSY 0x04
+#define RTSX_DMA_RST 0x80
+#define RTSX_DMA_128 (0 << 4)
+#define RTSX_DMA_256 (1 << 4)
+#define RTSX_DMA_512 (2 << 4)
+#define RTSX_DMA_1024 (3 << 4)
+#define RTSX_DMA_PACK_SIZE_MASK 0x30
+
+#define RTSX_RBCTL 0xFE34
+#define RTSX_RB_FLUSH 0x80
+
+#define RTSX_CFGADDR0 0xFE35
+#define RTSX_CFGADDR1 0xFE36
+#define RTSX_CFGDATA0 0xFE37
+#define RTSX_CFGDATA1 0xFE38
+#define RTSX_CFGDATA2 0xFE39
+#define RTSX_CFGDATA3 0xFE3A
+#define RTSX_CFGRWCTL 0xFE3B
+#define RTSX_CFG_WRITE_DATA0 0x01
+#define RTSX_CFG_WRITE_DATA1 0x02
+#define RTSX_CFG_WRITE_DATA2 0x04
+#define RTSX_CFG_WRITE_DATA3 0x08
+#define RTSX_CFG_BUSY 0x80
+
+#define RTSX_SDIOCFG_REG 0x724
+#define RTSX_SDIOCFG_NO_BYPASS_SDIO 0x02
+#define RTSX_SDIOCFG_HAVE_SDIO 0x04
+#define RTSX_SDIOCFG_SINGLE_LUN 0x08
+#define RTSX_SDIOCFG_SDIO_ONLY 0x80
+
+#define RTSX_HOST_SLEEP_STATE 0xFE60
+#define RTSX_HOST_ENTER_S1 0x01
+#define RTSX_HOST_ENTER_S3 0x02
+
+#define RTSX_SDIO_CFG 0xFE70
+#define RTSX_SDIO_BUS_AUTO_SWITCH 0x10
+
+#define RTSX_NFTS_TX_CTRL 0xFE72
+#define RTSX_INT_READ_CLR 0x02
+
+#define RTSX_PWR_GATE_CTRL 0xFE75
+#define RTSX_PWR_GATE_EN 0x01
+#define RTSX_LDO3318_ON 0x00
+#define RTSX_LDO3318_SUSPEND 0x04
+#define RTSX_LDO3318_OFF 0x06
+#define RTSX_PWD_SUSPEND_EN 0xFE76
+
+#define RTSX_PHY_RWCTL 0xFE3C
+#define RTSX_PHY_READ 0x00
+#define RTSX_PHY_WRITE 0x01
+#define RTSX_PHY_BUSY 0x80
+#define RTSX_PHY_DATA0 0xFE3D
+#define RTSX_PHY_DATA1 0xFE3E
+#define RTSX_PHY_ADDR 0xFE3F
+
+#define RTSX_PHY_VOLTAGE 0x08
+#define RTSX_PHY_VOLTAGE_MASK 0x3F
+
+#define RTSX_PETXCFG 0xFE49
+#define RTSX_PETXCFG_CLKREQ_PIN 0x08
+
+#define RTSX_CARD_AUTO_BLINK 0xFE56
+#define RTSX_LED_BLINK_EN 0x08
+#define RTSX_LED_BLINK_SPEED 0x05
+
+#define RTSX_WAKE_SEL_CTL 0xFE54
+#define RTSX_PME_FORCE_CTL 0xFE56
+
+#define RTSX_CHANGE_LINK_STATE 0xFE5B
+#define RTSX_CD_RST_CORE_EN 0x01
+#define RTSX_FORCE_RST_CORE_EN 0x02
+#define RTSX_NON_STICKY_RST_N_DBG 0x08
+#define RTSX_MAC_PHY_RST_N_DBG 0x10
+
+#define RTSX_PERST_GLITCH_WIDTH 0xFE5C
+
+#define RTSX_SD30_DRIVE_SEL 0xFE5E
+#define RTSX_SD30_DRIVE_SEL_3V3 0x01
+#define RTSX_SD30_DRIVE_SEL_1V8 0x03
+#define RTSX_SD30_DRIVE_SEL_MASK 0x07
+
+#define RTSX_SG_INT 0x04
+#define RTSX_SG_END 0x02
+#define RTSX_SG_VALID 0x01
+
+#define RTSX_SG_NO_OP 0x00
+#define RTSX_SG_TRANS_DATA (0x02 << 4)
+#define RTSX_SG_LINK_DESC (0x03 << 4)
+
+#endif
diff --git a/sys/dev/ic/rtsxvar.h b/sys/dev/ic/rtsxvar.h
new file mode 100644
index 00000000000..814522d9c25
--- /dev/null
+++ b/sys/dev/ic/rtsxvar.h
@@ -0,0 +1,52 @@
+/* $OpenBSD: rtsxvar.h,v 1.1 2012/11/29 23:36:34 stsp Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ * Copyright (c) 2012 Stefan Sperling <stsp@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _RTSXVAR_H_
+#define _RTSXVAR_H_
+
+#include <machine/bus.h>
+
+/* Number of registers to save for suspend/resume in terms of their ranges. */
+#define RTSX_NREG ((0XFDAE - 0XFDA0) + (0xFD69 - 0xFD32) + (0xFE34 - 0xFE20))
+
+struct rtsx_softc {
+ struct device sc_dev;
+ struct device *sdmmc; /* generic SD/MMC device */
+ bus_space_tag_t iot; /* host register set tag */
+ bus_space_handle_t ioh; /* host register set handle */
+ bus_dma_tag_t dmat; /* DMA tag from attachment driver */
+ bus_dmamap_t dmap_cmd; /* DMA map for command transfer */
+ bus_dmamap_t dmap_data; /* DMA map for data transfer */
+ int flags;
+ u_int32_t intr_status; /* soft interrupt status */
+ u_int8_t regs[RTSX_NREG];/* host controller state */
+ u_int32_t regs4[6]; /* host controller state */
+};
+
+/* Host controller functions called by the attachment driver. */
+int rtsx_attach(struct rtsx_softc *, bus_space_tag_t,
+ bus_space_handle_t, bus_size_t, bus_dma_tag_t);
+int rtsx_activate(struct device *, int);
+int rtsx_intr(void *);
+
+/* flag values */
+#define RTSX_F_CARD_PRESENT 0x01
+#define RTSX_F_SDIO_SUPPORT 0x02
+
+#endif
diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci
index bb373a701dd..4f8ad7545f9 100644
--- a/sys/dev/pci/files.pci
+++ b/sys/dev/pci/files.pci
@@ -1,4 +1,4 @@
-# $OpenBSD: files.pci,v 1.289 2012/10/29 18:36:42 mikeb Exp $
+# $OpenBSD: files.pci,v 1.290 2012/11/29 23:36:34 stsp Exp $
# $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $
#
# Config file and device description for machine-independent PCI code.
@@ -823,6 +823,10 @@ device glxpcib: isabus, gpiobus, i2cbus
attach glxpcib at pci
file dev/pci/glxpcib.c glxpcib
+# Realtek RTS5209 Card Reader
+attach rtsx at pci with rtsx_pci
+file dev/pci/rtsx_pci.c rtsx
+
# VirtIO
device virtio {}
file dev/pci/virtio.c virtio
diff --git a/sys/dev/pci/rtsx_pci.c b/sys/dev/pci/rtsx_pci.c
new file mode 100644
index 00000000000..127f43f341a
--- /dev/null
+++ b/sys/dev/pci/rtsx_pci.c
@@ -0,0 +1,105 @@
+/* $OpenBSD: rtsx_pci.c,v 1.1 2012/11/29 23:36:34 stsp Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ * Copyright (c) 2012 Stefan Sperling <stsp@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
+#include <dev/ic/rtsxreg.h>
+#include <dev/ic/rtsxvar.h>
+#include <dev/sdmmc/sdmmcvar.h>
+
+#define RTSX_PCI_BAR 0x10
+
+struct rtsx_pci_softc {
+ struct rtsx_softc sc;
+ void *sc_ih;
+};
+
+int rtsx_pci_match(struct device *, void *, void *);
+void rtsx_pci_attach(struct device *, struct device *, void *);
+
+struct cfattach rtsx_pci_ca = {
+ sizeof(struct rtsx_pci_softc), rtsx_pci_match, rtsx_pci_attach,
+ NULL, rtsx_activate
+};
+
+int
+rtsx_pci_match(struct device *parent, void *match, void *aux)
+{
+ struct pci_attach_args *pa = aux;
+
+ if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_REALTEK &&
+ PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_REALTEK_RTS5209)
+ return 1;
+
+ return 0;
+}
+
+void
+rtsx_pci_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct rtsx_pci_softc *sc = (struct rtsx_pci_softc *)self;
+ struct pci_attach_args *pa = aux;
+ pci_intr_handle_t ih;
+ char const *intrstr;
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+ bus_size_t size;
+
+ if ((pci_conf_read(pa->pa_pc, pa->pa_tag, RTSX_CFG_PCI)
+ & RTSX_CFG_ASIC) != 0) {
+ printf("%s: no asic\n", sc->sc.sc_dev.dv_xname);
+ return;
+ }
+
+ if (pci_intr_map(pa, &ih)) {
+ printf(": can't map interrupt\n");
+ return;
+ }
+
+ intrstr = pci_intr_string(pa->pa_pc, ih);
+ sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_SDMMC,
+ rtsx_intr, sc, sc->sc.sc_dev.dv_xname);
+ if (sc->sc_ih == NULL) {
+ printf(": can't establish interrupt\n");
+ return;
+ }
+ printf(": %s\n", intrstr);
+
+ if (pci_mem_find(pa->pa_pc, pa->pa_tag, RTSX_PCI_BAR,
+ NULL, NULL, NULL) != 0) {
+ printf("%s: can't find registers\n", sc->sc.sc_dev.dv_xname);
+ return;
+ }
+
+ if (pci_mapreg_map(pa, RTSX_PCI_BAR, PCI_MAPREG_TYPE_MEM, 0,
+ &iot, &ioh, NULL, &size, 0)) {
+ printf("%s: can't map registers\n", sc->sc.sc_dev.dv_xname);
+ return;
+ }
+
+ pci_set_powerstate(pa->pa_pc, pa->pa_tag, PCI_PMCSR_STATE_D0);
+
+ if (rtsx_attach(&sc->sc, iot, ioh, size, pa->pa_dmat) != 0)
+ printf("%s: can't initialize chip\n", sc->sc.sc_dev.dv_xname);
+}