summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2018-05-26 15:25:26 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2018-05-26 15:25:26 +0000
commitf4eabd5ecd21c9ffbf807914fbd43e281d3f4f66 (patch)
treeefae058c720d1d44dfeb1b395aede6e976b7b8e5 /sys
parent0e8848a81cf3d70971e05debf4863d068c3bd71b (diff)
Add SDIO support.
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/fdt/dwmmc.c152
1 files changed, 142 insertions, 10 deletions
diff --git a/sys/dev/fdt/dwmmc.c b/sys/dev/fdt/dwmmc.c
index 00fa8e10db0..1b2c3c97e33 100644
--- a/sys/dev/fdt/dwmmc.c
+++ b/sys/dev/fdt/dwmmc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: dwmmc.c,v 1.8 2018/04/07 15:05:25 kettenis Exp $ */
+/* $OpenBSD: dwmmc.c,v 1.9 2018/05/26 15:25:25 kettenis Exp $ */
/*
* Copyright (c) 2017 Mark Kettenis
*
@@ -33,6 +33,7 @@
#define SDMMC_CTRL 0x0000
#define SDMMC_CTRL_USE_INTERNAL_DMAC (1 << 25)
+#define SDMMC_CTRL_INT_ENABLE (1 << 4)
#define SDMMC_CTRL_DMA_RESET (1 << 2)
#define SDMMC_CTRL_FIFO_RESET (1 << 1)
#define SDMMC_CTRL_CONTROLLER_RESET (1 << 0)
@@ -71,6 +72,7 @@
#define SDMMC_RESP3 0x003c
#define SDMMC_MINTSTS 0x0040
#define SDMMC_RINTSTS 0x0044
+#define SDMMC_RINTSTS_SDIO (1 << 24)
#define SDMMC_RINTSTS_EBE (1 << 15)
#define SDMMC_RINTSTS_ACD (1 << 14)
#define SDMMC_RINTSTS_SBE (1 << 13)
@@ -146,6 +148,9 @@ struct dwmmc_softc {
void (*sc_write_data)(struct dwmmc_softc *, u_char *, int);
uint32_t sc_gpio[4];
+ int sc_sdio_irq;
+ uint32_t sc_pwrseq;
+ uint32_t sc_vdd;
struct device *sc_sdmmc;
};
@@ -171,6 +176,8 @@ int dwmmc_bus_power(sdmmc_chipset_handle_t, uint32_t);
int dwmmc_bus_clock(sdmmc_chipset_handle_t, int, int);
int dwmmc_bus_width(sdmmc_chipset_handle_t, int);
void dwmmc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *);
+void dwmmc_card_intr_mask(sdmmc_chipset_handle_t, int);
+void dwmmc_card_intr_ack(sdmmc_chipset_handle_t);
struct sdmmc_chip_functions dwmmc_chip_functions = {
.host_reset = dwmmc_host_reset,
@@ -181,6 +188,8 @@ struct sdmmc_chip_functions dwmmc_chip_functions = {
.bus_clock = dwmmc_bus_clock,
.bus_width = dwmmc_bus_width,
.exec_command = dwmmc_exec_command,
+ .card_intr_mask = dwmmc_card_intr_mask,
+ .card_intr_ack = dwmmc_card_intr_ack,
};
void dwmmc_transfer_data(struct dwmmc_softc *, struct sdmmc_command *);
@@ -188,6 +197,8 @@ void dwmmc_read_data32(struct dwmmc_softc *, u_char *, int);
void dwmmc_write_data32(struct dwmmc_softc *, u_char *, int);
void dwmmc_read_data64(struct dwmmc_softc *, u_char *, int);
void dwmmc_write_data64(struct dwmmc_softc *, u_char *, int);
+void dwmmc_pwrseq_pre(uint32_t);
+void dwmmc_pwrseq_post(uint32_t);
int
dwmmc_match(struct device *parent, void *match, void *aux)
@@ -270,6 +281,9 @@ dwmmc_attach(struct device *parent, struct device *self, void *aux)
if (sc->sc_gpio[0])
gpio_controller_config_pin(sc->sc_gpio, GPIO_CONFIG_INPUT);
+ sc->sc_sdio_irq = (OF_getproplen(sc->sc_node, "cap-sdio-irq") == 0);
+ sc->sc_pwrseq = OF_getpropint(sc->sc_node, "mmc-pwrseq", 0);
+
printf(": %d MHz base clock\n", sc->sc_clkbase / 1000000);
HSET4(sc, SDMMC_CTRL, SDMMC_CTRL_ALL_RESET);
@@ -284,9 +298,10 @@ dwmmc_attach(struct device *parent, struct device *self, void *aux)
/* We don't do DMA yet. */
HCLR4(sc, SDMMC_CTRL, SDMMC_CTRL_USE_INTERNAL_DMAC);
- /* Set card read threshold to the size of a block. */
- HWRITE4(sc, SDMMC_CARDTHRCTL,
- 512 << SDMMC_CARDTHRCTL_RDTHR_SHIFT | SDMMC_CARDTHRCTL_RDTHREN);
+ /* Enable interrupts, but mask them all. */
+ HWRITE4(sc, SDMMC_INTMASK, 0);
+ HWRITE4(sc, SDMMC_RINTSTS, 0xffffffff);
+ HSET4(sc, SDMMC_CTRL, SDMMC_CTRL_INT_ENABLE);
dwmmc_bus_width(sc, 1);
@@ -311,7 +326,38 @@ unmap:
int
dwmmc_intr(void *arg)
{
- return 1;
+ struct dwmmc_softc *sc = arg;
+ uint32_t stat;
+ int handled = 0;
+
+ stat = HREAD4(sc, SDMMC_MINTSTS);
+ if (stat & SDMMC_RINTSTS_SDIO) {
+ HCLR4(sc, SDMMC_INTMASK, SDMMC_RINTSTS_SDIO);
+ sdmmc_card_intr(sc->sc_sdmmc);
+ handled = 1;
+ }
+
+ return handled;
+}
+
+void
+dwmmc_card_intr_mask(sdmmc_chipset_handle_t sch, int enable)
+{
+ struct dwmmc_softc *sc = sch;
+
+ if (enable)
+ HSET4(sc, SDMMC_INTMASK, SDMMC_RINTSTS_SDIO);
+ else
+ HCLR4(sc, SDMMC_INTMASK, SDMMC_RINTSTS_SDIO);
+}
+
+void
+dwmmc_card_intr_ack(sdmmc_chipset_handle_t sch)
+{
+ struct dwmmc_softc *sc = sch;
+
+ HWRITE4(sc, SDMMC_RINTSTS, SDMMC_RINTSTS_SDIO);
+ HSET4(sc, SDMMC_INTMASK, SDMMC_RINTSTS_SDIO);
}
int
@@ -359,12 +405,23 @@ int
dwmmc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr)
{
struct dwmmc_softc *sc = sch;
+ uint32_t vdd = 0;
+
+ if (ISSET(ocr, MMC_OCR_3_2V_3_3V|MMC_OCR_3_3V_3_4V))
+ vdd = 3300000;
+
+ if (sc->sc_vdd == 0 && vdd > 0)
+ dwmmc_pwrseq_pre(sc->sc_pwrseq);
if (ISSET(ocr, MMC_OCR_3_2V_3_3V|MMC_OCR_3_3V_3_4V))
HSET4(sc, SDMMC_PWREN, 1);
else
HCLR4(sc, SDMMC_PWREN, 0);
+ if (sc->sc_vdd == 0 && vdd > 0)
+ dwmmc_pwrseq_post(sc->sc_pwrseq);
+
+ sc->sc_vdd = vdd;
return 0;
}
@@ -373,6 +430,7 @@ dwmmc_bus_clock(sdmmc_chipset_handle_t sch, int freq, int timing)
{
struct dwmmc_softc *sc = sch;
int div = 0, timeout;
+ uint32_t clkena;
HWRITE4(sc, SDMMC_CLKENA, 0);
HWRITE4(sc, SDMMC_CLKSRC, 0);
@@ -400,9 +458,11 @@ dwmmc_bus_clock(sdmmc_chipset_handle_t sch, int freq, int timing)
return ETIMEDOUT;
}
- /* Enable clock in low power mode. */
- HWRITE4(sc, SDMMC_CLKENA,
- SDMMC_CLKENA_CCLK_ENABLE | SDMMC_CLKENA_CCLK_LOW_POWER);
+ /* Enable clock; low power mode only for memory mode. */
+ clkena = SDMMC_CLKENA_CCLK_ENABLE;
+ if (!sc->sc_sdio_irq)
+ clkena |= SDMMC_CLKENA_CCLK_LOW_POWER;
+ HWRITE4(sc, SDMMC_CLKENA, clkena);
/* Update clock again. */
HWRITE4(sc, SDMMC_CMD, SDMMC_CMD_START_CMD |
@@ -506,12 +566,19 @@ dwmmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd)
if (timeout == 0)
printf("%s: FIFO reset failed\n", sc->sc_dev.dv_xname);
+ if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
+ /* Set card read threshold to the size of a block. */
+ HWRITE4(sc, SDMMC_CARDTHRCTL,
+ cmd->c_blklen << SDMMC_CARDTHRCTL_RDTHR_SHIFT |
+ SDMMC_CARDTHRCTL_RDTHREN);
+ }
+
cmdval |= SDMMC_CMD_DATA_EXPECTED;
if (!ISSET(cmd->c_flags, SCF_CMD_READ))
cmdval |= SDMMC_CMD_WR;
}
- HWRITE4(sc, SDMMC_RINTSTS, 0xffffffff);
+ HWRITE4(sc, SDMMC_RINTSTS, ~SDMMC_RINTSTS_SDIO);
HWRITE4(sc, SDMMC_CMDARG, cmd->c_arg);
HWRITE4(sc, SDMMC_CMD, cmdval | cmd->c_opcode);
@@ -585,7 +652,7 @@ dwmmc_transfer_data(struct dwmmc_softc *sc, struct sdmmc_command *cmd)
fifodr |= SDMMC_RINTSTS_RXDR;
else
fifodr |= SDMMC_RINTSTS_TXDR;
-
+
while (datalen > 0) {
status = HREAD4(sc, SDMMC_RINTSTS);
if (status & SDMMC_RINTSTS_DATA_ERR) {
@@ -721,3 +788,68 @@ dwmmc_write_data64(struct dwmmc_softc *sc, u_char *datap, int datalen)
}
HWRITE4(sc, SDMMC_RINTSTS, SDMMC_RINTSTS_TXDR);
}
+
+void
+dwmmc_pwrseq_pre(uint32_t phandle)
+{
+ uint32_t *gpios, *gpio;
+ int node;
+ int len;
+
+ node = OF_getnodebyphandle(phandle);
+ if (node == 0)
+ return;
+
+ if (!OF_is_compatible(node, "mmc-pwrseq-simple"))
+ return;
+
+ pinctrl_byname(node, "default");
+
+ clock_enable(node, "ext_clock");
+
+ len = OF_getproplen(node, "reset-gpios");
+ if (len <= 0)
+ return;
+
+ gpios = malloc(len, M_TEMP, M_WAITOK);
+ OF_getpropintarray(node, "reset-gpios", gpios, len);
+
+ gpio = gpios;
+ while (gpio && gpio < gpios + (len / sizeof(uint32_t))) {
+ gpio_controller_config_pin(gpio, GPIO_CONFIG_OUTPUT);
+ gpio_controller_set_pin(gpio, 1);
+ gpio = gpio_controller_next_pin(gpio);
+ }
+
+ free(gpios, M_TEMP, len);
+}
+
+void
+dwmmc_pwrseq_post(uint32_t phandle)
+{
+ uint32_t *gpios, *gpio;
+ int node;
+ int len;
+
+ node = OF_getnodebyphandle(phandle);
+ if (node == 0)
+ return;
+
+ if (!OF_is_compatible(node, "mmc-pwrseq-simple"))
+ return;
+
+ len = OF_getproplen(node, "reset-gpios");
+ if (len <= 0)
+ return;
+
+ gpios = malloc(len, M_TEMP, M_WAITOK);
+ OF_getpropintarray(node, "reset-gpios", gpios, len);
+
+ gpio = gpios;
+ while (gpio && gpio < gpios + (len / sizeof(uint32_t))) {
+ gpio_controller_set_pin(gpio, 0);
+ gpio = gpio_controller_next_pin(gpio);
+ }
+
+ free(gpios, M_TEMP, len);
+}