commit 375f42a6723dea131b1ec4810770dff8756c0197 Author: Kevin Bowling Date: Wed Sep 18 23:33:28 2024 -0700 WIP diff --git a/sys/arch/evbmips/conf/OCTEON b/sys/arch/evbmips/conf/OCTEON index f253bcd44e87..4c7024eb4002 100644 --- a/sys/arch/evbmips/conf/OCTEON +++ b/sys/arch/evbmips/conf/OCTEON @@ -153,6 +153,10 @@ octpip* at fdt? pass 4 octgmx* at octpip? cnmac* at octgmx? +octmmc* at fdt? # MMC host controller +sdmmc* at octmmc? # SD/MMC bus +ld* at sdmmc? + octrnm* at iobus? # Random Number Memory (and generator) dwctwo* at iobus? diff --git a/sys/arch/mips/cavium/dev/octeon_mmc.c b/sys/arch/mips/cavium/dev/octeon_mmc.c new file mode 100644 index 000000000000..2939bb53e73d --- /dev/null +++ b/sys/arch/mips/cavium/dev/octeon_mmc.c @@ -0,0 +1,1032 @@ +/* $NetBSD: octeon_mmc.c,v $ */ +/* $OpenBSD: octmmc.c,v 1.15 2023/04/12 02:20:07 jsg Exp $ */ + +/* + * Copyright (c) 2016, 2017 Visa Hankala + * Copyright (c) 2026 Kevin Bowling + * + * Permission to use, copy, modify, and/or 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. + */ + +/* Driver for OCTEON MMC host controller. */ + +#include +__KERNEL_RCSID(0, "$NetBSD: octeon_mmc.c,v $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#define OCTMMC_BLOCK_SIZE 512 +#define OCTMMC_CMD_TIMEOUT 5 /* in seconds */ +#define OCTMMC_MAX_DMASEG MIN(MAXPHYS, (1u << 18)) +#define OCTMMC_MAX_NDMASEG_6130 1 +#define OCTMMC_MAX_NDMASEG_7890 16 +#define OCTMMC_MAX_FREQ 52000 /* in kHz */ +#define OCTMMC_MAX_BUSES 4 +#define OCTMMC_MAX_INTRS 4 + +#define DEF_STS_MASK 0xe4390080ul +#define PIO_STS_MASK 0x00b00000ul + +#define MMC_RD_8(sc, reg) \ + bus_space_read_8((sc)->sc_iot, (sc)->sc_mmc_ioh, (reg)) +#define MMC_WR_8(sc, reg, val) \ + bus_space_write_8((sc)->sc_iot, (sc)->sc_mmc_ioh, (reg), (val)) +#define DMA_WR_8(sc, reg, val) \ + bus_space_write_8((sc)->sc_iot, (sc)->sc_dma_ioh, (reg), (val)) +#define FIFO_WR_8(sc, reg, val) \ + bus_space_write_8((sc)->sc_iot, (sc)->sc_fifo_ioh, (reg), (val)) + +#define divround(n, d) (((n) + (d) / 2) / (d)) + +struct octmmc_softc; + +struct octmmc_bus { + struct octmmc_softc *bus_hc; + device_t bus_sdmmc; + struct fdtbus_gpio_pin *bus_cd_gpio; + uint32_t bus_id; + uint32_t bus_cmd_skew; + uint32_t bus_dat_skew; + uint32_t bus_max_freq; /* in kHz */ + uint64_t bus_switch; + uint64_t bus_rca; + uint64_t bus_wdog; +}; + +struct octmmc_softc { + device_t sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_mmc_ioh; + bus_space_handle_t sc_dma_ioh; + bus_space_handle_t sc_fifo_ioh; + bus_dma_tag_t sc_dmat; + bus_dmamap_t sc_dma_data; + void *sc_bounce_buf; + bus_dma_segment_t sc_bounce_seg; + void *sc_ihs[OCTMMC_MAX_INTRS]; + int sc_nihs; + + struct octmmc_bus sc_buses[OCTMMC_MAX_BUSES]; + struct octmmc_bus *sc_current_bus; + + uint64_t sc_current_switch; + uint64_t sc_intr_status; + kmutex_t sc_intr_mtx; + kcondvar_t sc_intr_cv; + + int sc_hwrev; +#define OCTMMC_HWREV_6130 0 +#define OCTMMC_HWREV_7890 1 +}; + +static int octmmc_match(device_t, cfdata_t, void *); +static void octmmc_attach(device_t, device_t, void *); + +int octmmc_host_reset(sdmmc_chipset_handle_t); +uint32_t octmmc_host_ocr(sdmmc_chipset_handle_t); +int octmmc_host_maxblklen(sdmmc_chipset_handle_t); +int octmmc_card_detect(sdmmc_chipset_handle_t); +int octmmc_write_protect(sdmmc_chipset_handle_t); +int octmmc_bus_width(sdmmc_chipset_handle_t, int); +int octmmc_bus_power(sdmmc_chipset_handle_t, uint32_t); +int octmmc_bus_clock(sdmmc_chipset_handle_t, int); +int octmmc_bus_rod(sdmmc_chipset_handle_t, int); +void octmmc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *); + +void octmmc_exec_dma(struct octmmc_bus *, struct sdmmc_command *); +void octmmc_exec_pio(struct octmmc_bus *, struct sdmmc_command *); + +void octmmc_acquire(struct octmmc_bus *); +void octmmc_release(struct octmmc_bus *); + +paddr_t octmmc_dma_load_6130(struct octmmc_softc *, struct sdmmc_command *); +void octmmc_dma_load_7890(struct octmmc_softc *, struct sdmmc_command *); +void octmmc_dma_unload_6130(struct octmmc_softc *, paddr_t); +void octmmc_dma_unload_7890(struct octmmc_softc *); +uint64_t octmmc_crtype_fixup(struct sdmmc_command *); +void octmmc_get_response(struct octmmc_softc *, struct sdmmc_command *); +int octmmc_init_bus(struct octmmc_bus *); +int octmmc_intr(void *); +int octmmc_wait_intr(struct octmmc_softc *, uint64_t, int); + +CFATTACH_DECL_NEW(octmmc_fdt, sizeof(struct octmmc_softc), + octmmc_match, octmmc_attach, NULL, NULL); + +static struct sdmmc_chip_functions octmmc_funcs = { + .host_reset = octmmc_host_reset, + .host_ocr = octmmc_host_ocr, + .host_maxblklen = octmmc_host_maxblklen, + .card_detect = octmmc_card_detect, + .write_protect = octmmc_write_protect, + .bus_power = octmmc_bus_power, + .bus_clock = octmmc_bus_clock, + .bus_width = octmmc_bus_width, + .bus_rod = octmmc_bus_rod, + .exec_command = octmmc_exec_command, +}; + +static const struct device_compatible_entry compat_data[] = { + { .compat = "cavium,octeon-6130-mmc" }, + { .compat = "cavium,octeon-7890-mmc" }, + DEVICE_COMPAT_EOL +}; + +static const struct device_compatible_entry compat_7890[] = { + { .compat = "cavium,octeon-7890-mmc" }, + DEVICE_COMPAT_EOL +}; + +static const struct device_compatible_entry compat_slot[] = { + { .compat = "cavium,octeon-6130-mmc-slot" }, + DEVICE_COMPAT_EOL +}; + +static const int octmmc_6130_interrupts[] = { 0, 1, -1 }; +static const int octmmc_7890_interrupts[] = { 1, 2, 3, 4, -1 }; + +static krwlock_t octmmc_lock; + +static int +octmmc_match(device_t parent, cfdata_t cf, void *aux) +{ + struct fdt_attach_args * const faa = aux; + + return of_compatible_match(faa->faa_phandle, compat_data); +} + +static void +octmmc_attach(device_t parent, device_t self, void *aux) +{ + struct sdmmcbus_attach_args saa; + struct fdt_attach_args * const faa = aux; + struct octmmc_softc *sc = device_private(self); + struct octmmc_bus *bus; + void *ih; + const int *interrupts; + const int phandle = faa->faa_phandle; + uint64_t reg; + uint32_t bus_id, bus_width; + bus_addr_t mmc_addr, dma_addr; + bus_size_t mmc_size, dma_size; + int i, node; + int maxsegs, rsegs; + + sc->sc_dev = self; + sc->sc_iot = faa->faa_bst; + sc->sc_dmat = faa->faa_dmat; + + if (of_compatible_match(phandle, compat_7890)) { + sc->sc_hwrev = OCTMMC_HWREV_7890; + interrupts = octmmc_7890_interrupts; + maxsegs = OCTMMC_MAX_NDMASEG_7890; + } else { + sc->sc_hwrev = OCTMMC_HWREV_6130; + interrupts = octmmc_6130_interrupts; + maxsegs = OCTMMC_MAX_NDMASEG_6130; + } + + if (fdtbus_get_reg(phandle, 0, &mmc_addr, &mmc_size) != 0) { + aprint_error(": could not get MMC registers\n"); + return; + } + if (fdtbus_get_reg(phandle, 1, &dma_addr, &dma_size) != 0) { + aprint_error(": could not get DMA registers\n"); + return; + } + + if (bus_space_map(sc->sc_iot, mmc_addr, mmc_size, 0, &sc->sc_mmc_ioh)) { + aprint_error(": could not map MMC registers\n"); + goto error; + } + if (bus_space_map(sc->sc_iot, dma_addr, dma_size, 0, &sc->sc_dma_ioh)) { + aprint_error(": could not map DMA registers\n"); + goto error; + } + if (sc->sc_hwrev == OCTMMC_HWREV_7890 && + bus_space_map(sc->sc_iot, dma_addr - MIO_EMM_DMA_FIFO_REGSIZE, + MIO_EMM_DMA_FIFO_REGSIZE, 0, &sc->sc_fifo_ioh)) { + aprint_error(": could not map FIFO registers\n"); + goto error; + } + if (bus_dmamem_alloc(sc->sc_dmat, OCTMMC_MAX_DMASEG, 0, 0, + &sc->sc_bounce_seg, 1, &rsegs, BUS_DMA_NOWAIT)) { + aprint_error(": could not allocate bounce buffer\n"); + goto error; + } + if (bus_dmamem_map(sc->sc_dmat, &sc->sc_bounce_seg, rsegs, + OCTMMC_MAX_DMASEG, &sc->sc_bounce_buf, + BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) { + aprint_error(": could not map bounce buffer\n"); + goto error_free; + } + if (bus_dmamap_create(sc->sc_dmat, OCTMMC_MAX_DMASEG, maxsegs, + OCTMMC_MAX_DMASEG, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, + &sc->sc_dma_data)) { + aprint_error(": could not create data dmamap\n"); + goto error_unmap; + } + + /* Disable all buses. */ + reg = MMC_RD_8(sc, MIO_EMM_CFG); + reg &= ~((1u << OCTMMC_MAX_BUSES) - 1); + MMC_WR_8(sc, MIO_EMM_CFG, reg); + + /* Clear any pending interrupts. */ + reg = MMC_RD_8(sc, MIO_EMM_INT); + MMC_WR_8(sc, MIO_EMM_INT, reg); + + for (i = 0; interrupts[i] != -1; i++) { + KASSERT(i < OCTMMC_MAX_INTRS); + ih = fdtbus_intr_establish_xname(phandle, interrupts[i], + IPL_SDMMC, FDT_INTR_MPSAFE, octmmc_intr, sc, + device_xname(self)); + if (ih == NULL) { + aprint_error(": could not establish interrupt %d\n", i); + goto error_intr; + } + sc->sc_ihs[i] = ih; + sc->sc_nihs++; + } + + aprint_normal(": Octeon MMC host controller\n"); + + sc->sc_current_bus = NULL; + sc->sc_current_switch = ~0ull; + + mutex_init(&sc->sc_intr_mtx, MUTEX_DEFAULT, IPL_SDMMC); + cv_init(&sc->sc_intr_cv, "octmmcirq"); + rw_init(&octmmc_lock); + + for (node = OF_child(phandle); node != 0; node = OF_peer(node)) { + uint32_t tmp; + + if (!of_compatible_match(node, compat_slot)) + continue; + + if (of_getprop_uint32(node, "reg", &bus_id) != 0) + bus_id = (uint32_t)-1; + if (bus_id >= OCTMMC_MAX_BUSES) + continue; + + bus = &sc->sc_buses[bus_id]; + bus->bus_hc = sc; + bus->bus_id = bus_id; + + if (of_getprop_uint32(node, "cavium,cmd-clk-skew", &tmp) == 0) + bus->bus_cmd_skew = tmp; + else + bus->bus_cmd_skew = 0; + + if (of_getprop_uint32(node, "cavium,dat-clk-skew", &tmp) == 0) + bus->bus_dat_skew = tmp; + else + bus->bus_dat_skew = 0; + + if (of_getprop_uint32(node, "spi-max-frequency", &tmp) == 0) + bus->bus_max_freq = tmp / 1000; + else + bus->bus_max_freq = 0; + if (bus->bus_max_freq == 0 || + bus->bus_max_freq > OCTMMC_MAX_FREQ) + bus->bus_max_freq = OCTMMC_MAX_FREQ; + + if (of_getprop_uint32(node, "bus-width", &bus_width) != 0) + bus_width = 0; + if (bus_width == 0) { + if (of_getprop_uint32(node, "cavium,bus-max-width", + &bus_width) != 0) + bus_width = 8; + } + + bus->bus_cd_gpio = fdtbus_gpio_acquire(node, "cd-gpios", + GPIO_PIN_INPUT); + + if (octmmc_init_bus(bus)) { + aprint_error_dev(self, "could not init bus %d\n", + bus_id); + continue; + } + + memset(&saa, 0, sizeof(saa)); + saa.saa_busname = "sdmmc"; + saa.saa_sct = &octmmc_funcs; + saa.saa_sch = bus; + saa.saa_clkmin = SDMMC_SDCLK_400K; + saa.saa_clkmax = bus->bus_max_freq; + saa.saa_caps = SMC_CAPS_MMC_HIGHSPEED | SMC_CAPS_AUTO_STOP; + if (bus_width >= 8) + saa.saa_caps |= SMC_CAPS_8BIT_MODE; + if (bus_width >= 4) + saa.saa_caps |= SMC_CAPS_4BIT_MODE; + + bus->bus_sdmmc = config_found(self, &saa, NULL, CFARGS_NONE); + if (bus->bus_sdmmc == NULL) + aprint_error_dev(self, "bus %d: could not attach sdmmc\n", + bus_id); + } + return; + +error_intr: + for (i = 0; i < sc->sc_nihs; i++) + fdtbus_intr_disestablish(phandle, sc->sc_ihs[i]); +error_unmap: + bus_dmamem_unmap(sc->sc_dmat, sc->sc_bounce_buf, OCTMMC_MAX_DMASEG); +error_free: + bus_dmamem_free(sc->sc_dmat, &sc->sc_bounce_seg, rsegs); +error: + if (sc->sc_dma_data != NULL) + bus_dmamap_destroy(sc->sc_dmat, sc->sc_dma_data); + if (sc->sc_fifo_ioh != 0) + bus_space_unmap(sc->sc_iot, sc->sc_fifo_ioh, + MIO_EMM_DMA_FIFO_REGSIZE); + if (sc->sc_dma_ioh != 0) + bus_space_unmap(sc->sc_iot, sc->sc_dma_ioh, dma_size); + if (sc->sc_mmc_ioh != 0) + bus_space_unmap(sc->sc_iot, sc->sc_mmc_ioh, mmc_size); +} + +void +octmmc_acquire(struct octmmc_bus *bus) +{ + struct octmmc_softc *sc = bus->bus_hc; + uint64_t period, sample; + + rw_enter(&octmmc_lock, RW_WRITER); + + /* Acquire the bootbus. */ + octeon_xkphys_write_8(MIO_BOOT_CFG, 0); + + if (sc->sc_current_bus == bus && + sc->sc_current_switch == bus->bus_switch) + return; + + /* Save relative card address. */ + if (sc->sc_current_bus != NULL) + sc->sc_current_bus->bus_rca = MMC_RD_8(sc, MIO_EMM_RCA); + + sc->sc_current_bus = NULL; + sc->sc_current_switch = ~0ull; + + /* Set bus parameters. */ + MMC_WR_8(sc, MIO_EMM_SWITCH, bus->bus_switch & + ~MIO_EMM_SWITCH_BUS_ID); + MMC_WR_8(sc, MIO_EMM_SWITCH, bus->bus_switch); + + /* Set relative card address. */ + MMC_WR_8(sc, MIO_EMM_RCA, bus->bus_rca); + + /* Set command timeout. */ + MMC_WR_8(sc, MIO_EMM_WDOG, bus->bus_wdog); + + /* Set sampling skew parameters. */ + period = 1000000000000ull / octeon_ioclock_speed(); + sample = (divround(bus->bus_cmd_skew, period) << + MIO_EMM_SAMPLE_CMD_CNT_SHIFT) & + MIO_EMM_SAMPLE_CMD_CNT; + sample |= (divround(bus->bus_dat_skew, period) << + MIO_EMM_SAMPLE_DAT_CNT_SHIFT) & + MIO_EMM_SAMPLE_DAT_CNT; + MMC_WR_8(bus->bus_hc, MIO_EMM_SAMPLE, sample); + + sc->sc_current_bus = bus; + sc->sc_current_switch = bus->bus_switch; + return; +} + +void +octmmc_release(struct octmmc_bus *bus) +{ + rw_exit(&octmmc_lock); +} + +int +octmmc_init_bus(struct octmmc_bus *bus) +{ + struct octmmc_softc *sc = bus->bus_hc; + uint64_t init_freq = SDMMC_SDCLK_400K; + uint64_t period; + uint64_t reg; + int s; + + period = divround(octeon_ioclock_speed(), init_freq * 1000 * 2); + if (period > MIO_EMM_SWITCH_CLK_MAX) + period = MIO_EMM_SWITCH_CLK_MAX; + + /* Set initial parameters. */ + bus->bus_switch = (uint64_t)bus->bus_id << MIO_EMM_SWITCH_BUS_ID_SHIFT; + bus->bus_switch |= 10ull << MIO_EMM_SWITCH_POWER_CLASS_SHIFT; + bus->bus_switch |= period << MIO_EMM_SWITCH_CLK_HI_SHIFT; + bus->bus_switch |= period << MIO_EMM_SWITCH_CLK_LO_SHIFT; + bus->bus_rca = 1; + + /* Make hardware timeout happen before timeout in software. */ + bus->bus_wdog = init_freq * 900 * OCTMMC_CMD_TIMEOUT; + if (bus->bus_wdog > MIO_EMM_WDOG_CLK_CNT) + bus->bus_wdog = MIO_EMM_WDOG_CLK_CNT; + + s = splsdmmc(); + + /* Enable the bus. */ + reg = MMC_RD_8(sc, MIO_EMM_CFG); + reg |= 1u << bus->bus_id; + MMC_WR_8(sc, MIO_EMM_CFG, reg); + + octmmc_acquire(bus); + + /* + * Enable interrupts. + * + * The mask register is present only on the revision 6130 controller + * where interrupt causes share an interrupt vector. + * The 7890 controller has a separate vector for each interrupt cause. + */ + if (sc->sc_hwrev == OCTMMC_HWREV_6130) { + MMC_WR_8(sc, MIO_EMM_INT_EN, + MIO_EMM_INT_CMD_ERR | MIO_EMM_INT_CMD_DONE | + MIO_EMM_INT_DMA_ERR | MIO_EMM_INT_DMA_DONE); + } + + MMC_WR_8(sc, MIO_EMM_STS_MASK, DEF_STS_MASK); + + octmmc_release(bus); + + splx(s); + + return 0; +} + +int +octmmc_intr(void *arg) +{ + struct octmmc_softc *sc = arg; + uint64_t isr; + + /* Get and acknowledge pending interrupts. */ + isr = MMC_RD_8(sc, MIO_EMM_INT); + if (isr == 0) + return 0; + MMC_WR_8(sc, MIO_EMM_INT, isr); + + if (ISSET(isr, MIO_EMM_INT_CMD_DONE) || + ISSET(isr, MIO_EMM_INT_CMD_ERR) || + ISSET(isr, MIO_EMM_INT_DMA_DONE) || + ISSET(isr, MIO_EMM_INT_DMA_ERR)) { + mutex_enter(&sc->sc_intr_mtx); + sc->sc_intr_status |= isr; + cv_broadcast(&sc->sc_intr_cv); + mutex_exit(&sc->sc_intr_mtx); + } + + return 1; +} + +int +octmmc_host_reset(sdmmc_chipset_handle_t sch) +{ + struct octmmc_bus *bus = sch; + int s; + + /* Force reswitch. */ + bus->bus_hc->sc_current_switch = ~0ull; + + s = splsdmmc(); + octmmc_acquire(bus); + octmmc_release(bus); + splx(s); + + return 0; +} + +uint32_t +octmmc_host_ocr(sdmmc_chipset_handle_t sch) +{ + /* The hardware does only 3.3V. */ + return MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V; +} + +int +octmmc_host_maxblklen(sdmmc_chipset_handle_t sch) +{ + return OCTMMC_BLOCK_SIZE; +} + +int +octmmc_card_detect(sdmmc_chipset_handle_t sch) +{ + struct octmmc_bus *bus = sch; + + if (bus->bus_cd_gpio != NULL) + return fdtbus_gpio_read(bus->bus_cd_gpio); + + return 1; +} + +int +octmmc_write_protect(sdmmc_chipset_handle_t sch) +{ + return 0; +} + +int +octmmc_bus_rod(sdmmc_chipset_handle_t sch, int on) +{ + return 0; +} + +int +octmmc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr) +{ + if (ocr == 0) + octmmc_host_reset(sch); + + return 0; +} + +int +octmmc_bus_clock(sdmmc_chipset_handle_t sch, int freq) +{ + struct octmmc_bus *bus = sch; + uint64_t ioclock = octeon_ioclock_speed(); + uint64_t period; + + if (freq == 0) + return 0; + if (freq > bus->bus_max_freq) + freq = bus->bus_max_freq; + period = divround(ioclock, freq * 1000 * 2); + if (period > MIO_EMM_SWITCH_CLK_MAX) + period = MIO_EMM_SWITCH_CLK_MAX; + + bus->bus_switch &= ~MIO_EMM_SWITCH_CLK_HI; + bus->bus_switch &= ~MIO_EMM_SWITCH_CLK_LO; + bus->bus_switch |= period << MIO_EMM_SWITCH_CLK_HI_SHIFT; + bus->bus_switch |= period << MIO_EMM_SWITCH_CLK_LO_SHIFT; + + /* Make hardware timeout happen before timeout in software. */ + bus->bus_wdog = freq * 900 * OCTMMC_CMD_TIMEOUT; + if (bus->bus_wdog > MIO_EMM_WDOG_CLK_CNT) + bus->bus_wdog = MIO_EMM_WDOG_CLK_CNT; + + /* Enable high-speed timing for frequencies above 26 MHz. */ + if (freq > 26000) + bus->bus_switch |= MIO_EMM_SWITCH_HS_TIMING; + else + bus->bus_switch &= ~MIO_EMM_SWITCH_HS_TIMING; + + return 0; +} + +int +octmmc_bus_width(sdmmc_chipset_handle_t sch, int width) +{ + struct octmmc_bus *bus = sch; + uint64_t bus_width; + + switch (width) { + case 1: + bus_width = 0; + break; + case 4: + bus_width = 1; + break; + case 8: + bus_width = 2; + break; + default: + return ENOTSUP; + } + bus->bus_switch &= ~MIO_EMM_SWITCH_BUS_WIDTH; + bus->bus_switch |= bus_width << MIO_EMM_SWITCH_BUS_WIDTH_SHIFT; + + return 0; +} + +void +octmmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) +{ + struct octmmc_bus *bus = sch; + + /* + * Refuse SDIO probe. Proper SDIO operation is not possible + * because of a lack of card interrupt handling. + */ + if (cmd->c_opcode == SD_IO_SEND_OP_COND) { + cmd->c_error = ENOTSUP; + return; + } + + /* + * The DMA mode can only do data block transfers. Other commands have + * to use the PIO mode. Single-block transfers can use PIO because + * it has low setup overhead. + */ + if (cmd->c_opcode == MMC_READ_BLOCK_MULTIPLE || + cmd->c_opcode == MMC_WRITE_BLOCK_MULTIPLE) + octmmc_exec_dma(bus, cmd); + else + octmmc_exec_pio(bus, cmd); +} + +void +octmmc_exec_dma(struct octmmc_bus *bus, struct sdmmc_command *cmd) +{ + struct octmmc_softc *sc = bus->bus_hc; + uint64_t dmacmd, status; + paddr_t locked_block = 0; + int bounce = 0; + int s; + + if (cmd->c_datalen > OCTMMC_MAX_DMASEG) { + cmd->c_error = ENOMEM; + return; + } + + s = splsdmmc(); + octmmc_acquire(bus); + + /* + * Attempt to use the buffer directly for DMA. In case the region + * is not physically contiguous, bounce the data. + */ + if (bus_dmamap_load(sc->sc_dmat, sc->sc_dma_data, cmd->c_data, + cmd->c_datalen, NULL, BUS_DMA_WAITOK)) { + cmd->c_error = bus_dmamap_load(sc->sc_dmat, sc->sc_dma_data, + sc->sc_bounce_buf, cmd->c_datalen, NULL, BUS_DMA_WAITOK); + if (cmd->c_error != 0) + goto dma_out; + + bounce = 1; + if (!ISSET(cmd->c_flags, SCF_CMD_READ)) + memcpy(sc->sc_bounce_buf, cmd->c_data, cmd->c_datalen); + } + + bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_data, 0, cmd->c_datalen, + ISSET(cmd->c_flags, SCF_CMD_READ) ? BUS_DMASYNC_PREREAD : + BUS_DMASYNC_PREWRITE); + + if (sc->sc_hwrev == OCTMMC_HWREV_7890) + octmmc_dma_load_7890(sc, cmd); + else + locked_block = octmmc_dma_load_6130(sc, cmd); + + /* Set status mask. */ + MMC_WR_8(sc, MIO_EMM_STS_MASK, DEF_STS_MASK); + + mutex_enter(&sc->sc_intr_mtx); + + /* Prepare and issue the command. */ + dmacmd = MIO_EMM_DMA_DMA_VAL | MIO_EMM_DMA_MULTI | MIO_EMM_DMA_SECTOR; + dmacmd |= (uint64_t)bus->bus_id << MIO_EMM_DMA_BUS_ID_SHIFT; + dmacmd |= (uint64_t)(cmd->c_datalen / cmd->c_blklen) << + MIO_EMM_DMA_BLOCK_CNT_SHIFT; + dmacmd |= cmd->c_arg; + if (!ISSET(cmd->c_flags, SCF_CMD_READ)) + dmacmd |= MIO_EMM_DMA_RW; + MMC_WR_8(sc, MIO_EMM_DMA, dmacmd); + +wait_intr: + cmd->c_error = octmmc_wait_intr(sc, MIO_EMM_INT_CMD_ERR | + MIO_EMM_INT_DMA_DONE | MIO_EMM_INT_DMA_ERR, OCTMMC_CMD_TIMEOUT); + + status = MMC_RD_8(sc, MIO_EMM_RSP_STS); + + /* Check DMA engine error. */ + if (ISSET(sc->sc_intr_status, MIO_EMM_INT_DMA_ERR)) { + aprint_error_dev(bus->bus_sdmmc, "dma error\n"); + + if (ISSET(status, MIO_EMM_RSP_STS_DMA_PEND)) { + /* Try to stop the DMA engine. */ + dmacmd = MMC_RD_8(sc, MIO_EMM_DMA); + dmacmd |= MIO_EMM_DMA_DMA_VAL | MIO_EMM_DMA_DAT_NULL; + dmacmd &= ~MIO_EMM_DMA_BUS_ID; + dmacmd |= (uint64_t)bus->bus_id << + MIO_EMM_DMA_BUS_ID_SHIFT; + MMC_WR_8(sc, MIO_EMM_DMA, dmacmd); + goto wait_intr; + } + cmd->c_error = EIO; + } + + mutex_exit(&sc->sc_intr_mtx); + + if (cmd->c_error != 0) + goto unload_dma; + + /* Check command status. */ + if (((status & MIO_EMM_RSP_STS_CMD_IDX) >> + MIO_EMM_RSP_STS_CMD_IDX_SHIFT) != cmd->c_opcode || + ISSET(status, MIO_EMM_RSP_STS_BLK_CRC_ERR) || + ISSET(status, MIO_EMM_RSP_STS_DBUF_ERR) || + ISSET(status, MIO_EMM_RSP_STS_RSP_BAD_STS) || + ISSET(status, MIO_EMM_RSP_STS_RSP_CRC_ERR)) { + cmd->c_error = EIO; + goto unload_dma; + } + if (ISSET(status, MIO_EMM_RSP_STS_BLK_TIMEOUT) || + ISSET(status, MIO_EMM_RSP_STS_RSP_TIMEOUT)) { + cmd->c_error = ETIMEDOUT; + goto unload_dma; + } + + if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) + octmmc_get_response(sc, cmd); + + bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_data, 0, cmd->c_datalen, + ISSET(cmd->c_flags, SCF_CMD_READ) ? BUS_DMASYNC_POSTREAD : + BUS_DMASYNC_POSTWRITE); + + if (bounce && ISSET(cmd->c_flags, SCF_CMD_READ)) + memcpy(cmd->c_data, sc->sc_bounce_buf, cmd->c_datalen); + +unload_dma: + if (sc->sc_hwrev == OCTMMC_HWREV_7890) + octmmc_dma_unload_7890(sc); + else + octmmc_dma_unload_6130(sc, locked_block); + + bus_dmamap_unload(sc->sc_dmat, sc->sc_dma_data); + +dma_out: + octmmc_release(bus); + splx(s); +} + +void +octmmc_exec_pio(struct octmmc_bus *bus, struct sdmmc_command *cmd) +{ + struct octmmc_softc *sc = bus->bus_hc; + unsigned char *ptr; + uint64_t piocmd, status, value; + unsigned int i; + int s; + + if (cmd->c_datalen > OCTMMC_BLOCK_SIZE || + cmd->c_datalen % sizeof(uint64_t) != 0) { + cmd->c_error = EINVAL; + return; + } + + s = splsdmmc(); + octmmc_acquire(bus); + + /* If this is a write, copy data to the controller's buffer. */ + if (cmd->c_data != NULL && !ISSET(cmd->c_flags, SCF_CMD_READ)) { + /* Reset index and set autoincrement. */ + MMC_WR_8(sc, MIO_EMM_BUF_IDX, MIO_EMM_BUF_IDX_INC); + + ptr = cmd->c_data; + for (i = 0; i < cmd->c_datalen / sizeof(value); i++) { + memcpy(&value, ptr, sizeof(value)); + MMC_WR_8(sc, MIO_EMM_BUF_DAT, value); + ptr += sizeof(value); + } + } + + /* Set status mask. */ + MMC_WR_8(sc, MIO_EMM_STS_MASK, PIO_STS_MASK); + + mutex_enter(&sc->sc_intr_mtx); + + /* Issue the command. */ + piocmd = MIO_EMM_CMD_CMD_VAL; + piocmd |= (uint64_t)bus->bus_id << MIO_EMM_CMD_BUS_ID_SHIFT; + piocmd |= (uint64_t)cmd->c_opcode << MIO_EMM_CMD_CMD_IDX_SHIFT; + piocmd |= cmd->c_arg; + piocmd |= octmmc_crtype_fixup(cmd); + MMC_WR_8(sc, MIO_EMM_CMD, piocmd); + + cmd->c_error = octmmc_wait_intr(sc, MIO_EMM_INT_CMD_DONE | + MIO_EMM_INT_CMD_ERR, OCTMMC_CMD_TIMEOUT); + + mutex_exit(&sc->sc_intr_mtx); + + if (cmd->c_error != 0) + goto pio_out; + if (ISSET(sc->sc_intr_status, MIO_EMM_INT_CMD_ERR)) { + cmd->c_error = EIO; + goto pio_out; + } + + /* Check command status. */ + status = MMC_RD_8(sc, MIO_EMM_RSP_STS); + if (((status & MIO_EMM_RSP_STS_CMD_IDX) >> + MIO_EMM_RSP_STS_CMD_IDX_SHIFT) != cmd->c_opcode || + ISSET(status, MIO_EMM_RSP_STS_BLK_CRC_ERR) || + ISSET(status, MIO_EMM_RSP_STS_DBUF_ERR) || + ISSET(status, MIO_EMM_RSP_STS_RSP_BAD_STS) || + ISSET(status, MIO_EMM_RSP_STS_RSP_CRC_ERR)) { + cmd->c_error = EIO; + goto pio_out; + } + if (ISSET(status, MIO_EMM_RSP_STS_BLK_TIMEOUT) || + ISSET(status, MIO_EMM_RSP_STS_RSP_TIMEOUT)) { + cmd->c_error = ETIMEDOUT; + goto pio_out; + } + + if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) + octmmc_get_response(sc, cmd); + + /* If this is a read, copy data from the controller's buffer. */ + if (cmd->c_data != NULL && ISSET(cmd->c_flags, SCF_CMD_READ)) { + /* Reset index and set autoincrement. */ + MMC_WR_8(sc, MIO_EMM_BUF_IDX, MIO_EMM_BUF_IDX_INC); + + ptr = cmd->c_data; + for (i = 0; i < cmd->c_datalen / sizeof(value); i++) { + value = MMC_RD_8(sc, MIO_EMM_BUF_DAT); + memcpy(ptr, &value, sizeof(value)); + ptr += sizeof(value); + } + } + +pio_out: + octmmc_release(bus); + splx(s); +} + +paddr_t +octmmc_dma_load_6130(struct octmmc_softc *sc, struct sdmmc_command *cmd) +{ + uint64_t dmacfg; + paddr_t locked_block = 0; + + /* + * The DMA hardware has a silicon bug that can corrupt data + * (erratum EMMC-17978). As a workaround, Linux locks the second last + * block of a transfer into the L2 cache if the opcode is multi-block + * write and there are more than two data blocks to write. + * In Linux, it is not described under what circumstances + * the corruption can happen. + * Lacking better information, use the same workaround here. + */ + if (cmd->c_opcode == MMC_WRITE_BLOCK_MULTIPLE && + cmd->c_datalen > OCTMMC_BLOCK_SIZE * 2) { + locked_block = sc->sc_dma_data->dm_segs[0].ds_addr + + sc->sc_dma_data->dm_segs[0].ds_len - OCTMMC_BLOCK_SIZE * 2; + octeon_lock_secondary_cache(locked_block, OCTMMC_BLOCK_SIZE); + } + + /* Set up the DMA engine. */ + dmacfg = MIO_NDF_DMA_CFG_EN; + if (!ISSET(cmd->c_flags, SCF_CMD_READ)) + dmacfg |= MIO_NDF_DMA_CFG_RW; + dmacfg |= (sc->sc_dma_data->dm_segs[0].ds_len / sizeof(uint64_t) - 1) + << MIO_NDF_DMA_CFG_SIZE_SHIFT; + dmacfg |= sc->sc_dma_data->dm_segs[0].ds_addr; + DMA_WR_8(sc, MIO_NDF_DMA_CFG, dmacfg); + + return locked_block; +} + +void +octmmc_dma_unload_6130(struct octmmc_softc *sc, paddr_t locked_block) +{ + if (locked_block != 0) + octeon_unlock_secondary_cache(locked_block, OCTMMC_BLOCK_SIZE); +} + +void +octmmc_dma_load_7890(struct octmmc_softc *sc, struct sdmmc_command *cmd) +{ + bus_dma_segment_t *seg; + uint64_t fifocmd; + int i; + + /* Enable the FIFO. */ + FIFO_WR_8(sc, MIO_EMM_DMA_FIFO_CFG, 0); + + for (i = 0; i < sc->sc_dma_data->dm_nsegs; i++) { + seg = &sc->sc_dma_data->dm_segs[i]; + + fifocmd = (seg->ds_len / sizeof(uint64_t) - 1) << + MIO_EMM_DMA_FIFO_CMD_SIZE_SHIFT; + if (!ISSET(cmd->c_flags, SCF_CMD_READ)) + fifocmd |= MIO_EMM_DMA_FIFO_CMD_RW; + if (i < sc->sc_dma_data->dm_nsegs - 1) + fifocmd |= MIO_EMM_DMA_FIFO_CMD_INTDIS; + + /* Create a FIFO entry. */ + FIFO_WR_8(sc, MIO_EMM_DMA_FIFO_ADR, seg->ds_addr); + FIFO_WR_8(sc, MIO_EMM_DMA_FIFO_CMD, fifocmd); + } +} + +void +octmmc_dma_unload_7890(struct octmmc_softc *sc) +{ + /* Disable the FIFO. */ + FIFO_WR_8(sc, MIO_EMM_DMA_FIFO_CFG, MIO_EMM_DMA_FIFO_CFG_CLR); +} + +/* + * The controller uses MMC command and response types by default. + * When the command and response types of an SD opcode differ from those + * of an overlapping MMC opcode, it is necessary to fix the types. + * Fixing is also needed with a non-overlapping SD opcode when the command + * is a data transfer (adtc) or if a response is expected. + */ +uint64_t +octmmc_crtype_fixup(struct sdmmc_command *cmd) +{ + uint64_t cxor = 0; + uint64_t rxor = 0; + + switch (cmd->c_opcode) { + case SD_IO_SEND_OP_COND: + cxor = 0x00; + rxor = 0x02; + break; + case SD_SEND_IF_COND: + /* The opcode overlaps with MMC_SEND_EXT_CSD. */ + if ((cmd->c_flags & SCF_CMD_MASK) == SCF_CMD_BCR) { + cxor = 0x01; + rxor = 0x00; + } + break; + case SD_APP_OP_COND: + cxor = 0x00; + rxor = 0x03; + break; + case SD_IO_RW_DIRECT: + case SD_IO_RW_EXTENDED: + cxor = 0x00; + rxor = 0x01; + break; + } + return (cxor << MIO_EMM_CMD_CTYPE_XOR_SHIFT) | + (rxor << MIO_EMM_CMD_RTYPE_XOR_SHIFT); +} + +void +octmmc_get_response(struct octmmc_softc *sc, struct sdmmc_command *cmd) +{ + uint64_t hi, lo; + + if (ISSET(cmd->c_flags, SCF_RSP_136)) { + hi = MMC_RD_8(sc, MIO_EMM_RSP_HI); + lo = MMC_RD_8(sc, MIO_EMM_RSP_LO); + + /* Discard the checksum. */ + lo = (lo >> 8) | (hi << 56); + hi >>= 8; + + /* + * On NetBSD these are 32b in host byte order + */ + cmd->c_resp[0] = lo & 0xffffffffu; + cmd->c_resp[1] = lo >> 32; + cmd->c_resp[2] = hi & 0xffffffffu; + cmd->c_resp[3] = hi >> 32; + } else { + cmd->c_resp[0] = MMC_RD_8(sc, MIO_EMM_RSP_LO) >> 8; + } +} + +int +octmmc_wait_intr(struct octmmc_softc *sc, uint64_t mask, int secs) +{ + + KASSERT(mutex_owned(&sc->sc_intr_mtx)); + + sc->sc_intr_status = 0; + while ((sc->sc_intr_status & mask) == 0) { + if (cv_timedwait(&sc->sc_intr_cv, &sc->sc_intr_mtx, + secs * hz) == ETIMEDOUT) + return ETIMEDOUT; + } + return 0; +} diff --git a/sys/arch/mips/cavium/dev/octeon_mmcreg.h b/sys/arch/mips/cavium/dev/octeon_mmcreg.h new file mode 100644 index 000000000000..77e048bad91d --- /dev/null +++ b/sys/arch/mips/cavium/dev/octeon_mmcreg.h @@ -0,0 +1,176 @@ +/* $NetBSD: octeon_mmc.c,v $ */ +/* $OpenBSD: octmmcreg.h,v 1.3 2017/10/16 14:18:47 visa Exp $ */ + +/* + * Copyright (c) 2016 Visa Hankala + * + * Permission to use, copy, modify, and/or 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. + */ + +#ifndef _OCTMMCREG_H_ +#define _OCTMMCREG_H_ + +#define MIO_BOOT_CFG 0x00011800000000d0ull + +/* + * MMC registers + */ + +#define MIO_EMM_CFG 0x00 +#define MIO_EMM_SWITCH 0x48 +#define MIO_EMM_SWITCH_RES_62_63 0xc000000000000000ull +#define MIO_EMM_SWITCH_BUS_ID 0x3000000000000000ull +#define MIO_EMM_SWITCH_BUS_ID_SHIFT 60 +#define MIO_EMM_SWITCH_SWITCH_EXE 0x0800000000000000ull +#define MIO_EMM_SWITCH_SWITCH_ERR0 0x0400000000000000ull +#define MIO_EMM_SWITCH_SWITCH_ERR1 0x0200000000000000ull +#define MIO_EMM_SWITCH_SWITCH_ERR2 0x0100000000000000ull +#define MIO_EMM_SWITCH_RES_49_55 0x00fe000000000000ull +#define MIO_EMM_SWITCH_HS_TIMING 0x0001000000000000ull +#define MIO_EMM_SWITCH_HS_TIMING_SHIFT 48 +#define MIO_EMM_SWITCH_RES_43_47 0x0000f80000000000ull +#define MIO_EMM_SWITCH_BUS_WIDTH 0x0000070000000000ull +#define MIO_EMM_SWITCH_BUS_WIDTH_SHIFT 40 +#define MIO_EMM_SWITCH_RES_36_39 0x000000f000000000ull +#define MIO_EMM_SWITCH_POWER_CLASS 0x0000000f00000000ull +#define MIO_EMM_SWITCH_POWER_CLASS_SHIFT 32 +#define MIO_EMM_SWITCH_CLK_HI 0x00000000ffff0000ull +#define MIO_EMM_SWITCH_CLK_HI_SHIFT 16 +#define MIO_EMM_SWITCH_CLK_LO 0x000000000000ffffull +#define MIO_EMM_SWITCH_CLK_LO_SHIFT 0 +#define MIO_EMM_SWITCH_CLK_MAX 0xffffu +#define MIO_EMM_DMA 0x50 +#define MIO_EMM_DMA_RES_62_63 0xc000000000000000ull +#define MIO_EMM_DMA_BUS_ID 0x3000000000000000ull +#define MIO_EMM_DMA_BUS_ID_SHIFT 60 +#define MIO_EMM_DMA_DMA_VAL 0x0800000000000000ull +#define MIO_EMM_DMA_SECTOR 0x0400000000000000ull +#define MIO_EMM_DMA_DAT_NULL 0x0200000000000000ull +#define MIO_EMM_DMA_THRES 0x01f8000000000000ull +#define MIO_EMM_DMA_REL_WR 0x0004000000000000ull +#define MIO_EMM_DMA_RW 0x0002000000000000ull +#define MIO_EMM_DMA_MULTI 0x0001000000000000ull +#define MIO_EMM_DMA_BLOCK_CNT 0x0000ffff00000000ull +#define MIO_EMM_DMA_BLOCK_CNT_SHIFT 32 +#define MIO_EMM_DMA_CARD_ADDR 0x00000000ffffffffull +#define MIO_EMM_CMD 0x58 +#define MIO_EMM_CMD_RES_62_63 0xc000000000000000ull +#define MIO_EMM_CMD_BUS_ID 0x3000000000000000ull +#define MIO_EMM_CMD_BUS_ID_SHIFT 60 +#define MIO_EMM_CMD_CMD_VAL 0x0800000000000000ull +#define MIO_EMM_CMD_RES_56_58 0x0700000000000000ull +#define MIO_EMM_CMD_DBUF 0x0080000000000000ull +#define MIO_EMM_CMD_OFFSET 0x007e000000000000ull +#define MIO_EMM_CMD_RES_43_48 0x0001f80000000000ull +#define MIO_EMM_CMD_CTYPE_XOR 0x0000060000000000ull +#define MIO_EMM_CMD_CTYPE_XOR_SHIFT 41 +#define MIO_EMM_CMD_RTYPE_XOR 0x000001c000000000ull +#define MIO_EMM_CMD_RTYPE_XOR_SHIFT 38 +#define MIO_EMM_CMD_CMD_IDX 0x0000003f00000000ull +#define MIO_EMM_CMD_CMD_IDX_SHIFT 32 +#define MIO_EMM_CMD_ARG 0x00000000ffffffffull +#define MIO_EMM_RSP_STS 0x60 +#define MIO_EMM_RSP_STS_RES_62_63 0xc000000000000000ull +#define MIO_EMM_RSP_STS_BUS_ID 0x3000000000000000ull +#define MIO_EMM_RSP_STS_CMD_VAL 0x0800000000000000ull +#define MIO_EMM_RSP_STS_SWITCH_VAL 0x0400000000000000ull +#define MIO_EMM_RSP_STS_DMA_VAL 0x0200000000000000ull +#define MIO_EMM_RSP_STS_DMA_PEND 0x0100000000000000ull +#define MIO_EMM_RSP_STS_RES_29_55 0x00ffffffe0000000ull +#define MIO_EMM_RSP_STS_DBUF_ERR 0x0000000010000000ull +#define MIO_EMM_RSP_STS_RES_24_27 0x000000000f000000ull +#define MIO_EMM_RSP_STS_DBUF 0x0000000000800000ull +#define MIO_EMM_RSP_STS_DBUF_SHIFT 23 +#define MIO_EMM_RSP_STS_BLK_TIMEOUT 0x0000000000400000ull +#define MIO_EMM_RSP_STS_BLK_CRC_ERR 0x0000000000200000ull +#define MIO_EMM_RSP_STS_RSP_BUSY 0x0000000000100000ull +#define MIO_EMM_RSP_STS_STP_TIMEOUT 0x0000000000080000ull +#define MIO_EMM_RSP_STS_STP_CRC_ERR 0x0000000000040000ull +#define MIO_EMM_RSP_STS_STP_BAD_STS 0x0000000000020000ull +#define MIO_EMM_RSP_STS_STP_VAL 0x0000000000010000ull +#define MIO_EMM_RSP_STS_RSP_TIMEOUT 0x0000000000008000ull +#define MIO_EMM_RSP_STS_RSP_CRC_ERR 0x0000000000004000ull +#define MIO_EMM_RSP_STS_RSP_BAD_STS 0x0000000000002000ull +#define MIO_EMM_RSP_STS_RSP_VAL 0x0000000000001000ull +#define MIO_EMM_RSP_STS_RSP_TYPE 0x0000000000000e00ull +#define MIO_EMM_RSP_STS_CMD_TYPE 0x0000000000000180ull +#define MIO_EMM_RSP_STS_CMD_IDX 0x000000000000007eull +#define MIO_EMM_RSP_STS_CMD_IDX_SHIFT 1 +#define MIO_EMM_RSP_STS_CMD_DONE 0x0000000000000001ull +#define MIO_EMM_RSP_LO 0x68 +#define MIO_EMM_RSP_HI 0x70 +#define MIO_EMM_INT 0x78 +#define MIO_EMM_INT_RES_7_63 0xffffffffffffff80ull +#define MIO_EMM_INT_SWITCH_ERR 0x0000000000000040ull +#define MIO_EMM_INT_SWITCH_DONE 0x0000000000000020ull +#define MIO_EMM_INT_DMA_ERR 0x0000000000000010ull +#define MIO_EMM_INT_CMD_ERR 0x0000000000000008ull +#define MIO_EMM_INT_DMA_DONE 0x0000000000000004ull +#define MIO_EMM_INT_CMD_DONE 0x0000000000000002ull +#define MIO_EMM_INT_BUF_DONE 0x0000000000000001ull +#define MIO_EMM_INT_EN 0x80 +#define MIO_EMM_WDOG 0x88 +#define MIO_EMM_WDOG_CLK_CNT 0x0000000003ffffffull +#define MIO_EMM_SAMPLE 0x90 +#define MIO_EMM_SAMPLE_CMD_CNT 0x0000000003ff0000ull +#define MIO_EMM_SAMPLE_CMD_CNT_SHIFT 16 +#define MIO_EMM_SAMPLE_DAT_CNT 0x00000000000003ffull +#define MIO_EMM_SAMPLE_DAT_CNT_SHIFT 0 +#define MIO_EMM_STS_MASK 0x98 +#define MIO_EMM_RCA 0xa0 +#define MIO_EMM_BUF_IDX 0xe0 +#define MIO_EMM_BUF_IDX_RES_17_63 0xfffffffffffe0000ull +#define MIO_EMM_BUF_IDX_INC 0x0000000000010000ull +#define MIO_EMM_BUF_IDX_RES_7_15 0x000000000000ff80ull +#define MIO_EMM_BUF_IDX_BUF_NUM 0x0000000000000040ull +#define MIO_EMM_BUF_IDX_BUF_NUM_SHIFT 6 +#define MIO_EMM_BUF_IDX_OFFSET 0x000000000000003full +#define MIO_EMM_BUF_DAT 0xe8 + +/* + * MMC DMA FIFO registers + */ + +/* Size of the register space. */ +#define MIO_EMM_DMA_FIFO_REGSIZE 0x20 + +#define MIO_EMM_DMA_FIFO_CFG 0x00 +#define MIO_EMM_DMA_FIFO_CFG_CLR 0x0000000000010000ull + +#define MIO_EMM_DMA_FIFO_ADR 0x10 + +#define MIO_EMM_DMA_FIFO_CMD 0x18 +#define MIO_EMM_DMA_FIFO_CMD_RW 0x4000000000000000ull +#define MIO_EMM_DMA_FIFO_CMD_INTDIS 0x1000000000000000ull +#define MIO_EMM_DMA_FIFO_CMD_SIZE 0x00fffff000000000ull +#define MIO_EMM_DMA_FIFO_CMD_SIZE_SHIFT 36 + +/* + * NAND flash DMA registers + */ + +#define MIO_NDF_DMA_CFG 0x00 +#define MIO_NDF_DMA_CFG_EN 0x8000000000000000ull +#define MIO_NDF_DMA_CFG_RW 0x4000000000000000ull +#define MIO_NDF_DMA_CFG_CLR 0x2000000000000000ull +#define MIO_NDF_DMA_CFG_RES_60_60 0x1000000000000000ull +#define MIO_NDF_DMA_CFG_SWAP32 0x0800000000000000ull +#define MIO_NDF_DMA_CFG_SWAP16 0x0400000000000000ull +#define MIO_NDF_DMA_CFG_SWAP8 0x0200000000000000ull +#define MIO_NDF_DMA_CFG_ENDIAN 0x0100000000000000ull +#define MIO_NDF_DMA_CFG_SIZE 0x00fffff000000000ull +#define MIO_NDF_DMA_CFG_SIZE_SHIFT 36 +#define MIO_NDF_DMA_CFG_ADDR 0x0000000fffffffffull +#define MIO_NDF_DMA_ADR 0x08 + +#endif /* !_OCTMMCREG_H_ */ diff --git a/sys/arch/mips/conf/files.octeon b/sys/arch/mips/conf/files.octeon index cf6e5a0c6e84..20a407f2cf14 100644 --- a/sys/arch/mips/conf/files.octeon +++ b/sys/arch/mips/conf/files.octeon @@ -93,6 +93,14 @@ device octgmx {} attach octgmx at octpip file arch/mips/cavium/dev/octeon_gmx.c octgmx +# Machine-independent MMC drivers +include "dev/sdmmc/files.sdmmc" + +# SDMMC +device octmmc: sdmmcbus +attach octmmc at fdt with octmmc_fdt +file arch/mips/cavium/dev/octeon_mmc.c octmmc + # On-chip ethernet device(s) device cnmac: ether, ifnet, arp, mii attach cnmac at octgmx diff --git a/sys/arch/mips/include/cache_octeon.h b/sys/arch/mips/include/cache_octeon.h index 481a94806b88..c99d54857bb8 100644 --- a/sys/arch/mips/include/cache_octeon.h +++ b/sys/arch/mips/include/cache_octeon.h @@ -80,6 +80,8 @@ void octeon_pdcache_inv_all(void); void octeon_pdcache_inv_range(register_t va, vsize_t size); void octeon_pdcache_inv_range_index(vaddr_t va, vsize_t size); void octeon_pdcache_wb_range(register_t va, vsize_t size); +void octeon_lock_secondary_cache(paddr_t, size_t); +void octeon_unlock_secondary_cache(paddr_t, size_t); #endif /* !_LOCORE */ #endif /* _MIPS_CACHE_OCTEON_H_ */ diff --git a/sys/arch/mips/mips/cache_octeon.c b/sys/arch/mips/mips/cache_octeon.c index c80b31e2efa0..a02575c3e9e0 100644 --- a/sys/arch/mips/mips/cache_octeon.c +++ b/sys/arch/mips/mips/cache_octeon.c @@ -86,6 +86,47 @@ octeon_pdcache_inv_range_index(vaddr_t va, vsize_t size) SYNC; } +void +octeon_lock_secondary_cache(paddr_t _pa, size_t _sz) +{ + size_t linesize = mips_cache_info.mci_sdcache_line_size; + size_t sz; + paddr_t pa; + vaddr_t end, va; + + pa = _pa & ~(linesize - 1); + sz = ((_pa + _sz + linesize - 1) & ~(linesize - 1)) - pa; + + va = MIPS_PHYS_TO_XKPHYS(pa, 0ul); + end = va + sz; + while (va < end) { + asm volatile ("cache 31, (%0)" : : "r" (va)); + va += linesize; + } + + /* Wait for the lock operations to finish. */ + SYNC; +} + +void +octeon_unlock_secondary_cache(paddr_t _pa, size_t _sz) +{ + size_t linesize = mips_cache_info.mci_sdcache_line_size; + size_t sz; + paddr_t pa; + vaddr_t end, va; + + pa = _pa & ~(linesize - 1); + sz = ((_pa + _sz + linesize - 1) & ~(linesize - 1)) - pa; + + va = MIPS_PHYS_TO_XKPHYS(pa, 0ul); + end = va + sz; + while (va < end) { + asm volatile ("cache 23, (%0)" : : "r" (va)); + va += linesize; + } +} + /* ---- debug dump */ #ifdef OCTEON_ICACHE_DEBUG