From owner-svn-src-user@freebsd.org Sat Nov 30 16:59:30 2019 Return-Path: Delivered-To: svn-src-user@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id 25E8E1B0A5C for ; Sat, 30 Nov 2019 16:59:30 +0000 (UTC) (envelope-from nwhitehorn@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 47QHfV0xYvz4Dwg; Sat, 30 Nov 2019 16:59:30 +0000 (UTC) (envelope-from nwhitehorn@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id DDB2914F5; Sat, 30 Nov 2019 16:59:29 +0000 (UTC) (envelope-from nwhitehorn@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id xAUGxTea037002; Sat, 30 Nov 2019 16:59:29 GMT (envelope-from nwhitehorn@FreeBSD.org) Received: (from nwhitehorn@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id xAUGxTF8037000; Sat, 30 Nov 2019 16:59:29 GMT (envelope-from nwhitehorn@FreeBSD.org) Message-Id: <201911301659.xAUGxTF8037000@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: nwhitehorn set sender to nwhitehorn@FreeBSD.org using -f From: Nathan Whitehorn Date: Sat, 30 Nov 2019 16:59:29 +0000 (UTC) To: src-committers@freebsd.org, svn-src-user@freebsd.org Subject: svn commit: r355229 - user/nwhitehorn/rtsx X-SVN-Group: user X-SVN-Commit-Author: nwhitehorn X-SVN-Commit-Paths: user/nwhitehorn/rtsx X-SVN-Commit-Revision: 355229 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-user@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the experimental " user" src tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 30 Nov 2019 16:59:30 -0000 Author: nwhitehorn Date: Sat Nov 30 16:59:29 2019 New Revision: 355229 URL: https://svnweb.freebsd.org/changeset/base/355229 Log: In-progress driver for the Realtek SD card reader annoyingly found in my new Thinkpad 495. Attaches, detects cards, and processes some commands. Not yet actually working, but close enough to start tracking history now that I can change it in ways that make it work less well. Obtained from: OpenBSD Added: user/nwhitehorn/rtsx/ user/nwhitehorn/rtsx/rtsx.c (contents, props changed) user/nwhitehorn/rtsx/rtsx_pci.c (contents, props changed) user/nwhitehorn/rtsx/rtsxreg.h (contents, props changed) user/nwhitehorn/rtsx/rtsxvar.h (contents, props changed) Added: user/nwhitehorn/rtsx/rtsx.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ user/nwhitehorn/rtsx/rtsx.c Sat Nov 30 16:59:29 2019 (r355229) @@ -0,0 +1,1582 @@ +/* $OpenBSD: rtsx.c,v 1.21 2017/10/09 20:06:36 stsp Exp $ */ + +/* + * Copyright (c) 2006 Uwe Stuehler + * Copyright (c) 2012 Stefan Sperling + * + * 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 RTS52xx/RTL84xx Card Reader driver. + */ + +#include +__FBSDID("$FreeBSD$"); +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "rtsxreg.h" +#include "rtsxvar.h" +#include "mmcbr_if.h" + +/* + * We use three DMA buffers: a command buffer, a data buffer, and a buffer for + * ADMA transfer descriptors which describe scatter-gather (SG) I/O operations. + * + * 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. + * SD/MMC commands which do not transfer any data from/to the card only use + * the command buffer. + * + * The smmmc stack provides DMA-safe buffers with data transfer commands. + * In this case we write a list of descriptors to the ADMA descriptor buffer, + * instructing the chip to transfer data directly from/to mmc DMA buffers. + * + * Data transfer is controlled via the RTSX_HDBAR register + * and completion is signalled by the TRANS_OK interrupt. + * + * The chip is unable to perform DMA above 4GB. + */ + +#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 RTSX_ADMA_DESC_SIZE (sizeof(uint64_t) * ((MAXPHYS / PAGE_SIZE) + 1)) + +#define SDMMC_SDCLK_400KHZ 400000 +#define SDMMC_SDCLK_25MHZ 25000000 +#define SDMMC_SDCLK_50MHZ 50000000 + +#define READ4(sc, reg) \ + (bus_read_4((sc)->sc_mem, (reg))) +#define WRITE4(sc, reg, val) \ + bus_write_4((sc)->sc_mem, (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(struct rtsx_softc *); +int rtsx_card_detect(struct rtsx_softc *); +int rtsx_bus_clock(struct rtsx_softc *, int); +int rtsx_bus_width(struct rtsx_softc *, int); +void rtsx_exec_command(struct rtsx_softc *, struct mmc_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_intr_status(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); +static int rtsx_request2(device_t bus, device_t child, struct mmc_request *req); +static int rtsx_request3(device_t bus, device_t child, struct mmc_request *req); +#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); +u_int8_t rtsx_response_type(u_int16_t); +int rtsx_xfer(struct rtsx_softc *, struct mmc_command *, u_int32_t *); +int rtsx_xfer_adma(struct rtsx_softc *, struct mmc_command *); +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 *); + +#define RTSX_DEBUG +#ifdef RTSX_DEBUG +int rtsxdebug = 3; +#define DPRINTF(n,...) \ + do { \ + if ((n) <= rtsxdebug) device_printf(sc->sc_dev, __VA_ARGS__); \ + } while (0) +#else +#define DPRINTF(n,...) do {} while(0) +#endif + +/* + * Called by attachment driver. + */ +static void +rtsx_dma_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) +{ + struct rtsx_softc *sc = xsc; + + if (error != 0) + return; + if (nsegs != 1) + panic("%s: bad ADMA buffer segment count", __func__); + sc->adma_segs[0] = segs[0]; +} + +static void +rtsx_cmd_dma_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) +{ + struct rtsx_softc *sc = xsc; + + if (error != 0) + return; + if (nsegs != 1) + panic("%s: bad CMD buffer segment count", __func__); + sc->cmd_segs[0] = segs[0]; +} + +int +rtsx_attach(struct rtsx_softc *sc, bus_dma_tag_t dmat, int flags) +{ + u_int32_t sdio_cfg; + + sc->flags = flags; + + if (rtsx_init(sc, 1)) + return 1; + + if (rtsx_read_cfg(sc, 0, RTSX_SDIOCFG_REG, &sdio_cfg) == 0) { + if ((sdio_cfg & RTSX_SDIOCFG_SDIO_ONLY) || + (sdio_cfg & RTSX_SDIOCFG_HAVE_SDIO)) + sc->flags |= RTSX_F_SDIO_SUPPORT; + } + + bus_dma_tag_create(dmat, 1, 0, BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, NULL, NULL, RTSX_HOSTCMD_BUFSIZE, 1, + RTSX_DMA_MAX_SEGSIZE, BUS_DMA_NOWAIT, NULL, NULL, &sc->cmd_tag); + if (bus_dmamem_alloc(sc->cmd_tag, &sc->cmdbuf, + BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->dmap_cmd)) + return 1; + if (bus_dmamap_load(sc->cmd_tag, sc->dmap_cmd, sc->cmdbuf, + RTSX_HOSTCMD_BUFSIZE, rtsx_cmd_dma_callback, sc, 0)) + goto destroy_cmd; + bus_dma_tag_create(dmat, 1, 0, BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, NULL, NULL, RTSX_DMA_DATA_BUFSIZE, 1, + RTSX_DMA_MAX_SEGSIZE, BUS_DMA_NOWAIT, NULL, NULL, &sc->data_tag); + if (bus_dmamap_create(sc->data_tag, 0, &sc->dmap_data) != 0) + goto free_cmd; + bus_dma_tag_create(dmat, 1, 0, BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, NULL, NULL, RTSX_ADMA_DESC_SIZE, 1, + RTSX_DMA_MAX_SEGSIZE, 0, NULL, NULL, &sc->adma_tag); + if (bus_dmamem_alloc(sc->adma_tag, &sc->admabuf, + BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->dmap_adma)) + goto destroy_data; + if (bus_dmamap_load(sc->adma_tag, sc->dmap_adma, sc->admabuf, + RTSX_ADMA_DESC_SIZE, rtsx_dma_callback, sc, 0)) + goto free_adma; + + mtx_init(&sc->sc_mtx, device_get_name(sc->sc_dev), NULL, MTX_DEF); + + /* Now handle cards discovered during attachment. */ + if (sc->flags & RTSX_F_CARD_PRESENT) + rtsx_card_insert(sc); + + return 0; + +free_adma: + bus_dmamem_free(sc->adma_tag, sc->admabuf, sc->dmap_adma); +destroy_data: + bus_dmamap_destroy(sc->data_tag, sc->dmap_data); +free_cmd: + bus_dmamem_free(sc->cmd_tag, sc->cmdbuf, sc->dmap_cmd); +destroy_cmd: + bus_dmamap_destroy(sc->cmd_tag, sc->dmap_cmd); + return 1; +} + +int +rtsx_detach(struct rtsx_softc *sc) +{ + mtx_lock(&sc->sc_mtx); + rtsx_card_eject(sc); + mtx_unlock(&sc->sc_mtx); + + mtx_destroy(&sc->sc_mtx); + + if (sc->dmap_adma) { + bus_dmamap_unload(sc->adma_tag, sc->dmap_adma); + bus_dmamem_free(sc->adma_tag, sc->admabuf, sc->dmap_adma); + } + + if (sc->dmap_data) + bus_dmamap_destroy(sc->data_tag, sc->dmap_data); + + if (sc->dmap_cmd) + bus_dmamap_destroy(sc->cmd_tag, sc->dmap_cmd); + + return (0); +} + +int +rtsx_init(struct rtsx_softc *sc, int attaching) +{ + u_int32_t status; + u_int8_t version; + int error; + + /* Read IC version from dummy register. */ + if (sc->flags & RTSX_F_5229) { + RTSX_READ(sc, RTSX_DUMMY_REG, &version); + switch (version & 0x0F) { + case RTSX_IC_VERSION_A: + case RTSX_IC_VERSION_B: + case RTSX_IC_VERSION_D: + break; + case RTSX_IC_VERSION_C: + sc->flags |= RTSX_F_5229_TYPE_C; + break; + default: + device_printf(sc->sc_dev, + "rtsx_init: unknown ic %02x\n", version); + return (1); + } + } + + /* 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); + + /* Check for cards already inserted at attach time. */ + if (attaching && (status & RTSX_SD_EXIST)) + sc->flags |= RTSX_F_CARD_PRESENT; + + /* Enable interrupts. */ + WRITE4(sc, RTSX_BIER, + RTSX_TRANS_OK_INT_EN | RTSX_TRANS_FAIL_INT_EN | RTSX_SD_INT_EN); + + /* Power on SSC clock. */ + RTSX_CLR(sc, RTSX_FPDCTL, RTSX_SSC_POWER_DOWN); + DELAY(200); + + /* XXX magic numbers from linux driver */ + if (sc->flags & RTSX_F_5209) + error = rtsx_write_phy(sc, 0x00, 0xB966); + else + error = rtsx_write_phy(sc, 0x00, 0xBA42); + if (error) { + device_printf(sc->sc_dev, "cannot write phy register\n"); + 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. */ + if (sc->flags & RTSX_F_5209) { + RTSX_WRITE(sc, RTSX_CARD_GPIO, 0x03); + RTSX_WRITE(sc, RTSX_CARD_GPIO_DIR, 0x03); + } else { + RTSX_SET(sc, RTSX_GPIO_CTL, RTSX_GPIO_LED_ON); + /* Switch LDO3318 source from DV33 to 3V3. */ + RTSX_CLR(sc, RTSX_LDO_PWR_SEL, RTSX_LDO_PWR_SEL_DV33); + RTSX_SET(sc, RTSX_LDO_PWR_SEL, RTSX_LDO_PWR_SEL_3V3); + /* Set default OLT blink period. */ + RTSX_SET(sc, RTSX_OLT_LED_CTL, RTSX_OLT_LED_PERIOD); + } + + return (0); +} + +int +rtsx_led_enable(struct rtsx_softc *sc) +{ + if (sc->flags & RTSX_F_5209) { + 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); + } else { + RTSX_SET(sc, RTSX_GPIO_CTL, RTSX_GPIO_LED_ON); + RTSX_SET(sc, RTSX_OLT_LED_CTL, RTSX_OLT_LED_AUTOBLINK); + } + + return 0; +} + +int +rtsx_led_disable(struct rtsx_softc *sc) +{ + if (sc->flags & RTSX_F_5209) { + RTSX_CLR(sc, RTSX_CARD_AUTO_BLINK, RTSX_LED_BLINK_EN); + RTSX_WRITE(sc, RTSX_CARD_GPIO, RTSX_CARD_GPIO_LED_OFF); + } else { + RTSX_CLR(sc, RTSX_OLT_LED_CTL, RTSX_OLT_LED_AUTOBLINK); + RTSX_CLR(sc, RTSX_GPIO_CTL, RTSX_GPIO_LED_ON); + } + + return 0; +} + +/* + * Reset the host controller. Called during initialization, when + * cards are removed, upon resume, and during error recovery. + */ +int +rtsx_host_reset(struct rtsx_softc *sc) +{ + DPRINTF(1, "host reset\n"); + + if (sc->flags & RTSX_F_CARD_PRESENT) + rtsx_soft_reset(sc); + + if (rtsx_init(sc, 0)) + return 1; + + return 0; +} + +/* + * Return non-zero if the card is currently inserted. + */ +int +rtsx_card_detect(struct rtsx_softc *sc) +{ + + return (sc->flags & RTSX_F_CARD_PRESENT); +} + +/* + * Notice that the meaning of RTSX_PWR_GATE_CTRL changes between RTS5209 and + * RTS5229. In RTS5209 it is a mask of disabled power gates, while in RTS5229 + * it is a mask of *enabled* gates. + */ + +int +rtsx_bus_power_off(struct rtsx_softc *sc) +{ + int error; + u_int8_t disable3; + + 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. */ + disable3 = RTSX_PULL_CTL_DISABLE3; + if (sc->flags & RTSX_F_5209) + RTSX_SET(sc, RTSX_PWR_GATE_CTRL, RTSX_LDO3318_OFF); + else { + RTSX_CLR(sc, RTSX_PWR_GATE_CTRL, RTSX_LDO3318_VCC1 | + RTSX_LDO3318_VCC2); + if (sc->flags & RTSX_F_5229_TYPE_C) + disable3 = RTSX_PULL_CTL_DISABLE3_TYPE_C; + } + + 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, disable3); + + return 0; +} + +int +rtsx_bus_power_on(struct rtsx_softc *sc) +{ + u_int8_t enable3; + int err; + + if (sc->flags & RTSX_F_525A) { + err = rtsx_write(sc, RTSX_LDO_VCC_CFG1, RTSX_LDO_VCC_TUNE_MASK, + RTSX_LDO_VCC_3V3); + if (err) + return (err); + } + + /* 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); + if (sc->flags & RTSX_F_5229_TYPE_C) + enable3 = RTSX_PULL_CTL_ENABLE3_TYPE_C; + else + enable3 = RTSX_PULL_CTL_ENABLE3; + RTSX_WRITE(sc, RTSX_CARD_PULL_CTL3, enable3); + + /* + * To avoid a current peak, enable card power in two phases with a + * delay in between. + */ + + /* Partial power. */ + RTSX_SET(sc, RTSX_CARD_PWR_CTL, RTSX_SD_PARTIAL_PWR_ON); + if (sc->flags & RTSX_F_5209) + RTSX_SET(sc, RTSX_PWR_GATE_CTRL, RTSX_LDO3318_SUSPEND); + else + RTSX_SET(sc, RTSX_PWR_GATE_CTRL, RTSX_LDO3318_VCC1); + + DELAY(200); + + /* Full power. */ + RTSX_CLR(sc, RTSX_CARD_PWR_CTL, RTSX_SD_PWR_OFF); + if (sc->flags & RTSX_F_5209) + RTSX_CLR(sc, RTSX_PWR_GATE_CTRL, RTSX_LDO3318_OFF); + else + RTSX_SET(sc, RTSX_PWR_GATE_CTRL, RTSX_LDO3318_VCC2); + + /* 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; + int error; + + 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; + } + + error = rtsx_write(sc, RTSX_SD_CFG1, RTSX_BUS_WIDTH_MASK, bus_width); + return error; +} + +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 SDCLK frequency or disable the SD clock. + * Return zero on success. + */ +int +rtsx_bus_clock(struct rtsx_softc *sc, int freq) +{ + u_int8_t n; + int div; + int mcu; + int error = 0; + + if (freq == 0) { + error = rtsx_stop_sd_clock(sc); + goto ret; + } + + /* Round down to a supported frequency. */ + if (freq >= SDMMC_SDCLK_50MHZ) + freq = SDMMC_SDCLK_50MHZ; + else if (freq >= SDMMC_SDCLK_25MHZ) + freq = SDMMC_SDCLK_25MHZ; + else + freq = SDMMC_SDCLK_400KHZ; + + /* + * 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; + case SDMMC_SDCLK_50MHZ: + n = 100; + div = RTSX_CLK_DIV_2; + 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: + 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, + ("too many commands (%d vs. %d)", *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 i; + u_int16_t reg; + + 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. */ +} + +void +rtsx_restore_regs(struct rtsx_softc *sc) +{ + int i; + u_int16_t reg; + + 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++]); +} + +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[] = { + { MMC_RSP_NONE, RTSX_SD_RSP_TYPE_R0 }, + { MMC_RSP_R1, RTSX_SD_RSP_TYPE_R1 }, + { MMC_RSP_R1B, RTSX_SD_RSP_TYPE_R1B }, + { MMC_RSP_R2, RTSX_SD_RSP_TYPE_R2 }, + { MMC_RSP_R3, RTSX_SD_RSP_TYPE_R3 }, + { MMC_RSP_R4, RTSX_SD_RSP_TYPE_R4 }, + { MMC_RSP_R5, RTSX_SD_RSP_TYPE_R5 }, + { MMC_RSP_R6, RTSX_SD_RSP_TYPE_R6 }, + { MMC_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) +{ + + /* Tell the chip where the command buffer is and run the commands. */ + WRITE4(sc, RTSX_HCBAR, sc->cmd_segs[0].ds_addr); + WRITE4(sc, RTSX_HCBCTLR, + ((ncmd * 4) & 0x00ffffff) | RTSX_START_CMD | RTSX_HW_AUTO_RSP); + + return 0; +} + +int +rtsx_xfer(struct rtsx_softc *sc, struct mmc_command *cmd, u_int32_t *cmdbuf) +{ + int ncmd, dma_dir, error, tmode; + int read = !!(cmd->data->flags & MMC_DATA_READ); + u_int8_t cfg2; + + DPRINTF(3, "%s xfer: %zd bytes with block size %zd\n", + read ? "read" : "write", + cmd->data->len, cmd->data->xfer_len); + + if (cmd->data->len > RTSX_DMA_DATA_BUFSIZE) { + DPRINTF(3, "cmd->data->len too large: %zd > %d\n", + cmd->data->len, 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, + (cmd->data->xfer_len & 0xff)); + rtsx_hostcmd(cmdbuf, &ncmd, + RTSX_WRITE_REG_CMD, RTSX_SD_BYTE_CNT_H, 0xff, + (cmd->data->xfer_len >> 8)); + rtsx_hostcmd(cmdbuf, &ncmd, + RTSX_WRITE_REG_CMD, RTSX_SD_BLOCK_CNT_L, 0xff, + ((cmd->data->len / cmd->data->xfer_len) & 0xff)); + rtsx_hostcmd(cmdbuf, &ncmd, + RTSX_WRITE_REG_CMD, RTSX_SD_BLOCK_CNT_H, 0xff, + ((cmd->data->len / cmd->data->xfer_len) >> 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->data->len >> 24); + rtsx_hostcmd(cmdbuf, &ncmd, + RTSX_WRITE_REG_CMD, RTSX_DMATC2, 0xff, cmd->data->len >> 16); + rtsx_hostcmd(cmdbuf, &ncmd, + RTSX_WRITE_REG_CMD, RTSX_DMATC1, 0xff, cmd->data->len >> 8); + rtsx_hostcmd(cmdbuf, &ncmd, + RTSX_WRITE_REG_CMD, RTSX_DMATC0, 0xff, cmd->data->len); + 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); + if (error) + goto ret; + + error = rtsx_xfer_adma(sc, cmd); +ret: + DPRINTF(3, "xfer done, error=%d\n", error); + return error; +} + +static void +rtsx_xfer_adma_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) +{ + struct rtsx_softc *sc = xsc; + uint64_t *descp; + int i; + *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***