diff --git a/sys/arch/arm64/arm64/machdep.c b/sys/arch/arm64/arm64/machdep.c index 45f6451066a..ef2b75e1ec5 100644 --- a/sys/arch/arm64/arm64/machdep.c +++ b/sys/arch/arm64/arm64/machdep.c @@ -978,10 +978,6 @@ initarm(struct arm64_bootparams *abp) * EfiLoaderData so it won't be added again here. */ for (i = 0; i < mmap_size / mmap_desc_size; i++) { - printf("type 0x%x pa 0x%llx va 0x%llx pages 0x%llx attr 0x%llx\n", - desc->Type, desc->PhysicalStart, - desc->VirtualStart, desc->NumberOfPages, - desc->Attribute); if (desc->Type == EfiConventionalMemory && desc->NumberOfPages >= 16) { uvm_page_physload(atop(desc->PhysicalStart), diff --git a/sys/arch/arm64/compile/CUSTOM/.gitignore b/sys/arch/arm64/compile/CUSTOM/.gitignore new file mode 100644 index 00000000000..2416a678e7d --- /dev/null +++ b/sys/arch/arm64/compile/CUSTOM/.gitignore @@ -0,0 +1 @@ +obj/ diff --git a/sys/arch/arm64/compile/CUSTOM/Makefile b/sys/arch/arm64/compile/CUSTOM/Makefile new file mode 100644 index 00000000000..01b5f23410c --- /dev/null +++ b/sys/arch/arm64/compile/CUSTOM/Makefile @@ -0,0 +1 @@ +.include "../Makefile.inc" diff --git a/sys/arch/arm64/conf/CUSTOM b/sys/arch/arm64/conf/CUSTOM new file mode 100644 index 00000000000..c0d6808d21b --- /dev/null +++ b/sys/arch/arm64/conf/CUSTOM @@ -0,0 +1,14 @@ +# $OpenBSD: GENERIC.MP,v 1.5 2018/07/01 21:05:07 kettenis Exp $ + +include "arch/arm64/conf/GENERIC" + +option MULTIPROCESSOR +option SDMMC_DEBUG +#option MP_LOCKDEBUG +#option WITNESS + +cpu* at mainbus? +bcm2835_dmac* at fdt? + +bcm2835_mmc* at fdt? +sdmmc* at bcm2835_mmc? diff --git a/sys/arch/arm64/dev/simplebus.c b/sys/arch/arm64/dev/simplebus.c index c2b19a1fb7e..e6274239d9f 100644 --- a/sys/arch/arm64/dev/simplebus.c +++ b/sys/arch/arm64/dev/simplebus.c @@ -154,13 +154,28 @@ simplebus_print(void *aux, const char *pnp) { struct fdt_attach_args *fa = aux; char name[32]; + char compatible[256]; + char* data = compatible; + int length; if (!pnp) return (QUIET); if (OF_getprop(fa->fa_node, "name", name, sizeof(name)) > 0) { name[sizeof(name) - 1] = 0; - printf("\"%s\"", name); + printf("\"%s", name); + + // print strings that can be used to match against this device in new drivers + length = OF_getprop(fa->fa_node, "compatible", compatible, sizeof(compatible)); + if (length > 0) { + compatible[sizeof(compatible) - 1] = 0; + while (length > 0) { + printf("<%s>", data); + length -= strlen(data) + 1; + data += strlen(data) + 1; + } + } + printf("\""); } else printf("node %u", fa->fa_node); diff --git a/sys/dev/fdt/bcm2835_dmac.c b/sys/dev/fdt/bcm2835_dmac.c new file mode 100644 index 00000000000..41e8782a35f --- /dev/null +++ b/sys/dev/fdt/bcm2835_dmac.c @@ -0,0 +1,315 @@ +/* $OpenBSD: mmc.c,v 1.0 2019/01/13 23:55:29 neil Exp $ */ + +/* + * Copyright (c) 2019 Neil Ashford + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "bcm2835_dmac.h" + +#define BCM2835_DMAC_CHANNELMASK ((1<<12) - 1) +#define DEVNAME(sc) ((sc)->sc_dev.dv_xname) + +struct bcm2835_dmac_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + int sc_fa_node; + + struct mutex sc_lock; + struct bcm2835_dmac_channel *sc_channels; + int sc_nchannels; + u_int32_t sc_channelmask; +}; + +static volatile void *attached_sc = NULL; + +struct bcm2835_dmac_channel { + struct bcm2835_dmac_softc *ch_sc; + void *ch_ih; + u_int8_t ch_index; + void (*ch_callback)(u_int32_t, u_int32_t, void *); + void *ch_callbackarg; + u_int32_t ch_debug; +}; + +int bcm2835_dmac_match(struct device *, void *, void *); +void bcm2835_dmac_attach(struct device *, struct device *, void *); + +struct cfattach bcm2835_dmac_ca = { + sizeof(struct bcm2835_dmac_softc), + bcm2835_dmac_match, + bcm2835_dmac_attach, +}; + +struct cfdriver bcm2835_dmac_cd = { + NULL, "dmac", DV_DULL +}; + +/* utilities */ +enum bcm2835_dmac_type +bcm2835_dmac_channel_type(struct bcm2835_dmac_channel ch) +{ + if (ISSET(ch.ch_debug, DMAC_DEBUG_LITE)) + return BCM2835_DMAC_TYPE_LITE; + else + return BCM2835_DMAC_TYPE_NORMAL; +} + +int +bcm2835_dmac_channel_used(struct bcm2835_dmac_channel ch) +{ + return ch.ch_callback != NULL; +} + +void +bcm2835_dmac_write(struct bcm2835_dmac_softc *sc, bus_size_t offset, u_int32_t value) +{ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, offset, value); +} + +u_int32_t +bcm2835_dmac_read(struct bcm2835_dmac_softc *sc, bus_size_t offset) +{ + return bus_space_read_4(sc->sc_iot, sc->sc_ioh, offset); +} + +/* driver handles */ +int +bcm2835_dmac_match(struct device *parent, void *match, void *aux) +{ + struct fdt_attach_args *faa = aux; + + return OF_is_compatible(faa->fa_node, "brcm,bcm2835-dma"); +} + +void +bcm2835_dmac_attach(struct device *parent, struct device *self, void *aux) +{ + struct bcm2835_dmac_softc *sc = (struct bcm2835_dmac_softc *)self; + struct fdt_attach_args *faa = aux; + struct bcm2835_dmac_channel *ch; + u_int32_t val; + int index; + + if (atomic_cas_ptr(&attached_sc, NULL, sc)) { + printf(": a similar device has already attached\n"); + return; + } + + sc->sc_iot = faa->fa_iot; + sc->sc_fa_node = faa->fa_node; + + bus_addr_t addr; + bus_size_t size; + + if (faa->fa_nreg < 1) { + printf(": no registers\n"); + return; + } + + addr = faa->fa_reg[0].addr; + size = faa->fa_reg[0].size; + if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh)) { + printf(": can't map registers\n"); + return; + } + + sc->sc_channelmask = OF_getpropint(faa->fa_node, "brcm,dma-channel-mask", -1); + sc->sc_channelmask &= BCM2835_DMAC_CHANNELMASK; + + mtx_init(&sc->sc_lock, IPL_SCHED); + + sc->sc_nchannels = 31 - __builtin_clz(sc->sc_channelmask); + sc->sc_channels = malloc( + sizeof(*sc->sc_channels) * sc->sc_nchannels, M_DEVBUF, M_WAITOK); + + for (index = 0; index < sc->sc_nchannels; ++index) { + ch = &sc->sc_channels[index]; + ch->ch_sc = sc; + ch->ch_index = index; + ch->ch_callback = NULL; + ch->ch_callbackarg = NULL; + ch->ch_ih = NULL; + + if (!ISSET(sc->sc_channelmask, (1<ch_debug = bcm2835_dmac_read(sc, DMAC_DEBUG(index)); + + val = bcm2835_dmac_read(sc, DMAC_CS(index)); + val |= DMAC_CS_RESET; + bcm2835_dmac_write(sc, DMAC_CS(index), val); + } + + printf("\n"); +} + +int +bcm2835_dmac_intr(void *arg) +{ + struct bcm2835_dmac_channel *ch = arg; + struct bcm2835_dmac_softc *sc = ch->ch_sc; + u_int32_t cs, ce; + + cs = bcm2835_dmac_read(sc, DMAC_CS(ch->ch_index)); + bcm2835_dmac_write(sc, DMAC_CS(ch->ch_index), cs); + cs &= DMAC_CS_INT | DMAC_CS_END | DMAC_CS_ERROR; + + ce = bcm2835_dmac_read(sc, DMAC_DEBUG(ch->ch_index)); + ce &= DMAC_DEBUG_READ_ERROR | DMAC_DEBUG_FIFO_ERROR + | DMAC_DEBUG_READ_LAST_NOT_SET_ERROR; + bcm2835_dmac_write(sc, DMAC_DEBUG(ch->ch_index), ce); + + if (ch->ch_callback) + ch->ch_callback(cs, ce, ch->ch_callbackarg); + + return 1; +} + +struct bcm2835_dmac_channel * +bcm2835_dmac_alloc(enum bcm2835_dmac_type type, int ipl, + void (*cb)(u_int32_t, u_int32_t, void *), void *cbarg) +{ + struct bcm2835_dmac_softc *sc; + struct bcm2835_dmac_channel *ch = NULL; + int index; + + // get the current ptr - this is in a loop in case someone modifies it + // while we are going + do { + sc = (struct bcm2835_dmac_softc *)attached_sc; + } while (sc != atomic_cas_ptr(&attached_sc, sc, sc)); + + if (sc == NULL) + return NULL; + + mtx_enter(&sc->sc_lock); + for (index = 0; index < sc->sc_nchannels; ++index) { + if (!ISSET(sc->sc_channelmask, (1<sc_channels[index]) != type) + continue; + if (bcm2835_dmac_channel_used(sc->sc_channels[index])) + continue; + + ch = &sc->sc_channels[index]; + ch->ch_callback = cb; + ch->ch_callbackarg = cbarg; + break; + } + mtx_leave(&sc->sc_lock); + + if (ch == NULL) + return NULL; + + KASSERT(ch->ch_ih == NULL); + + ch->ch_ih = fdt_intr_establish_idx(sc->sc_fa_node, ch->ch_index, ipl, + bcm2835_dmac_intr, ch, + sc->sc_dev.dv_xname); + + if (ch->ch_ih == NULL) { + printf("%s: failed to establish interrupt for DMA%d\n", + DEVNAME(sc), ch->ch_index); + ch->ch_callback = NULL; + ch->ch_callbackarg = NULL; + ch = NULL; + } + + return ch; +} + +void +bcm2835_dmac_free(struct bcm2835_dmac_channel *ch) +{ + struct bcm2835_dmac_softc *sc = ch->ch_sc; + u_int32_t val; + + bcm2835_dmac_halt(ch); + + /* reset chip */ + val = bcm2835_dmac_read(sc, DMAC_CS(ch->ch_index)); + val |= DMAC_CS_RESET; + val &= ~DMAC_CS_ACTIVE; + bcm2835_dmac_write(sc, DMAC_CS(ch->ch_index), val); + + mtx_enter(&sc->sc_lock); + + fdt_intr_disestablish(ch->ch_ih); + ch->ch_ih = NULL; + ch->ch_callback = NULL; + ch->ch_callbackarg = NULL; + + mtx_leave(&sc->sc_lock); +} + +void +bcm2835_dmac_set_conblk_addr(struct bcm2835_dmac_channel *ch, bus_addr_t addr) +{ + struct bcm2835_dmac_softc *sc = ch->ch_sc; + + bcm2835_dmac_write(sc, DMAC_CONBLK_AD(ch->ch_index), addr); +} + +int +bcm2835_dmac_transfer(struct bcm2835_dmac_channel *ch) +{ + struct bcm2835_dmac_softc *sc = ch->ch_sc; + u_int32_t val; + + val = bcm2835_dmac_read(sc, DMAC_CS(ch->ch_index)); + if (ISSET(val, DMAC_CS_ACTIVE)) + return EBUSY; + + val |= DMAC_CS_ACTIVE; + bcm2835_dmac_write(sc, DMAC_CS(ch->ch_index), val); + + return 0; +} + +void +bcm2835_dmac_halt(struct bcm2835_dmac_channel *ch) +{ + struct bcm2835_dmac_softc *sc = ch->ch_sc; + u_int32_t val; + + /* pause DMA */ + val = bcm2835_dmac_read(sc, DMAC_CS(ch->ch_index)); + val &= ~DMAC_CS_ACTIVE; + bcm2835_dmac_write(sc, DMAC_CS(ch->ch_index), val); + + /* XXX wait for paused state */ + + /* end descriptor chain */ + bcm2835_dmac_write(sc, DMAC_NEXTCONBK(ch->ch_index), 0); + + /* resume DMA, which then stops */ + val |= DMAC_CS_ACTIVE | DMAC_CS_ABORT; + bcm2835_dmac_write(sc, DMAC_CS(ch->ch_index), val); +} diff --git a/sys/dev/fdt/bcm2835_dmac.h b/sys/dev/fdt/bcm2835_dmac.h new file mode 100644 index 00000000000..9d8ac484b8a --- /dev/null +++ b/sys/dev/fdt/bcm2835_dmac.h @@ -0,0 +1,102 @@ +/* $OpenBSD: mmc.c,v 1.0 2019/01/13 23:55:29 neil Exp $ */ + +/* + * Copyright (c) 2019 Neil Ashford + * + * 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. + */ + +#ifndef BCM2835_DMAC_H +#define BCM2835_DMAC_H + +#define DMAC_CS(n) (0x00 + (0x100 * (n))) +#define DMAC_CS_RESET (1<<31) +#define DMAC_CS_ABORT (1<<30) +#define DMAC_CS_DISDEBUG (1<<29) +#define DMAC_CS_WAIT_FOR_OUTSTANDING_WRITES (1<<28) +#define DMAC_CS_PANIC_PRIORITY (((1<<24) - 1) ^ (1<<20)) +#define DMAC_CS_PRIORITY (((1<<20) - 1) ^ (1<<16)) +#define DMAC_CS_ERROR (1<<8) +#define DMAC_CS_WAITING_FOR_OUTSTANDING_WRITES (1<<6) +#define DMAC_CS_DREQ_STOPS_DMA (1<<5) +#define DMAC_CS_PAUSED (1<<4) +#define DMAC_CS_DREQ (1<<3) +#define DMAC_CS_INT (1<<2) +#define DMAC_CS_END (1<<1) +#define DMAC_CS_ACTIVE (1<<0) +#define DMAC_CS_INTMASK (DMAC_CS_INT|DMAC_CS_END) +#define DMAC_CONBLK_AD(n) (0x04 + (0x100 * (n))) +#define DMAC_TI(n) (0x08 + (0x100 * (n))) +#define DMAC_SOURCE_AD(n) (0x0c + (0x100 * (n))) +#define DMAC_DEST_AD(n) (0x10 + (0x100 * (n))) +#define DMAC_TXFR_LEN(n) (0x14 + (0x100 * (n))) +#define DMAC_STRIDE(n) (0x18 + (0x100 * (n))) +#define DMAC_NEXTCONBK(n) (0x1c + (0x100 * (n))) +#define DMAC_DEBUG(n) (0x20 + (0x100 * (n))) +#define DMAC_DEBUG_LITE (1<<28) +#define DMAC_DEBUG_VERSION (((1<<28) - 1) ^ (1<<25)) +#define DMAC_DEBUG_DMA_STATE (((1<<25) - 1) ^ (1<<16)) +#define DMAC_DEBUG_DMA_ID (((1<<16) - 1) ^ (1<<8)) +#define DMAC_DEBUG_OUTSTANDING_WRITES (((1<<8) - 1) ^ (1<<4)) +#define DMAC_DEBUG_READ_ERROR (1<<2) +#define DMAC_DEBUG_FIFO_ERROR (1<<1) +#define DMAC_DEBUG_READ_LAST_NOT_SET_ERROR (1<<0) + +struct bcm2835_dmac_conblk { + uint32_t cb_ti; +#define DMAC_TI_NO_WIDE_BURSTS (1<<26) +#define DMAC_TI_WAITS (((1<<26) - 1) ^ (1<<21)) +#define DMAC_TI_PERMAP (((1<<21) - 1) ^ (1<<16)) +#define DMAC_TI_PERMAP_BASE (1<<16) +#define DMAC_TI_BURST_LENGTH (((1<<16) - 1) ^ (1<<12)) +#define DMAC_TI_SRC_IGNORE (1<<11) +#define DMAC_TI_SRC_DREQ (1<<10) +#define DMAC_TI_SRC_WIDTH (1<<9) +#define DMAC_TI_SRC_INC (1<<8) +#define DMAC_TI_DEST_IGNORE (1<<7) +#define DMAC_TI_DEST_DREQ (1<<6) +#define DMAC_TI_DEST_WIDTH (1<<5) +#define DMAC_TI_DEST_INC (1<<4) +#define DMAC_TI_WAIT_RESP (1<<3) +#define DMAC_TI_TDMODE (1<<1) +#define DMAC_TI_INTEN (1<<0) + uint32_t cb_source_ad; + uint32_t cb_dest_ad; + uint32_t cb_txfr_len; +#define DMAC_TXFR_LEN_YLENGTH (((1<<30) - 1) ^ (1<<16)) +#define DMAC_TXFR_LEN_XLENGTH (((1<<16) - 1) ^ (1<<0)) + uint32_t cb_stride; +#define DMAC_STRIDE_D_STRIDE (((1<<32) - 1) ^ (1<<16)) +#define DMAC_STRIDE_S_STRIDE (((1<<16) - 1) ^ (1<<0)) + uint32_t cb_nextconbk; + uint32_t cb_padding[2]; +} __packed; + +#define DMAC_INT_STATUS 0xfe0 +#define DMAC_ENABLE 0xff0 + +enum bcm2835_dmac_type { + BCM2835_DMAC_TYPE_NORMAL, + BCM2835_DMAC_TYPE_LITE +}; + +struct bcm2835_dmac_channel; + +struct bcm2835_dmac_channel *bcm2835_dmac_alloc(enum bcm2835_dmac_type, int, + void (*)(uint32_t, uint32_t, void *), void *); +void bcm2835_dmac_free(struct bcm2835_dmac_channel *); +void bcm2835_dmac_set_conblk_addr(struct bcm2835_dmac_channel *, bus_addr_t); +int bcm2835_dmac_transfer(struct bcm2835_dmac_channel *); +void bcm2835_dmac_halt(struct bcm2835_dmac_channel *); + +#endif /* BCM2835_DMAC_H */ diff --git a/sys/dev/fdt/bcm2835_mmc.c b/sys/dev/fdt/bcm2835_mmc.c new file mode 100644 index 00000000000..49378178bc7 --- /dev/null +++ b/sys/dev/fdt/bcm2835_mmc.c @@ -0,0 +1,658 @@ +/* $OpenBSD: mmc.c,v 1.0 2019/01/13 23:55:29 neil Exp $ */ + +/* + * Copyright (c) 2019 Neil Ashford + * + * 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. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#define SDCMD 0x00 +#define SDCMD_NEW (1 << 15) +#define SDCMD_FAIL (1 << 14) +#define SDCMD_BUSY (1 << 11) +#define SDCMD_NORESP (1 << 10) +#define SDCMD_LONGRESP (1 << 9) +#define SDCMD_WRITE (1 << 7) +#define SDCMD_READ (1 << 6) +#define SDARG 0x04 +#define SDTOUT 0x08 +#define SDTOUT_DEFAULT 0xf00000 +#define SDCDIV 0x0c +#define SDCDIV_MASK ((1 << 11) - 1) +#define SDRSP0 0x10 +#define SDRSP1 0x14 +#define SDRSP2 0x18 +#define SDRSP3 0x1c +#define SDHSTS 0x20 +#define SDHSTS_BUSY (1 << 10) +#define SDHSTS_BLOCK (1 << 9) +#define SDHSTS_SDIO (1 << 8) +#define SDHSTS_REW_TO (1 << 7) +#define SDHSTS_CMD_TO (1 << 6) +#define SDHSTS_CRC16_E (1 << 5) +#define SDHSTS_CRC7_E (1 << 4) +#define SDHSTS_FIFO_E (1 << 3) +#define SDHSTS_DATA (1 << 0) +#define SDVDD 0x30 +#define SDVDD_POWER (1 << 0) +#define SDEDM 0x34 +#define SDEDM_RD_FIFO (((1 << 19) - 1) ^ ((1 << 14) - 1)) +#define SDEDM_RD_FIFO_BASE (1 << 14) +#define SDEDM_WR_FIFO (((1 << 14) - 1) ^ ((1 << 9) - 1)) +#define SDEDM_WR_FIFO_BASE (1 << 9) +#define SDHCFG 0x38 +#define SDHCFG_BUSY_EN (1 << 10) +#define SDHCFG_BLOCK_EN (1 << 8) +#define SDHCFG_SDIO_EN (1 << 5) +#define SDHCFG_DATA_EN (1 << 4) +#define SDHCFG_SLOW (1 << 3) +#define SDHCFG_WIDE_EXT (1 << 2) +#define SDHCFG_WIDE_INT (1 << 1) +#define SDHCFG_REL_CMD (1 << 0) +#define SDHBCT 0x3c +#define SDDATA 0x40 +#define SDHBLC 0x50 + +struct bcm2835_mmc_softc { + /* device */ + struct device sc_dev; + + /* interrupts */ + void *sc_ih; + + /* registers */ + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + bus_addr_t sc_addr; + bus_size_t sc_size; + + /* direct memory access */ + bus_dma_tag_t sc_dmat; + bus_dmamap_t sc_dmamap; + bus_dma_segment_t sc_segs[1]; + struct bcm2835_dmac_conblk *sc_cblk; + struct bcm2835_dmac_channel *sc_dmac; + + /* synchronisation control */ + struct mutex sc_intr_lock; + u_int32_t sc_intr_hsts; + u_int32_t sc_intr_cv; + u_int32_t sc_dma_cv; + + + /* data transfer stats */ + u_int sc_rate; + + int sc_mmc_width; + int sc_mmc_presnt; + + u_int32_t sc_dma_status; + u_int32_t sc_dma_error; + + /* attached child driver */ + struct device *sc_sdmmc; +}; + +/* general driver functions */ +int bcm2835_mmc_match(struct device *, void *, void *); +void bcm2835_mmc_attach(struct device *, struct device *, void *); +int bcm2835_mmc_detach(struct device *, int); + +struct cfattach bcm2835_mmc_ca = { + sizeof(struct bcm2835_mmc_softc), + bcm2835_mmc_match, + bcm2835_mmc_attach, + bcm2835_mmc_detach, +}; + +/* sdmmc driver functions */ +int bcm2835_mmc_host_reset(sdmmc_chipset_handle_t); +u_int32_t bcm2835_mmc_host_ocr(sdmmc_chipset_handle_t); +int bcm2835_mmc_host_maxblklen(sdmmc_chipset_handle_t); +int bcm2835_mmc_card_detect(sdmmc_chipset_handle_t); +int bcm2835_mmc_bus_power(sdmmc_chipset_handle_t, u_int32_t); +int bcm2835_mmc_bus_clock(sdmmc_chipset_handle_t, int, int); +int bcm2835_mmc_bus_width(sdmmc_chipset_handle_t, int); +void bcm2835_mmc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *); + +struct sdmmc_chip_functions bcm2835_mmc_chip_functions = { + .host_reset = bcm2835_mmc_host_reset, + .host_ocr = bcm2835_mmc_host_ocr, + .host_maxblklen = bcm2835_mmc_host_maxblklen, + .card_detect = bcm2835_mmc_card_detect, + .bus_power = bcm2835_mmc_bus_power, + .bus_clock = bcm2835_mmc_bus_clock, + .exec_command = bcm2835_mmc_exec_command, +}; + +/* driver logic */ +int bcm2835_mmc_wait_idle(struct bcm2835_mmc_softc *sc, int timeout); +int bcm2835_mmc_dma_wait(struct bcm2835_mmc_softc *, struct sdmmc_command *); +int bcm2835_mmc_dma_transfer(struct bcm2835_mmc_softc *, struct sdmmc_command *); +void bcm2835_mmc_dma_done(u_int32_t, u_int32_t, void *); +void bcm2835_mmc_write(struct bcm2835_mmc_softc *, bus_size_t, u_int32_t); +u_int32_t bcm2835_mmc_read(struct bcm2835_mmc_softc *, bus_size_t); +int bcm2835_mmc_intr(void *); + +struct cfdriver bcm2835_mmc_cd = { + NULL, "bcm2835_mmc", DV_DISK +}; + +int +bcm2835_mmc_match(struct device *parent, void *match, void *aux) +{ + struct fdt_attach_args *faa = aux; + + return OF_is_compatible(faa->fa_node, "brcm,bcm2835-sdhost"); +} + +void +bcm2835_mmc_attach(struct device *parent, struct device *self, void *aux) +{ + struct bcm2835_mmc_softc *sc = (struct bcm2835_mmc_softc *)self; + struct fdt_attach_args *faa = aux; + struct sdmmcbus_attach_args saa; + int rseg; + + /* setup synchronisation primitives */ + mtx_init(&sc->sc_intr_lock, IPL_BIO); + + /* load registers */ + if (faa->fa_nreg < 1) { + printf(": no registers\n"); + return; + } + + sc->sc_iot = faa->fa_iot; + sc->sc_size = faa->fa_reg[0].size; + sc->sc_addr = faa->fa_reg[0].addr; + if (bus_space_map(sc->sc_iot, sc->sc_addr, sc->sc_size, 0, + &sc->sc_ioh)) { + printf(": can't map registers\n"); + return; + } + + /* check disabled XXX */ + + /* enable clocks */ + clock_enable_all(faa->fa_node); + + /* load DMA */ + sc->sc_dmac = bcm2835_dmac_alloc(BCM2835_DMAC_TYPE_NORMAL, IPL_SDMMC, + bcm2835_mmc_dma_done, sc); + if (sc->sc_dmac == NULL) { + printf(": can't open dmac\n"); + goto clean_clocks; + } + + sc->sc_dmat = faa->fa_dmat; + if (bus_dmamem_alloc(sc->sc_dmat, PAGE_SIZE, PAGE_SIZE, PAGE_SIZE, + sc->sc_segs, 1, &rseg, BUS_DMA_WAITOK)) { + printf(": can't allocate dmamap\n"); + goto clean_dmac_channel; + } + + if (bus_dmamem_map(sc->sc_dmat, sc->sc_segs, rseg, PAGE_SIZE, + (char **)&sc->sc_cblk, BUS_DMA_WAITOK)) { + printf(": can't map bus\n"); + goto clean_dmamap_free; + } + + memset(sc->sc_cblk, 0, PAGE_SIZE); + + if (bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1, PAGE_SIZE, 0, + BUS_DMA_WAITOK, &sc->sc_dmamap)) { + printf(": can't map bus\n"); + goto clean_dmamap_unmap; + } + + if (bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_cblk, + PAGE_SIZE, NULL, BUS_DMA_WAITOK | BUS_DMA_WRITE)) { + printf(": can't load mapped bus\n"); + goto clean_dmamap_destroy; + } + + /* enable interrupts */ + sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_SDMMC, bcm2835_mmc_intr, + sc, sc->sc_dev.dv_xname); + if (sc->sc_ih == NULL) { + printf(": can't establish interrupt\n"); + goto clean_dmamap; + } + + /* attach the parent driver */ + printf("\n"); + + tsleep(&sc->sc_intr_cv, PPAUSE, "pause", 0); + + + bcm2835_mmc_host_reset(sc); + bcm2835_mmc_bus_width(sc, 1); + bcm2835_mmc_bus_clock(sc, 400, false); + + memset(&saa, 0, sizeof(saa)); + saa.saa_busname = "sdmmc"; + saa.sct = &bcm2835_mmc_chip_functions; + saa.sch = sc; + saa.dmat = sc->sc_dmat; + saa.dmap = sc->sc_dmamap; + saa.flags = SMF_SD_MODE /*| SMF_MEM_MODE*/; + saa.caps = SMC_CAPS_DMA | + SMC_CAPS_MULTI_SEG_DMA | + SMC_CAPS_SD_HIGHSPEED | + SMC_CAPS_MMC_HIGHSPEED | + SMC_CAPS_4BIT_MODE; + + + sc->sc_sdmmc = config_found(self, &saa, NULL); + return; + +clean_dmamap: + bus_dmamap_unload(sc->sc_dmat, sc->sc_dmamap); +clean_dmamap_destroy: + bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap); +clean_dmamap_unmap: + bus_dmamem_unmap(sc->sc_dmat, (void *)sc->sc_cblk, PAGE_SIZE); +clean_dmamap_free: + bus_dmamem_free(sc->sc_dmat, sc->sc_segs, 1); +clean_dmac_channel: + bcm2835_dmac_free(sc->sc_dmac); +clean_clocks: + clock_disable_all(faa->fa_node); + bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size); +} + +int +bcm2835_mmc_detach(struct device *self, int flags) +{ + struct bcm2835_mmc_softc *sc = (struct bcm2835_mmc_softc *)self; + + // XXX + bus_dmamap_unload(sc->sc_dmat, sc->sc_dmamap); + bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap); + bus_dmamem_unmap(sc->sc_dmat, (void *)sc->sc_cblk, PAGE_SIZE); + bus_dmamem_free(sc->sc_dmat, sc->sc_segs, 1); + bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size); + + return 0; +} + + +int +bcm2835_mmc_host_reset(sdmmc_chipset_handle_t sch) +{ + struct bcm2835_mmc_softc *sc = sch; + u_int32_t edm; + + bcm2835_mmc_write(sc, SDVDD, 0); + bcm2835_mmc_write(sc, SDCMD, 0); + bcm2835_mmc_write(sc, SDARG, 0); + bcm2835_mmc_write(sc, SDTOUT, SDTOUT_DEFAULT); + bcm2835_mmc_write(sc, SDCDIV, 0); + bcm2835_mmc_write(sc, SDHSTS, bcm2835_mmc_read(sc, SDHSTS)); + bcm2835_mmc_write(sc, SDHCFG, 0); + bcm2835_mmc_write(sc, SDHBCT, 0); + bcm2835_mmc_write(sc, SDHBLC, 0); + + edm = bcm2835_mmc_read(sc, SDEDM); + edm &= ~(SDEDM_RD_FIFO | SDEDM_WR_FIFO); + edm |= 4 * SDEDM_RD_FIFO_BASE; + edm |= 4 * SDEDM_WR_FIFO_BASE; + bcm2835_mmc_write(sc, SDEDM, edm); + + delay(20000); + bcm2835_mmc_write(sc, SDVDD, SDVDD_POWER); + delay(20000); + + bcm2835_mmc_write(sc, SDHCFG, 0); + bcm2835_mmc_write(sc, SDCDIV, SDCDIV_MASK); + + + return 0; +} + +u_int32_t +bcm2835_mmc_host_ocr(sdmmc_chipset_handle_t sch) +{ + return MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V | MMC_OCR_HCS; +} + +int +bcm2835_mmc_host_maxblklen(sdmmc_chipset_handle_t sch) +{ + return 8192; +} + +int +bcm2835_mmc_card_detect(sdmmc_chipset_handle_t sch) +{ + return 1; /* XXX */ +} + +int +bcm2835_mmc_bus_power(sdmmc_chipset_handle_t sch, u_int32_t ocr) +{ + return 0; +} + +int +bcm2835_mmc_bus_clock(sdmmc_chipset_handle_t sch, int freq, int ddr) +{ + struct bcm2835_mmc_softc *sc = sch; + u_int target_rate = freq * 1000; + int div; + + if (freq == 0) + div = SDCDIV_MASK; + else { + div = sc->sc_rate / target_rate; + if (div < 2) + div = 2; + if ((sc->sc_rate / div) > target_rate) + div++; + div -= 2; + if (div > SDCDIV_MASK) + div = SDCDIV_MASK; + } + + bcm2835_mmc_write(sc, SDCDIV, div); + + return 0; +} + +int +bcm2835_mmc_bus_width(sdmmc_chipset_handle_t sch, int width) +{ + struct bcm2835_mmc_softc *sc = sch; + u_int32_t hcfg; + + hcfg = bcm2835_mmc_read(sc, SDHCFG); + if (width == 4) + hcfg |= SDHCFG_WIDE_EXT; + else + hcfg &= ~SDHCFG_WIDE_EXT; + hcfg |= (SDHCFG_WIDE_INT | SDHCFG_SLOW); + bcm2835_mmc_write(sc, SDHCFG, hcfg); + + return 0; +} + +void +bcm2835_mmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) +{ + struct bcm2835_mmc_softc *sc = sch; + u_int32_t cmdval, hcfg; + u_int nblks; +printf("%s: BEGIN exec_command\n", DEVNAME(sc)); + + mtx_enter(&sc->sc_intr_lock); + + hcfg = bcm2835_mmc_read(sc, SDHCFG); + bcm2835_mmc_write(sc, SDHCFG, hcfg | SDHCFG_BUSY_EN); + + sc->sc_intr_hsts = 0; + + cmd->c_error = bcm2835_mmc_wait_idle(sc, 5000); + if (cmd->c_error != 0) // device busy + goto done; + + cmdval = SDCMD_NEW; + if (!ISSET(cmd->c_flags, SCF_RSP_PRESENT)) + cmdval |= SDCMD_NORESP; + if (ISSET(cmd->c_flags, SCF_RSP_136)) + cmdval |= SDCMD_LONGRESP; + if (ISSET(cmd->c_flags, SCF_RSP_BSY)) + cmdval |= SDCMD_BUSY; + + if (cmd->c_datalen > 0) { + if (ISSET(cmd->c_flags, SCF_CMD_READ)) + cmdval |= SDCMD_READ; + else + cmdval |= SDCMD_WRITE; + + nblks = cmd->c_datalen / cmd->c_blklen; + if (nblks == 0 || (cmd->c_datalen % cmd->c_blklen) != 0) + ++nblks; + + bcm2835_mmc_write(sc, SDHBCT, cmd->c_blklen); + bcm2835_mmc_write(sc, SDHBLC, nblks); + + cmd->c_resid = cmd->c_datalen; + cmd->c_error = bcm2835_mmc_dma_transfer(sc, cmd); + if (cmd->c_error != 0) + goto done; + } + + bcm2835_mmc_write(sc, SDARG, cmd->c_arg); + bcm2835_mmc_write(sc, SDCMD, cmdval | cmd->c_opcode); + + if (cmd->c_datalen > 0) { + cmd->c_error = bcm2835_mmc_dma_wait(sc, cmd); + if (cmd->c_error != 0) + goto done; + } + + cmd->c_error = bcm2835_mmc_wait_idle(sc, 5000); + + if (ISSET(bcm2835_mmc_read(sc, SDCMD), SDCMD_FAIL)) { + cmd->c_error = EIO; + goto done; + } + + if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) { + if (ISSET(cmd->c_flags, SCF_RSP_136)) { + cmd->c_resp[0] = bcm2835_mmc_read(sc, SDRSP0); + cmd->c_resp[1] = bcm2835_mmc_read(sc, SDRSP1); + cmd->c_resp[2] = bcm2835_mmc_read(sc, SDRSP2); + cmd->c_resp[3] = bcm2835_mmc_read(sc, SDRSP3); + if (ISSET(cmd->c_flags, SCF_RSP_CRC)) { + cmd->c_resp[0] = (cmd->c_resp[0] >> 8) | + (cmd->c_resp[1] << 24); + cmd->c_resp[1] = (cmd->c_resp[1] >> 8) | + (cmd->c_resp[2] << 24); + cmd->c_resp[2] = (cmd->c_resp[2] >> 8) | + (cmd->c_resp[3] << 24); + cmd->c_resp[3] = (cmd->c_resp[3] >> 8); + } + } else { + cmd->c_resp[0] = bcm2835_mmc_read(sc, SDRSP0); + } + } + +done: + cmd->c_flags |= SCF_ITSDONE; + bcm2835_mmc_write(sc, SDHCFG, hcfg); + bcm2835_mmc_write(sc, SDHSTS, bcm2835_mmc_read(sc, SDHSTS)); + mtx_leave(&sc->sc_intr_lock); + + printf("%s: command %d error %d\n", DEVNAME(sc), cmd->c_opcode, cmd->c_error); +} + +int +bcm2835_mmc_wait_idle(struct bcm2835_mmc_softc *sc, int timeout) +{ + int retry; + + retry = timeout * 1000; + + while (--retry > 0) { + const u_int32_t cmd = bcm2835_mmc_read(sc, SDCMD); + if (!ISSET(cmd, SDCMD_NEW)) + return 0; + delay(1); + } + + return ETIMEDOUT; +} + +int +bcm2835_mmc_dma_wait(struct bcm2835_mmc_softc *sc, struct sdmmc_command *cmd) +{ + int error = 0; + + while (sc->sc_dma_status == 0 && sc->sc_dma_error == 0) { + error = msleep(&sc->sc_dma_cv, &sc->sc_intr_lock, PPAUSE, "pause", 5); + if (error == EWOULDBLOCK) { + printf("%s: transfer timeout!\n", DEVNAME(sc)); + bcm2835_dmac_halt(sc->sc_dmac); + error = ETIMEDOUT; + break; + } + } + + if (ISSET(sc->sc_dma_status, DMAC_CS_END)) { + cmd->c_resid = 0; + error = 0; + } else { + error = EIO; + } + + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, 0, + sc->sc_dmamap->dm_mapsize, BUS_DMASYNC_POSTWRITE); + + return error; +} + +int +bcm2835_mmc_dma_transfer(struct bcm2835_mmc_softc *sc, struct sdmmc_command *cmd) +{ + size_t seg; + int error; + + for (seg = 0; seg < cmd->c_dmamap->dm_nsegs; seg++) { + sc->sc_cblk[seg].cb_ti = 13 * DMAC_TI_PERMAP_BASE; + sc->sc_cblk[seg].cb_txfr_len = cmd->c_dmamap->dm_segs[seg].ds_len; + const bus_addr_t ad_sddata = sc->sc_addr + SDDATA; + + /* + * All transfers are assumed to be multiples of 32 bits + */ + KASSERTMSG((sc->sc_cblk[seg].cb_txfr_len & 0x3) == 0, + "seg %zu len %d", seg, sc->sc_cblk[seg].cb_txfr_len); + /* Use 128-bit mode if transfer is a multiple of 16 bytes. */ + if (ISSET(cmd->c_flags, SCF_CMD_READ)) { + sc->sc_cblk[seg].cb_ti |= DMAC_TI_DEST_INC; + if ((sc->sc_cblk[seg].cb_txfr_len & 0xf) == 0) + sc->sc_cblk[seg].cb_ti |= DMAC_TI_DEST_WIDTH; + sc->sc_cblk[seg].cb_ti |= DMAC_TI_SRC_DREQ; + sc->sc_cblk[seg].cb_source_ad = ad_sddata; + sc->sc_cblk[seg].cb_dest_ad = + cmd->c_dmamap->dm_segs[seg].ds_addr; + } else { + sc->sc_cblk[seg].cb_ti |= DMAC_TI_SRC_INC; + if ((sc->sc_cblk[seg].cb_txfr_len & 0xf) == 0) + sc->sc_cblk[seg].cb_ti |= DMAC_TI_SRC_WIDTH; + sc->sc_cblk[seg].cb_ti |= DMAC_TI_DEST_DREQ; + sc->sc_cblk[seg].cb_ti |= DMAC_TI_WAIT_RESP; + sc->sc_cblk[seg].cb_source_ad = + cmd->c_dmamap->dm_segs[seg].ds_addr; + sc->sc_cblk[seg].cb_dest_ad = ad_sddata; + } + sc->sc_cblk[seg].cb_stride = 0; + if (seg == cmd->c_dmamap->dm_nsegs - 1) { + sc->sc_cblk[seg].cb_ti |= DMAC_TI_INTEN; + sc->sc_cblk[seg].cb_nextconbk = 0; + } else { + sc->sc_cblk[seg].cb_nextconbk = + sc->sc_dmamap->dm_segs[0].ds_addr + + sizeof(struct bcm2835_dmac_conblk) * (seg + 1); + } + sc->sc_cblk[seg].cb_padding[0] = 0; + sc->sc_cblk[seg].cb_padding[1] = 0; + } + + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, 0, + sc->sc_dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE); + + error = 0; + + sc->sc_dma_status = 0; + sc->sc_dma_error = 0; + + bcm2835_dmac_set_conblk_addr(sc->sc_dmac, + sc->sc_dmamap->dm_segs[0].ds_addr); + error = bcm2835_dmac_transfer(sc->sc_dmac); + + if (error) + return error; + + return 0; +} + +void +bcm2835_mmc_dma_done(u_int32_t status, u_int32_t error, void *arg) +{ + struct bcm2835_mmc_softc *sc = arg; + + if (status != (DMAC_CS_INT | DMAC_CS_END)) + printf("%s: dma status %#x error %#x\n", DEVNAME(sc), status, + error); + + mtx_enter(&sc->sc_intr_lock); + + sc->sc_dma_status = status; + sc->sc_dma_error = error; + wakeup(&sc->sc_dma_cv); + + mtx_leave(&sc->sc_intr_lock); +} + +void +bcm2835_mmc_write(struct bcm2835_mmc_softc *sc, bus_size_t offset, u_int32_t value) +{ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, offset, value); +} + +u_int32_t +bcm2835_mmc_read(struct bcm2835_mmc_softc *sc, bus_size_t offset) +{ + return bus_space_read_4(sc->sc_iot, sc->sc_ioh, offset); +} + +int +bcm2835_mmc_intr(void *priv) +{ + struct bcm2835_mmc_softc *sc = priv; + + mtx_enter(&sc->sc_intr_lock); + const u_int32_t hsts = bcm2835_mmc_read(sc, SDHSTS); + + if (hsts) { + bcm2835_mmc_write(sc, SDHSTS, hsts); + sc->sc_intr_hsts |= hsts; + wakeup(&sc->sc_intr_cv); + } + + + mtx_leave(&sc->sc_intr_lock); + + return hsts ? 1 : 0; +} diff --git a/sys/dev/fdt/files.fdt b/sys/dev/fdt/files.fdt index be0e2686e94..809637f1e7d 100644 --- a/sys/dev/fdt/files.fdt +++ b/sys/dev/fdt/files.fdt @@ -297,3 +297,11 @@ device ssdfb: wsemuldisplaydev, rasops1 attach ssdfb at spi with ssdfb_spi attach ssdfb at i2c with ssdfb_i2c file dev/fdt/ssdfb.c ssdfb + +device bcm2835_mmc: sdmmcbus +attach bcm2835_mmc at fdt +file dev/fdt/bcm2835_mmc.c bcm2835_mmc + +device bcm2835_dmac +attach bcm2835_dmac at fdt +file dev/fdt/bcm2835_dmac.c bcm2835_dmac diff --git a/sys/dev/fdt/sdhc_fdt.c b/sys/dev/fdt/sdhc_fdt.c index 6eef5a3d61d..34fdbe7fb7b 100644 --- a/sys/dev/fdt/sdhc_fdt.c +++ b/sys/dev/fdt/sdhc_fdt.c @@ -56,7 +56,8 @@ sdhc_fdt_match(struct device *parent, void *match, void *aux) { struct fdt_attach_args *faa = aux; - return OF_is_compatible(faa->fa_node, "arasan,sdhci-5.1"); + return OF_is_compatible(faa->fa_node, "arasan,sdhci-5.1") + || OF_is_compatible(faa->fa_node, "brcm,bcm2835-sdhci"); } void @@ -114,7 +115,19 @@ sdhc_fdt_attach(struct device *parent, struct device *self, void *aux) /* XXX Doesn't work on Rockchip RK3399. */ sc->sc.sc_flags |= SDHC_F_NODDR50; - sdhc_host_found(&sc->sc, sc->sc_iot, sc->sc_ioh, sc->sc_size, 1, 0); + /* + * Set the base clock frequency of the bcm2835. + */ + if (OF_is_compatible(faa->fa_node, "brcm,bcm2835-sdhci")) + sc->sc.sc_clkbase = 50000; + + u_int32_t caps = 0; + + if (OF_is_compatible(faa->fa_node, "brcm,bcm2835-sdhci")) + caps |= SDHC_VOLTAGE_SUPP_3_3V | SDHC_HIGH_SPEED_SUPP | + (SDHC_MAX_BLK_LEN_1024 << SDHC_MAX_BLK_LEN_SHIFT); + + sdhc_host_found(&sc->sc, sc->sc_iot, sc->sc_ioh, sc->sc_size, 1, caps); return; unmap: diff --git a/sys/dev/sdmmc/sdmmc_cis.c b/sys/dev/sdmmc/sdmmc_cis.c index 21cf530b24f..9dd101640ef 100644 --- a/sys/dev/sdmmc/sdmmc_cis.c +++ b/sys/dev/sdmmc/sdmmc_cis.c @@ -43,7 +43,12 @@ sdmmc_cisptr(struct sdmmc_function *sf) rw_assert_wrlock(&sf->sc->sc_lock); - reg = SD_IO_CCCR_CISPTR + (sf->number * SD_IO_CCCR_SIZE); + if (sf->number == 0) { + reg = SD_IO_CCCR_CISPTR; + } else { + reg = SD_IO_CCCR_CISPTR + SD_IO_FBR_BASE(sf->number); + } + cisptr |= sdmmc_io_read_1(sf0, reg + 0) << 0; cisptr |= sdmmc_io_read_1(sf0, reg + 1) << 8; cisptr |= sdmmc_io_read_1(sf0, reg + 2) << 16; @@ -51,6 +56,101 @@ sdmmc_cisptr(struct sdmmc_function *sf) return cisptr; } +void +sdmmc_read_cis_funcid(struct sdmmc_function *sf, struct sdmmc_cis *cis, + int reg, u_int8_t tpllen) +{ + if (tpllen < 2) { + printf("%s: bad CISTPL_FUNCID length\n", DEVNAME(sf->sc)); + return; + } + + struct sdmmc_function *sf0 = sf->sc->sc_fn0; + cis->function = sdmmc_io_read_1(sf0, reg); +} + +void +sdmmc_read_cis_funce(struct sdmmc_function *sf, struct sdmmc_cis *cis, + int reg, u_int8_t tpllen) +{ + const static int speed_exponent[8] = { 10, 100, 1000, 10000 }; + const static int speed_mantissa[16] = { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 }; + + int tran_speed; + struct sdmmc_function *sf0 = sf->sc->sc_fn0; + if (sdmmc_io_read_1(sf0, reg++) == 0) { + if (tpllen < 4) { + printf("%s: bad CISTPL_FUNCE length\n", + DEVNAME(sf->sc)); + return; + } + + sf->csd.read_bl_len = sdmmc_io_read_1(sf0, reg++); + sf->csd.read_bl_len |= sdmmc_io_read_1(sf0, reg++) << 8; + + tran_speed = sdmmc_io_read_1(sf0, reg++); + sf->csd.tran_speed = speed_exponent[tran_speed & 7] * speed_mantissa[tran_speed >> 3 & 15]; + } else if (sf->number == 0) { + printf("%s: unexpected CISTPL_FUNCE found in common\n", + DEVNAME(sf->sc)); + } else { + int sdiox; + + sdiox = SD_IO_CCCR_SDIO_REV(sdmmc_io_read_1(sf0, 0)); + if ((sdiox == 0 && tpllen < 0x1c) || + (sdiox != 0 && tpllen < 0x2a)) { + printf("%s: bad CISTPL_FUNCE length\n", DEVNAME(sf->sc)); + return; + } + + sf->csd.read_bl_len = sdmmc_io_read_1(sf0, reg + 11); + sf->csd.read_bl_len |= sdmmc_io_read_1(sf0, reg + 12) << 8; + } +} + +void +sdmmc_read_cis_manfid(struct sdmmc_function *sf, struct sdmmc_cis *cis, + int reg, u_int8_t tpllen) +{ + struct sdmmc_function *sf0 = sf->sc->sc_fn0; + if (tpllen < 4) { + printf("%s: bad CISTPL_MANFID length\n", DEVNAME(sf->sc)); + return; + } + cis->manufacturer = sdmmc_io_read_1(sf0, reg++); + cis->manufacturer |= sdmmc_io_read_1(sf0, reg++) << 8; + cis->product = sdmmc_io_read_1(sf0, reg++); + cis->product |= sdmmc_io_read_1(sf0, reg++) << 8; +} + +void +sdmmc_read_cis_vers_1(struct sdmmc_function *sf, struct sdmmc_cis *cis, + int reg, u_int8_t tpllen) +{ + struct sdmmc_function *sf0 = sf->sc->sc_fn0; + if (tpllen < 2) { + printf("%s: CISTPL_VERS_1 too short\n", DEVNAME(sf->sc)); + return; + } + + int start, i, ch, count; + + cis->cis1_major = sdmmc_io_read_1(sf0, reg++); + cis->cis1_minor = sdmmc_io_read_1(sf0, reg++); + + for (count = 0, start = 0, i = 0; (count < 4) && ((i + 4) < 256); ++i) { + ch = sdmmc_io_read_1(sf0, reg + i); + if (ch == 0xff) + break; + cis->cis1_info_buf[i] = ch; + if (ch == 0) { + cis->cis1_info[count] = cis->cis1_info_buf + start; + start = i + 1; + count++; + } + } +} + int sdmmc_read_cis(struct sdmmc_function *sf, struct sdmmc_cis *cis) { @@ -85,63 +185,23 @@ sdmmc_read_cis(struct sdmmc_function *sf, struct sdmmc_cis *cis) switch (tplcode) { case SD_IO_CISTPL_FUNCID: - if (tpllen < 2) { - printf("%s: bad CISTPL_FUNCID length\n", - DEVNAME(sf->sc)); - reg += tpllen; - break; - } - cis->function = sdmmc_io_read_1(sf0, reg); - reg += tpllen; + sdmmc_read_cis_funcid(sf, cis, reg, tpllen); + break; + case SD_IO_CISTPL_FUNCE: + sdmmc_read_cis_funce(sf, cis, reg, tpllen); break; case SD_IO_CISTPL_MANFID: - if (tpllen < 4) { - printf("%s: bad CISTPL_MANFID length\n", - DEVNAME(sf->sc)); - reg += tpllen; - break; - } - cis->manufacturer = sdmmc_io_read_1(sf0, reg++); - cis->manufacturer |= sdmmc_io_read_1(sf0, reg++) << 8; - cis->product = sdmmc_io_read_1(sf0, reg++); - cis->product |= sdmmc_io_read_1(sf0, reg++) << 8; + sdmmc_read_cis_manfid(sf, cis, reg, tpllen); break; case SD_IO_CISTPL_VERS_1: - if (tpllen < 2) { - printf("%s: CISTPL_VERS_1 too short\n", - DEVNAME(sf->sc)); - reg += tpllen; - break; - } - { - int start, i, ch, count; - - cis->cis1_major = sdmmc_io_read_1(sf0, reg++); - cis->cis1_minor = sdmmc_io_read_1(sf0, reg++); - - for (count = 0, start = 0, i = 0; - (count < 4) && ((i + 4) < 256); i++) { - ch = sdmmc_io_read_1(sf0, reg + i); - if (ch == 0xff) - break; - cis->cis1_info_buf[i] = ch; - if (ch == 0) { - cis->cis1_info[count] = - cis->cis1_info_buf + start; - start = i + 1; - count++; - } - } - - reg += tpllen - 2; - } + sdmmc_read_cis_vers_1(sf, cis, reg, tpllen); break; default: DPRINTF(("%s: unknown tuple code %#x, length %d\n", DEVNAME(sf->sc), tplcode, tpllen)); - reg += tpllen; break; } + reg += tpllen; } return 0; diff --git a/sys/dev/sdmmc/sdmmc_ioreg.h b/sys/dev/sdmmc/sdmmc_ioreg.h index b074ba785e7..adf0bfc2ae9 100644 --- a/sys/dev/sdmmc/sdmmc_ioreg.h +++ b/sys/dev/sdmmc/sdmmc_ioreg.h @@ -82,9 +82,12 @@ #define CCCR_SPEED_SDR104 (3<<1) #define CCCR_SPEED_DDR50 (4<<1) #define CCCR_SPEED_MASK (0x7<<1) +#define SD_IO_CCCR_SDIO_REV(r) ((r) & 0xf) /* Function Basic Registers (FBR) */ -#define SD_IO_FBR_BASE(f) ((f) * 0x100) +#define SD_IO_FBR_START 0x00100 +#define SD_IO_FBR_SIZE 0x100 +#define SD_IO_FBR_BASE(func) ((((func) - 1) * SD_IO_FBR_SIZE) + SD_IO_FBR_START) #define SD_IO_FBR_BLOCKLEN 0x10 /* Card Information Structure (CIS) */ diff --git a/sys/dev/sdmmc/sdmmcreg.h b/sys/dev/sdmmc/sdmmcreg.h index c984afc9f78..4063b79e1f4 100644 --- a/sys/dev/sdmmc/sdmmcreg.h +++ b/sys/dev/sdmmc/sdmmcreg.h @@ -50,8 +50,9 @@ /* OCR bits */ #define MMC_OCR_MEM_READY (1<<31) /* memory power-up status bit */ -#define MMC_OCR_ACCESS_MODE_MASK 0x60000000 /* bits 30:29 */ -#define MMC_OCR_SECTOR_MODE (1<<30) +#define MMC_OCR_HCS (1<<30) +#define MMC_OCR_ACCESS_MODE_MASK (3<<29) /* bits 30:29 */ +#define MMC_OCR_SECTOR_MODE (1<<29) #define MMC_OCR_BYTE_MODE (1<<29) #define MMC_OCR_3_5V_3_6V (1<<23) #define MMC_OCR_3_4V_3_5V (1<<22) diff --git a/sys/dev/sdmmc/sdmmcvar.h b/sys/dev/sdmmc/sdmmcvar.h index 1cc635e3182..a4515036c59 100644 --- a/sys/dev/sdmmc/sdmmcvar.h +++ b/sys/dev/sdmmc/sdmmcvar.h @@ -37,6 +37,7 @@ struct sdmmc_csd { int sector_size; /* sector size in bytes */ int read_bl_len; /* block length for reads */ int ccc; /* Card Command Class for SD */ + int tran_speed; /* Transition speed, in kHZ */ /* ... */ };