Module Name: src
Committed By: jmcneill
Date: Sat Dec 27 19:18:04 UTC 2014
Modified Files:
src/sys/dev/ic: dwc_mmc.c dwc_mmc_reg.h dwc_mmc_var.h
Log Message:
- Explicitly initialize CLKSRC register.
- Reset FIFO and DMA along with controller.
- Support chips with inverted PWREN logic.
- Enable RXDR and TXDR irqs.
- Set max clk based on supplied clock freq.
- Use external clk control as well as internal divisor to set clock rates.
Requires bus glue to implement sc_set_clkdiv callback.
To generate a diff of this commit:
cvs rdiff -u -r1.1 -r1.2 src/sys/dev/ic/dwc_mmc.c \
src/sys/dev/ic/dwc_mmc_reg.h src/sys/dev/ic/dwc_mmc_var.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/ic/dwc_mmc.c
diff -u src/sys/dev/ic/dwc_mmc.c:1.1 src/sys/dev/ic/dwc_mmc.c:1.2
--- src/sys/dev/ic/dwc_mmc.c:1.1 Sat Dec 27 01:18:48 2014
+++ src/sys/dev/ic/dwc_mmc.c Sat Dec 27 19:18:04 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: dwc_mmc.c,v 1.1 2014/12/27 01:18:48 jmcneill Exp $ */
+/* $NetBSD: dwc_mmc.c,v 1.2 2014/12/27 19:18:04 jmcneill Exp $ */
/*-
* Copyright (c) 2014 Jared D. McNeill <[email protected]>
@@ -29,7 +29,7 @@
#include "opt_dwc_mmc.h"
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: dwc_mmc.c,v 1.1 2014/12/27 01:18:48 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: dwc_mmc.c,v 1.2 2014/12/27 19:18:04 jmcneill Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@@ -99,21 +99,21 @@ dwc_mmc_init(struct dwc_mmc_softc *sc)
dwc_mmc_host_reset(sc);
dwc_mmc_bus_width(sc, 1);
- dwc_mmc_bus_clock(sc, 400);
memset(&saa, 0, sizeof(saa));
saa.saa_busname = "sdmmc";
saa.saa_sct = &dwc_mmc_chip_functions;
saa.saa_sch = sc;
saa.saa_clkmin = 400;
- saa.saa_clkmax = 25000;
+ saa.saa_clkmax = sc->sc_clock_freq / 1000;
saa.saa_caps = SMC_CAPS_4BIT_MODE|
SMC_CAPS_8BIT_MODE|
SMC_CAPS_SD_HIGHSPEED|
SMC_CAPS_MMC_HIGHSPEED|
SMC_CAPS_AUTO_STOP;
-#if notyet
saa.saa_dmat = sc->sc_dmat;
+
+#if notyet
saa.saa_caps |= SMC_CAPS_DMA|
SMC_CAPS_MULTI_SEG_DMA;
#endif
@@ -162,17 +162,24 @@ static int
dwc_mmc_set_clock(struct dwc_mmc_softc *sc, u_int freq)
{
u_int pll_freq = sc->sc_clock_freq / 1000;
- u_int n = howmany(pll_freq, freq) >> 1;
+ u_int clk_div, mmc_div;
+
+ mmc_div = min(howmany(pll_freq, freq), 0x3c);
+ clk_div = howmany(pll_freq / mmc_div, freq);
+ if (clk_div > 1 && (clk_div & 1) != 0)
+ clk_div++;
#ifdef DWC_MMC_DEBUG
- device_printf(sc->sc_dev, "%s: n=%u freq=%u\n",
- __func__, n, n ? pll_freq / (2 * n) : pll_freq);
+ device_printf(sc->sc_dev, "%s: mmc_div=%u clk_div=%u freq=%u\n",
+ __func__, mmc_div, clk_div, pll_freq / mmc_div / clk_div);
#endif
MMC_WRITE(sc, DWC_MMC_CLKDIV_REG,
- __SHIFTIN(n, DWC_MMC_CLKDIV_CLK_DIVIDER0));
+ __SHIFTIN(clk_div >> 1, DWC_MMC_CLKDIV_CLK_DIVIDER0));
+ if (dwc_mmc_update_clock(sc))
+ return ETIMEDOUT;
- return dwc_mmc_update_clock(sc);
+ return sc->sc_set_clkdiv(sc, mmc_div);
}
static int
@@ -277,23 +284,30 @@ dwc_mmc_host_reset(sdmmc_chipset_handle_
uint32_t ctrl, fifoth;
uint32_t rx_wmark, tx_wmark;
- MMC_WRITE(sc, DWC_MMC_PWREN_REG, DWC_MMC_PWREN_POWER_ENABLE);
+ if (sc->sc_flags & DWC_MMC_F_PWREN_CLEAR) {
+ MMC_WRITE(sc, DWC_MMC_PWREN_REG, 0);
+ } else {
+ MMC_WRITE(sc, DWC_MMC_PWREN_REG, DWC_MMC_PWREN_POWER_ENABLE);
+ }
MMC_WRITE(sc, DWC_MMC_CTRL_REG,
- MMC_READ(sc, DWC_MMC_CTRL_REG) | DWC_MMC_CTRL_CONTROLLER_RESET);
+ MMC_READ(sc, DWC_MMC_CTRL_REG) | DWC_MMC_CTRL_RESET_ALL);
while (--retry > 0) {
ctrl = MMC_READ(sc, DWC_MMC_CTRL_REG);
- if (ctrl & DWC_MMC_CTRL_CONTROLLER_RESET)
+ if ((ctrl & DWC_MMC_CTRL_RESET_ALL) == 0)
break;
delay(100);
}
- MMC_WRITE(sc, DWC_MMC_TMOUT_REG, 0xffffffff);
+ MMC_WRITE(sc, DWC_MMC_CLKSRC_REG, 0);
+
+ MMC_WRITE(sc, DWC_MMC_TMOUT_REG, 0xffffff40);
MMC_WRITE(sc, DWC_MMC_RINTSTS_REG, 0xffffffff);
MMC_WRITE(sc, DWC_MMC_INTMASK_REG,
DWC_MMC_INT_CD | DWC_MMC_INT_ACD | DWC_MMC_INT_DTO |
- DWC_MMC_INT_ERROR | DWC_MMC_INT_CARDDET);
+ DWC_MMC_INT_ERROR | DWC_MMC_INT_CARDDET |
+ DWC_MMC_INT_RXDR | DWC_MMC_INT_TXDR);
rx_wmark = (sc->sc_fifo_depth / 2) - 1;
tx_wmark = sc->sc_fifo_depth / 2;
@@ -352,36 +366,29 @@ static int
dwc_mmc_bus_clock(sdmmc_chipset_handle_t sch, int freq)
{
struct dwc_mmc_softc *sc = sch;
- uint32_t clkdiv, clkena;
+ uint32_t clkena;
#ifdef DWC_MMC_DEBUG
device_printf(sc->sc_dev, "%s: freq %d\n", __func__, freq);
#endif
- clkena = MMC_READ(sc, DWC_MMC_CLKENA_REG);
- if (clkena & DWC_MMC_CLKENA_CCLK_ENABLE) {
- clkena &= ~DWC_MMC_CLKENA_CCLK_ENABLE;
- MMC_WRITE(sc, DWC_MMC_CLKENA_REG, clkena);
- if (dwc_mmc_update_clock(sc) != 0)
- return ETIMEDOUT;
- }
+ MMC_WRITE(sc, DWC_MMC_CLKENA_REG, 0);
+ if (dwc_mmc_update_clock(sc) != 0)
+ return ETIMEDOUT;
if (freq) {
- clkdiv = MMC_READ(sc, DWC_MMC_CLKDIV_REG);
- clkdiv &= ~DWC_MMC_CLKDIV_CLK_DIVIDER0;
- if (dwc_mmc_update_clock(sc) != 0)
- return ETIMEDOUT;
-
if (dwc_mmc_set_clock(sc, freq) != 0)
return EIO;
- clkena |= DWC_MMC_CLKENA_CCLK_ENABLE;
+ clkena = DWC_MMC_CLKENA_CCLK_ENABLE;
clkena |= DWC_MMC_CLKENA_CCLK_LOW_POWER; /* XXX SD/MMC only */
MMC_WRITE(sc, DWC_MMC_CLKENA_REG, clkena);
if (dwc_mmc_update_clock(sc) != 0)
return ETIMEDOUT;
}
+ delay(1000);
+
return 0;
}
@@ -423,6 +430,11 @@ dwc_mmc_exec_command(sdmmc_chipset_handl
uint32_t cmdval = DWC_MMC_CMD_START_CMD;
uint32_t ctrl;
+#ifdef DWC_MMC_DEBUG
+ device_printf(sc->sc_dev, "exec opcode=%d flags=%#x\n",
+ cmd->c_opcode, cmd->c_flags);
+#endif
+
if (sc->sc_flags & DWC_MMC_F_USE_HOLD_REG)
cmdval |= DWC_MMC_CMD_USE_HOLD_REG;
Index: src/sys/dev/ic/dwc_mmc_reg.h
diff -u src/sys/dev/ic/dwc_mmc_reg.h:1.1 src/sys/dev/ic/dwc_mmc_reg.h:1.2
--- src/sys/dev/ic/dwc_mmc_reg.h:1.1 Sat Dec 27 01:18:48 2014
+++ src/sys/dev/ic/dwc_mmc_reg.h Sat Dec 27 19:18:04 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: dwc_mmc_reg.h,v 1.1 2014/12/27 01:18:48 jmcneill Exp $ */
+/* $NetBSD: dwc_mmc_reg.h,v 1.2 2014/12/27 19:18:04 jmcneill Exp $ */
/*-
* Copyright (c) 2014 Jared D. McNeill <[email protected]>
@@ -32,6 +32,7 @@
#define DWC_MMC_CTRL_REG 0x0000
#define DWC_MMC_PWREN_REG 0x0004
#define DWC_MMC_CLKDIV_REG 0x0008
+#define DWC_MMC_CLKSRC_REG 0x000c
#define DWC_MMC_CLKENA_REG 0x0010
#define DWC_MMC_TMOUT_REG 0x0014
#define DWC_MMC_CTYPE_REG 0x0018
@@ -69,6 +70,10 @@
#define DWC_MMC_CTRL_DMA_RESET __BIT(2)
#define DWC_MMC_CTRL_FIFO_RESET __BIT(1)
#define DWC_MMC_CTRL_CONTROLLER_RESET __BIT(0)
+#define DWC_MMC_CTRL_RESET_ALL \
+ (DWC_MMC_CTRL_CONTROLLER_RESET | \
+ DWC_MMC_CTRL_FIFO_RESET | \
+ DWC_MMC_CTRL_DMA_RESET)
#define DWC_MMC_PWREN_POWER_ENABLE __BIT(0)
Index: src/sys/dev/ic/dwc_mmc_var.h
diff -u src/sys/dev/ic/dwc_mmc_var.h:1.1 src/sys/dev/ic/dwc_mmc_var.h:1.2
--- src/sys/dev/ic/dwc_mmc_var.h:1.1 Sat Dec 27 01:18:48 2014
+++ src/sys/dev/ic/dwc_mmc_var.h Sat Dec 27 19:18:04 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: dwc_mmc_var.h,v 1.1 2014/12/27 01:18:48 jmcneill Exp $ */
+/* $NetBSD: dwc_mmc_var.h,v 1.2 2014/12/27 19:18:04 jmcneill Exp $ */
/*-
* Copyright (c) 2014 Jared D. McNeill <[email protected]>
@@ -38,7 +38,9 @@ struct dwc_mmc_softc {
unsigned int sc_clock_freq;
unsigned int sc_fifo_depth;
uint32_t sc_flags;
-#define DWC_MMC_F_USE_HOLD_REG 0x0001
+#define DWC_MMC_F_USE_HOLD_REG 0x0001 /* set USE_HOLD_REG with every cmd */
+#define DWC_MMC_F_PWREN_CLEAR 0x0002 /* clear POWER_ENABLE bit to enable */
+ int (*sc_set_clkdiv)(struct dwc_mmc_softc *, int);
device_t sc_sdmmc_dev;
kmutex_t sc_intr_lock;