diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2018-05-26 15:25:26 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2018-05-26 15:25:26 +0000 |
commit | f4eabd5ecd21c9ffbf807914fbd43e281d3f4f66 (patch) | |
tree | efae058c720d1d44dfeb1b395aede6e976b7b8e5 /sys | |
parent | 0e8848a81cf3d70971e05debf4863d068c3bd71b (diff) |
Add SDIO support.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/fdt/dwmmc.c | 152 |
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); +} |