svn commit: r355229 - user/nwhitehorn/rtsx

Nathan Whitehorn nwhitehorn at FreeBSD.org
Sat Nov 30 16:59:30 UTC 2019


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 <uwe at openbsd.org>
+ * Copyright (c) 2012 Stefan Sperling <stsp at 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 RTS52xx/RTL84xx Card Reader driver.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/pciio.h>
+#include <sys/endian.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <dev/mmc/bridge.h>
+#include <dev/mmc/mmcreg.h>
+#include <dev/mmc/mmcbrvar.h>
+
+#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 ***


More information about the svn-src-user mailing list