From nobody Wed Apr 9 11:23:26 2025
X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1])
by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4ZXgYV3Cslz5swFH;
Wed, 09 Apr 2025 11:23:26 +0000 (UTC)
(envelope-from git@FreeBSD.org)
Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3])
(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256
client-signature RSA-PSS (4096 bits) client-digest SHA256)
(Client CN "mxrelay.nyi.freebsd.org", Issuer "R10" (verified OK))
by mx1.freebsd.org (Postfix) with ESMTPS id 4ZXgYV1fPvz3TxP;
Wed, 09 Apr 2025 11:23:26 +0000 (UTC)
(envelope-from git@FreeBSD.org)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim;
t=1744197806;
h=from:from:reply-to:subject:subject:date:date:message-id:message-id:
to:to:cc:mime-version:mime-version:content-type:content-type:
content-transfer-encoding:content-transfer-encoding;
bh=Pna5GvGQkRo5GyJn6YpBaBk07NePtP3I9xL9NUmvlq0=;
b=x7QKm5saewBV0ScczeDgoF0ed1ha4uWRzyl1HItEqk6p6vw9YpAzrQQayFHTe3SakwVbMR
9dob+V4jmuBvvg8tWls/nofNcrM/K21NJ591qShIWbup66t5+lQ44QTAt/rUPO0NO0EszI
CJ7sJIW/s0b9Q08eRlZRHJP8v3CQyi7PnRP6wAIO5ze06eEksBXU4dzPSwngY7upRiMGqB
7i17NA1VVAsizq5o8Dqw3dxxMxUAn/Gm9IgW2aZqJkM8i8Qa2BDMTzRhzVEQrmcr4en02K
wUURF5J5Bq5UwAPbnhXjC9YVTiTML8Dhfj+7C7VbPuQOCpUtoHSYcQUFspanFw==
ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1744197806; a=rsa-sha256; cv=none;
b=CuZ7tqOrYMql5lKgkAiFgbyza0ywcHxxVGc1hrNb3rjd3dPJbegMCZaX6TMagf6MZZ2VBc
aRgn4d6RacG9wubRzkU/+jxyN59+QENwx1C4g4GcGaVD+AjxhE9Z/GdYtDGatEG8Odt2D4
90NKUr98IpHsaTLCIN2xf6kxPEXVPCuIrIulU56F6Tz/LRBeFnw2GnKArfCdlEXldaizF5
Syp9AEOZ6EeIRFpS9dXVj5mpOKPN9mCPETlNjKsaVEYTc9HmGQ7LvFV0Jtks0yvAavBJ2D
PFTZurENC99OvRi+raps8eooS60I9iSeVrl/OqXaAT5nQwWXkIw1nTt7T8apNw==
ARC-Authentication-Results: i=1;
mx1.freebsd.org;
none
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org;
s=dkim; t=1744197806;
h=from:from:reply-to:subject:subject:date:date:message-id:message-id:
to:to:cc:mime-version:mime-version:content-type:content-type:
content-transfer-encoding:content-transfer-encoding;
bh=Pna5GvGQkRo5GyJn6YpBaBk07NePtP3I9xL9NUmvlq0=;
b=UGR4spTJ451tricRa52dpOKnupO1xggkELW4//shk96F/FmEB4FJwOOMA1zZDKZ6gyNo3X
rO7vRS4l/48YmZTJXICN1DC9tLjuq7QB8xTtI3yvqC9+JXAjRY1jARBeIU7Ox9ciEgMY8N
tZuPBnUjmVW31aOKBu+vCMPcTzaD4rlQvc22M5orub1tRnfrLUG9gDdbmiHGrtgLorLzmi
oUbuSKERSWKevMtMXQOimiqfa123PEQN/SFkKCS4yezW2vo50g9wwsY4DcYVE+ItE1FlPK
EXnOEaFcIHDetLzI6+VPaQ9Z+D8iXhYzz7dZQg5/16Wp6gQEP9LxJeo7oq6PSQ==
Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5])
(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256)
(Client did not present a certificate)
by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4ZXgYV14tvzVk;
Wed, 09 Apr 2025 11:23:26 +0000 (UTC)
(envelope-from git@FreeBSD.org)
Received: from gitrepo.freebsd.org ([127.0.1.44])
by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 539BNQHL065935;
Wed, 9 Apr 2025 11:23:26 GMT
(envelope-from git@gitrepo.freebsd.org)
Received: (from git@localhost)
by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 539BNQRJ065932;
Wed, 9 Apr 2025 11:23:26 GMT
(envelope-from git)
Date: Wed, 9 Apr 2025 11:23:26 GMT
Message-Id: <202504091123.539BNQRJ065932@gitrepo.freebsd.org>
To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org,
dev-commits-src-main@FreeBSD.org
From: Ruslan Bukin
Subject: git: 253c83058deb - main - mmc: SPI-mode support for SD
cards.
List-Id: Commit messages for all branches of the src repository
List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all
List-Help:
List-Post:
List-Subscribe:
List-Unsubscribe:
X-BeenThere: dev-commits-src-all@freebsd.org
Sender: owner-dev-commits-src-all@FreeBSD.org
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
X-Git-Committer: br
X-Git-Repository: src
X-Git-Refname: refs/heads/main
X-Git-Reftype: branch
X-Git-Commit: 253c83058deb5ff77dc0a3b60bf7c1d10f9ef5e8
Auto-Submitted: auto-generated
The branch main has been updated by br:
URL: https://cgit.FreeBSD.org/src/commit/?id=253c83058deb5ff77dc0a3b60bf7c1d10f9ef5e8
commit 253c83058deb5ff77dc0a3b60bf7c1d10f9ef5e8
Author: Ruslan Bukin
AuthorDate: 2025-04-09 10:23:48 +0000
Commit: Ruslan Bukin
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
+ *
+ * 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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#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 ***