diff options
author | Stefan Sperling <stsp@cvs.openbsd.org> | 2012-11-29 23:36:35 +0000 |
---|---|---|
committer | Stefan Sperling <stsp@cvs.openbsd.org> | 2012-11-29 23:36:35 +0000 |
commit | e9bd58b2f6eac5915430d66b9e0140f121efde1b (patch) | |
tree | 68b0564450aca0c59e743bc2f0776a284697a894 /sys | |
parent | 9fd3649d3fe844fed5cb9a0d99e6be49b6a23c54 (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/files | 6 | ||||
-rw-r--r-- | sys/dev/ic/rtsx.c | 1272 | ||||
-rw-r--r-- | sys/dev/ic/rtsxreg.h | 495 | ||||
-rw-r--r-- | sys/dev/ic/rtsxvar.h | 52 | ||||
-rw-r--r-- | sys/dev/pci/files.pci | 6 | ||||
-rw-r--r-- | sys/dev/pci/rtsx_pci.c | 105 |
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); +} |