diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2009-10-03 19:51:54 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2009-10-03 19:51:54 +0000 |
commit | 16c6240794cf359fadd08000b4f1443f6218e0b2 (patch) | |
tree | 1c00a78ed08ecdafc7fd1427a47e3db39d22a953 /sys | |
parent | ba435fbe998f7a5b22cb053a23f196a5c520e66d (diff) |
Driver for the SD/MMC part of the Winbond W83L518D/W83L519D Integrated Media
Reader.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/conf/files | 7 | ||||
-rw-r--r-- | sys/dev/ic/w83l518d.c | 126 | ||||
-rw-r--r-- | sys/dev/ic/w83l518d_sdmmc.c | 595 | ||||
-rw-r--r-- | sys/dev/ic/w83l518d_sdmmc.h | 36 | ||||
-rw-r--r-- | sys/dev/ic/w83l518dreg.h | 169 | ||||
-rw-r--r-- | sys/dev/ic/w83l518dvar.h | 60 |
6 files changed, 992 insertions, 1 deletions
diff --git a/sys/conf/files b/sys/conf/files index e0579379c7f..3a56607f91e 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.476 2009/09/08 17:52:17 michele Exp $ +# $OpenBSD: files,v 1.477 2009/10/03 19:51:53 kettenis Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -434,6 +434,11 @@ file dev/usb/ehci.c ehci needs-flag device sdhc: sdmmcbus file dev/sdmmc/sdhc.c sdhc needs-flag +# Winbond Integrated Media Reader +device wbsd: sdmmcbus +file dev/ic/w83l518d.c wbsd +file dev/ic/w83l518d_sdmmc.c wbsd + # radio devices, attaches to radio hardware driver device radio attach radio at radiobus diff --git a/sys/dev/ic/w83l518d.c b/sys/dev/ic/w83l518d.c new file mode 100644 index 00000000000..9aad19704ae --- /dev/null +++ b/sys/dev/ic/w83l518d.c @@ -0,0 +1,126 @@ +/* $OpenBSD: w83l518d.c,v 1.1 2009/10/03 19:51:53 kettenis Exp $ */ +/* $NetBSD: w83l518d.c,v 1.1 2009/09/30 20:44:50 jmcneill Exp $ */ + +/* + * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/syslog.h> +#include <sys/device.h> +#include <sys/proc.h> + +#include <machine/bus.h> + +#include <dev/ic/w83l518dreg.h> +#include <dev/ic/w83l518dvar.h> +#include <dev/ic/w83l518d_sdmmc.h> + +uint8_t +wb_idx_read(struct wb_softc *wb, uint8_t reg) +{ + bus_space_write_1(wb->wb_iot, wb->wb_ioh, WB_SD_INDEX, reg); + return bus_space_read_1(wb->wb_iot, wb->wb_ioh, WB_SD_DATA); +} + +void +wb_idx_write(struct wb_softc *wb, uint8_t reg, uint8_t val) +{ + bus_space_write_1(wb->wb_iot, wb->wb_ioh, WB_SD_INDEX, reg); + bus_space_write_1(wb->wb_iot, wb->wb_ioh, WB_SD_DATA, val); +} + +uint8_t +wb_read(struct wb_softc *wb, uint8_t reg) +{ + return bus_space_read_1(wb->wb_iot, wb->wb_ioh, reg); +} + +void +wb_write(struct wb_softc *wb, uint8_t reg, uint8_t val) +{ + bus_space_write_1(wb->wb_iot, wb->wb_ioh, reg, val); +} + +void +wb_led(struct wb_softc *wb, int enable) +{ + uint8_t val; + + val = wb_read(wb, WB_SD_CSR); + if (enable) + val |= WB_CSR_MS_LED; + else + val &= ~WB_CSR_MS_LED; + wb_write(wb, WB_SD_CSR, val); +} + +void +wb_attach(struct wb_softc *wb) +{ + switch (wb->wb_type) { + case WB_DEVNO_SD: + wb_sdmmc_attach(wb); + break; + case WB_DEVNO_MS: + break; + case WB_DEVNO_SC: + break; + case WB_DEVNO_GPIO: + break; + } +} + +int +wb_detach(struct wb_softc *wb, int flags) +{ + switch (wb->wb_type) { + case WB_DEVNO_SD: + wb_sdmmc_detach(wb, flags); + break; + } + + return 0; +} + +/* + * intr handler + */ +int +wb_intr(void *opaque) +{ + struct wb_softc *wb = opaque; + + switch (wb->wb_type) { + case WB_DEVNO_SD: + return wb_sdmmc_intr(wb); + break; + } + + return 0; +} diff --git a/sys/dev/ic/w83l518d_sdmmc.c b/sys/dev/ic/w83l518d_sdmmc.c new file mode 100644 index 00000000000..9f03535a969 --- /dev/null +++ b/sys/dev/ic/w83l518d_sdmmc.c @@ -0,0 +1,595 @@ +/* $OpenBSD: w83l518d_sdmmc.c,v 1.1 2009/10/03 19:51:53 kettenis Exp $ */ +/* $NetBSD: w83l518d_sdmmc.c,v 1.1 2009/09/30 20:44:50 jmcneill Exp $ */ + +/* + * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/syslog.h> +#include <sys/device.h> +#include <sys/proc.h> + +#include <machine/bus.h> + +#include <dev/sdmmc/sdmmcvar.h> +#include <dev/sdmmc/sdmmcchip.h> +#include <dev/sdmmc/sdmmc_ioreg.h> + +#include <dev/ic/w83l518dreg.h> +#include <dev/ic/w83l518dvar.h> +#include <dev/ic/w83l518d_sdmmc.h> + +/* #define WB_SDMMC_DEBUG */ + +#ifdef WB_SDMMC_DEBUG +static int wb_sdmmc_debug = 1; +#else +static int wb_sdmmc_debug = 0; +#endif + +#define REPORT(_wb, ...) \ + if (wb_sdmmc_debug > 0) \ + printf(__VA_ARGS__) + +int wb_sdmmc_host_reset(sdmmc_chipset_handle_t); +uint32_t wb_sdmmc_host_ocr(sdmmc_chipset_handle_t); +int wb_sdmmc_host_maxblklen(sdmmc_chipset_handle_t); +int wb_sdmmc_card_detect(sdmmc_chipset_handle_t); +#ifdef notyet +int wb_sdmmc_write_protect(sdmmc_chipset_handle_t); +#endif +int wb_sdmmc_bus_power(sdmmc_chipset_handle_t, uint32_t); +int wb_sdmmc_bus_clock(sdmmc_chipset_handle_t, int); +#if 0 +int wb_sdmmc_bus_width(sdmmc_chipset_handle_t, int); +#endif +void wb_sdmmc_exec_command(sdmmc_chipset_handle_t, + struct sdmmc_command *); +void wb_sdmmc_card_intr_mask(sdmmc_chipset_handle_t, int); +void wb_sdmmc_card_intr_ack(sdmmc_chipset_handle_t); + +struct sdmmc_chip_functions wb_sdmmc_chip_functions = { + /* host controller reset */ + wb_sdmmc_host_reset, + /* host controlle capabilities */ + wb_sdmmc_host_ocr, + wb_sdmmc_host_maxblklen, + /* card detection */ + wb_sdmmc_card_detect, +#ifdef notyet + .write_protect = wb_sdmmc_write_protect, +#endif + /* bus power and clock frequency */ + wb_sdmmc_bus_power, + wb_sdmmc_bus_clock, +#if 0 + .bus_width = wb_sdmmc_bus_width, +#endif + /* command execution */ + wb_sdmmc_exec_command, + /* card interrupt */ + wb_sdmmc_card_intr_mask, + wb_sdmmc_card_intr_ack +}; + +void wb_sdmmc_read_data(struct wb_softc *, uint8_t *, int); +void wb_sdmmc_write_data(struct wb_softc *, uint8_t *, int); +void wb_sdmmc_discover(void *); +int wb_sdmmc_enable(struct wb_softc *); +int wb_sdmmc_disable(struct wb_softc *); +int wb_sdmmc_transfer_data(struct wb_softc *, struct sdmmc_command *); +void wb_sdmmc_rsp_read_long(struct wb_softc *, struct sdmmc_command *); +void wb_sdmmc_rsp_read_short(struct wb_softc *, struct sdmmc_command *); + +void +wb_sdmmc_read_data(struct wb_softc *wb, uint8_t *data, int len) +{ + bus_space_read_multi_1(wb->wb_iot, wb->wb_ioh, WB_SD_FIFO, data, len); +} + +void +wb_sdmmc_write_data(struct wb_softc *wb, uint8_t *data, int len) +{ + bus_space_write_multi_1(wb->wb_iot, wb->wb_ioh, WB_SD_FIFO, data, len); +} + +void +wb_sdmmc_discover(void *opaque) +{ + struct wb_softc *wb = opaque; + + REPORT(wb, "TRACE: discover(wb)\n"); + + sdmmc_needs_discover(wb->wb_sdmmc_dev); +} + +int +wb_sdmmc_enable(struct wb_softc *wb) +{ + int i = 5000; + + REPORT(wb, "TRACE: enable(wb)\n"); + + /* put the device in a known state */ + wb_idx_write(wb, WB_INDEX_SETUP, WB_SETUP_SOFT_RST); + while (--i > 0 && wb_idx_read(wb, WB_INDEX_SETUP) & WB_SETUP_SOFT_RST) + delay(10); + if (i == 0) { + printf("%s: timeout resetting device\n", wb->wb_dev.dv_xname); + return 0; + } + wb_idx_write(wb, WB_INDEX_CLK, WB_CLK_375K); + wb_idx_write(wb, WB_INDEX_FIFOEN, 0); + wb_idx_write(wb, WB_INDEX_DMA, 0); + wb_idx_write(wb, WB_INDEX_PBSMSB, 0); + wb_idx_write(wb, WB_INDEX_PBSLSB, 0); + /* drain FIFO */ + while ((wb_read(wb, WB_SD_FIFOSTS) & WB_FIFO_EMPTY) == 0) + wb_read(wb, WB_SD_FIFO); + + wb_write(wb, WB_SD_CSR, 0); + + wb_write(wb, WB_SD_INTCTL, WB_INT_DEFAULT); + + wb_sdmmc_card_detect(wb); + + return 1; +} + +int +wb_sdmmc_disable(struct wb_softc *wb) +{ + uint8_t val; + + REPORT(wb, "TRACE: disable(wb)\n"); + + val = wb_read(wb, WB_SD_CSR); + val |= WB_CSR_POWER_N; + wb_write(wb, WB_SD_CSR, val); + + return 1; +} + +void +wb_sdmmc_attach(struct wb_softc *wb) +{ + struct sdmmcbus_attach_args saa; + + timeout_set(&wb->wb_sdmmc_to, wb_sdmmc_discover, wb); + + wb->wb_sdmmc_width = 1; + + if (wb_sdmmc_enable(wb) == 0) + return; + + memset(&saa, 0, sizeof(saa)); + saa.saa_busname = "sdmmc"; + saa.sct = &wb_sdmmc_chip_functions; + saa.sch = wb; + saa.flags = SMF_STOP_AFTER_MULTIPLE; + + wb->wb_sdmmc_dev = config_found(&wb->wb_dev, &saa, NULL); +} + +int +wb_sdmmc_detach(struct wb_softc *wb, int flags) +{ + int rv; + + if (wb->wb_sdmmc_dev) { + rv = config_detach(wb->wb_sdmmc_dev, flags); + if (rv) + return rv; + } + wb_sdmmc_disable(wb); + + timeout_del(&wb->wb_sdmmc_to); + + return 0; +} + +/* + * SD/MMC interface + */ +int +wb_sdmmc_host_reset(sdmmc_chipset_handle_t sch) +{ + REPORT(sch, "TRACE: sdmmc/host_reset(wb)\n"); + + return 0; +} + +uint32_t +wb_sdmmc_host_ocr(sdmmc_chipset_handle_t sch) +{ + REPORT(sch, "TRACE: sdmmc/host_ocr(wb)\n"); + + return MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V; +} + +int +wb_sdmmc_host_maxblklen(sdmmc_chipset_handle_t sch) +{ + REPORT(sch, "TRACE: sdmmc/host_maxblklen(wb)\n"); + + return 512; /* XXX */ +} + +int +wb_sdmmc_card_detect(sdmmc_chipset_handle_t sch) +{ + struct wb_softc *wb = sch; + int rv; + + wb_led(wb, 1); + rv = (wb_read(wb, WB_SD_CSR) & WB_CSR_CARD_PRESENT) ? 1 : 0; + wb_led(wb, 0); + + REPORT(wb, "TRACE: sdmmc/card_detect(wb) -> %d\n", rv); + + return rv; +} + +#ifdef notyet +int +wb_sdmmc_write_protect(sdmmc_chipset_handle_t sch) +{ + struct wb_softc *wb = sch; + int rv; + + wb_led(wb, 1); + rv = (wb_read(wb, WB_SD_CSR) & WB_CSR_WRITE_PROTECT) ? 1 : 0; + wb_led(wb, 0); + + REPORT(wb, "TRACE: sdmmc/write_protect(wb) -> %d\n", rv); + + return rv; +} +#endif + +int +wb_sdmmc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr) +{ + REPORT(sch, "TRACE: sdmmc/bus_power(wb, ocr=%d)\n", ocr); + + return 0; +} + +int +wb_sdmmc_bus_clock(sdmmc_chipset_handle_t sch, int freq) +{ + struct wb_softc *wb = sch; + uint8_t clk; + + REPORT(wb, "TRACE: sdmmc/bus_clock(wb, freq=%d)\n", freq); + + if (freq >= 24000) + clk = WB_CLK_24M; + else if (freq >= 16000) + clk = WB_CLK_16M; + else if (freq >= 12000) + clk = WB_CLK_12M; + else + clk = WB_CLK_375K; + + if (wb_idx_read(wb, WB_INDEX_CLK) != clk) + wb_idx_write(wb, WB_INDEX_CLK, clk); + + return 0; +} + +#if 0 +int +wb_sdmmc_bus_width(sdmmc_chipset_handle_t sch, int width) +{ + struct wb_softc *wb = sch; + + REPORT(wb, "TRACE: sdmmc/bus_width(wb, width=%d)\n", width); + + if (width != 1 && width != 4) + return 1; + + wb->wb_sdmmc_width = width; + + return 0; +} +#endif + +void +wb_sdmmc_rsp_read_long(struct wb_softc *wb, struct sdmmc_command *cmd) +{ + uint8_t *p = (uint8_t *)cmd->c_resp; + int i; + + if (wb_idx_read(wb, WB_INDEX_RESPLEN) != 1) { + cmd->c_error = ENXIO; + return; + } + + for (i = 12; i >= 0; i -= 4) { + p[0] = wb_idx_read(wb, WB_INDEX_RESP(i + 0)); + p[1] = wb_idx_read(wb, WB_INDEX_RESP(i + 1)); + p[2] = wb_idx_read(wb, WB_INDEX_RESP(i + 2)); + p[3] = wb_idx_read(wb, WB_INDEX_RESP(i + 3)); + p += 4; + } +} + +void +wb_sdmmc_rsp_read_short(struct wb_softc *wb, struct sdmmc_command *cmd) +{ + uint8_t *p = (uint8_t *)cmd->c_resp; + + if (wb_idx_read(wb, WB_INDEX_RESPLEN) != 0) { + cmd->c_error = ENXIO; + return; + } + +#if BYTE_ORDER == LITTLE_ENDIAN + p[3] = wb_idx_read(wb, WB_INDEX_RESP(12)); + p[2] = wb_idx_read(wb, WB_INDEX_RESP(13)); + p[1] = wb_idx_read(wb, WB_INDEX_RESP(14)); + p[0] = wb_idx_read(wb, WB_INDEX_RESP(15)); +#else + p[0] = wb_idx_read(wb, WB_INDEX_RESP(12)); + p[1] = wb_idx_read(wb, WB_INDEX_RESP(13)); + p[2] = wb_idx_read(wb, WB_INDEX_RESP(14)); + p[3] = wb_idx_read(wb, WB_INDEX_RESP(15)); +#endif +} + +int +wb_sdmmc_transfer_data(struct wb_softc *wb, struct sdmmc_command *cmd) +{ + uint8_t fifosts; + int datalen, retry = 5000; + + if (wb->wb_sdmmc_intsts & WB_INT_CARD) + return EIO; + + fifosts = wb_read(wb, WB_SD_FIFOSTS); + if (ISSET(cmd->c_flags, SCF_CMD_READ)) { + if (fifosts & WB_FIFO_EMPTY) { + while (--retry > 0) { + fifosts = wb_read(wb, WB_SD_FIFOSTS); + if ((fifosts & WB_FIFO_EMPTY) == 0) + break; + delay(100); + } + if (retry == 0) + return EBUSY; + } + + if (fifosts & WB_FIFO_FULL) + datalen = 16; + else + datalen = fifosts & WB_FIFO_DEPTH_MASK; + } else { + if (fifosts & WB_FIFO_FULL) { + while (--retry > 0) { + fifosts = wb_read(wb, WB_SD_FIFOSTS); + if ((fifosts & WB_FIFO_FULL) == 0) + break; + delay(100); + } + if (retry == 0) + return EBUSY; + } + + if (fifosts & WB_FIFO_EMPTY) + datalen = 16; + else + datalen = 16 - (fifosts & WB_FIFO_DEPTH_MASK); + } + + datalen = MIN(datalen, cmd->c_resid); + if (datalen > 0) { + if (ISSET(cmd->c_flags, SCF_CMD_READ)) + wb_sdmmc_read_data(wb, cmd->c_buf, datalen); + else + wb_sdmmc_write_data(wb, cmd->c_buf, datalen); + + cmd->c_buf += datalen; + cmd->c_resid -= datalen; + } + + return 0; +} + +void +wb_sdmmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) +{ + static const int opcodes[] = { + 11, 17, 18, 20, 24, 25, 26, 27, 30, 42, 51, 56 + }; + struct wb_softc *wb = sch; + uint8_t val; + int blklen; + int error; + int i, retry; + int s; + + REPORT(wb, "TRACE: sdmmc/exec_command(wb, cmd) " + "opcode %d flags 0x%x data %p datalen %d\n", + cmd->c_opcode, cmd->c_flags, cmd->c_data, cmd->c_datalen); + + if (cmd->c_datalen > 0) { + /* controller only supports a select number of data opcodes */ + for (i = 0; i < nitems(opcodes); i++) + if (opcodes[i] == cmd->c_opcode) + break; + if (i == nitems(opcodes)) { + cmd->c_error = EINVAL; + goto done; + } + + /* Fragment the data into proper blocks */ + blklen = MIN(cmd->c_datalen, cmd->c_blklen); + + if (cmd->c_datalen % blklen > 0) { + printf("%s: data is not a multiple of %u bytes\n", + wb->wb_dev.dv_xname, blklen); + cmd->c_error = EINVAL; + goto done; + } + + /* setup block size registers */ + blklen = blklen + 2 * wb->wb_sdmmc_width; + wb_idx_write(wb, WB_INDEX_PBSMSB, + ((blklen >> 4) & 0xf0) | (wb->wb_sdmmc_width / 4)); + wb_idx_write(wb, WB_INDEX_PBSLSB, blklen & 0xff); + + /* clear FIFO */ + val = wb_idx_read(wb, WB_INDEX_SETUP); + val |= WB_SETUP_FIFO_RST; + wb_idx_write(wb, WB_INDEX_SETUP, val); + while (wb_idx_read(wb, WB_INDEX_SETUP) & WB_SETUP_FIFO_RST) + ; + + cmd->c_resid = cmd->c_datalen; + cmd->c_buf = cmd->c_data; + + /* setup FIFO thresholds */ + if (ISSET(cmd->c_flags, SCF_CMD_READ)) + wb_idx_write(wb, WB_INDEX_FIFOEN, WB_FIFOEN_FULL | 8); + else { + wb_idx_write(wb, WB_INDEX_FIFOEN, WB_FIFOEN_EMPTY | 8); + + /* pre-fill the FIFO on write */ + error = wb_sdmmc_transfer_data(wb, cmd); + if (error) { + cmd->c_error = error; + goto done; + } + } + } + + s = splsdmmc(); + wb->wb_sdmmc_intsts = 0; + wb_write(wb, WB_SD_COMMAND, cmd->c_opcode); + wb_write(wb, WB_SD_COMMAND, (cmd->c_arg >> 24) & 0xff); + wb_write(wb, WB_SD_COMMAND, (cmd->c_arg >> 16) & 0xff); + wb_write(wb, WB_SD_COMMAND, (cmd->c_arg >> 8) & 0xff); + wb_write(wb, WB_SD_COMMAND, (cmd->c_arg >> 0) & 0xff); + splx(s); + + retry = 100000; + while (wb_idx_read(wb, WB_INDEX_STATUS) & WB_STATUS_CARD_TRAFFIC) { + if (--retry == 0) + break; + delay(1); + } + if (wb_idx_read(wb, WB_INDEX_STATUS) & WB_STATUS_CARD_TRAFFIC) { + REPORT(wb, + "command timed out, WB_INDEX_STATUS = 0x%02x\n", + wb_idx_read(wb, WB_INDEX_STATUS)); + cmd->c_error = ETIMEDOUT; + goto done; + } + + if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) { + if (wb->wb_sdmmc_intsts & WB_INT_TIMEOUT) { + cmd->c_error = ETIMEDOUT; + goto done; + } + + if (ISSET(cmd->c_flags, SCF_RSP_136)) + wb_sdmmc_rsp_read_long(wb, cmd); + else + wb_sdmmc_rsp_read_short(wb, cmd); + } + + if (cmd->c_error == 0 && cmd->c_datalen > 0) { + wb_led(wb, 1); + while (cmd->c_resid > 0) { + error = wb_sdmmc_transfer_data(wb, cmd); + if (error) { + cmd->c_error = error; + break; + } + } + wb_led(wb, 0); + } + +done: + SET(cmd->c_flags, SCF_ITSDONE); + + if (cmd->c_error) { + REPORT(wb, + "cmd error = %d, op = %d [%s] " + "blklen %d datalen %d resid %d\n", + cmd->c_error, cmd->c_opcode, + ISSET(cmd->c_flags, SCF_CMD_READ) ? "rd" : "wr", + cmd->c_blklen, cmd->c_datalen, cmd->c_resid); + } +} + +void +wb_sdmmc_card_intr_mask(sdmmc_chipset_handle_t sch, int enable) +{ + REPORT(sch, "TRACE: sdmmc/card_enable_intr(wb, enable=%d)\n", enable); +} + +void +wb_sdmmc_card_intr_ack(sdmmc_chipset_handle_t sch) +{ + REPORT(sch, "TRACE: sdmmc/card_intr_ack(wb)\n"); +} + +/* + * intr handler + */ +int +wb_sdmmc_intr(struct wb_softc *wb) +{ + uint8_t val; + + val = wb_read(wb, WB_SD_INTSTS); + if (val == 0xff || val == 0x00) + return 0; + + if (wb->wb_sdmmc_dev == NULL) + return 1; + + wb->wb_sdmmc_intsts |= val; + + if (wb_sdmmc_debug) { + char buf[64]; + snprintf(buf, sizeof(buf), + "\20\1TC\2BUSYEND\3PROGEND\4TIMEOUT" + "\5CRC\6FIFO\7CARD\010PENDING", + val); + REPORT(wb, "WB_SD_INTSTS = %s\n", buf); + } + + if (val & WB_INT_CARD) + timeout_add(&wb->wb_sdmmc_to, hz / 4); + + return 1; +} diff --git a/sys/dev/ic/w83l518d_sdmmc.h b/sys/dev/ic/w83l518d_sdmmc.h new file mode 100644 index 00000000000..e2ef3e7194e --- /dev/null +++ b/sys/dev/ic/w83l518d_sdmmc.h @@ -0,0 +1,36 @@ +/* $OpenBSD: w83l518d_sdmmc.h,v 1.1 2009/10/03 19:51:53 kettenis Exp $ */ +/* $NetBSD: w83l518d_sdmmc.h,v 1.1 2009/09/30 20:44:50 jmcneill Exp $ */ + +/* + * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _SYS_DEV_IC_W83L518D_SDMMC_H +#define _SYS_DEV_IC_W83L518D_SDMMC_H + +void wb_sdmmc_attach(struct wb_softc *); +int wb_sdmmc_detach(struct wb_softc *, int); +int wb_sdmmc_intr(struct wb_softc *); + +#endif diff --git a/sys/dev/ic/w83l518dreg.h b/sys/dev/ic/w83l518dreg.h new file mode 100644 index 00000000000..3497df8ed1e --- /dev/null +++ b/sys/dev/ic/w83l518dreg.h @@ -0,0 +1,169 @@ +/* $OpenBSD: w83l518dreg.h,v 1.1 2009/10/03 19:51:53 kettenis Exp $ */ +/* $NetBSD: w83l518dreg.h,v 1.1 2009/09/30 20:44:50 jmcneill Exp $ */ + +/* + * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _SYS_DEV_IC_W83L518DREG_H +#define _SYS_DEV_IC_W83L518DREG_H + +/* + * Global Registers + */ + +#define WB_REG_RESET 0x02 +#define WB_RESET_SWRST 0x01 /* software reset */ + +#define WB_REG_DEVNO 0x07 +#define WB_DEVNO_SC 0x00 /* Smart Card interface */ +#define WB_DEVNO_MS 0x01 /* Memory Stick interface */ +#define WB_DEVNO_GPIO 0x02 /* GPIO */ +#define WB_DEVNO_SD 0x03 /* SD memory card interface */ + +#define WB_REG_DEVID_HI 0x20 +#define WB_REG_DEVID_LO 0x21 +#define WB_DEVID_W83L518D 0x7110 +#define WB_DEVID_W83L519D 0x7120 +#define WB_DEVID_REVISION(id) ((id) & 0xf) + +#define WB_REG_POWER 0x22 +#define WB_POWER_SC 0x80 /* Smart Card interface */ +#define WB_POWER_MS 0x40 /* Memory Stick interface */ +#define WB_POWER_SD 0x20 /* SD memory card interface */ + +#define WB_REG_PME 0x23 +#define WB_PME_PME_EN 0x80 /* Global PM event enable */ +#define WB_PME_MSPME_EN 0x40 /* MS PM event enable */ +#define WB_PME_SDPME_EN 0x20 /* SD PM event enable */ +#define WB_PME_SCPME_EN 0x10 /* SC PM event enable */ + +#define WB_REG_PMESTS 0x24 /* PM event status */ +#define WB_PMESTS_MSPME_STS 0x40 /* MS PM event status */ +#define WB_PMESTS_SDPME_STS 0x20 /* SD PM event status */ +#define WB_PMESTS_SCPME_STS 0x10 /* SC PM event status */ + +#define WB_REG_CFG 0x26 +#define WB_CFG_HEFRAS 0x40 /* Extended func reg addr select */ +#define WB_CFG_LOCKREG 0x20 /* Config register access control */ + +#define WB_REG_MFSEL 0x29 /* Multi-function sel (518 only) */ + +/* + * Logical Device Interface + */ + +#define WB_REG_DEV_EN 0x30 +#define WB_DEV_EN_ACTIVE 0x01 /* Logical device active bit */ + +#define WB_REG_DEV_BASE_HI 0x60 +#define WB_REG_DEV_BASE_LO 0x61 + +#define WB_REG_DEV_IRQ 0x70 +#define WB_DEV_IRQ_MASK 0x0f + +#define WB_REG_DEV_DRQ 0x74 +#define WB_DEV_DRQ_MASK 0x0f + +#define WB_REG_DEV_MISC 0xf0 +#define WB_DEV_MISC_SCIRQ_SHR 0x80 /* SC: IRQ sharing control */ +#define WB_DEV_MISC_SCPSNT_POL 0x01 /* SC: SC present polarity */ +#define WB_DEV_MISC_MSIRQ_POLL 0x10 /* MS: IRQ polarity control (level) */ +#define WB_DEV_MISC_MSIRQ_POLP 0x08 /* MS: IRQ polarity control (pulse) */ +#define WB_DEV_MISC_MSIRQ_SHR 0x04 /* MS: IRQ sharing control */ +#define WB_DEV_MISC_MS4OUT_POL 0x02 /* MS: MS4 output polarity control */ +#define WB_DEV_MISC_MS4OUT_EN 0x01 /* MS: MS4 output enable */ +#define WB_DEV_MISC_SDDATA3_HI 0x20 /* SD: DATA3 pin will output high */ +#define WB_DEV_MISC_SDDATA3_OUT 0x10 /* SD: DATA3 pin to output pin */ +#define WB_DEV_MISC_SDGP11_HI 0x04 /* SD: GP11 card-detect pin pole */ +#define WB_DEV_MISC_SDGP11_DET 0x02 /* SD: GP11 card-detect enable */ +#define WB_DEV_MISC_SDDATA3_DET 0x01 /* SD: DATA3 card-detect enable */ + +#define WB_REG_DEV_IRQCFG 0xf1 +#define WB_DEV_IRQCFG_HI_L 0x08 +#define WB_DEV_IRQCFG_HI_P 0x04 +#define WB_DEV_IRQCFG_MODE 0x02 +#define WB_DEV_IRQCFG_DEBOUNCE 0x01 + +/* + * SD Card interface registers + */ + +#define WB_SD_COMMAND 0x00 +#define WB_SD_FIFO 0x01 +#define WB_SD_INTCTL 0x02 +#define WB_SD_INTSTS 0x03 +#define WB_INT_PENDING 0x80 +#define WB_INT_CARD 0x40 +#define WB_INT_FIFO 0x20 +#define WB_INT_CRC 0x10 +#define WB_INT_TIMEOUT 0x08 +#define WB_INT_PROGEND 0x04 +#define WB_INT_BUSYEND 0x02 +#define WB_INT_TC 0x01 +#define WB_INT_DEFAULT \ + (WB_INT_CARD|WB_INT_FIFO|WB_INT_CRC|WB_INT_TIMEOUT) +#define WB_SD_FIFOSTS 0x04 +#define WB_FIFO_EMPTY 0x80 +#define WB_FIFO_FULL 0x40 +#define WB_FIFO_EMPTY_THRES 0x20 +#define WB_FIFO_FULL_THRES 0x10 +#define WB_FIFO_DEPTH_MASK 0x0f +#define WB_SD_INDEX 0x05 +#define WB_INDEX_CLK 0x01 +#define WB_CLK_375K 0x00 +#define WB_CLK_12M 0x01 +#define WB_CLK_16M 0x02 +#define WB_CLK_24M 0x03 +#define WB_INDEX_PBSMSB 0x02 +#define WB_INDEX_TAAC 0x03 +#define WB_INDEX_NSAC 0x04 +#define WB_INDEX_PBSLSB 0x05 +#define WB_INDEX_SETUP 0x06 +#define WB_SETUP_DATA3_HI 0x08 +#define WB_SETUP_FIFO_RST 0x04 +#define WB_SETUP_SOFT_RST 0x02 +#define WB_INDEX_DMA 0x07 +#define WB_INDEX_FIFOEN 0x08 +#define WB_FIFOEN_EMPTY 0x20 +#define WB_FIFOEN_FULL 0x10 +#define WB_INDEX_STATUS 0x10 +#define WB_STATUS_BLOCK_READ 0x80 +#define WB_STATUS_BLOCK_WRITE 0x40 +#define WB_STATUS_BUSY 0x20 +#define WB_STATUS_CARD_TRAFFIC 0x04 +#define WB_STATUS_SEND_COMMAND 0x02 +#define WB_STATUS_RECV_RES 0x01 +#define WB_INDEX_RESPLEN 0x1e +#define WB_INDEX_RESP(n) (0x1f + (n)) +#define WB_INDEX_CRCSTS 0x30 +#define WB_INDEX_ISR 0x3f +#define WB_SD_DATA 0x06 +#define WB_SD_CSR 0x07 +#define WB_CSR_MS_LED 0x20 +#define WB_CSR_POWER_N 0x10 +#define WB_CSR_WRITE_PROTECT 0x04 +#define WB_CSR_CARD_PRESENT 0x01 + +#endif diff --git a/sys/dev/ic/w83l518dvar.h b/sys/dev/ic/w83l518dvar.h new file mode 100644 index 00000000000..a02691cfb9b --- /dev/null +++ b/sys/dev/ic/w83l518dvar.h @@ -0,0 +1,60 @@ +/* $OpenBSD: w83l518dvar.h,v 1.1 2009/10/03 19:51:53 kettenis Exp $ */ +/* $NetBSD: w83l518dvar.h,v 1.1 2009/09/30 20:44:50 jmcneill Exp $ */ + +/* + * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _SYS_DEV_IC_W83L518DVAR_H +#define _SYS_DEV_IC_W83L518DVAR_H + +struct wb_softc { + struct device wb_dev; + + /* to be filled in by bus driver */ + bus_space_tag_t wb_iot; + bus_space_handle_t wb_ioh; + uint8_t wb_type; + uint16_t wb_base; + uint8_t wb_irq; + + /* private */ + struct device *wb_sdmmc_dev; + int wb_sdmmc_width; + uint8_t wb_sdmmc_intsts; + struct timeout wb_sdmmc_to; +}; + +void wb_attach(struct wb_softc *); +int wb_detach(struct wb_softc *, int); +int wb_intr(void *); + +uint8_t wb_read(struct wb_softc *, uint8_t); +void wb_write(struct wb_softc *, uint8_t, uint8_t); +uint8_t wb_idx_read(struct wb_softc *, uint8_t); +void wb_idx_write(struct wb_softc *, uint8_t, uint8_t); + +void wb_led(struct wb_softc *, int); + +#endif |