The branch main has been updated by br:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=253c83058deb5ff77dc0a3b60bf7c1d10f9ef5e8

commit 253c83058deb5ff77dc0a3b60bf7c1d10f9ef5e8
Author:     Ruslan Bukin <b...@freebsd.org>
AuthorDate: 2025-04-09 10:23:48 +0000
Commit:     Ruslan Bukin <b...@freebsd.org>
CommitDate: 2025-04-09 11:21:05 +0000

    mmc: SPI-mode support for SD cards.
    
    Introduce SPI-mode support which allows an SD card to communicate with a
    host system using SPI protocol, as described in the SD Card Specification.
    
    This feature is useful for low-end, FPGA or RISC-V research systems when a
    SoC is limited in terms of peripherals available (e.g. lack of a dedicated
    MMC controller in hardware). Examples of such systems include Codasip,
    lowRISC and CVA6.
    
    Project timeline:
    2007: Warner first discussed SPI operational mode in his MMC presentation:
          https://people.freebsd.org/~imp/bsdcan2007.pdf
    2012: Patrick Kelsey engineered the support.
    2025: Ruslan cleaned up, tested on Codasip X730 platform (RISC-V FPGA)
          and put the patch to review.
    2025: Patrick Kelsey reviewed the patch and aligned with the current MMC
          code.
    
    Reviewed by:    pkelsey
    Sponsored by:   UKRI
    Differential Revision:  https://reviews.freebsd.org/D49248
---
 sys/conf/files       |    1 +
 sys/dev/mmc/mmcspi.c | 2378 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 2379 insertions(+)

diff --git a/sys/conf/files b/sys/conf/files
index 2f4b7126a9cd..1372c6b1b5b9 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -2427,6 +2427,7 @@ dev/mmc/mmc.c                     optional mmc !mmccam
 dev/mmc/mmcbr_if.m             standard
 dev/mmc/mmcbus_if.m            standard
 dev/mmc/mmcsd.c                        optional mmcsd !mmccam
+dev/mmc/mmcspi.c               optional mmcsd spibus !mmccam
 dev/mmc/mmc_fdt_helpers.c      optional mmc regulator clk fdt | mmccam 
regulator clk fdt
 dev/mmc/mmc_helpers.c          optional mmc gpio regulator clk | mmccam gpio 
regulator clk
 dev/mmc/mmc_pwrseq.c           optional mmc clk regulator fdt | mmccam clk 
regulator fdt
diff --git a/sys/dev/mmc/mmcspi.c b/sys/dev/mmc/mmcspi.c
new file mode 100644
index 000000000000..d18f7aeb8cc0
--- /dev/null
+++ b/sys/dev/mmc/mmcspi.c
@@ -0,0 +1,2378 @@
+/*-
+ * Copyright (c) 2012-2025 Patrick Kelsey.  All rights reserved.
+ * Copyright (c) 2025 Ruslan Bukin <b...@bsdpad.com>
+ *
+ * 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. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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.
+ *
+ * Portions of this software may have been developed with reference to
+ * the SD Simplified Specification.  The following disclaimer may apply:
+ *
+ * The following conditions apply to the release of the simplified
+ * specification ("Simplified Specification") by the SD Card Association and
+ * the SD Group. The Simplified Specification is a subset of the complete SD
+ * Specification which is owned by the SD Card Association and the SD
+ * Group. This Simplified Specification is provided on a non-confidential
+ * basis subject to the disclaimers below. Any implementation of the
+ * Simplified Specification may require a license from the SD Card
+ * Association, SD Group, SD-3C LLC or other third parties.
+ *
+ * Disclaimers:
+ *
+ * The information contained in the Simplified Specification is presented only
+ * as a standard specification for SD Cards and SD Host/Ancillary products and
+ * is provided "AS-IS" without any representations or warranties of any
+ * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD
+ * Card Association for any damages, any infringements of patents or other
+ * right of the SD Group, SD-3C LLC, the SD Card Association or any third
+ * parties, which may result from its use. No license is granted by
+ * implication, estoppel or otherwise under any patent or other rights of the
+ * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing
+ * herein shall be construed as an obligation by the SD Group, the SD-3C LLC
+ * or the SD Card Association to disclose or distribute any technical
+ * information, know-how or other confidential information to any third party.
+ *
+ *
+ * CRC routines adapted from public domain code written by Lammert Bies.
+ *
+ *
+ * This is an implementation of mmcbr that communicates with SD/MMC cards in
+ * SPI mode via spibus_if.  In order to minimize changes to the existing
+ * MMC/SD stack (and allow for maximal reuse of the same), the behavior of
+ * the SD-bus command set is emulated as much as possible, where required.
+ *
+ * The SPI bus ownership behavior is to acquire the SPI bus for the entire
+ * duration that the MMC host is acquired.
+ *
+ * CRC checking is enabled by default, but can be disabled at runtime
+ * per-card via sysctl (e.g. sysctl dev.mmcspi.0.use_crc=0).
+ *
+ * Considered, but not implemented:
+ *   - Card presence detection
+ *   - Card power control
+ *   - Detection of lock switch state on cards that have them
+ *   - Yielding the CPU during long card busy cycles
+ *
+ * Originally developed and tested using a MicroTik RouterBOARD RB450G and
+ * 31 microSD cards available circa 2012.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/sysctl.h>
+
+#include <dev/mmc/bridge.h>
+#include <dev/mmc/mmcreg.h>
+#include <dev/mmc/mmcbrvar.h>
+#include <dev/spibus/spi.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "mmcbr_if.h"
+#include "spibus_if.h"
+
+#define        MMCSPI_RETRIES          3
+#define        MMCSPI_TIMEOUT_SEC      3
+
+#define        MMCSPI_MAX_RSP_LEN      5  /* max length of an Rn response */
+#define        MMCSPI_OCR_LEN          4
+
+#define        MMCSPI_DATA_BLOCK_LEN   512
+#define        MMCSPI_DATA_CRC_LEN     2
+
+#define        MMCSPI_POLL_LEN         8  /* amount to read when searching */
+
+#define        MMCSPI_R1_MASK  0x80 /* mask used to search for R1 tokens */
+#define        MMCSPI_R1_VALUE 0x00 /* value used to search for R1 tokens */
+#define        MMCSPI_DR_MASK  0x11 /* mask used to search for data resp 
tokens */
+#define        MMCSPI_DR_VALUE 0x01 /* value used to search for data resp 
tokens */
+
+#define        MMCSPI_DR_ERR_MASK      0x0e
+#define        MMCSPI_DR_ERR_NONE      0x04
+#define        MMCSPI_DR_ERR_CRC       0x0a
+#define        MMCSPI_DR_ERR_WRITE     0x0c
+
+#define        MMCSPI_TOKEN_SB         0xfe /* start block token for read 
single,
+                                       read multi, and write single */
+#define        MMCSPI_TOKEN_SB_WM      0xfc /* start block token for write 
multi */
+#define        MMCSPI_TOKEN_ST         0xfd /* stop transmission token */
+#define        MMCSPI_IS_DE_TOKEN(x)   (0 == ((x) & 0xf0)) /* detector for data
+                                                      error token */
+
+#define        MMCSPI_R1_IDLE          0x01
+#define        MMCSPI_R1_ERASE_RST     0x02
+#define        MMCSPI_R1_ILL_CMD       0x04
+#define        MMCSPI_R1_CRC_ERR       0x08
+#define        MMCSPI_R1_ERASE_ERR     0x10
+#define        MMCSPI_R1_ADDR_ERR      0x20
+#define        MMCSPI_R1_PARAM_ERR     0x40
+
+#define        MMCSPI_R1_ERR_MASK      (MMCSPI_R1_PARAM_ERR | 
MMCSPI_R1_ADDR_ERR | \
+                                MMCSPI_R1_ERASE_ERR | MMCSPI_R1_CRC_ERR | \
+                                MMCSPI_R1_ILL_CMD)
+
+#define        MMCSPI_R2_LOCKED        0x01
+#define        MMCSPI_R2_WP_ER_LCK     0x02
+#define        MMCSPI_R2_ERR           0x04
+#define        MMCSPI_R2_CC_ERR        0x08
+#define        MMCSPI_R2_ECC_FAIL      0x10
+#define        MMCSPI_R2_WP_VIOLATE    0x20
+#define        MMCSPI_R2_ERASE_PARAM   0x40
+#define        MMCSPI_R2_OOR_CSD_OW    0x80
+
+/* commands that only apply to the SPI interface */
+#define        MMCSPI_READ_OCR         58
+#define        MMCSPI_CRC_ON_OFF       59
+
+static struct ofw_compat_data compat_data[] = {
+       { "mmc-spi-slot",       1 },
+       { NULL,                 0 }
+};
+
+struct mmcspi_command {
+       struct mmc_command *mmc_cmd;    /* command passed from mmc layer */
+       uint32_t        opcode;         /* possibly translated opcode */
+       uint32_t        arg;            /* possibly translated arg */
+       uint32_t        flags;          /* possibly translated flags */
+       uint32_t        retries;        /* possibly translated retry count */
+       struct mmc_data *data;          /* possibly redirected data segment */
+       unsigned int    error_mask;     /* R1 errors check mask */
+       unsigned char   use_crc;        /* do crc checking for this command */
+       unsigned char   rsp_type;       /* SPI response type of this command */
+#define        MMCSPI_RSP_R1   0
+#define        MMCSPI_RSP_R1B  1
+#define        MMCSPI_RSP_R2   2
+#define        MMCSPI_RSP_R3   3
+#define        MMCSPI_RSP_R7   4
+       unsigned char   rsp_len;        /* response len of this command */
+       unsigned char   mmc_rsp_type;   /* MMC reponse type to translate to */
+#define        MMCSPI_TO_MMC_RSP_NONE  0
+#define        MMCSPI_TO_MMC_RSP_R1    1
+#define        MMCSPI_TO_MMC_RSP_R1B   2
+#define        MMCSPI_TO_MMC_RSP_R2    3
+#define        MMCSPI_TO_MMC_RSP_R3    4
+#define        MMCSPI_TO_MMC_RSP_R6    5
+#define        MMCSPI_TO_MMC_RSP_R7    6
+       struct mmc_data ldata;          /* local read data */
+};
+
+struct mmcspi_slot {
+       struct mmcspi_softc *sc;        /* back pointer to parent bridge */
+       device_t        dev;            /* mmc device for slot */
+       boolean_t       bus_busy;       /* host has been acquired */
+       struct mmc_host host;           /* host parameters */
+       struct mtx      mtx;            /* slot mutex */
+       uint8_t         last_ocr[MMCSPI_OCR_LEN]; /* ocr retrieved after CMD8 */
+       uint32_t        last_opcode;    /* last opcode requested by mmc layer */
+       uint32_t        last_flags;     /* last flags requested by mmc layer */
+       unsigned int    crc_enabled;    /* crc checking is enabled */
+       unsigned int    crc_init_done;  /* whether the initial crc setting has
+                                          been sent to the card */
+#define        MMCSPI_MAX_LDATA_LEN 16
+       uint8_t ldata_buf[MMCSPI_MAX_LDATA_LEN];
+};
+
+struct mmcspi_softc {
+       device_t                dev;            /* this mmc bridge device */
+       device_t                busdev;
+       struct mmcspi_slot      slot;
+       unsigned int            use_crc;        /* command CRC checking */
+};
+
+#if defined(MMCSPI_ENABLE_DEBUG_FUNCS)
+static void mmcspi_dump_data(device_t dev, const char *label, uint8_t *data,
+    unsigned int len);
+static void mmcspi_dump_spi_bus(device_t dev, unsigned int len);
+#endif
+
+#define        MMCSPI_LOCK_SLOT(_slot)                 mtx_lock(&(_slot)->mtx)
+#define        MMCSPI_UNLOCK_SLOT(_slot)               
mtx_unlock(&(_slot)->mtx)
+#define        MMCSPI_SLOT_LOCK_INIT(_slot)            mtx_init(&(_slot)->mtx, 
\
+    "SD slot mtx", "mmcspi", MTX_DEF)
+#define        MMCSPI_SLOT_LOCK_DESTROY(_slot)         
mtx_destroy(&(_slot)->mtx);
+#define        MMCSPI_ASSERT_SLOT_LOCKED(_slot)        
mtx_assert(&(_slot)->mtx, \
+    MA_OWNED);
+#define        MMCSPI_ASSERT_SLOT_UNLOCKED(_slot)      
mtx_assert(&(_slot)->mtx, \
+    MA_NOTOWNED);
+
+#define        TRACE_ZONE_ENABLED(zone) (trace_zone_mask & TRACE_ZONE_##zone)
+
+#define        TRACE_ENTER(dev)                                        \
+       if (TRACE_ZONE_ENABLED(ENTER)) {                        \
+               device_printf(dev, "%s: enter\n", __func__);    \
+       }
+
+#define        TRACE_EXIT(dev)                                         \
+       if (TRACE_ZONE_ENABLED(EXIT)) {                         \
+               device_printf(dev, "%s: exit\n", __func__);     \
+       }
+
+#define        TRACE(dev, zone, ...)                           \
+       if (TRACE_ZONE_ENABLED(zone)) {                 \
+               device_printf(dev, __VA_ARGS__);        \
+       }
+
+#define        TRACE_ZONE_ENTER   (1ul << 0)  /* function entrance */
+#define        TRACE_ZONE_EXIT    (1ul << 1)  /* function exit */
+#define        TRACE_ZONE_ACTION  (1ul << 2)  /* for narrating major actions 
taken */
+#define        TRACE_ZONE_RESULT  (1ul << 3)  /* for narrating results of 
actions */
+#define        TRACE_ZONE_ERROR   (1ul << 4)  /* for reporting errors */
+#define        TRACE_ZONE_DATA    (1ul << 5)  /* for dumping bus data */
+#define        TRACE_ZONE_DETAILS (1ul << 6)  /* for narrating minor 
actions/results */
+
+#define        TRACE_ZONE_NONE    0
+#define        TRACE_ZONE_ALL     0xffffffff
+
+#define        CRC7_INITIAL 0x00
+#define        CRC16_INITIAL 0x0000
+
+SYSCTL_NODE(_hw, OID_AUTO, mmcspi, CTLFLAG_RD, 0, "mmcspi driver");
+
+static unsigned int trace_zone_mask = TRACE_ZONE_ERROR;
+
+static uint8_t crc7tab[256];
+static uint16_t crc16tab[256];
+static uint8_t onesbuf[MMCSPI_DATA_BLOCK_LEN];  /* for driving the tx line
+                                                  when receiving */
+static uint8_t junkbuf[MMCSPI_DATA_BLOCK_LEN];  /* for receiving data when
+                                                  transmitting */
+
+static uint8_t
+update_crc7(uint8_t crc, uint8_t *buf, unsigned int len)
+{
+       uint8_t tmp;
+       int i;
+
+       for (i = 0; i < len; i++) {
+               tmp = (crc << 1) ^ buf[i];
+               crc = crc7tab[tmp];
+       }
+
+       return (crc);
+}
+
+static uint16_t
+update_crc16(uint16_t crc, uint8_t *buf, unsigned int len)
+{
+       uint16_t tmp, c16;
+       int i;
+
+       for (i = 0; i < len; i++) {
+               c16  = 0x00ff & (uint16_t)buf[i];
+
+               tmp = (crc >> 8) ^ c16;
+               crc = (crc << 8) ^ crc16tab[tmp];
+       }
+
+       return (crc);
+}
+
+static void
+init_crc7tab(void)
+{
+#define        P_CRC7 0x89
+
+       int i, j;
+       uint8_t crc, c;
+
+       for (i = 0; i < 256; i++) {
+
+               c = (uint8_t)i;
+               crc = (c & 0x80) ? c ^ P_CRC7 : c;
+
+               for (j=1; j<8; j++) {
+                       crc = crc << 1;
+
+                       if (crc & 0x80)
+                               crc = crc ^ P_CRC7;
+               }
+
+               crc7tab[i] = crc;
+       }
+}
+
+static void
+init_crc16tab(void)
+{
+#define        P_CCITT 0x1021
+
+       int i, j;
+       uint16_t crc, c;
+
+       for (i = 0; i < 256; i++) {
+
+               crc = 0;
+               c   = ((uint16_t) i) << 8;
+
+               for (j=0; j<8; j++) {
+
+                       if ((crc ^ c) & 0x8000) crc = ( crc << 1 ) ^ P_CCITT;
+                       else                    crc =   crc << 1;
+
+                       c = c << 1;
+               }
+
+               crc16tab[i] = crc;
+       }
+}
+
+static void
+mmcspi_slot_init(device_t brdev, struct mmcspi_slot *slot)
+{
+       struct mmcspi_softc *sc;
+
+       TRACE_ENTER(brdev);
+
+       sc = device_get_softc(brdev);
+
+       slot->sc = sc;
+       slot->dev = NULL;  /* will get real value when card is added */
+       slot->bus_busy = false;
+       slot->host.f_min = 100000; /* this should be as low as we need to go
+                                     for any card */
+       slot->host.caps = 0;
+       /* SPI mode requires 3.3V operation */
+       slot->host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340;
+
+       MMCSPI_SLOT_LOCK_INIT(slot);
+
+       TRACE_EXIT(brdev);
+}
+
+static void
+mmcspi_slot_fini(device_t brdev, struct mmcspi_slot *slot)
+{
+       TRACE_ENTER(brdev);
+
+       MMCSPI_SLOT_LOCK_DESTROY(slot);
+
+       TRACE_EXIT(brdev);
+}
+
+static void
+mmcspi_card_add(struct mmcspi_slot *slot)
+{
+       device_t brdev;
+       device_t child;
+
+       brdev = slot->sc->dev;
+
+       TRACE_ENTER(brdev);
+
+       child = device_add_child(brdev, "mmc", DEVICE_UNIT_ANY);
+
+       MMCSPI_LOCK_SLOT(slot);
+       slot->dev = child;
+       device_set_ivars(slot->dev, slot);
+       MMCSPI_UNLOCK_SLOT(slot);
+
+       device_probe_and_attach(slot->dev);
+
+       TRACE_EXIT(brdev);
+}
+
+static void
+mmcspi_card_delete(struct mmcspi_slot *slot)
+{
+       device_t brdev;
+       device_t dev;
+
+       brdev = slot->sc->dev;
+
+       TRACE_ENTER(brdev);
+
+       MMCSPI_LOCK_SLOT(slot);
+       dev = slot->dev;
+       slot->dev = NULL;
+       MMCSPI_UNLOCK_SLOT(slot);
+       device_delete_child(brdev, dev);
+
+       TRACE_EXIT(brdev);
+}
+
+static int
+mmcspi_probe(device_t dev)
+{
+
+       if (!ofw_bus_status_okay(dev))
+               return (ENXIO);
+
+       if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+               return (ENXIO);
+
+       device_set_desc(dev, "MMC SPI mode controller");
+
+       return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mmcspi_attach(device_t dev)
+{
+       struct mmcspi_softc *sc;
+       struct sysctl_ctx_list *ctx;
+       struct sysctl_oid *tree;
+       struct sysctl_oid_list *child;
+
+       TRACE_ENTER(dev);
+
+       sc = device_get_softc(dev);
+       ctx = device_get_sysctl_ctx(dev);
+       tree = device_get_sysctl_tree(dev);
+       child = SYSCTL_CHILDREN(tree);
+
+       sc->dev = dev;
+       sc->busdev = device_get_parent(dev);
+       sc->use_crc = 1;
+
+       SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "use_crc", CTLFLAG_RW,
+           &sc->use_crc, sizeof(sc->use_crc), "Enable/disable crc checking");
+
+       SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "trace_mask", CTLFLAG_RW,
+           &trace_zone_mask, sizeof(trace_zone_mask), "Bitmask for adjusting "
+           "trace messages");
+
+       mmcspi_slot_init(dev, &sc->slot);
+
+       /* XXX trigger this from card insert detection */
+       mmcspi_card_add(&sc->slot);
+
+       TRACE_EXIT(dev);
+
+       return (0);
+}
+
+static int
+mmcspi_detach(device_t dev)
+{
+       struct mmcspi_softc *sc;
+
+       TRACE_ENTER(dev);
+
+       sc = device_get_softc(dev);
+
+       /* XXX trigger this from card removal detection */
+       mmcspi_card_delete(&sc->slot);
+
+       mmcspi_slot_fini(dev, &sc->slot);
+
+       TRACE_EXIT(dev);
+
+       return (0);
+}
+
+static int
+mmcspi_suspend(device_t dev)
+{
+       int err;
+
+       TRACE_ENTER(dev);
+       err = bus_generic_suspend(dev);
+       if (err) {
+               TRACE_EXIT(dev);
+               return (err);
+       }
+       TRACE_EXIT(dev);
+
+       return (0);
+}
+
+static int
+mmcspi_resume(device_t dev)
+{
+       int err;
+
+       TRACE_ENTER(dev);
+       err = bus_generic_resume(dev);
+       if (err) {
+               TRACE_EXIT(dev);
+               return (err);
+       }
+       TRACE_EXIT(dev);
+
+       return (0);
+}
+
+static int
+mmcspi_read_ivar(device_t bus, device_t child, int which, uintptr_t *result)
+{
+       struct mmcspi_slot *slot;
+
+       TRACE_ENTER(bus);
+
+       slot = device_get_ivars(child);
+
+       switch (which) {
+       case MMCBR_IVAR_BUS_TYPE:
+               *result = bus_type_spi;
+               break;
+       case MMCBR_IVAR_BUS_MODE:
+               *result = slot->host.ios.bus_mode;
+               break;
+       case MMCBR_IVAR_BUS_WIDTH:
+               *result = slot->host.ios.bus_width;
+               break;
+       case MMCBR_IVAR_CHIP_SELECT:
+               *result = slot->host.ios.chip_select;
+               break;
+       case MMCBR_IVAR_CLOCK:
+               *result = slot->host.ios.clock;
+               break;
+       case MMCBR_IVAR_F_MIN:
+               *result = slot->host.f_min;
+               break;
+       case MMCBR_IVAR_F_MAX:
+               *result = slot->host.f_max;
+               break;
+       case MMCBR_IVAR_HOST_OCR:
+               *result = slot->host.host_ocr;
+               break;
+       case MMCBR_IVAR_MODE:
+               *result = slot->host.mode;
+               break;
+       case MMCBR_IVAR_OCR:
+               *result = slot->host.ocr;
+               break;
+       case MMCBR_IVAR_POWER_MODE:
+               *result = slot->host.ios.power_mode;
+               break;
+       case MMCBR_IVAR_VDD:
+               *result = slot->host.ios.vdd;
+               break;
+       case MMCBR_IVAR_VCCQ:
+               *result = slot->host.ios.vccq;
+               break;
+       case MMCBR_IVAR_CAPS:
+               *result = slot->host.caps;
+               break;
+       case MMCBR_IVAR_TIMING:
+               *result = slot->host.ios.timing;
+               break;
+       case MMCBR_IVAR_MAX_DATA:
+               /* seems reasonable, not dictated by anything */
+               *result = 64 * 1024;
+               break;
+       default:
+               return (EINVAL);
+       }
+
+       TRACE_EXIT(bus);
+
+       return (0);
+}
+
+static int
+mmcspi_write_ivar(device_t bus, device_t child, int which, uintptr_t value)
+{
+       struct mmcspi_slot *slot;
+
+       TRACE_ENTER(bus);
+
+       slot = device_get_ivars(child);
+
+       switch (which) {
+       default:
+               return (EINVAL);
+       case MMCBR_IVAR_BUS_MODE:
+               slot->host.ios.bus_mode = value;
+               break;
+       case MMCBR_IVAR_BUS_WIDTH:
+               slot->host.ios.bus_width = value;
+               break;
+       case MMCBR_IVAR_CLOCK:
+               slot->host.ios.clock = value;
+               break;
+       case MMCBR_IVAR_CHIP_SELECT:
+               slot->host.ios.chip_select = value;
+               break;
+       case MMCBR_IVAR_MODE:
+               slot->host.mode = value;
+               break;
+       case MMCBR_IVAR_OCR:
+               slot->host.ocr = value;
+               break;
+       case MMCBR_IVAR_POWER_MODE:
+               slot->host.ios.power_mode = value;
+               break;
+       case MMCBR_IVAR_VDD:
+               slot->host.ios.vdd = value;
+               break;
+       case MMCBR_IVAR_VCCQ:
+               slot->host.ios.vccq = value;
+               break;
+       case MMCBR_IVAR_TIMING:
+               slot->host.ios.timing = value;
+               break;
+       case MMCBR_IVAR_BUS_TYPE:
+       case MMCBR_IVAR_CAPS:
+       case MMCBR_IVAR_HOST_OCR:
+       case MMCBR_IVAR_F_MIN:
+       case MMCBR_IVAR_F_MAX:
+       case MMCBR_IVAR_MAX_DATA:
+               return (EINVAL);
+       }
+       TRACE_EXIT(bus);
+
+       return (0);
+}
+
+static unsigned int
+mmcspi_do_spi_read(device_t dev, uint8_t *data, unsigned int len)
+{
+       struct spi_command spi_cmd;
+       struct mmcspi_softc *sc;
+       int err;
+
+       TRACE_ENTER(dev);
+
+       sc = device_get_softc(dev);
+
+       spi_cmd.tx_cmd = onesbuf;
+       spi_cmd.rx_cmd = data;
+       spi_cmd.tx_cmd_sz = len;
+       spi_cmd.rx_cmd_sz = len;
+       spi_cmd.tx_data = NULL;
+       spi_cmd.rx_data = NULL;
+       spi_cmd.tx_data_sz = 0;
+       spi_cmd.rx_data_sz = 0;
+
+       err = SPIBUS_TRANSFER(sc->busdev, sc->dev, &spi_cmd);
+
+#ifdef DEBUG_RX
+       int i;
+       if (err == 0) {
+               printf("rx val: ");
+               for (i = 0; i < len; i++)
+                       printf("%x ", data[i]);
+               printf("\n");
+       }
+#endif
+
+       TRACE_EXIT(dev);
+
+       return (err ? MMC_ERR_FAILED : MMC_ERR_NONE);
+}
+
+static unsigned int
+mmcspi_do_spi_write(device_t dev, uint8_t *cmd, unsigned int cmdlen,
+    uint8_t *data, unsigned int datalen)
+{
+       struct mmcspi_softc *sc;
+       struct spi_command spi_cmd;
+       int err;
+
+       TRACE_ENTER(dev);
+
+       sc = device_get_softc(dev);
+
+       spi_cmd.tx_cmd = cmd;
+       spi_cmd.rx_cmd = junkbuf;
+       spi_cmd.tx_cmd_sz = cmdlen;
+       spi_cmd.rx_cmd_sz = cmdlen;
+       spi_cmd.tx_data = data;
+       spi_cmd.rx_data = junkbuf;
+       spi_cmd.tx_data_sz = datalen;
+       spi_cmd.rx_data_sz = datalen;
+
+       err = SPIBUS_TRANSFER(sc->busdev, sc->dev, &spi_cmd);
+
+       TRACE_EXIT(dev);
+
+       return (err ? MMC_ERR_FAILED : MMC_ERR_NONE);
+}
+
+static unsigned int
+mmcspi_wait_for_not_busy(device_t dev)
+{
+       unsigned int busy_length;
+       uint8_t pollbuf[MMCSPI_POLL_LEN];
+       struct bintime start, elapsed;
+       unsigned int err;
+       int i;
+
+       busy_length = 0;
+
+       TRACE_ENTER(dev);
+       TRACE(dev, ACTION, "waiting for not busy\n");
+
+       getbintime(&start);
+       do {
+               TRACE(dev, DETAILS, "looking for end of busy\n");
+               err = mmcspi_do_spi_read(dev, pollbuf, MMCSPI_POLL_LEN);
+               if (MMC_ERR_NONE != err) {
+                       TRACE(dev, ERROR, "spi read failed\n");
+                       TRACE_EXIT(dev);
+                       return (err);
+               }
+
+               for (i = 0; i < MMCSPI_POLL_LEN; i++) {
+                       if (pollbuf[i] != 0x00) {
+                               TRACE(dev, DETAILS,
+                                   "end of busy found at %d\n", i);
+                               break;
+                       }
+                       busy_length++;
+               }
+
+               getbintime(&elapsed);
+               bintime_sub(&elapsed, &start);
+
+               if (elapsed.sec > MMCSPI_TIMEOUT_SEC) {
+                       TRACE(dev, ERROR, "card busy timeout\n");
+                       return (MMC_ERR_TIMEOUT);
+               }
+       } while (MMCSPI_POLL_LEN == i);
+
+       TRACE(dev, RESULT, "busy for %u byte slots\n", busy_length);
+       TRACE_EXIT(dev);
+
+       return (MMC_ERR_NONE);
+}
+
+static int
+mmcspi_update_ios(device_t brdev, device_t reqdev)
+{
+       struct mmcspi_softc *sc;
+       struct mmcspi_slot *slot;
+       struct spi_command spi_cmd;
+
+       TRACE_ENTER(brdev);
+
+       sc = device_get_softc(brdev);
+       slot = device_get_ivars(reqdev);
+
+       if (power_up == slot->host.ios.power_mode) {
+               /*
+                * This sequence provides the initialization steps required
+                * by the spec after card power is applied, but before any
+                * commands are issued.  These operations are harmless if
+                * applied at any other time (after a warm reset, for
+                * example).
+                */
+
+               /*
+                * XXX Power-on portion of implementation of card power
+                * control should go here.  Should probably include a power
+                * off first to ensure card is fully reset from any previous
+                * state.
+                */
+
+               /*
+                * Make sure power to card has ramped up.  The spec requires
+                * power to ramp up in 35ms or less.
+                */
+               DELAY(35000);
+
+               /*
+                * Provide at least 74 clocks with CS and MOSI high that the
+                * spec requires after card power stabilizes.
+                */
+
+               spi_cmd.tx_cmd = onesbuf;
+               spi_cmd.tx_cmd_sz = 10;
+               spi_cmd.rx_cmd = junkbuf;
+               spi_cmd.rx_cmd_sz = 10;
+               spi_cmd.tx_data = NULL;
+               spi_cmd.rx_data = NULL;
+               spi_cmd.tx_data_sz = 0;
+               spi_cmd.rx_data_sz = 0;
+
+               SPIBUS_TRANSFER(sc->busdev, sc->dev, &spi_cmd);
+
+               /*
+                * Perhaps this was a warm reset and the card is in the
+                * middle of a long operation.
+                */
+               mmcspi_wait_for_not_busy(brdev);
+
+               slot->last_opcode = 0xffffffff;
+               slot->last_flags = 0;
+               memset(slot->last_ocr, 0, MMCSPI_OCR_LEN);
+               slot->crc_enabled = 0;
+               slot->crc_init_done = 0;
+       }
+
+       if (power_off == slot->host.ios.power_mode) {
+               /*
+                * XXX Power-off portion of implementation of card power
+                * control should go here.
+                */
+       }
+
+       TRACE_EXIT(brdev);
+
+       return (0);
+}
+
+static unsigned int
+mmcspi_shift_copy(uint8_t *dest, uint8_t *src, unsigned int dest_len,
+    unsigned int shift)
+{
+       unsigned int i;
+
+       if (0 == shift)
+               memcpy(dest, src, dest_len);
+       else {
+               for (i = 0; i < dest_len; i++) {
+                       dest[i] =
+                           (src[i] << shift) |
+                           (src[i + 1] >> (8 - shift));
+               }
+       }
+
+       return (dest_len);
+}
+
+static unsigned int
+mmcspi_get_response_token(device_t dev, uint8_t mask, uint8_t value,
+    unsigned int len, unsigned int has_busy, uint8_t *rspbuf)
+{
+       uint8_t pollbuf[2 * MMCSPI_MAX_RSP_LEN];
+       struct bintime start, elapsed;
+       boolean_t found;
+       unsigned int err;
+       unsigned int offset;
+       unsigned int shift = 0;
+       unsigned int remaining;
+       uint16_t search_space;
+       uint16_t search_mask;
+       uint16_t search_value;
+       int i;
+
+       TRACE_ENTER(dev);
+
+       /*
+        * This loop searches data clocked out of the card for a response
+        * token matching the given mask and value.  It will locate tokens
+        * that are not byte-aligned, as some cards send non-byte-aligned
+        * response tokens in some situations.  For example, the following
+        * card consistently sends an unaligned response token to the stop
+        * command used to terminate multi-block reads:
+        *
+        * Transcend 2GB SDSC card, cid:
+        * mid=0x1b oid=0x534d pnm="00000" prv=1.0 mdt=00.2000
+        */
+
+       offset = 0;
+       found = false;
+       getbintime(&start);
+       do {
+               TRACE(dev, DETAILS, "looking for response token with "
+                   "mask 0x%02x, value 0x%02x\n", mask, value);
+               err = mmcspi_do_spi_read(dev, &pollbuf[offset], len);
+               if (MMC_ERR_NONE != err) {
+                       TRACE(dev, ERROR, "spi read of resp token failed\n");
+                       TRACE_EXIT(dev);
+                       return (err);
+               }
+
+               for (i = 0; i < len + offset; i++) {
+                       if ((pollbuf[i] & mask) == value) {
+                               TRACE(dev, DETAILS, "response token found at "
+                                   "%d (0x%02x)\n", i, pollbuf[i]);
+                               shift = 0;
+                               found = true;
+                               break;
+                       } else if (i < len + offset - 1) {
+                               /*
+                                * Not the last byte in the buffer, so check
+                                * for a non-aligned response.
+                                */
+                               search_space = ((uint16_t)pollbuf[i] << 8) |
+                                   pollbuf[i + 1];
+                               search_mask  = (uint16_t)mask << 8;
+                               search_value = (uint16_t)value << 8;
+
+                               TRACE(dev, DETAILS, "search: space=0x%04x "
+                                   " mask=0x%04x val=0x%04x\n", search_space,
+                                   search_mask, search_value);
+
+                               for (shift = 1; shift < 8; shift++) {
+                                       search_space <<= 1;
+                                       if ((search_space & search_mask) ==
+                                           search_value) {
+                                               found = true;
+                                               TRACE(dev, DETAILS, "Found mat"
+                                                   "ch at shift %u\n", shift);
+                                               break;
+                                       }
+                               }
+
+                               if (shift < 8)
+                                       break;
+                       } else {
+                               /*
+                                * Move the last byte to the first position
+                                * and go 'round again.
*** 1447 LINES SKIPPED ***

Reply via email to