aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorWarner Losh <imp@FreeBSD.org>2017-07-09 16:57:24 +0000
committerWarner Losh <imp@FreeBSD.org>2017-07-09 16:57:24 +0000
commita94a63f0a6bc1ec25bf0d162e1dd9a53e020d176 (patch)
treedb10b0d42719099754162cafd389390c6841284e /sys
parent7683ad70d31f5502e20d0b15fba1d01bbd2e0992 (diff)
Notes
Diffstat (limited to 'sys')
-rw-r--r--sys/amd64/conf/MMCCAM36
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_sdhci.c6
-rw-r--r--sys/arm/conf/BEAGLEBONE-MMCCAM21
-rw-r--r--sys/arm/ti/ti_sdhci.c33
-rw-r--r--sys/cam/cam_ccb.h78
-rw-r--r--sys/cam/cam_periph.c12
-rw-r--r--sys/cam/cam_xpt.c21
-rw-r--r--sys/cam/cam_xpt.h2
-rw-r--r--sys/cam/cam_xpt_internal.h1
-rw-r--r--sys/cam/mmc/mmc.h94
-rw-r--r--sys/cam/mmc/mmc_all.h70
-rw-r--r--sys/cam/mmc/mmc_bus.h5
-rw-r--r--sys/cam/mmc/mmc_da.c1432
-rw-r--r--sys/cam/mmc/mmc_sdio.c126
-rw-r--r--sys/cam/mmc/mmc_sdio.h64
-rw-r--r--sys/cam/mmc/mmc_xpt.c1077
-rw-r--r--sys/cam/scsi/scsi_pass.c2
-rw-r--r--sys/conf/files10
-rw-r--r--sys/conf/options2
-rw-r--r--sys/dev/mmc/bridge.h2
-rw-r--r--sys/dev/mmc/mmcbrvar.h1
-rw-r--r--sys/dev/mmc/mmcreg.h102
-rw-r--r--sys/dev/sdhci/fsl_sdhci.c9
-rw-r--r--sys/dev/sdhci/sdhci.c519
-rw-r--r--sys/dev/sdhci/sdhci.h9
-rw-r--r--sys/dev/sdhci/sdhci_acpi.c3
-rw-r--r--sys/dev/sdhci/sdhci_pci.c12
-rw-r--r--sys/modules/Makefile2
28 files changed, 3727 insertions, 24 deletions
diff --git a/sys/amd64/conf/MMCCAM b/sys/amd64/conf/MMCCAM
new file mode 100644
index 0000000000000..c8bbeb817f5c3
--- /dev/null
+++ b/sys/amd64/conf/MMCCAM
@@ -0,0 +1,36 @@
+# MMCCAM is the kernel config for doing MMC on CAM development
+# and testing on bhyve
+# $FreeBSD$
+
+include MINIMAL
+
+ident MMCCAM
+
+# Access GPT-formatted and labeled root volume
+options GEOM_PART_GPT
+options GEOM_LABEL
+
+# UART -- for bhyve console
+device uart
+
+# kgdb stub
+device bvmdebug
+
+# VirtIO support, needed for bhyve
+device virtio # Generic VirtIO bus (required)
+device virtio_pci # VirtIO PCI device
+device vtnet # VirtIO Ethernet device
+device virtio_blk # VirtIO Block device
+device virtio_scsi # VirtIO SCSI device
+device virtio_balloon # VirtIO Memory Balloon device
+
+# CAM-specific stuff
+device pass
+device scbus
+device da
+device mmccam
+
+options MMCCAM
+# Add CAMDEBUG stuff
+options CAMDEBUG
+options CAM_DEBUG_FLAGS=(CAM_DEBUG_INFO|CAM_DEBUG_PROBE|CAM_DEBUG_PERIPH|CAM_DEBUG_TRACE)
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c b/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c
index 1954b7d40a60b..0471d42ad4529 100644
--- a/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c
+++ b/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c
@@ -52,6 +52,8 @@ __FBSDID("$FreeBSD$");
#include "mmcbr_if.h"
#include "sdhci_if.h"
+#include "opt_mmccam.h"
+
#include "bcm2835_dma.h"
#include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
#include "bcm2835_vcbus.h"
@@ -253,7 +255,11 @@ bcm_sdhci_attach(device_t dev)
bus_generic_probe(dev);
bus_generic_attach(dev);
+#ifdef MMCCAM
+ sdhci_cam_start_slot(&sc->sc_slot);
+#else
sdhci_start_slot(&sc->sc_slot);
+#endif
return (0);
diff --git a/sys/arm/conf/BEAGLEBONE-MMCCAM b/sys/arm/conf/BEAGLEBONE-MMCCAM
new file mode 100644
index 0000000000000..3d83352e79c06
--- /dev/null
+++ b/sys/arm/conf/BEAGLEBONE-MMCCAM
@@ -0,0 +1,21 @@
+#
+# BEAGLEBONE-MMCCAM
+#
+# Custom kernel for Beaglebone plus MMCCAM as opposed to the prior MMC stack. It is
+# present to keep it building in tree since it wouldn't work in LINT.
+#
+# $FreeBSD$
+
+include BEAGLEBONE
+
+# Add CAMDEBUG stuff
+options CAMDEBUG
+options CAM_DEBUG_FLAGS=(CAM_DEBUG_INFO|CAM_DEBUG_PROBE|CAM_DEBUG_PERIPH|CAM_DEBUG_TRACE)
+
+# pass(4) device
+device pass
+device mmccam
+options MMCCAM
+
+nodevice mmc
+nodevice mmcsd
diff --git a/sys/arm/ti/ti_sdhci.c b/sys/arm/ti/ti_sdhci.c
index 6b188ddd0728e..00c2fcfa49f25 100644
--- a/sys/arm/ti/ti_sdhci.c
+++ b/sys/arm/ti/ti_sdhci.c
@@ -39,6 +39,8 @@ __FBSDID("$FreeBSD$");
#include <sys/rman.h>
#include <sys/sysctl.h>
#include <sys/taskqueue.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
#include <machine/bus.h>
#include <machine/resource.h>
@@ -60,6 +62,8 @@ __FBSDID("$FreeBSD$");
#include <arm/ti/ti_hwmods.h>
#include "gpio_if.h"
+#include "opt_mmccam.h"
+
struct ti_sdhci_softc {
device_t dev;
struct sdhci_fdt_gpio * gpio;
@@ -122,6 +126,11 @@ static struct ofw_compat_data compat_data[] = {
#define MMCHS_SD_CAPA_VS30 (1 << 25)
#define MMCHS_SD_CAPA_VS33 (1 << 24)
+/* Forward declarations, CAM-relataed */
+// static void ti_sdhci_cam_poll(struct cam_sim *);
+// static void ti_sdhci_cam_action(struct cam_sim *, union ccb *);
+// static int ti_sdhci_cam_settran_settings(struct ti_sdhci_softc *sc, union ccb *);
+
static inline uint32_t
ti_mmchs_read_4(struct ti_sdhci_softc *sc, bus_size_t off)
{
@@ -241,6 +250,22 @@ ti_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off,
struct ti_sdhci_softc *sc = device_get_softc(dev);
uint32_t val32;
+#ifdef MMCCAM
+ uint32_t newval32;
+ if (off == SDHCI_HOST_CONTROL) {
+ val32 = ti_mmchs_read_4(sc, MMCHS_CON);
+ newval32 = val32;
+ if (val & SDHCI_CTRL_8BITBUS) {
+ device_printf(dev, "Custom-enabling 8-bit bus\n");
+ newval32 |= MMCHS_CON_DW8;
+ } else {
+ device_printf(dev, "Custom-disabling 8-bit bus\n");
+ newval32 &= ~MMCHS_CON_DW8;
+ }
+ if (newval32 != val32)
+ ti_mmchs_write_4(sc, MMCHS_CON, newval32);
+ }
+#endif
val32 = RD4(sc, off & ~3);
val32 &= ~(0xff << (off & 3) * 8);
val32 |= (val << (off & 3) * 8);
@@ -658,8 +683,11 @@ ti_sdhci_attach(device_t dev)
bus_generic_probe(dev);
bus_generic_attach(dev);
+#ifdef MMCCAM
+ sdhci_cam_start_slot(&sc->slot);
+#else
sdhci_start_slot(&sc->slot);
-
+#endif
return (0);
fail:
@@ -730,4 +758,7 @@ static driver_t ti_sdhci_driver = {
DRIVER_MODULE(sdhci_ti, simplebus, ti_sdhci_driver, ti_sdhci_devclass, NULL,
NULL);
MODULE_DEPEND(sdhci_ti, sdhci, 1, 1, 1);
+
+#ifndef MMCCAM
MMC_DECLARE_BRIDGE(sdhci_ti);
+#endif
diff --git a/sys/cam/cam_ccb.h b/sys/cam/cam_ccb.h
index bf9bb7a97692c..07e5dd8825c08 100644
--- a/sys/cam/cam_ccb.h
+++ b/sys/cam/cam_ccb.h
@@ -42,6 +42,7 @@
#include <cam/scsi/scsi_all.h>
#include <cam/ata/ata_all.h>
#include <cam/nvme/nvme_all.h>
+#include <cam/mmc/mmc_all.h>
/* General allocation length definitions for CCB structures */
#define IOCDBLEN CAM_MAX_CDBLEN /* Space for CDB bytes/pointer */
@@ -208,10 +209,10 @@ typedef enum {
XPT_NVME_IO = 0x1c | XPT_FC_DEV_QUEUED,
/* Execiute the requestred NVMe I/O operation */
- XPT_MMCSD_IO = 0x1d | XPT_FC_DEV_QUEUED,
+ XPT_MMC_IO = 0x1d | XPT_FC_DEV_QUEUED,
/* Placeholder for MMC / SD / SDIO I/O stuff */
- XPT_SCAN_TGT = 0x1E | XPT_FC_QUEUED | XPT_FC_USER_CCB
+ XPT_SCAN_TGT = 0x1e | XPT_FC_QUEUED | XPT_FC_USER_CCB
| XPT_FC_XPT_ONLY,
/* Scan Target */
@@ -267,6 +268,7 @@ typedef enum {
PROTO_SATAPM, /* SATA Port Multiplier */
PROTO_SEMB, /* SATA Enclosure Management Bridge */
PROTO_NVME, /* NVME */
+ PROTO_MMCSD, /* MMC, SD, SDIO */
} cam_proto;
typedef enum {
@@ -283,6 +285,7 @@ typedef enum {
XPORT_ISCSI, /* iSCSI */
XPORT_SRP, /* SCSI RDMA Protocol */
XPORT_NVME, /* NVMe over PCIe */
+ XPORT_MMCSD, /* MMC, SD, SDIO card */
} cam_xport;
#define XPORT_IS_NVME(t) ((t) == XPORT_NVME)
@@ -498,6 +501,7 @@ struct device_match_result {
cam_proto protocol;
struct scsi_inquiry_data inq_data;
struct ata_params ident_data;
+ struct mmc_params mmc_ident_data;
dev_result_flags flags;
};
@@ -773,6 +777,16 @@ struct ccb_ataio {
uint32_t unused;
};
+/*
+ * MMC I/O Request CCB used for the XPT_MMC_IO function code.
+ */
+struct ccb_mmcio {
+ struct ccb_hdr ccb_h;
+ union ccb *next_ccb; /* Ptr for next CCB for action */
+ struct mmc_command cmd;
+ struct mmc_command stop;
+};
+
struct ccb_accept_tio {
struct ccb_hdr ccb_h;
cdb_t cdb_io; /* Union for CDB bytes/pointer */
@@ -1005,7 +1019,28 @@ struct ccb_trans_settings_nvme
u_int max_xfer; /* Max transfer size (0 -> unlimited */
u_int caps;
};
-
+
+#include <cam/mmc/mmc_bus.h>
+struct ccb_trans_settings_mmc {
+ struct mmc_ios ios;
+#define MMC_CLK (1 << 1)
+#define MMC_VDD (1 << 2)
+#define MMC_CS (1 << 3)
+#define MMC_BW (1 << 4)
+#define MMC_PM (1 << 5)
+#define MMC_BT (1 << 6)
+#define MMC_BM (1 << 7)
+ uint32_t ios_valid;
+/* The folowing is used only for GET_TRAN_SETTINGS */
+ uint32_t host_ocr;
+ int host_f_min;
+ int host_f_max;
+#define MMC_CAP_4_BIT_DATA (1 << 0) /* Can do 4-bit data transfers */
+#define MMC_CAP_8_BIT_DATA (1 << 1) /* Can do 8-bit data transfers */
+#define MMC_CAP_HSPEED (1 << 2) /* Can do High Speed transfers */
+ uint32_t host_caps;
+};
+
/* Get/Set transfer rate/width/disconnection/tag queueing settings */
struct ccb_trans_settings {
struct ccb_hdr ccb_h;
@@ -1019,6 +1054,7 @@ struct ccb_trans_settings {
struct ccb_trans_settings_ata ata;
struct ccb_trans_settings_scsi scsi;
struct ccb_trans_settings_nvme nvme;
+ struct ccb_trans_settings_mmc mmc;
} proto_specific;
union {
u_int valid; /* Which fields to honor */
@@ -1284,6 +1320,7 @@ union ccb {
struct ccb_dev_advinfo cdai;
struct ccb_async casync;
struct ccb_nvmeio nvmeio;
+ struct ccb_mmcio mmcio;
};
#define CCB_CLEAR_ALL_EXCEPT_HDR(ccbp) \
@@ -1327,6 +1364,13 @@ cam_fill_smpio(struct ccb_smpio *smpio, uint32_t retries,
uint32_t timeout);
static __inline void
+cam_fill_mmcio(struct ccb_mmcio *mmcio, uint32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *), uint32_t flags,
+ uint32_t mmc_opcode, uint32_t mmc_arg, uint32_t mmc_flags,
+ struct mmc_data *mmc_d,
+ uint32_t timeout);
+
+static __inline void
cam_fill_csio(struct ccb_scsiio *csio, u_int32_t retries,
void (*cbfcnp)(struct cam_periph *, union ccb *),
u_int32_t flags, u_int8_t tag_action,
@@ -1415,6 +1459,34 @@ cam_fill_smpio(struct ccb_smpio *smpio, uint32_t retries,
}
static __inline void
+cam_fill_mmcio(struct ccb_mmcio *mmcio, uint32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *), uint32_t flags,
+ uint32_t mmc_opcode, uint32_t mmc_arg, uint32_t mmc_flags,
+ struct mmc_data *mmc_d,
+ uint32_t timeout)
+{
+ mmcio->ccb_h.func_code = XPT_MMC_IO;
+ mmcio->ccb_h.flags = flags;
+ mmcio->ccb_h.retry_count = retries;
+ mmcio->ccb_h.cbfcnp = cbfcnp;
+ mmcio->ccb_h.timeout = timeout;
+ mmcio->cmd.opcode = mmc_opcode;
+ mmcio->cmd.arg = mmc_arg;
+ mmcio->cmd.flags = mmc_flags;
+ mmcio->stop.opcode = 0;
+ mmcio->stop.arg = 0;
+ mmcio->stop.flags = 0;
+ if (mmc_d != NULL) {
+ mmcio->cmd.data = mmc_d;
+ } else
+ mmcio->cmd.data = NULL;
+ mmcio->cmd.resp[0] = 0;
+ mmcio->cmd.resp[1] = 0;
+ mmcio->cmd.resp[2] = 0;
+ mmcio->cmd.resp[3] = 0;
+}
+
+static __inline void
cam_set_ccbstatus(union ccb *ccb, cam_status status)
{
ccb->ccb_h.status &= ~CAM_STATUS_MASK;
diff --git a/sys/cam/cam_periph.c b/sys/cam/cam_periph.c
index 2c4c9cf9e27b3..35d2f5e74bd1d 100644
--- a/sys/cam/cam_periph.c
+++ b/sys/cam/cam_periph.c
@@ -827,6 +827,18 @@ cam_periph_mapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo,
dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK;
numbufs = 1;
break;
+ case XPT_MMC_IO:
+ if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE)
+ return(0);
+ /* Two mappings: one for cmd->data and one for cmd->data->data */
+ data_ptrs[0] = (unsigned char **)&ccb->mmcio.cmd.data;
+ lengths[0] = sizeof(struct mmc_data *);
+ dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK;
+ data_ptrs[1] = (unsigned char **)&ccb->mmcio.cmd.data->data;
+ lengths[1] = ccb->mmcio.cmd.data->len;
+ dirs[1] = ccb->ccb_h.flags & CAM_DIR_MASK;
+ numbufs = 2;
+ break;
case XPT_SMP_IO:
data_ptrs[0] = &ccb->smpio.smp_request;
lengths[0] = ccb->smpio.smp_request_len;
diff --git a/sys/cam/cam_xpt.c b/sys/cam/cam_xpt.c
index a848c3eee3d7d..2055d0c3f3f3e 100644
--- a/sys/cam/cam_xpt.c
+++ b/sys/cam/cam_xpt.c
@@ -329,7 +329,6 @@ static xpt_devicefunc_t xptsetasyncfunc;
static xpt_busfunc_t xptsetasyncbusfunc;
static cam_status xptregister(struct cam_periph *periph,
void *arg);
-static const char * xpt_action_name(uint32_t action);
static __inline int device_is_queued(struct cam_ed *device);
static __inline int
@@ -412,7 +411,7 @@ xptioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td
}
return (error);
}
-
+
static int
xptdoioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
{
@@ -820,6 +819,8 @@ xpt_scanner_thread(void *dummy)
TAILQ_REMOVE(&xsoftc.ccb_scanq, &ccb->ccb_h, sim_links.tqe);
xpt_unlock_buses();
+ printf("xpt_scanner_thread is firing on path ");
+ xpt_print_path(ccb->ccb_h.path);printf("\n");
/*
* Since lock can be dropped inside and path freed
* by completion callback even before return here,
@@ -1503,7 +1504,7 @@ xptdevicematch(struct dev_match_pattern *patterns, u_int num_patterns,
cur_pattern = &patterns[i].pattern.device_pattern;
- /* Error out if mutually exclusive options are specified. */
+ /* Error out if mutually exclusive options are specified. */
if ((cur_pattern->flags & (DEV_MATCH_INQUIRY|DEV_MATCH_DEVID))
== (DEV_MATCH_INQUIRY|DEV_MATCH_DEVID))
return(DM_RET_ERROR);
@@ -1905,6 +1906,9 @@ xptedtdevicefunc(struct cam_ed *device, void *arg)
bcopy(&device->ident_data,
&cdm->matches[j].result.device_result.ident_data,
sizeof(struct ata_params));
+ bcopy(&device->mmc_ident_data,
+ &cdm->matches[j].result.device_result.mmc_ident_data,
+ sizeof(struct mmc_params));
/* Let the user know whether this device is unconfigured */
if (device->flags & CAM_DEV_UNCONFIGURED)
@@ -2690,6 +2694,8 @@ xpt_action_default(union ccb *start_ccb)
if (start_ccb->ccb_h.func_code == XPT_NVME_IO)
start_ccb->nvmeio.resid = 0;
/* FALLTHROUGH */
+ case XPT_MMC_IO:
+ /* XXX just like nmve_io? */
case XPT_RESET_DEV:
case XPT_ENG_EXEC:
case XPT_SMP_IO:
@@ -2801,11 +2807,12 @@ call_sim:
mtx_lock(mtx);
else
mtx = NULL;
+
CAM_DEBUG(path, CAM_DEBUG_TRACE,
- ("sim->sim_action: func=%#x\n", start_ccb->ccb_h.func_code));
+ ("Calling sim->sim_action(): func=%#x\n", start_ccb->ccb_h.func_code));
(*(sim->sim_action))(sim, start_ccb);
CAM_DEBUG(path, CAM_DEBUG_TRACE,
- ("sim->sim_action: status=%#x\n", start_ccb->ccb_h.status));
+ ("sim->sim_action returned: status=%#x\n", start_ccb->ccb_h.status));
if (mtx)
mtx_unlock(mtx);
break;
@@ -5540,7 +5547,7 @@ static struct kv map[] = {
{ XPT_GET_SIM_KNOB, "XPT_GET_SIM_KNOB" },
{ XPT_SET_SIM_KNOB, "XPT_SET_SIM_KNOB" },
{ XPT_NVME_IO, "XPT_NVME_IO" },
- { XPT_MMCSD_IO, "XPT_MMCSD_IO" },
+ { XPT_MMC_IO, "XPT_MMC_IO" },
{ XPT_SMP_IO, "XPT_SMP_IO" },
{ XPT_SCAN_TGT, "XPT_SCAN_TGT" },
{ XPT_ENG_INQ, "XPT_ENG_INQ" },
@@ -5556,7 +5563,7 @@ static struct kv map[] = {
{ 0, 0 }
};
-static const char *
+const char *
xpt_action_name(uint32_t action)
{
static char buffer[32]; /* Only for unknown messages -- racy */
diff --git a/sys/cam/cam_xpt.h b/sys/cam/cam_xpt.h
index 8e6027e564471..47fdbd748b2d8 100644
--- a/sys/cam/cam_xpt.h
+++ b/sys/cam/cam_xpt.h
@@ -141,6 +141,8 @@ void xpt_copy_path(struct cam_path *new_path,
void xpt_release_path(struct cam_path *path);
+const char * xpt_action_name(uint32_t action);
+
#endif /* _KERNEL */
#endif /* _CAM_CAM_XPT_H */
diff --git a/sys/cam/cam_xpt_internal.h b/sys/cam/cam_xpt_internal.h
index b33b4e7a25f62..cdc11c83abc74 100644
--- a/sys/cam/cam_xpt_internal.h
+++ b/sys/cam/cam_xpt_internal.h
@@ -125,6 +125,7 @@ struct cam_ed {
uint32_t rcap_len;
uint8_t *rcap_buf;
struct ata_params ident_data;
+ struct mmc_params mmc_ident_data;
u_int8_t inq_flags; /*
* Current settings for inquiry flags.
* This allows us to override settings
diff --git a/sys/cam/mmc/mmc.h b/sys/cam/mmc/mmc.h
new file mode 100644
index 0000000000000..9fae837e9e037
--- /dev/null
+++ b/sys/cam/mmc/mmc.h
@@ -0,0 +1,94 @@
+/*-
+ * Copyright (c) 2014-2016 Ilya Bakulin. 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. 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 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.
+ *
+ * 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.
+ *
+ * Inspired coded in sys/dev/mmc. Thanks to Warner Losh <imp@FreeBSD.org>,
+ * Bernd Walter <tisco@FreeBSD.org>, and other authors.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef CAM_MMC_H
+#define CAM_MMC_H
+
+#include <dev/mmc/mmcreg.h>
+/*
+ * This structure describes an MMC/SD card
+ */
+struct mmc_params {
+ u_int8_t model[40]; /* Card model */
+
+ /* Card OCR */
+ uint32_t card_ocr;
+
+ /* OCR of the IO portion of the card */
+ uint32_t io_ocr;
+
+ /* Card CID -- raw and parsed */
+ uint32_t card_cid[4];
+ struct mmc_cid cid;
+
+ /* Card CSD -- raw */
+ uint32_t card_csd[4];
+
+ /* Card RCA */
+ uint16_t card_rca;
+
+ /* What kind of card is it */
+ uint32_t card_features;
+#define CARD_FEATURE_MEMORY 0x1
+#define CARD_FEATURE_SDHC 0x1 << 1
+#define CARD_FEATURE_SDIO 0x1 << 2
+#define CARD_FEATURE_SD20 0x1 << 3
+#define CARD_FEATURE_MMC 0x1 << 4
+
+ uint8_t sdio_func_count;
+} __packed;
+
+#endif
diff --git a/sys/cam/mmc/mmc_all.h b/sys/cam/mmc/mmc_all.h
new file mode 100644
index 0000000000000..c2494894ca2eb
--- /dev/null
+++ b/sys/cam/mmc/mmc_all.h
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 2014-2016 Ilya Bakulin. 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. 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 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * MMC function that should be visible to the CAM subsystem
+ * and are somehow useful should be declared here
+ *
+ * Like in other *_all.h, it's also a nice place to include
+ * some other transport-specific headers.
+ */
+
+#ifndef CAM_MMC_ALL_H
+#define CAM_MMC_ALL_H
+
+#include <cam/mmc/mmc.h>
+#include <dev/mmc/mmcreg.h>
+
+void mmc_print_ident(struct mmc_params *ident_data);
+
+#endif
diff --git a/sys/cam/mmc/mmc_bus.h b/sys/cam/mmc/mmc_bus.h
new file mode 100644
index 0000000000000..db77da5107884
--- /dev/null
+++ b/sys/cam/mmc/mmc_bus.h
@@ -0,0 +1,5 @@
+/*
+ * This file is in the public domain.
+ * $FreeBSD$
+ */
+#include <dev/mmc/bridge.h>
diff --git a/sys/cam/mmc/mmc_da.c b/sys/cam/mmc/mmc_da.c
new file mode 100644
index 0000000000000..31bbf000e14c3
--- /dev/null
+++ b/sys/cam/mmc/mmc_da.c
@@ -0,0 +1,1432 @@
+/*-
+ * Copyright (c) 2006 Bernd Walter <tisco@FreeBSD.org>
+ * Copyright (c) 2006 M. Warner Losh <imp@FreeBSD.org>
+ * Copyright (c) 2009 Alexander Motin <mav@FreeBSD.org>
+ * Copyright (c) 2015-2017 Ilya Bakulin <kibab@FreeBSD.org>
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 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 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.
+ *
+ * Some code derived from the sys/dev/mmc and sys/cam/ata
+ * Thanks to Warner Losh <imp@FreeBSD.org>, Alexander Motin <mav@FreeBSD.org>
+ * Bernd Walter <tisco@FreeBSD.org>, and other authors.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+//#include "opt_sdda.h"
+
+#include <sys/param.h>
+
+#ifdef _KERNEL
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bio.h>
+#include <sys/endian.h>
+#include <sys/taskqueue.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/conf.h>
+#include <sys/devicestat.h>
+#include <sys/eventhandler.h>
+#include <sys/malloc.h>
+#include <sys/cons.h>
+#include <sys/proc.h>
+#include <sys/reboot.h>
+#include <geom/geom_disk.h>
+#include <machine/_inttypes.h> /* for PRIu64 */
+#endif /* _KERNEL */
+
+#ifndef _KERNEL
+#include <stdio.h>
+#include <string.h>
+#endif /* _KERNEL */
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_queue.h>
+#include <cam/cam_periph.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_xpt_periph.h>
+#include <cam/cam_xpt_internal.h>
+#include <cam/cam_debug.h>
+
+
+#include <cam/mmc/mmc_all.h>
+
+#include <machine/md_var.h> /* geometry translation */
+
+#ifdef _KERNEL
+
+typedef enum {
+ SDDA_FLAG_OPEN = 0x0002,
+ SDDA_FLAG_DIRTY = 0x0004
+} sdda_flags;
+
+typedef enum {
+ SDDA_STATE_INIT,
+ SDDA_STATE_INVALID,
+ SDDA_STATE_NORMAL
+} sdda_state;
+
+struct sdda_softc {
+ struct bio_queue_head bio_queue;
+ int outstanding_cmds; /* Number of active commands */
+ int refcount; /* Active xpt_action() calls */
+ sdda_state state;
+ sdda_flags flags;
+ struct mmc_data *mmcdata;
+// sdda_quirks quirks;
+ struct task start_init_task;
+ struct disk *disk;
+ uint32_t raw_csd[4];
+ uint8_t raw_ext_csd[512]; /* MMC only? */
+ struct mmc_csd csd;
+ struct mmc_cid cid;
+ struct mmc_scr scr;
+ /* Calculated from CSD */
+ uint64_t sector_count;
+ uint64_t mediasize;
+
+ /* Calculated from CID */
+ char card_id_string[64];/* Formatted CID info (serial, MFG, etc) */
+ char card_sn_string[16];/* Formatted serial # for disk->d_ident */
+ /* Determined from CSD + is highspeed card*/
+ uint32_t card_f_max;
+};
+
+#define ccb_bp ppriv_ptr1
+
+static disk_strategy_t sddastrategy;
+static periph_init_t sddainit;
+static void sddaasync(void *callback_arg, u_int32_t code,
+ struct cam_path *path, void *arg);
+static periph_ctor_t sddaregister;
+static periph_dtor_t sddacleanup;
+static periph_start_t sddastart;
+static periph_oninv_t sddaoninvalidate;
+static void sddadone(struct cam_periph *periph,
+ union ccb *done_ccb);
+static int sddaerror(union ccb *ccb, u_int32_t cam_flags,
+ u_int32_t sense_flags);
+
+static uint16_t get_rca(struct cam_periph *periph);
+static cam_status sdda_hook_into_geom(struct cam_periph *periph);
+static void sdda_start_init(void *context, union ccb *start_ccb);
+static void sdda_start_init_task(void *context, int pending);
+
+static struct periph_driver sddadriver =
+{
+ sddainit, "sdda",
+ TAILQ_HEAD_INITIALIZER(sddadriver.units), /* generation */ 0
+};
+
+PERIPHDRIVER_DECLARE(sdda, sddadriver);
+
+static MALLOC_DEFINE(M_SDDA, "sd_da", "sd_da buffers");
+
+static const int exp[8] = {
+ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000
+};
+
+static const int mant[16] = {
+ 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80
+};
+
+static const int cur_min[8] = {
+ 500, 1000, 5000, 10000, 25000, 35000, 60000, 100000
+};
+
+static const int cur_max[8] = {
+ 1000, 5000, 10000, 25000, 35000, 45000, 800000, 200000
+};
+
+static uint16_t
+get_rca(struct cam_periph *periph) {
+ return periph->path->device->mmc_ident_data.card_rca;
+}
+
+static uint32_t
+mmc_get_bits(uint32_t *bits, int bit_len, int start, int size)
+{
+ const int i = (bit_len / 32) - (start / 32) - 1;
+ const int shift = start & 31;
+ uint32_t retval = bits[i] >> shift;
+ if (size + shift > 32)
+ retval |= bits[i - 1] << (32 - shift);
+ return (retval & ((1llu << size) - 1));
+}
+
+
+static void
+mmc_decode_csd_sd(uint32_t *raw_csd, struct mmc_csd *csd)
+{
+ int v;
+ int m;
+ int e;
+
+ memset(csd, 0, sizeof(*csd));
+ csd->csd_structure = v = mmc_get_bits(raw_csd, 128, 126, 2);
+ if (v == 0) {
+ m = mmc_get_bits(raw_csd, 128, 115, 4);
+ e = mmc_get_bits(raw_csd, 128, 112, 3);
+ csd->tacc = (exp[e] * mant[m] + 9) / 10;
+ csd->nsac = mmc_get_bits(raw_csd, 128, 104, 8) * 100;
+ m = mmc_get_bits(raw_csd, 128, 99, 4);
+ e = mmc_get_bits(raw_csd, 128, 96, 3);
+ csd->tran_speed = exp[e] * 10000 * mant[m];
+ csd->ccc = mmc_get_bits(raw_csd, 128, 84, 12);
+ csd->read_bl_len = 1 << mmc_get_bits(raw_csd, 128, 80, 4);
+ csd->read_bl_partial = mmc_get_bits(raw_csd, 128, 79, 1);
+ csd->write_blk_misalign = mmc_get_bits(raw_csd, 128, 78, 1);
+ csd->read_blk_misalign = mmc_get_bits(raw_csd, 128, 77, 1);
+ csd->dsr_imp = mmc_get_bits(raw_csd, 128, 76, 1);
+ csd->vdd_r_curr_min = cur_min[mmc_get_bits(raw_csd, 128, 59, 3)];
+ csd->vdd_r_curr_max = cur_max[mmc_get_bits(raw_csd, 128, 56, 3)];
+ csd->vdd_w_curr_min = cur_min[mmc_get_bits(raw_csd, 128, 53, 3)];
+ csd->vdd_w_curr_max = cur_max[mmc_get_bits(raw_csd, 128, 50, 3)];
+ m = mmc_get_bits(raw_csd, 128, 62, 12);
+ e = mmc_get_bits(raw_csd, 128, 47, 3);
+ csd->capacity = ((1 + m) << (e + 2)) * csd->read_bl_len;
+ csd->erase_blk_en = mmc_get_bits(raw_csd, 128, 46, 1);
+ csd->erase_sector = mmc_get_bits(raw_csd, 128, 39, 7) + 1;
+ csd->wp_grp_size = mmc_get_bits(raw_csd, 128, 32, 7);
+ csd->wp_grp_enable = mmc_get_bits(raw_csd, 128, 31, 1);
+ csd->r2w_factor = 1 << mmc_get_bits(raw_csd, 128, 26, 3);
+ csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 128, 22, 4);
+ csd->write_bl_partial = mmc_get_bits(raw_csd, 128, 21, 1);
+ } else if (v == 1) {
+ m = mmc_get_bits(raw_csd, 128, 115, 4);
+ e = mmc_get_bits(raw_csd, 128, 112, 3);
+ csd->tacc = (exp[e] * mant[m] + 9) / 10;
+ csd->nsac = mmc_get_bits(raw_csd, 128, 104, 8) * 100;
+ m = mmc_get_bits(raw_csd, 128, 99, 4);
+ e = mmc_get_bits(raw_csd, 128, 96, 3);
+ csd->tran_speed = exp[e] * 10000 * mant[m];
+ csd->ccc = mmc_get_bits(raw_csd, 128, 84, 12);
+ csd->read_bl_len = 1 << mmc_get_bits(raw_csd, 128, 80, 4);
+ csd->read_bl_partial = mmc_get_bits(raw_csd, 128, 79, 1);
+ csd->write_blk_misalign = mmc_get_bits(raw_csd, 128, 78, 1);
+ csd->read_blk_misalign = mmc_get_bits(raw_csd, 128, 77, 1);
+ csd->dsr_imp = mmc_get_bits(raw_csd, 128, 76, 1);
+ csd->capacity = ((uint64_t)mmc_get_bits(raw_csd, 128, 48, 22) + 1) *
+ 512 * 1024;
+ csd->erase_blk_en = mmc_get_bits(raw_csd, 128, 46, 1);
+ csd->erase_sector = mmc_get_bits(raw_csd, 128, 39, 7) + 1;
+ csd->wp_grp_size = mmc_get_bits(raw_csd, 128, 32, 7);
+ csd->wp_grp_enable = mmc_get_bits(raw_csd, 128, 31, 1);
+ csd->r2w_factor = 1 << mmc_get_bits(raw_csd, 128, 26, 3);
+ csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 128, 22, 4);
+ csd->write_bl_partial = mmc_get_bits(raw_csd, 128, 21, 1);
+ } else
+ panic("unknown SD CSD version");
+}
+
+static void
+mmc_decode_csd_mmc(uint32_t *raw_csd, struct mmc_csd *csd)
+{
+ int m;
+ int e;
+
+ memset(csd, 0, sizeof(*csd));
+ csd->csd_structure = mmc_get_bits(raw_csd, 128, 126, 2);
+ csd->spec_vers = mmc_get_bits(raw_csd, 128, 122, 4);
+ m = mmc_get_bits(raw_csd, 128, 115, 4);
+ e = mmc_get_bits(raw_csd, 128, 112, 3);
+ csd->tacc = exp[e] * mant[m] + 9 / 10;
+ csd->nsac = mmc_get_bits(raw_csd, 128, 104, 8) * 100;
+ m = mmc_get_bits(raw_csd, 128, 99, 4);
+ e = mmc_get_bits(raw_csd, 128, 96, 3);
+ csd->tran_speed = exp[e] * 10000 * mant[m];
+ csd->ccc = mmc_get_bits(raw_csd, 128, 84, 12);
+ csd->read_bl_len = 1 << mmc_get_bits(raw_csd, 128, 80, 4);
+ csd->read_bl_partial = mmc_get_bits(raw_csd, 128, 79, 1);
+ csd->write_blk_misalign = mmc_get_bits(raw_csd, 128, 78, 1);
+ csd->read_blk_misalign = mmc_get_bits(raw_csd, 128, 77, 1);
+ csd->dsr_imp = mmc_get_bits(raw_csd, 128, 76, 1);
+ csd->vdd_r_curr_min = cur_min[mmc_get_bits(raw_csd, 128, 59, 3)];
+ csd->vdd_r_curr_max = cur_max[mmc_get_bits(raw_csd, 128, 56, 3)];
+ csd->vdd_w_curr_min = cur_min[mmc_get_bits(raw_csd, 128, 53, 3)];
+ csd->vdd_w_curr_max = cur_max[mmc_get_bits(raw_csd, 128, 50, 3)];
+ m = mmc_get_bits(raw_csd, 128, 62, 12);
+ e = mmc_get_bits(raw_csd, 128, 47, 3);
+ csd->capacity = ((1 + m) << (e + 2)) * csd->read_bl_len;
+ csd->erase_blk_en = 0;
+ csd->erase_sector = (mmc_get_bits(raw_csd, 128, 42, 5) + 1) *
+ (mmc_get_bits(raw_csd, 128, 37, 5) + 1);
+ csd->wp_grp_size = mmc_get_bits(raw_csd, 128, 32, 5);
+ csd->wp_grp_enable = mmc_get_bits(raw_csd, 128, 31, 1);
+ csd->r2w_factor = 1 << mmc_get_bits(raw_csd, 128, 26, 3);
+ csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 128, 22, 4);
+ csd->write_bl_partial = mmc_get_bits(raw_csd, 128, 21, 1);
+}
+
+static void
+mmc_decode_cid_sd(uint32_t *raw_cid, struct mmc_cid *cid)
+{
+ int i;
+
+ /* There's no version info, so we take it on faith */
+ memset(cid, 0, sizeof(*cid));
+ cid->mid = mmc_get_bits(raw_cid, 128, 120, 8);
+ cid->oid = mmc_get_bits(raw_cid, 128, 104, 16);
+ for (i = 0; i < 5; i++)
+ cid->pnm[i] = mmc_get_bits(raw_cid, 128, 96 - i * 8, 8);
+ cid->pnm[5] = 0;
+ cid->prv = mmc_get_bits(raw_cid, 128, 56, 8);
+ cid->psn = mmc_get_bits(raw_cid, 128, 24, 32);
+ cid->mdt_year = mmc_get_bits(raw_cid, 128, 12, 8) + 2000;
+ cid->mdt_month = mmc_get_bits(raw_cid, 128, 8, 4);
+}
+
+static void
+mmc_decode_cid_mmc(uint32_t *raw_cid, struct mmc_cid *cid)
+{
+ int i;
+
+ /* There's no version info, so we take it on faith */
+ memset(cid, 0, sizeof(*cid));
+ cid->mid = mmc_get_bits(raw_cid, 128, 120, 8);
+ cid->oid = mmc_get_bits(raw_cid, 128, 104, 8);
+ for (i = 0; i < 6; i++)
+ cid->pnm[i] = mmc_get_bits(raw_cid, 128, 96 - i * 8, 8);
+ cid->pnm[6] = 0;
+ cid->prv = mmc_get_bits(raw_cid, 128, 48, 8);
+ cid->psn = mmc_get_bits(raw_cid, 128, 16, 32);
+ cid->mdt_month = mmc_get_bits(raw_cid, 128, 12, 4);
+ cid->mdt_year = mmc_get_bits(raw_cid, 128, 8, 4) + 1997;
+}
+
+static void
+mmc_format_card_id_string(struct sdda_softc *sc, struct mmc_params *mmcp)
+{
+ char oidstr[8];
+ uint8_t c1;
+ uint8_t c2;
+
+ /*
+ * Format a card ID string for use by the mmcsd driver, it's what
+ * appears between the <> in the following:
+ * mmcsd0: 968MB <SD SD01G 8.0 SN 2686905 Mfg 08/2008 by 3 TN> at mmc0
+ * 22.5MHz/4bit/128-block
+ *
+ * Also format just the card serial number, which the mmcsd driver will
+ * use as the disk->d_ident string.
+ *
+ * The card_id_string in mmc_ivars is currently allocated as 64 bytes,
+ * and our max formatted length is currently 55 bytes if every field
+ * contains the largest value.
+ *
+ * Sometimes the oid is two printable ascii chars; when it's not,
+ * format it as 0xnnnn instead.
+ */
+ c1 = (sc->cid.oid >> 8) & 0x0ff;
+ c2 = sc->cid.oid & 0x0ff;
+ if (c1 > 0x1f && c1 < 0x7f && c2 > 0x1f && c2 < 0x7f)
+ snprintf(oidstr, sizeof(oidstr), "%c%c", c1, c2);
+ else
+ snprintf(oidstr, sizeof(oidstr), "0x%04x", sc->cid.oid);
+ snprintf(sc->card_sn_string, sizeof(sc->card_sn_string),
+ "%08X", sc->cid.psn);
+ snprintf(sc->card_id_string, sizeof(sc->card_id_string),
+ "%s%s %s %d.%d SN %08X MFG %02d/%04d by %d %s",
+ mmcp->card_features & CARD_FEATURE_MMC ? "MMC" : "SD",
+ mmcp->card_features & CARD_FEATURE_SDHC ? "HC" : "",
+ sc->cid.pnm, sc->cid.prv >> 4, sc->cid.prv & 0x0f,
+ sc->cid.psn, sc->cid.mdt_month, sc->cid.mdt_year,
+ sc->cid.mid, oidstr);
+}
+
+static int
+sddaopen(struct disk *dp)
+{
+ struct cam_periph *periph;
+ struct sdda_softc *softc;
+ int error;
+
+ periph = (struct cam_periph *)dp->d_drv1;
+ if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
+ return(ENXIO);
+ }
+
+ cam_periph_lock(periph);
+ if ((error = cam_periph_hold(periph, PRIBIO|PCATCH)) != 0) {
+ cam_periph_unlock(periph);
+ cam_periph_release(periph);
+ return (error);
+ }
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH,
+ ("sddaopen\n"));
+
+ softc = (struct sdda_softc *)periph->softc;
+ softc->flags |= SDDA_FLAG_OPEN;
+
+ cam_periph_unhold(periph);
+ cam_periph_unlock(periph);
+ return (0);
+}
+
+static int
+sddaclose(struct disk *dp)
+{
+ struct cam_periph *periph;
+ struct sdda_softc *softc;
+// union ccb *ccb;
+// int error;
+
+ periph = (struct cam_periph *)dp->d_drv1;
+ softc = (struct sdda_softc *)periph->softc;
+ softc->flags &= ~SDDA_FLAG_OPEN;
+
+ cam_periph_lock(periph);
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH,
+ ("sddaclose\n"));
+
+ while (softc->refcount != 0)
+ cam_periph_sleep(periph, &softc->refcount, PRIBIO, "sddaclose", 1);
+ cam_periph_unlock(periph);
+ cam_periph_release(periph);
+ return (0);
+}
+
+static void
+sddaschedule(struct cam_periph *periph)
+{
+ struct sdda_softc *softc = (struct sdda_softc *)periph->softc;
+
+ /* Check if we have more work to do. */
+ if (bioq_first(&softc->bio_queue)) {
+ xpt_schedule(periph, CAM_PRIORITY_NORMAL);
+ }
+}
+
+/*
+ * Actually translate the requested transfer into one the physical driver
+ * can understand. The transfer is described by a buf and will include
+ * only one physical transfer.
+ */
+static void
+sddastrategy(struct bio *bp)
+{
+ struct cam_periph *periph;
+ struct sdda_softc *softc;
+
+ periph = (struct cam_periph *)bp->bio_disk->d_drv1;
+ softc = (struct sdda_softc *)periph->softc;
+
+ cam_periph_lock(periph);
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sddastrategy(%p)\n", bp));
+
+ /*
+ * If the device has been made invalid, error out
+ */
+ if ((periph->flags & CAM_PERIPH_INVALID) != 0) {
+ cam_periph_unlock(periph);
+ biofinish(bp, NULL, ENXIO);
+ return;
+ }
+
+ /*
+ * Place it in the queue of disk activities for this disk
+ */
+ bioq_disksort(&softc->bio_queue, bp);
+
+ /*
+ * Schedule ourselves for performing the work.
+ */
+ sddaschedule(periph);
+ cam_periph_unlock(periph);
+
+ return;
+}
+
+static void
+sddainit(void)
+{
+ cam_status status;
+
+ /*
+ * Install a global async callback. This callback will
+ * receive async callbacks like "new device found".
+ */
+ status = xpt_register_async(AC_FOUND_DEVICE, sddaasync, NULL, NULL);
+
+ if (status != CAM_REQ_CMP) {
+ printf("sdda: Failed to attach master async callback "
+ "due to status 0x%x!\n", status);
+ }
+}
+
+/*
+ * Callback from GEOM, called when it has finished cleaning up its
+ * resources.
+ */
+static void
+sddadiskgonecb(struct disk *dp)
+{
+ struct cam_periph *periph;
+
+ periph = (struct cam_periph *)dp->d_drv1;
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sddadiskgonecb\n"));
+
+ cam_periph_release(periph);
+}
+
+static void
+sddaoninvalidate(struct cam_periph *periph)
+{
+ struct sdda_softc *softc;
+
+ softc = (struct sdda_softc *)periph->softc;
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sddaoninvalidate\n"));
+
+ /*
+ * De-register any async callbacks.
+ */
+ xpt_register_async(0, sddaasync, periph, periph->path);
+
+ /*
+ * Return all queued I/O with ENXIO.
+ * XXX Handle any transactions queued to the card
+ * with XPT_ABORT_CCB.
+ */
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("bioq_flush start\n"));
+ bioq_flush(&softc->bio_queue, NULL, ENXIO);
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("bioq_flush end\n"));
+
+ disk_gone(softc->disk);
+}
+
+static void
+sddacleanup(struct cam_periph *periph)
+{
+ struct sdda_softc *softc;
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sddacleanup\n"));
+ softc = (struct sdda_softc *)periph->softc;
+
+ cam_periph_unlock(periph);
+
+ disk_destroy(softc->disk);
+ free(softc, M_DEVBUF);
+ cam_periph_lock(periph);
+}
+
+static void
+sddaasync(void *callback_arg, u_int32_t code,
+ struct cam_path *path, void *arg)
+{
+ struct ccb_getdev cgd;
+ struct cam_periph *periph;
+ struct sdda_softc *softc;
+
+ periph = (struct cam_periph *)callback_arg;
+ CAM_DEBUG(path, CAM_DEBUG_TRACE, ("sddaasync(code=%d)\n", code));
+ switch (code) {
+ case AC_FOUND_DEVICE:
+ {
+ CAM_DEBUG(path, CAM_DEBUG_TRACE, ("=> AC_FOUND_DEVICE\n"));
+ struct ccb_getdev *cgd;
+ cam_status status;
+
+ cgd = (struct ccb_getdev *)arg;
+ if (cgd == NULL)
+ break;
+
+ if (cgd->protocol != PROTO_MMCSD)
+ break;
+
+ if (!(path->device->mmc_ident_data.card_features & CARD_FEATURE_MEMORY)) {
+ CAM_DEBUG(path, CAM_DEBUG_TRACE, ("No memory on the card!\n"));
+ break;
+ }
+
+ /*
+ * Allocate a peripheral instance for
+ * this device and start the probe
+ * process.
+ */
+ status = cam_periph_alloc(sddaregister, sddaoninvalidate,
+ sddacleanup, sddastart,
+ "sdda", CAM_PERIPH_BIO,
+ path, sddaasync,
+ AC_FOUND_DEVICE, cgd);
+
+ if (status != CAM_REQ_CMP
+ && status != CAM_REQ_INPROG)
+ printf("sddaasync: Unable to attach to new device "
+ "due to status 0x%x\n", status);
+ break;
+ }
+ case AC_GETDEV_CHANGED:
+ {
+ CAM_DEBUG(path, CAM_DEBUG_TRACE, ("=> AC_GETDEV_CHANGED\n"));
+ softc = (struct sdda_softc *)periph->softc;
+ xpt_setup_ccb(&cgd.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
+ cgd.ccb_h.func_code = XPT_GDEV_TYPE;
+ xpt_action((union ccb *)&cgd);
+ cam_periph_async(periph, code, path, arg);
+ break;
+ }
+ case AC_ADVINFO_CHANGED:
+ {
+ uintptr_t buftype;
+ CAM_DEBUG(path, CAM_DEBUG_TRACE, ("=> AC_ADVINFO_CHANGED\n"));
+ buftype = (uintptr_t)arg;
+ if (buftype == CDAI_TYPE_PHYS_PATH) {
+ struct sdda_softc *softc;
+
+ softc = periph->softc;
+ disk_attr_changed(softc->disk, "GEOM::physpath",
+ M_NOWAIT);
+ }
+ break;
+ }
+ case AC_SENT_BDR:
+ case AC_BUS_RESET:
+ {
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("AC_BUS_RESET"));
+ }
+ default:
+ CAM_DEBUG(path, CAM_DEBUG_TRACE, ("=> default?!\n"));
+ cam_periph_async(periph, code, path, arg);
+ break;
+ }
+}
+
+
+static int
+sddagetattr(struct bio *bp)
+{
+ int ret;
+ struct cam_periph *periph;
+
+ periph = (struct cam_periph *)bp->bio_disk->d_drv1;
+ cam_periph_lock(periph);
+ ret = xpt_getattr(bp->bio_data, bp->bio_length, bp->bio_attribute,
+ periph->path);
+ cam_periph_unlock(periph);
+ if (ret == 0)
+ bp->bio_completed = bp->bio_length;
+ return ret;
+}
+
+static cam_status
+sddaregister(struct cam_periph *periph, void *arg)
+{
+ struct sdda_softc *softc;
+// struct ccb_pathinq cpi;
+ struct ccb_getdev *cgd;
+// char announce_buf[80], buf1[32];
+// caddr_t match;
+ union ccb *request_ccb; /* CCB representing the probe request */
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sddaregister\n"));
+ cgd = (struct ccb_getdev *)arg;
+ if (cgd == NULL) {
+ printf("sddaregister: no getdev CCB, can't register device\n");
+ return(CAM_REQ_CMP_ERR);
+ }
+
+ softc = (struct sdda_softc *)malloc(sizeof(*softc), M_DEVBUF,
+ M_NOWAIT|M_ZERO);
+
+ if (softc == NULL) {
+ printf("sddaregister: Unable to probe new device. "
+ "Unable to allocate softc\n");
+ return(CAM_REQ_CMP_ERR);
+ }
+
+ bioq_init(&softc->bio_queue);
+ softc->state = SDDA_STATE_INIT;
+ softc->mmcdata =
+ (struct mmc_data *) malloc(sizeof(struct mmc_data), M_DEVBUF, M_NOWAIT|M_ZERO);
+ periph->softc = softc;
+
+ request_ccb = (union ccb*) arg;
+ xpt_schedule(periph, CAM_PRIORITY_XPT);
+ TASK_INIT(&softc->start_init_task, 0, sdda_start_init_task, periph);
+ taskqueue_enqueue(taskqueue_thread, &softc->start_init_task);
+
+ return (CAM_REQ_CMP);
+}
+
+static cam_status
+sdda_hook_into_geom(struct cam_periph *periph)
+{
+ struct sdda_softc *softc;
+ struct ccb_pathinq cpi;
+ struct ccb_getdev cgd;
+ u_int maxio;
+
+ softc = (struct sdda_softc*) periph->softc;
+
+ bzero(&cpi, sizeof(cpi));
+ xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NONE);
+ cpi.ccb_h.func_code = XPT_PATH_INQ;
+ xpt_action((union ccb *)&cpi);
+
+ bzero(&cgd, sizeof(cgd));
+ xpt_setup_ccb(&cgd.ccb_h, periph->path, CAM_PRIORITY_NONE);
+ cpi.ccb_h.func_code = XPT_GDEV_TYPE;
+ xpt_action((union ccb *)&cgd);
+
+ /*
+ * Register this media as a disk
+ */
+ (void)cam_periph_hold(periph, PRIBIO);
+ cam_periph_unlock(periph);
+
+ softc->disk = disk_alloc();
+ softc->disk->d_rotation_rate = 0;
+ softc->disk->d_devstat = devstat_new_entry(periph->periph_name,
+ periph->unit_number, 512,
+ DEVSTAT_ALL_SUPPORTED,
+ DEVSTAT_TYPE_DIRECT |
+ XPORT_DEVSTAT_TYPE(cpi.transport),
+ DEVSTAT_PRIORITY_DISK);
+ softc->disk->d_open = sddaopen;
+ softc->disk->d_close = sddaclose;
+ softc->disk->d_strategy = sddastrategy;
+ softc->disk->d_getattr = sddagetattr;
+// softc->disk->d_dump = sddadump;
+ softc->disk->d_gone = sddadiskgonecb;
+ softc->disk->d_name = "sdda";
+ softc->disk->d_drv1 = periph;
+ maxio = cpi.maxio; /* Honor max I/O size of SIM */
+ if (maxio == 0)
+ maxio = DFLTPHYS; /* traditional default */
+ else if (maxio > MAXPHYS)
+ maxio = MAXPHYS; /* for safety */
+ softc->disk->d_maxsize = maxio;
+ softc->disk->d_unit = periph->unit_number;
+ softc->disk->d_flags = DISKFLAG_CANDELETE;
+ strlcpy(softc->disk->d_descr, softc->card_id_string,
+ MIN(sizeof(softc->disk->d_descr), sizeof(softc->card_id_string)));
+ strlcpy(softc->disk->d_ident, softc->card_sn_string,
+ MIN(sizeof(softc->disk->d_ident), sizeof(softc->card_sn_string)));
+ softc->disk->d_hba_vendor = cpi.hba_vendor;
+ softc->disk->d_hba_device = cpi.hba_device;
+ softc->disk->d_hba_subvendor = cpi.hba_subvendor;
+ softc->disk->d_hba_subdevice = cpi.hba_subdevice;
+
+ softc->disk->d_sectorsize = 512;
+ softc->disk->d_mediasize = softc->mediasize;
+ softc->disk->d_stripesize = 0;
+ softc->disk->d_fwsectors = 0;
+ softc->disk->d_fwheads = 0;
+
+ /*
+ * Acquire a reference to the periph before we register with GEOM.
+ * We'll release this reference once GEOM calls us back (via
+ * sddadiskgonecb()) telling us that our provider has been freed.
+ */
+ if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
+ xpt_print(periph->path, "%s: lost periph during "
+ "registration!\n", __func__);
+ cam_periph_lock(periph);
+ return (CAM_REQ_CMP_ERR);
+ }
+ disk_create(softc->disk, DISK_VERSION);
+ cam_periph_lock(periph);
+ cam_periph_unhold(periph);
+
+ xpt_announce_periph(periph, softc->card_id_string);
+
+ /*
+ * Add async callbacks for bus reset and
+ * bus device reset calls. I don't bother
+ * checking if this fails as, in most cases,
+ * the system will function just fine without
+ * them and the only alternative would be to
+ * not attach the device on failure.
+ */
+ xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE |
+ AC_GETDEV_CHANGED | AC_ADVINFO_CHANGED,
+ sddaasync, periph, periph->path);
+
+ return(CAM_REQ_CMP);
+}
+
+static int
+mmc_exec_app_cmd(struct cam_periph *periph, union ccb *ccb,
+ struct mmc_command *cmd) {
+ int err;
+
+ /* Send APP_CMD first */
+ memset(&ccb->mmcio.cmd, 0, sizeof(struct mmc_command));
+ memset(&ccb->mmcio.stop, 0, sizeof(struct mmc_command));
+ cam_fill_mmcio(&ccb->mmcio,
+ /*retries*/ 0,
+ /*cbfcnp*/ NULL,
+ /*flags*/ CAM_DIR_NONE,
+ /*mmc_opcode*/ MMC_APP_CMD,
+ /*mmc_arg*/ get_rca(periph) << 16,
+ /*mmc_flags*/ MMC_RSP_R1 | MMC_CMD_AC,
+ /*mmc_data*/ NULL,
+ /*timeout*/ 0);
+
+ err = cam_periph_runccb(ccb, sddaerror, CAM_FLAG_NONE, /*sense_flags*/0, NULL);
+ if (err != 0)
+ return err;
+ if (!(ccb->mmcio.cmd.resp[0] & R1_APP_CMD))
+ return MMC_ERR_FAILED;
+
+ /* Now exec actual command */
+ int flags = 0;
+ if (cmd->data != NULL) {
+ ccb->mmcio.cmd.data = cmd->data;
+ if (cmd->data->flags & MMC_DATA_READ)
+ flags |= CAM_DIR_IN;
+ if (cmd->data->flags & MMC_DATA_WRITE)
+ flags |= CAM_DIR_OUT;
+ } else flags = CAM_DIR_NONE;
+
+ cam_fill_mmcio(&ccb->mmcio,
+ /*retries*/ 0,
+ /*cbfcnp*/ NULL,
+ /*flags*/ flags,
+ /*mmc_opcode*/ cmd->opcode,
+ /*mmc_arg*/ cmd->arg,
+ /*mmc_flags*/ cmd->flags,
+ /*mmc_data*/ cmd->data,
+ /*timeout*/ 0);
+
+ err = cam_periph_runccb(ccb, sddaerror, CAM_FLAG_NONE, /*sense_flags*/0, NULL);
+ memcpy(cmd->resp, ccb->mmcio.cmd.resp, sizeof(cmd->resp));
+ cmd->error = ccb->mmcio.cmd.error;
+ if (err != 0)
+ return err;
+ return 0;
+}
+
+static int
+mmc_app_get_scr(struct cam_periph *periph, union ccb *ccb, uint32_t *rawscr) {
+ int err;
+ struct mmc_command cmd;
+ struct mmc_data d;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ memset(rawscr, 0, 8);
+ cmd.opcode = ACMD_SEND_SCR;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+ cmd.arg = 0;
+
+ d.data = rawscr;
+ d.len = 8;
+ d.flags = MMC_DATA_READ;
+ cmd.data = &d;
+
+ err = mmc_exec_app_cmd(periph, ccb, &cmd);
+ rawscr[0] = be32toh(rawscr[0]);
+ rawscr[1] = be32toh(rawscr[1]);
+ return (err);
+}
+
+static int
+mmc_send_ext_csd(struct cam_periph *periph, union ccb *ccb,
+ uint8_t *rawextcsd, size_t buf_len) {
+ int err;
+ struct mmc_data d;
+
+ KASSERT(buf_len == 512, ("Buffer for ext csd must be 512 bytes"));
+ d.data = rawextcsd;
+ d.len = buf_len;
+ d.flags = MMC_DATA_READ;
+ memset(d.data, 0, d.len);
+
+ cam_fill_mmcio(&ccb->mmcio,
+ /*retries*/ 0,
+ /*cbfcnp*/ NULL,
+ /*flags*/ CAM_DIR_IN,
+ /*mmc_opcode*/ MMC_SEND_EXT_CSD,
+ /*mmc_arg*/ 0,
+ /*mmc_flags*/ MMC_RSP_R1 | MMC_CMD_ADTC,
+ /*mmc_data*/ &d,
+ /*timeout*/ 0);
+
+ err = cam_periph_runccb(ccb, sddaerror, CAM_FLAG_NONE, /*sense_flags*/0, NULL);
+ if (err != 0)
+ return err;
+ if (!(ccb->mmcio.cmd.resp[0] & R1_APP_CMD))
+ return MMC_ERR_FAILED;
+
+ return MMC_ERR_NONE;
+}
+
+static void
+mmc_app_decode_scr(uint32_t *raw_scr, struct mmc_scr *scr)
+{
+ unsigned int scr_struct;
+
+ memset(scr, 0, sizeof(*scr));
+
+ scr_struct = mmc_get_bits(raw_scr, 64, 60, 4);
+ if (scr_struct != 0) {
+ printf("Unrecognised SCR structure version %d\n",
+ scr_struct);
+ return;
+ }
+ scr->sda_vsn = mmc_get_bits(raw_scr, 64, 56, 4);
+ scr->bus_widths = mmc_get_bits(raw_scr, 64, 48, 4);
+}
+
+static int
+mmc_switch(struct cam_periph *periph, union ccb *ccb,
+ uint8_t set, uint8_t index, uint8_t value)
+{
+ int arg = (MMC_SWITCH_FUNC_WR << 24) |
+ (index << 16) |
+ (value << 8) |
+ set;
+ cam_fill_mmcio(&ccb->mmcio,
+ /*retries*/ 0,
+ /*cbfcnp*/ NULL,
+ /*flags*/ CAM_DIR_NONE,
+ /*mmc_opcode*/ MMC_SWITCH_FUNC,
+ /*mmc_arg*/ arg,
+ /*mmc_flags*/ MMC_RSP_R1B | MMC_CMD_AC,
+ /*mmc_data*/ NULL,
+ /*timeout*/ 0);
+
+ cam_periph_runccb(ccb, sddaerror, CAM_FLAG_NONE, /*sense_flags*/0, NULL);
+
+ if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)) {
+ if (ccb->mmcio.cmd.error != 0) {
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_PERIPH,
+ ("%s: MMC command failed", __func__));
+ return EIO;
+ }
+ return 0; /* Normal return */
+ } else {
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_PERIPH,
+ ("%s: CAM request failed\n", __func__));
+ return EIO;
+ }
+
+}
+
+static int
+mmc_sd_switch(struct cam_periph *periph, union ccb *ccb,
+ uint8_t mode, uint8_t grp, uint8_t value,
+ uint8_t *res) {
+
+ struct mmc_data mmc_d;
+
+ memset(res, 0, 64);
+ mmc_d.len = 64;
+ mmc_d.data = res;
+ mmc_d.flags = MMC_DATA_READ;
+
+ cam_fill_mmcio(&ccb->mmcio,
+ /*retries*/ 0,
+ /*cbfcnp*/ NULL,
+ /*flags*/ CAM_DIR_IN,
+ /*mmc_opcode*/ SD_SWITCH_FUNC,
+ /*mmc_arg*/ mode << 31,
+ /*mmc_flags*/ MMC_RSP_R1 | MMC_CMD_ADTC,
+ /*mmc_data*/ &mmc_d,
+ /*timeout*/ 0);
+
+ cam_periph_runccb(ccb, sddaerror, CAM_FLAG_NONE, /*sense_flags*/0, NULL);
+
+ if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)) {
+ if (ccb->mmcio.cmd.error != 0) {
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_PERIPH,
+ ("%s: MMC command failed", __func__));
+ return EIO;
+ }
+ return 0; /* Normal return */
+ } else {
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_PERIPH,
+ ("%s: CAM request failed\n", __func__));
+ return EIO;
+ }
+}
+
+static int
+mmc_set_timing(struct cam_periph *periph,
+ union ccb *ccb,
+ enum mmc_bus_timing timing)
+{
+ u_char switch_res[64];
+ int err;
+ uint8_t value;
+ struct mmc_params *mmcp = &periph->path->device->mmc_ident_data;
+
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
+ ("mmc_set_timing(timing=%d)", timing));
+ switch (timing) {
+ case bus_timing_normal:
+ value = 0;
+ break;
+ case bus_timing_hs:
+ value = 1;
+ break;
+ default:
+ return (MMC_ERR_INVALID);
+ }
+ if (mmcp->card_features & CARD_FEATURE_MMC) {
+ err = mmc_switch(periph, ccb, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HS_TIMING, value);
+ } else {
+ err = mmc_sd_switch(periph, ccb, SD_SWITCH_MODE_SET, SD_SWITCH_GROUP1, value, switch_res);
+ }
+
+ /* Set high-speed timing on the host */
+ struct ccb_trans_settings_mmc *cts;
+ cts = &ccb->cts.proto_specific.mmc;
+ ccb->ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
+ ccb->ccb_h.flags = CAM_DIR_NONE;
+ ccb->ccb_h.retry_count = 0;
+ ccb->ccb_h.timeout = 100;
+ ccb->ccb_h.cbfcnp = NULL;
+ cts->ios.timing = timing;
+ cts->ios_valid = MMC_BT;
+ xpt_action(ccb);
+
+ return (err);
+}
+
+static void
+sdda_start_init_task(void *context, int pending) {
+ union ccb *new_ccb;
+ struct cam_periph *periph;
+
+ periph = (struct cam_periph *)context;
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sdda_start_init_task\n"));
+ new_ccb = xpt_alloc_ccb();
+ xpt_setup_ccb(&new_ccb->ccb_h, periph->path,
+ CAM_PRIORITY_NONE);
+
+ cam_periph_lock(periph);
+ sdda_start_init(context, new_ccb);
+ cam_periph_unlock(periph);
+ xpt_free_ccb(new_ccb);
+}
+
+static void
+sdda_set_bus_width(struct cam_periph *periph, union ccb *ccb, int width) {
+ struct mmc_params *mmcp = &periph->path->device->mmc_ident_data;
+ int err;
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sdda_set_bus_width\n"));
+
+ /* First set for the card, then for the host */
+ if (mmcp->card_features & CARD_FEATURE_MMC) {
+ uint8_t value;
+ switch (width) {
+ case bus_width_1:
+ value = EXT_CSD_BUS_WIDTH_1;
+ break;
+ case bus_width_4:
+ value = EXT_CSD_BUS_WIDTH_4;
+ break;
+ case bus_width_8:
+ value = EXT_CSD_BUS_WIDTH_8;
+ break;
+ default:
+ panic("Invalid bus width %d", width);
+ }
+ err = mmc_switch(periph, ccb, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH, value);
+ } else {
+ /* For SD cards we send ACMD6 with the required bus width in arg */
+ struct mmc_command cmd;
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ cmd.opcode = ACMD_SET_BUS_WIDTH;
+ cmd.arg = width;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ err = mmc_exec_app_cmd(periph, ccb, &cmd);
+ }
+
+ if (err != MMC_ERR_NONE) {
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, ("Error %d when setting bus width on the card\n", err));
+ return;
+ }
+ /* Now card is done, set the host to the same width */
+ struct ccb_trans_settings_mmc *cts;
+ cts = &ccb->cts.proto_specific.mmc;
+ ccb->ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
+ ccb->ccb_h.flags = CAM_DIR_NONE;
+ ccb->ccb_h.retry_count = 0;
+ ccb->ccb_h.timeout = 100;
+ ccb->ccb_h.cbfcnp = NULL;
+ cts->ios.bus_width = width;
+ cts->ios_valid = MMC_BW;
+ xpt_action(ccb);
+}
+
+static inline const char *bus_width_str(enum mmc_bus_width w) {
+ switch (w) {
+ case bus_width_1:
+ return "1-bit";
+ case bus_width_4:
+ return "4-bit";
+ case bus_width_8:
+ return "8-bit";
+ }
+}
+
+static void
+sdda_start_init(void *context, union ccb *start_ccb) {
+ struct cam_periph *periph;
+ periph = (struct cam_periph *)context;
+ int err;
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sdda_start_init\n"));
+ /* periph was held for us when this task was enqueued */
+ if ((periph->flags & CAM_PERIPH_INVALID) != 0) {
+ cam_periph_release(periph);
+ return;
+ }
+
+ struct sdda_softc *softc = (struct sdda_softc *)periph->softc;
+ //struct ccb_mmcio *mmcio = &start_ccb->mmcio;
+ struct mmc_params *mmcp = &periph->path->device->mmc_ident_data;
+ struct cam_ed *device = periph->path->device;
+
+ if (mmcp->card_features & CARD_FEATURE_MMC) {
+ mmc_decode_csd_mmc(mmcp->card_csd, &softc->csd);
+ mmc_decode_cid_mmc(mmcp->card_cid, &softc->cid);
+ if (softc->csd.spec_vers >= 4)
+ err = mmc_send_ext_csd(periph, start_ccb,
+ (uint8_t *)&softc->raw_ext_csd,
+ sizeof(softc->raw_ext_csd));
+ } else {
+ mmc_decode_csd_sd(mmcp->card_csd, &softc->csd);
+ mmc_decode_cid_sd(mmcp->card_cid, &softc->cid);
+ }
+
+ softc->sector_count = softc->csd.capacity / 512;
+ softc->mediasize = softc->csd.capacity;
+
+ /* MMC >= 4.x have EXT_CSD that has its own opinion about capacity */
+ if (softc->csd.spec_vers >= 4) {
+ uint32_t sec_count = softc->raw_ext_csd[EXT_CSD_SEC_CNT] +
+ (softc->raw_ext_csd[EXT_CSD_SEC_CNT + 1] << 8) +
+ (softc->raw_ext_csd[EXT_CSD_SEC_CNT + 2] << 16) +
+ (softc->raw_ext_csd[EXT_CSD_SEC_CNT + 3] << 24);
+ if (sec_count != 0) {
+ softc->sector_count = sec_count;
+ softc->mediasize = softc->sector_count * 512;
+ /* FIXME: there should be a better name for this option...*/
+ mmcp->card_features |= CARD_FEATURE_SDHC;
+ }
+
+ }
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH,
+ ("Capacity: %"PRIu64", sectors: %"PRIu64"\n",
+ softc->mediasize,
+ softc->sector_count));
+ mmc_format_card_id_string(softc, mmcp);
+
+ /* Update info for CAM */
+ device->serial_num_len = strlen(softc->card_sn_string);
+ device->serial_num =
+ (u_int8_t *)malloc((device->serial_num_len + 1),
+ M_CAMXPT, M_NOWAIT);
+ strlcpy(device->serial_num, softc->card_sn_string, device->serial_num_len);
+
+ device->device_id_len = strlen(softc->card_id_string);
+ device->device_id =
+ (u_int8_t *)malloc((device->device_id_len + 1),
+ M_CAMXPT, M_NOWAIT);
+ strlcpy(device->device_id, softc->card_id_string, device->device_id_len);
+
+ strlcpy(mmcp->model, softc->card_id_string, sizeof(mmcp->model));
+
+ /* Set the clock frequency that the card can handle */
+ struct ccb_trans_settings_mmc *cts;
+ cts = &start_ccb->cts.proto_specific.mmc;
+
+ /* First, get the host's max freq */
+ start_ccb->ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
+ start_ccb->ccb_h.flags = CAM_DIR_NONE;
+ start_ccb->ccb_h.retry_count = 0;
+ start_ccb->ccb_h.timeout = 100;
+ start_ccb->ccb_h.cbfcnp = NULL;
+ xpt_action(start_ccb);
+
+ if (start_ccb->ccb_h.status != CAM_REQ_CMP)
+ panic("Cannot get max host freq");
+ int host_f_max = cts->host_f_max;
+ uint32_t host_caps = cts->host_caps;
+ if (cts->ios.bus_width != bus_width_1)
+ panic("Bus width in ios is not 1-bit");
+
+ /* Now check if the card supports High-speed */
+ softc->card_f_max = softc->csd.tran_speed;
+
+ if (host_caps & MMC_CAP_HSPEED) {
+ /* Find out if the card supports High speed timing */
+ if (mmcp->card_features & CARD_FEATURE_SD20) {
+ /* Get and decode SCR */
+ uint32_t rawscr;
+ uint8_t res[64];
+ if (mmc_app_get_scr(periph, start_ccb, &rawscr)) {
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, ("Cannot get SCR\n"));
+ goto finish_hs_tests;
+ }
+ mmc_app_decode_scr(&rawscr, &softc->scr);
+
+ if ((softc->scr.sda_vsn >= 1) && (softc->csd.ccc & (1<<10))) {
+ mmc_sd_switch(periph, start_ccb, SD_SWITCH_MODE_CHECK,
+ SD_SWITCH_GROUP1, SD_SWITCH_NOCHANGE, res);
+ if (res[13] & 2) {
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, ("Card supports HS\n"));
+ softc->card_f_max = SD_HS_MAX;
+ }
+ } else {
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, ("Not trying the switch\n"));
+ goto finish_hs_tests;
+ }
+ }
+
+ if (mmcp->card_features & CARD_FEATURE_MMC && softc->csd.spec_vers >= 4) {
+ if (softc->raw_ext_csd[EXT_CSD_CARD_TYPE]
+ & EXT_CSD_CARD_TYPE_HS_52)
+ softc->card_f_max = MMC_TYPE_HS_52_MAX;
+ else if (softc->raw_ext_csd[EXT_CSD_CARD_TYPE]
+ & EXT_CSD_CARD_TYPE_HS_26)
+ softc->card_f_max = MMC_TYPE_HS_26_MAX;
+ }
+ }
+ int f_max;
+finish_hs_tests:
+ f_max = min(host_f_max, softc->card_f_max);
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, ("Set SD freq to %d MHz (min out of host f=%d MHz and card f=%d MHz)\n", f_max / 1000000, host_f_max / 1000000, softc->card_f_max / 1000000));
+
+ start_ccb->ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
+ start_ccb->ccb_h.flags = CAM_DIR_NONE;
+ start_ccb->ccb_h.retry_count = 0;
+ start_ccb->ccb_h.timeout = 100;
+ start_ccb->ccb_h.cbfcnp = NULL;
+ cts->ios.clock = f_max;
+ cts->ios_valid = MMC_CLK;
+ xpt_action(start_ccb);
+
+ /* Set bus width */
+ enum mmc_bus_width desired_bus_width = bus_width_1;
+ enum mmc_bus_width max_host_bus_width =
+ (host_caps & MMC_CAP_8_BIT_DATA ? bus_width_8 :
+ host_caps & MMC_CAP_4_BIT_DATA ? bus_width_4 : bus_width_1);
+ enum mmc_bus_width max_card_bus_width = bus_width_1;
+ if (mmcp->card_features & CARD_FEATURE_SD20 &&
+ softc->scr.bus_widths & SD_SCR_BUS_WIDTH_4)
+ max_card_bus_width = bus_width_4;
+ /*
+ * Unlike SD, MMC cards don't have any information about supported bus width...
+ * So we need to perform read/write test to find out the width.
+ */
+ /* TODO: figure out bus width for MMC; use 8-bit for now (to test on BBB) */
+ if (mmcp->card_features & CARD_FEATURE_MMC)
+ max_card_bus_width = bus_width_8;
+
+ desired_bus_width = min(max_host_bus_width, max_card_bus_width);
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH,
+ ("Set bus width to %s (min of host %s and card %s)\n",
+ bus_width_str(desired_bus_width),
+ bus_width_str(max_host_bus_width),
+ bus_width_str(max_card_bus_width)));
+ sdda_set_bus_width(periph, start_ccb, desired_bus_width);
+
+ if (f_max > 25000000) {
+ err = mmc_set_timing(periph, start_ccb, bus_timing_hs);
+ if (err != MMC_ERR_NONE)
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("Cannot switch card to high-speed mode"));
+ }
+ softc->state = SDDA_STATE_NORMAL;
+ sdda_hook_into_geom(periph);
+}
+
+/* Called with periph lock held! */
+static void
+sddastart(struct cam_periph *periph, union ccb *start_ccb)
+{
+ struct sdda_softc *softc = (struct sdda_softc *)periph->softc;
+ struct mmc_params *mmcp = &periph->path->device->mmc_ident_data;
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sddastart\n"));
+
+ if (softc->state != SDDA_STATE_NORMAL) {
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("device is not in SDDA_STATE_NORMAL yet"));
+ xpt_release_ccb(start_ccb);
+ return;
+ }
+ struct bio *bp;
+
+ /* Run regular command. */
+ bp = bioq_first(&softc->bio_queue);
+ if (bp == NULL) {
+ xpt_release_ccb(start_ccb);
+ return;
+ }
+ bioq_remove(&softc->bio_queue, bp);
+
+ switch (bp->bio_cmd) {
+ case BIO_WRITE:
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("BIO_WRITE\n"));
+ softc->flags |= SDDA_FLAG_DIRTY;
+ /* FALLTHROUGH */
+ case BIO_READ:
+ {
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("BIO_READ\n"));
+ uint64_t blockno = bp->bio_pblkno;
+ uint16_t count = bp->bio_bcount / 512;
+ uint16_t opcode;
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("Block %"PRIu64" cnt %u\n", blockno, count));
+
+ /* Construct new MMC command */
+ if (bp->bio_cmd == BIO_READ) {
+ if (count > 1)
+ opcode = MMC_READ_MULTIPLE_BLOCK;
+ else
+ opcode = MMC_READ_SINGLE_BLOCK;
+ } else {
+ if (count > 1)
+ opcode = MMC_WRITE_MULTIPLE_BLOCK;
+ else
+ opcode = MMC_WRITE_BLOCK;
+ }
+
+ start_ccb->ccb_h.func_code = XPT_MMC_IO;
+ start_ccb->ccb_h.flags = (bp->bio_cmd == BIO_READ ? CAM_DIR_IN : CAM_DIR_OUT);
+ start_ccb->ccb_h.retry_count = 0;
+ start_ccb->ccb_h.timeout = 15 * 1000;
+ start_ccb->ccb_h.cbfcnp = sddadone;
+ struct ccb_mmcio *mmcio;
+
+ mmcio = &start_ccb->mmcio;
+ mmcio->cmd.opcode = opcode;
+ mmcio->cmd.arg = blockno;
+ if (!(mmcp->card_features & CARD_FEATURE_SDHC))
+ mmcio->cmd.arg <<= 9;
+
+ mmcio->cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+ mmcio->cmd.data = softc->mmcdata;
+ mmcio->cmd.data->data = bp->bio_data;
+ mmcio->cmd.data->len = 512 * count;
+ mmcio->cmd.data->flags = (bp->bio_cmd == BIO_READ ? MMC_DATA_READ : MMC_DATA_WRITE);
+ /* Direct h/w to issue CMD12 upon completion */
+ if (count > 1) {
+ mmcio->stop.opcode = MMC_STOP_TRANSMISSION;
+ mmcio->stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
+ mmcio->stop.arg = 0;
+ }
+
+ break;
+ }
+ case BIO_FLUSH:
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("BIO_FLUSH\n"));
+ sddaschedule(periph);
+ break;
+ case BIO_DELETE:
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("BIO_DELETE\n"));
+ sddaschedule(periph);
+ break;
+ }
+ start_ccb->ccb_h.ccb_bp = bp;
+ softc->outstanding_cmds++;
+ softc->refcount++;
+ cam_periph_unlock(periph);
+ xpt_action(start_ccb);
+ cam_periph_lock(periph);
+ softc->refcount--;
+
+ /* May have more work to do, so ensure we stay scheduled */
+ sddaschedule(periph);
+}
+
+static void
+sddadone(struct cam_periph *periph, union ccb *done_ccb)
+{
+ struct sdda_softc *softc;
+ struct ccb_mmcio *mmcio;
+// struct ccb_getdev *cgd;
+ struct cam_path *path;
+// int state;
+
+ softc = (struct sdda_softc *)periph->softc;
+ mmcio = &done_ccb->mmcio;
+ path = done_ccb->ccb_h.path;
+
+ CAM_DEBUG(path, CAM_DEBUG_TRACE, ("sddadone\n"));
+
+ struct bio *bp;
+ int error = 0;
+
+// cam_periph_lock(periph);
+ if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ CAM_DEBUG(path, CAM_DEBUG_TRACE, ("Error!!!\n"));
+ if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
+ cam_release_devq(path,
+ /*relsim_flags*/0,
+ /*reduction*/0,
+ /*timeout*/0,
+ /*getcount_only*/0);
+ error = 5; /* EIO */
+ } else {
+ if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
+ panic("REQ_CMP with QFRZN");
+ error = 0;
+ }
+
+
+ bp = (struct bio *)done_ccb->ccb_h.ccb_bp;
+ bp->bio_error = error;
+ if (error != 0) {
+ bp->bio_resid = bp->bio_bcount;
+ bp->bio_flags |= BIO_ERROR;
+ } else {
+ /* XXX: How many bytes remaining? */
+ bp->bio_resid = 0;
+ if (bp->bio_resid > 0)
+ bp->bio_flags |= BIO_ERROR;
+ }
+
+ uint32_t card_status = mmcio->cmd.resp[0];
+ CAM_DEBUG(path, CAM_DEBUG_TRACE,
+ ("Card status: %08x\n", R1_STATUS(card_status)));
+ CAM_DEBUG(path, CAM_DEBUG_TRACE,
+ ("Current state: %d\n", R1_CURRENT_STATE(card_status)));
+
+ softc->outstanding_cmds--;
+ xpt_release_ccb(done_ccb);
+ biodone(bp);
+}
+
+static int
+sddaerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
+{
+ return(cam_periph_error(ccb, cam_flags, sense_flags, NULL));
+}
+#endif /* _KERNEL */
diff --git a/sys/cam/mmc/mmc_sdio.c b/sys/cam/mmc/mmc_sdio.c
new file mode 100644
index 0000000000000..093da15d6fac5
--- /dev/null
+++ b/sys/cam/mmc/mmc_sdio.c
@@ -0,0 +1,126 @@
+/*-
+ * Copyright (c) 2015 Ilya Bakulin <ilya@bakulin.de>
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 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 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/time.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/interrupt.h>
+#include <sys/sbuf.h>
+
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sysctl.h>
+#include <sys/condvar.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_queue.h>
+#include <cam/cam_periph.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_xpt_periph.h>
+#include <cam/cam_xpt_internal.h>
+#include <cam/cam_debug.h>
+
+#include <cam/mmc/mmc.h>
+#include <cam/mmc/mmc_bus.h>
+#include <cam/mmc/mmc_sdio.h>
+
+#include <machine/stdarg.h> /* for xpt_print below */
+#include <machine/_inttypes.h> /* for PRIu64 */
+#include "opt_cam.h"
+
+
+void
+sdio_print_stupid_message(struct cam_periph *periph) {
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
+ ("%s\n", __func__));
+}
+
+/*
+ * f - function to read from / write to
+ * wr - is write
+ * adr - address to r/w
+ * data - actual data to write
+ */
+void sdio_fill_mmcio_rw_direct(union ccb *ccb, uint8_t f, uint8_t wr, uint32_t adr, uint8_t *data) {
+ struct ccb_mmcio *mmcio;
+
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
+ ("sdio_fill_mmcio(f=%d, wr=%d, adr=%02x, data=%02x)\n", f, wr, adr, (data == NULL ? 0 : *data)));
+ mmcio = &ccb->mmcio;
+
+ mmcio->cmd.opcode = SD_IO_RW_DIRECT;
+ mmcio->cmd.arg = SD_IO_RW_FUNC(f) | SD_IO_RW_ADR(adr);
+ if (wr)
+ mmcio->cmd.arg |= SD_IO_RW_WR | SD_IO_RW_RAW | SD_IO_RW_DAT(*data);
+ mmcio->cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
+ mmcio->cmd.data->len = 0;
+}
+
+uint8_t sdio_parse_mmcio_rw_direct(union ccb *ccb, uint8_t *data) {
+ struct ccb_mmcio *mmcio;
+
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
+ ("sdio_parse_mmcio(datap=%p)\n", data));
+ mmcio = &ccb->mmcio;
+
+ if (mmcio->cmd.error)
+ return (mmcio->cmd.error);
+ if (mmcio->cmd.resp[0] & R5_COM_CRC_ERROR)
+ return (MMC_ERR_BADCRC);
+ if (mmcio->cmd.resp[0] & (R5_ILLEGAL_COMMAND | R5_FUNCTION_NUMBER))
+ return (MMC_ERR_INVALID);
+ if (mmcio->cmd.resp[0] & R5_OUT_OF_RANGE)
+ return (MMC_ERR_FAILED);
+
+ /* Just for information... */
+ if (R5_IO_CURRENT_STATE(mmcio->cmd.resp[0]) != 1)
+ printf("!!! SDIO state %d\n", R5_IO_CURRENT_STATE(mmcio->cmd.resp[0]));
+
+ if (mmcio->cmd.resp[0] & R5_ERROR)
+ printf("An error was detected!\n");
+
+ if (mmcio->cmd.resp[0] & R5_COM_CRC_ERROR)
+ printf("A CRC error was detected!\n");
+
+ if (data != NULL)
+ *data = (uint8_t) (mmcio->cmd.resp[0] & 0xff);
+ return (MMC_ERR_NONE);
+
+}
diff --git a/sys/cam/mmc/mmc_sdio.h b/sys/cam/mmc/mmc_sdio.h
new file mode 100644
index 0000000000000..6d22ffc02c13a
--- /dev/null
+++ b/sys/cam/mmc/mmc_sdio.h
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 2014 Ilya Bakulin. 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. 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 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Various SDIO-related stuff
+ */
+
+#ifndef CAM_MMC_SDIO_H
+#define CAM_MMC_SDIO_H
+
+void sdio_print_stupid_message(struct cam_periph *periph);
+void sdio_fill_mmcio_rw_direct(union ccb *ccb, uint8_t f, uint8_t wr, uint32_t adr, uint8_t *data);
+uint8_t sdio_parse_mmcio_rw_direct(union ccb *ccb, uint8_t *data);
+#endif
diff --git a/sys/cam/mmc/mmc_xpt.c b/sys/cam/mmc/mmc_xpt.c
new file mode 100644
index 0000000000000..0142a6f079165
--- /dev/null
+++ b/sys/cam/mmc/mmc_xpt.c
@@ -0,0 +1,1077 @@
+/*-
+ * Copyright (c) 2013,2014 Ilya Bakulin <ilya@bakulin.de>
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 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 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/time.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/interrupt.h>
+#include <sys/sbuf.h>
+
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sysctl.h>
+#include <sys/condvar.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_queue.h>
+#include <cam/cam_periph.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_xpt_periph.h>
+#include <cam/cam_xpt_internal.h>
+#include <cam/cam_debug.h>
+
+#include <cam/mmc/mmc.h>
+#include <cam/mmc/mmc_bus.h>
+#include <cam/mmc/mmc_sdio.h>
+
+#include <machine/stdarg.h> /* for xpt_print below */
+#include <machine/_inttypes.h> /* for PRIu64 */
+#include "opt_cam.h"
+
+static struct cam_ed * mmc_alloc_device(struct cam_eb *bus,
+ struct cam_et *target, lun_id_t lun_id);
+static void mmc_dev_async(u_int32_t async_code, struct cam_eb *bus,
+ struct cam_et *target, struct cam_ed *device, void *async_arg);
+static void mmc_action(union ccb *start_ccb);
+static void mmc_dev_advinfo(union ccb *start_ccb);
+static void mmc_announce_periph(struct cam_periph *periph);
+static void mmc_scan_lun(struct cam_periph *periph,
+ struct cam_path *path, cam_flags flags, union ccb *ccb);
+
+/* mmcprobe methods */
+static cam_status mmcprobe_register(struct cam_periph *periph, void *arg);
+static void mmcprobe_start(struct cam_periph *periph, union ccb *start_ccb);
+static void mmcprobe_cleanup(struct cam_periph *periph);
+static void mmcprobe_done(struct cam_periph *periph, union ccb *done_ccb);
+
+static void mmc_proto_announce(struct cam_ed *device);
+static void mmc_proto_denounce(struct cam_ed *device);
+static void mmc_proto_debug_out(union ccb *ccb);
+
+typedef enum {
+ PROBE_RESET,
+ PROBE_IDENTIFY,
+ PROBE_SDIO_RESET,
+ PROBE_SEND_IF_COND,
+ PROBE_SDIO_INIT,
+ PROBE_MMC_INIT,
+ PROBE_SEND_APP_OP_COND,
+ PROBE_GET_CID,
+ PROBE_GET_CSD,
+ PROBE_SEND_RELATIVE_ADDR,
+ PROBE_SELECT_CARD,
+ PROBE_DONE,
+ PROBE_INVALID
+} probe_action;
+
+static char *probe_action_text[] = {
+ "PROBE_RESET",
+ "PROBE_IDENTIFY",
+ "PROBE_SDIO_RESET",
+ "PROBE_SEND_IF_COND",
+ "PROBE_SDIO_INIT",
+ "PROBE_MMC_INIT",
+ "PROBE_SEND_APP_OP_COND",
+ "PROBE_GET_CID",
+ "PROBE_GET_CSD",
+ "PROBE_SEND_RELATIVE_ADDR",
+ "PROBE_SELECT_CARD",
+ "PROBE_DONE",
+ "PROBE_INVALID"
+};
+
+#define PROBE_SET_ACTION(softc, newaction) \
+do { \
+ char **text; \
+ text = probe_action_text; \
+ CAM_DEBUG((softc)->periph->path, CAM_DEBUG_PROBE, \
+ ("Probe %s to %s\n", text[(softc)->action], \
+ text[(newaction)])); \
+ (softc)->action = (newaction); \
+} while(0)
+
+static struct xpt_xport_ops mmc_xport_ops = {
+ .alloc_device = mmc_alloc_device,
+ .action = mmc_action,
+ .async = mmc_dev_async,
+ .announce = mmc_announce_periph,
+};
+
+#define MMC_XPT_XPORT(x, X) \
+ static struct xpt_xport mmc_xport_ ## x = { \
+ .xport = XPORT_ ## X, \
+ .name = #x, \
+ .ops = &mmc_xport_ops, \
+ }; \
+ CAM_XPT_XPORT(mmc_xport_ ## x);
+
+MMC_XPT_XPORT(mmc, MMCSD);
+
+static struct xpt_proto_ops mmc_proto_ops = {
+ .announce = mmc_proto_announce,
+ .denounce = mmc_proto_denounce,
+ .debug_out = mmc_proto_debug_out,
+};
+
+static struct xpt_proto mmc_proto = {
+ .proto = PROTO_MMCSD,
+ .name = "mmcsd",
+ .ops = &mmc_proto_ops,
+};
+CAM_XPT_PROTO(mmc_proto);
+
+typedef struct {
+ probe_action action;
+ int restart;
+ union ccb saved_ccb;
+ uint32_t flags;
+#define PROBE_FLAG_ACMD_SENT 0x1 /* CMD55 is sent, card expects ACMD */
+ struct cam_periph *periph;
+} mmcprobe_softc;
+
+/* XPort functions -- an interface to CAM at periph side */
+
+static struct cam_ed *
+mmc_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id)
+{
+ struct cam_ed *device;
+
+ printf("mmc_alloc_device()\n");
+ device = xpt_alloc_device(bus, target, lun_id);
+ if (device == NULL)
+ return (NULL);
+
+ device->quirk = NULL;
+ device->mintags = 0;
+ device->maxtags = 0;
+ bzero(&device->inq_data, sizeof(device->inq_data));
+ device->inq_flags = 0;
+ device->queue_flags = 0;
+ device->serial_num = NULL;
+ device->serial_num_len = 0;
+ return (device);
+}
+
+static void
+mmc_dev_async(u_int32_t async_code, struct cam_eb *bus, struct cam_et *target,
+ struct cam_ed *device, void *async_arg)
+{
+
+ printf("mmc_dev_async(async_code=0x%x, path_id=%d, target_id=%x, lun_id=%" SCNx64 "\n",
+ async_code,
+ bus->path_id,
+ target->target_id,
+ device->lun_id);
+ /*
+ * We only need to handle events for real devices.
+ */
+ if (target->target_id == CAM_TARGET_WILDCARD
+ || device->lun_id == CAM_LUN_WILDCARD)
+ return;
+
+ if (async_code == AC_LOST_DEVICE) {
+ if ((device->flags & CAM_DEV_UNCONFIGURED) == 0) {
+ printf("AC_LOST_DEVICE -> set to unconfigured\n");
+ device->flags |= CAM_DEV_UNCONFIGURED;
+ xpt_release_device(device);
+ } else {
+ printf("AC_LOST_DEVICE on unconfigured device\n");
+ }
+ } else if (async_code == AC_FOUND_DEVICE) {
+ printf("Got AC_FOUND_DEVICE -- whatever...\n");
+ } else if (async_code == AC_PATH_REGISTERED) {
+ printf("Got AC_PATH_REGISTERED -- whatever...\n");
+ } else if (async_code == AC_PATH_DEREGISTERED ) {
+ printf("Got AC_PATH_DEREGISTERED -- whatever...\n");
+ } else
+ panic("Unknown async code\n");
+}
+
+/* Taken from nvme_scan_lun, thanks to bsdimp@ */
+static void
+mmc_scan_lun(struct cam_periph *periph, struct cam_path *path,
+ cam_flags flags, union ccb *request_ccb)
+{
+ struct ccb_pathinq cpi;
+ cam_status status;
+ struct cam_periph *old_periph;
+ int lock;
+
+ CAM_DEBUG(path, CAM_DEBUG_TRACE, ("mmc_scan_lun\n"));
+
+ xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NONE);
+ cpi.ccb_h.func_code = XPT_PATH_INQ;
+ xpt_action((union ccb *)&cpi);
+
+ if (cpi.ccb_h.status != CAM_REQ_CMP) {
+ if (request_ccb != NULL) {
+ request_ccb->ccb_h.status = cpi.ccb_h.status;
+ xpt_done(request_ccb);
+ }
+ return;
+ }
+
+ if (xpt_path_lun_id(path) == CAM_LUN_WILDCARD) {
+ CAM_DEBUG(path, CAM_DEBUG_TRACE, ("mmd_scan_lun ignoring bus\n"));
+ request_ccb->ccb_h.status = CAM_REQ_CMP; /* XXX signal error ? */
+ xpt_done(request_ccb);
+ return;
+ }
+
+ lock = (xpt_path_owned(path) == 0);
+ if (lock)
+ xpt_path_lock(path);
+
+ if ((old_periph = cam_periph_find(path, "mmcprobe")) != NULL) {
+ if ((old_periph->flags & CAM_PERIPH_INVALID) == 0) {
+// mmcprobe_softc *softc;
+// softc = (mmcprobe_softc *)old_periph->softc;
+// Not sure if we need request ccb queue for mmc
+// TAILQ_INSERT_TAIL(&softc->request_ccbs,
+// &request_ccb->ccb_h, periph_links.tqe);
+// softc->restart = 1;
+ CAM_DEBUG(path, CAM_DEBUG_INFO,
+ ("Got scan request, but mmcprobe already exists\n"));
+ request_ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ xpt_done(request_ccb);
+ } else {
+ request_ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ xpt_done(request_ccb);
+ }
+ } else {
+ xpt_print(path, " Set up the mmcprobe device...\n");
+
+ status = cam_periph_alloc(mmcprobe_register, NULL,
+ mmcprobe_cleanup,
+ mmcprobe_start,
+ "mmcprobe",
+ CAM_PERIPH_BIO,
+ path, NULL, 0,
+ request_ccb);
+ if (status != CAM_REQ_CMP) {
+ xpt_print(path, "xpt_scan_lun: cam_alloc_periph "
+ "returned an error, can't continue probe\n");
+ }
+ request_ccb->ccb_h.status = status;
+ xpt_done(request_ccb);
+ }
+
+ if (lock)
+ xpt_path_unlock(path);
+}
+
+static void
+mmc_action(union ccb *start_ccb)
+{
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO,
+ ("mmc_action! func_code=%x, action %s\n", start_ccb->ccb_h.func_code,
+ xpt_action_name(start_ccb->ccb_h.func_code)));
+ switch (start_ccb->ccb_h.func_code) {
+
+ case XPT_SCAN_BUS:
+ /* FALLTHROUGH */
+ case XPT_SCAN_TGT:
+ /* FALLTHROUGH */
+ case XPT_SCAN_LUN:
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO,
+ ("XPT_SCAN_{BUS,TGT,LUN}\n"));
+ mmc_scan_lun(start_ccb->ccb_h.path->periph,
+ start_ccb->ccb_h.path, start_ccb->crcn.flags,
+ start_ccb);
+ break;
+
+ case XPT_DEV_ADVINFO:
+ {
+ mmc_dev_advinfo(start_ccb);
+ break;
+ }
+
+ default:
+ xpt_action_default(start_ccb);
+ break;
+ }
+}
+
+static void
+mmc_dev_advinfo(union ccb *start_ccb)
+{
+ struct cam_ed *device;
+ struct ccb_dev_advinfo *cdai;
+ off_t amt;
+
+ start_ccb->ccb_h.status = CAM_REQ_INVALID;
+ device = start_ccb->ccb_h.path->device;
+ cdai = &start_ccb->cdai;
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_TRACE,
+ ("%s: request %x\n", __func__, cdai->buftype));
+
+ /* We don't support writing any data */
+ if (cdai->flags & CDAI_FLAG_STORE)
+ panic("Attempt to store data?!");
+
+ switch(cdai->buftype) {
+ case CDAI_TYPE_SCSI_DEVID:
+ cdai->provsiz = device->device_id_len;
+ if (device->device_id_len == 0)
+ break;
+ amt = MIN(cdai->provsiz, cdai->bufsiz);
+ memcpy(cdai->buf, device->device_id, amt);
+ break;
+ case CDAI_TYPE_SERIAL_NUM:
+ cdai->provsiz = device->serial_num_len;
+ if (device->serial_num_len == 0)
+ break;
+ amt = MIN(cdai->provsiz, cdai->bufsiz);
+ memcpy(cdai->buf, device->serial_num, amt);
+ break;
+ case CDAI_TYPE_PHYS_PATH: /* pass(4) wants this */
+ cdai->provsiz = 0;
+ break;
+ default:
+ panic("Unknown buftype");
+ return;
+ }
+ start_ccb->ccb_h.status = CAM_REQ_CMP;
+}
+
+static void
+mmc_announce_periph(struct cam_periph *periph)
+{
+ struct ccb_pathinq cpi;
+ struct ccb_trans_settings cts;
+ struct cam_path *path = periph->path;
+
+ cam_periph_assert(periph, MA_OWNED);
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
+ ("mmc_announce_periph: called\n"));
+
+ xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NORMAL);
+ cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
+ cts.type = CTS_TYPE_CURRENT_SETTINGS;
+ xpt_action((union ccb*)&cts);
+ if ((cts.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
+ return;
+ xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NORMAL);
+ cpi.ccb_h.func_code = XPT_PATH_INQ;
+ xpt_action((union ccb *)&cpi);
+ printf("XPT info: CLK %04X, ...\n", cts.proto_specific.mmc.ios.clock);
+}
+
+/* This func is called per attached device :-( */
+void
+mmc_print_ident(struct mmc_params *ident_data)
+{
+ printf("Relative addr: %08x\n", ident_data->card_rca);
+ printf("Card features: <");
+ if (ident_data->card_features & CARD_FEATURE_MMC)
+ printf("MMC ");
+ if (ident_data->card_features & CARD_FEATURE_MEMORY)
+ printf("Memory ");
+ if (ident_data->card_features & CARD_FEATURE_SDHC)
+ printf("High-Capacity ");
+ if (ident_data->card_features & CARD_FEATURE_SD20)
+ printf("SD2.0-Conditions ");
+ if (ident_data->card_features & CARD_FEATURE_SDIO)
+ printf("SDIO ");
+ printf(">\n");
+
+ if (ident_data->card_features & CARD_FEATURE_MEMORY)
+ printf("Card memory OCR: %08x\n", ident_data->card_ocr);
+
+ if (ident_data->card_features & CARD_FEATURE_SDIO) {
+ printf("Card IO OCR: %08x\n", ident_data->io_ocr);
+ printf("Number of funcitions: %u\n", ident_data->sdio_func_count);
+ }
+}
+
+static void
+mmc_proto_announce(struct cam_ed *device)
+{
+ mmc_print_ident(&device->mmc_ident_data);
+}
+
+static void
+mmc_proto_denounce(struct cam_ed *device)
+{
+ mmc_print_ident(&device->mmc_ident_data);
+}
+
+static void
+mmc_proto_debug_out(union ccb *ccb)
+{
+ if (ccb->ccb_h.func_code != XPT_MMC_IO)
+ return;
+
+ CAM_DEBUG(ccb->ccb_h.path,
+ CAM_DEBUG_CDB,("mmc_proto_debug_out\n"));
+}
+
+static periph_init_t probe_periph_init;
+
+static struct periph_driver probe_driver =
+{
+ probe_periph_init, "mmcprobe",
+ TAILQ_HEAD_INITIALIZER(probe_driver.units), /* generation */ 0,
+ CAM_PERIPH_DRV_EARLY
+};
+
+PERIPHDRIVER_DECLARE(mmcprobe, probe_driver);
+
+#define CARD_ID_FREQUENCY 400000 /* Spec requires 400kHz max during ID phase. */
+
+static void
+probe_periph_init()
+{
+}
+
+static cam_status
+mmcprobe_register(struct cam_periph *periph, void *arg)
+{
+ union ccb *request_ccb; /* CCB representing the probe request */
+ cam_status status;
+ mmcprobe_softc *softc;
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("mmcprobe_register\n"));
+
+ request_ccb = (union ccb *)arg;
+ if (request_ccb == NULL) {
+ printf("mmcprobe_register: no probe CCB, "
+ "can't register device\n");
+ return(CAM_REQ_CMP_ERR);
+ }
+
+ softc = (mmcprobe_softc *)malloc(sizeof(*softc), M_CAMXPT, M_NOWAIT);
+
+ if (softc == NULL) {
+ printf("proberegister: Unable to probe new device. "
+ "Unable to allocate softc\n");
+ return(CAM_REQ_CMP_ERR);
+ }
+
+ softc->flags = 0;
+ periph->softc = softc;
+ softc->periph = periph;
+ softc->action = PROBE_INVALID;
+ softc->restart = 0;
+ status = cam_periph_acquire(periph);
+
+ memset(&periph->path->device->mmc_ident_data, 0, sizeof(struct mmc_params));
+ if (status != CAM_REQ_CMP) {
+ printf("proberegister: cam_periph_acquire failed (status=%d)\n",
+ status);
+ return (status);
+ }
+ CAM_DEBUG(periph->path, CAM_DEBUG_PROBE, ("Probe started\n"));
+
+ if (periph->path->device->flags & CAM_DEV_UNCONFIGURED)
+ PROBE_SET_ACTION(softc, PROBE_RESET);
+ else
+ PROBE_SET_ACTION(softc, PROBE_IDENTIFY);
+
+ /* This will kick the ball */
+ xpt_schedule(periph, CAM_PRIORITY_XPT);
+
+ return(CAM_REQ_CMP);
+}
+
+static int
+mmc_highest_voltage(uint32_t ocr)
+{
+ int i;
+
+ for (i = MMC_OCR_MAX_VOLTAGE_SHIFT;
+ i >= MMC_OCR_MIN_VOLTAGE_SHIFT; i--)
+ if (ocr & (1 << i))
+ return (i);
+ return (-1);
+}
+
+static inline void
+init_standard_ccb(union ccb *ccb, uint32_t cmd)
+{
+ ccb->ccb_h.func_code = cmd;
+ ccb->ccb_h.flags = CAM_DIR_OUT;
+ ccb->ccb_h.retry_count = 0;
+ ccb->ccb_h.timeout = 15 * 1000;
+ ccb->ccb_h.cbfcnp = mmcprobe_done;
+}
+
+static void
+mmcprobe_start(struct cam_periph *periph, union ccb *start_ccb)
+{
+ mmcprobe_softc *softc;
+ struct cam_path *path;
+ struct ccb_mmcio *mmcio;
+ struct mtx *p_mtx = cam_periph_mtx(periph);
+ struct ccb_trans_settings_mmc *cts;
+
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("mmcprobe_start\n"));
+ softc = (mmcprobe_softc *)periph->softc;
+ path = start_ccb->ccb_h.path;
+ mmcio = &start_ccb->mmcio;
+ cts = &start_ccb->cts.proto_specific.mmc;
+ struct mmc_params *mmcp = &path->device->mmc_ident_data;
+
+ memset(&mmcio->cmd, 0, sizeof(struct mmc_command));
+
+ if (softc->restart) {
+ softc->restart = 0;
+ if (path->device->flags & CAM_DEV_UNCONFIGURED)
+ softc->action = PROBE_RESET;
+ else
+ softc->action = PROBE_IDENTIFY;
+
+ }
+
+ /* Here is the place where the identify fun begins */
+ switch (softc->action) {
+ case PROBE_RESET:
+ /* FALLTHROUGH */
+ case PROBE_IDENTIFY:
+ init_standard_ccb(start_ccb, XPT_PATH_INQ);
+ xpt_action(start_ccb);
+
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("Start with PROBE_RESET\n"));
+ init_standard_ccb(start_ccb, XPT_SET_TRAN_SETTINGS);
+ cts->ios.power_mode = power_off;
+ cts->ios_valid = MMC_PM;
+ xpt_action(start_ccb);
+ mtx_sleep(periph, p_mtx, 0, "mmcios", 100);
+
+ /* mmc_power_up */
+ /* Get the host OCR */
+ init_standard_ccb(start_ccb, XPT_GET_TRAN_SETTINGS);
+ xpt_action(start_ccb);
+
+ uint32_t hv = mmc_highest_voltage(cts->host_ocr);
+ init_standard_ccb(start_ccb, XPT_SET_TRAN_SETTINGS);
+ cts->ios.vdd = hv;
+ cts->ios.bus_mode = opendrain;
+ cts->ios.chip_select = cs_dontcare;
+ cts->ios.power_mode = power_up;
+ cts->ios.bus_width = bus_width_1;
+ cts->ios.clock = 0;
+ cts->ios_valid = MMC_VDD | MMC_PM | MMC_BM |
+ MMC_CS | MMC_BW | MMC_CLK;
+ xpt_action(start_ccb);
+ mtx_sleep(periph, p_mtx, 0, "mmcios", 100);
+
+ init_standard_ccb(start_ccb, XPT_SET_TRAN_SETTINGS);
+ cts->ios.power_mode = power_on;
+ cts->ios.clock = CARD_ID_FREQUENCY;
+ cts->ios.timing = bus_timing_normal;
+ cts->ios_valid = MMC_PM | MMC_CLK | MMC_BT;
+ xpt_action(start_ccb);
+ mtx_sleep(periph, p_mtx, 0, "mmcios", 100);
+ /* End for mmc_power_on */
+
+ /* Begin mmc_idle_cards() */
+ init_standard_ccb(start_ccb, XPT_SET_TRAN_SETTINGS);
+ cts->ios.chip_select = cs_high;
+ cts->ios_valid = MMC_CS;
+ xpt_action(start_ccb);
+ mtx_sleep(periph, p_mtx, 0, "mmcios", 1);
+
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("Send first XPT_MMC_IO\n"));
+ init_standard_ccb(start_ccb, XPT_MMC_IO);
+ mmcio->cmd.opcode = MMC_GO_IDLE_STATE; /* CMD 0 */
+ mmcio->cmd.arg = 0;
+ mmcio->cmd.flags = MMC_RSP_NONE | MMC_CMD_BC;
+ mmcio->cmd.data = NULL;
+ mmcio->stop.opcode = 0;
+
+ /* XXX Reset I/O portion as well */
+ break;
+ case PROBE_SDIO_RESET:
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("Start with PROBE_SDIO_RESET\n"));
+ uint32_t mmc_arg = SD_IO_RW_ADR(SD_IO_CCCR_CTL)
+ | SD_IO_RW_DAT(CCCR_CTL_RES) | SD_IO_RW_WR | SD_IO_RW_RAW;
+ cam_fill_mmcio(&start_ccb->mmcio,
+ /*retries*/ 0,
+ /*cbfcnp*/ mmcprobe_done,
+ /*flags*/ CAM_DIR_NONE,
+ /*mmc_opcode*/ SD_IO_RW_DIRECT,
+ /*mmc_arg*/ mmc_arg,
+ /*mmc_flags*/ MMC_RSP_R5 | MMC_CMD_AC,
+ /*mmc_data*/ NULL,
+ /*timeout*/ 1000);
+ break;
+ case PROBE_SEND_IF_COND:
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("Start with PROBE_SEND_IF_COND\n"));
+ init_standard_ccb(start_ccb, XPT_MMC_IO);
+ mmcio->cmd.opcode = SD_SEND_IF_COND; /* CMD 8 */
+ mmcio->cmd.arg = (1 << 8) + 0xAA;
+ mmcio->cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR;
+ mmcio->stop.opcode = 0;
+ break;
+
+ case PROBE_SDIO_INIT:
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("Start with PROBE_SDIO_INIT\n"));
+ init_standard_ccb(start_ccb, XPT_MMC_IO);
+ mmcio->cmd.opcode = IO_SEND_OP_COND; /* CMD 5 */
+ mmcio->cmd.arg = mmcp->io_ocr;
+ mmcio->cmd.flags = MMC_RSP_R4;
+ mmcio->stop.opcode = 0;
+ break;
+
+ case PROBE_MMC_INIT:
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("Start with PROBE_MMC_INIT\n"));
+ init_standard_ccb(start_ccb, XPT_MMC_IO);
+ mmcio->cmd.opcode = MMC_SEND_OP_COND; /* CMD 1 */
+ mmcio->cmd.arg = MMC_OCR_CCS | mmcp->card_ocr; /* CCS + ocr */;
+ mmcio->cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
+ mmcio->stop.opcode = 0;
+ break;
+
+ case PROBE_SEND_APP_OP_COND:
+ init_standard_ccb(start_ccb, XPT_MMC_IO);
+ if (softc->flags & PROBE_FLAG_ACMD_SENT) {
+ mmcio->cmd.opcode = ACMD_SD_SEND_OP_COND; /* CMD 41 */
+ /*
+ * We set CCS bit because we do support SDHC cards.
+ * XXX: Don't set CCS if no response to CMD8.
+ */
+ mmcio->cmd.arg = MMC_OCR_CCS | mmcp->card_ocr; /* CCS + ocr */
+ mmcio->cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
+ } else {
+ mmcio->cmd.opcode = MMC_APP_CMD; /* CMD 55 */
+ mmcio->cmd.arg = 0; /* rca << 16 */
+ mmcio->cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ }
+ mmcio->stop.opcode = 0;
+ break;
+
+ case PROBE_GET_CID: /* XXX move to mmc_da */
+ init_standard_ccb(start_ccb, XPT_MMC_IO);
+ mmcio->cmd.opcode = MMC_ALL_SEND_CID;
+ mmcio->cmd.arg = 0;
+ mmcio->cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR;
+ mmcio->stop.opcode = 0;
+ break;
+
+ case PROBE_SEND_RELATIVE_ADDR:
+ init_standard_ccb(start_ccb, XPT_MMC_IO);
+ mmcio->cmd.opcode = SD_SEND_RELATIVE_ADDR;
+ mmcio->cmd.arg = 0;
+ mmcio->cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR;
+ mmcio->stop.opcode = 0;
+ break;
+ case PROBE_SELECT_CARD:
+ init_standard_ccb(start_ccb, XPT_MMC_IO);
+ mmcio->cmd.opcode = MMC_SELECT_CARD;
+ mmcio->cmd.arg = (uint32_t)path->device->mmc_ident_data.card_rca << 16;
+ mmcio->cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
+ mmcio->stop.opcode = 0;
+ break;
+ case PROBE_GET_CSD: /* XXX move to mmc_da */
+ init_standard_ccb(start_ccb, XPT_MMC_IO);
+ mmcio->cmd.opcode = MMC_SEND_CSD;
+ mmcio->cmd.arg = (uint32_t)path->device->mmc_ident_data.card_rca << 16;
+ mmcio->cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR;
+ mmcio->stop.opcode = 0;
+ break;
+ case PROBE_DONE:
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("Start with PROBE_DONE\n"));
+ init_standard_ccb(start_ccb, XPT_SET_TRAN_SETTINGS);
+ cts->ios.bus_mode = pushpull;
+ cts->ios_valid = MMC_BM;
+ xpt_action(start_ccb);
+ return;
+ /* NOTREACHED */
+ break;
+ case PROBE_INVALID:
+ break;
+ default:
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("probestart: invalid action state 0x%x\n", softc->action));
+ panic("default: case in mmc_probe_start()");
+ }
+
+ start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE;
+ xpt_action(start_ccb);
+}
+
+static void mmcprobe_cleanup(struct cam_periph *periph)
+{
+ free(periph->softc, M_CAMXPT);
+}
+
+static void
+mmcprobe_done(struct cam_periph *periph, union ccb *done_ccb)
+{
+ mmcprobe_softc *softc;
+ struct cam_path *path;
+
+ int err;
+ struct ccb_mmcio *mmcio;
+ u_int32_t priority;
+
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("mmcprobe_done\n"));
+ softc = (mmcprobe_softc *)periph->softc;
+ path = done_ccb->ccb_h.path;
+ priority = done_ccb->ccb_h.pinfo.priority;
+
+ switch (softc->action) {
+ case PROBE_RESET:
+ /* FALLTHROUGH */
+ case PROBE_IDENTIFY:
+ {
+ printf("Starting completion of PROBE_RESET\n");
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("done with PROBE_RESET\n"));
+ mmcio = &done_ccb->mmcio;
+ err = mmcio->cmd.error;
+
+ if (err != MMC_ERR_NONE) {
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("GO_IDLE_STATE failed with error %d\n",
+ err));
+
+ /* There was a device there, but now it's gone... */
+ if ((path->device->flags & CAM_DEV_UNCONFIGURED) == 0) {
+ xpt_async(AC_LOST_DEVICE, path, NULL);
+ PROBE_SET_ACTION(softc, PROBE_INVALID);
+ }
+ }
+ path->device->protocol = PROTO_MMCSD;
+ PROBE_SET_ACTION(softc, PROBE_SEND_IF_COND);
+ break;
+ }
+ case PROBE_SEND_IF_COND:
+ {
+ mmcio = &done_ccb->mmcio;
+ err = mmcio->cmd.error;
+ struct mmc_params *mmcp = &path->device->mmc_ident_data;
+
+ if (err != MMC_ERR_NONE || mmcio->cmd.resp[0] != 0x1AA) {
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("IF_COND: error %d, pattern %08x\n",
+ err, mmcio->cmd.resp[0]));
+ } else {
+ mmcp->card_features |= CARD_FEATURE_SD20;
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("SD 2.0 interface conditions: OK\n"));
+
+ }
+ PROBE_SET_ACTION(softc, PROBE_SDIO_RESET);
+ break;
+ }
+ case PROBE_SDIO_RESET:
+ {
+ mmcio = &done_ccb->mmcio;
+ err = mmcio->cmd.error;
+
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("SDIO_RESET: error %d, CCCR CTL register: %08x\n",
+ err, mmcio->cmd.resp[0]));
+ PROBE_SET_ACTION(softc, PROBE_SDIO_INIT);
+ break;
+ }
+ case PROBE_SDIO_INIT:
+ {
+ mmcio = &done_ccb->mmcio;
+ err = mmcio->cmd.error;
+ struct mmc_params *mmcp = &path->device->mmc_ident_data;
+
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("SDIO_INIT: error %d, %08x %08x %08x %08x\n",
+ err, mmcio->cmd.resp[0],
+ mmcio->cmd.resp[1],
+ mmcio->cmd.resp[2],
+ mmcio->cmd.resp[3]));
+
+ /*
+ * Error here means that this card is not SDIO,
+ * so proceed with memory init as if nothing has happened
+ */
+ if (err != MMC_ERR_NONE) {
+ PROBE_SET_ACTION(softc, PROBE_SEND_APP_OP_COND);
+ break;
+ }
+ mmcp->card_features |= CARD_FEATURE_SDIO;
+ uint32_t ioifcond = mmcio->cmd.resp[0];
+ uint32_t io_ocr = ioifcond & R4_IO_OCR_MASK;
+
+ mmcp->sdio_func_count = R4_IO_NUM_FUNCTIONS(ioifcond);
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("SDIO card: %d functions\n", mmcp->sdio_func_count));
+ if (io_ocr == 0) {
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("SDIO OCR invalid?!\n"));
+ break; /* Retry */
+ }
+
+ if (io_ocr != 0 && mmcp->io_ocr == 0) {
+ mmcp->io_ocr = io_ocr;
+ break; /* Retry, this time with non-0 OCR */
+ }
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("SDIO OCR: %08x\n", mmcp->io_ocr));
+
+ if (ioifcond & R4_IO_MEM_PRESENT) {
+ /* Combo card -- proceed to memory initialization */
+ PROBE_SET_ACTION(softc, PROBE_SEND_APP_OP_COND);
+ } else {
+ /* No memory portion -- get RCA and select card */
+ PROBE_SET_ACTION(softc, PROBE_SEND_RELATIVE_ADDR);
+ }
+ break;
+ }
+ case PROBE_MMC_INIT:
+ {
+ mmcio = &done_ccb->mmcio;
+ err = mmcio->cmd.error;
+ struct mmc_params *mmcp = &path->device->mmc_ident_data;
+
+ if (err != MMC_ERR_NONE) {
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("MMC_INIT: error %d, resp %08x\n",
+ err, mmcio->cmd.resp[0]));
+ PROBE_SET_ACTION(softc, PROBE_INVALID);
+ break;
+ }
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("MMC card, OCR %08x\n", mmcio->cmd.resp[0]));
+
+ if (mmcp->card_ocr == 0) {
+ /* We haven't sent the OCR to the card yet -- do it */
+ mmcp->card_ocr = mmcio->cmd.resp[0];
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("-> sending OCR to card\n"));
+ break;
+ }
+
+ if (!(mmcio->cmd.resp[0] & MMC_OCR_CARD_BUSY)) {
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("Card is still powering up\n"));
+ break;
+ }
+
+ mmcp->card_features |= CARD_FEATURE_MMC | CARD_FEATURE_MEMORY;
+ PROBE_SET_ACTION(softc, PROBE_GET_CID);
+ break;
+ }
+ case PROBE_SEND_APP_OP_COND:
+ {
+ mmcio = &done_ccb->mmcio;
+ err = mmcio->cmd.error;
+
+ if (err != MMC_ERR_NONE) {
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("APP_OP_COND: error %d, resp %08x\n",
+ err, mmcio->cmd.resp[0]));
+ PROBE_SET_ACTION(softc, PROBE_MMC_INIT);
+ break;
+ }
+
+ if (!(softc->flags & PROBE_FLAG_ACMD_SENT)) {
+ /* Don't change the state */
+ softc->flags |= PROBE_FLAG_ACMD_SENT;
+ break;
+ }
+
+ softc->flags &= ~PROBE_FLAG_ACMD_SENT;
+ if ((mmcio->cmd.resp[0] & MMC_OCR_CARD_BUSY) ||
+ (mmcio->cmd.arg & MMC_OCR_VOLTAGE) == 0) {
+ struct mmc_params *mmcp = &path->device->mmc_ident_data;
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("Card OCR: %08x\n", mmcio->cmd.resp[0]));
+ if (mmcp->card_ocr == 0) {
+ mmcp->card_ocr = mmcio->cmd.resp[0];
+ /* Now when we know OCR that we want -- send it to card */
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("-> sending OCR to card\n"));
+ } else {
+ /* We already know the OCR and despite of that we
+ * are processing the answer to ACMD41 -> move on
+ */
+ PROBE_SET_ACTION(softc, PROBE_GET_CID);
+ }
+ /* Getting an answer to ACMD41 means the card has memory */
+ mmcp->card_features |= CARD_FEATURE_MEMORY;
+
+ /* Standard capacity vs High Capacity memory card */
+ if (mmcio->cmd.resp[0] & MMC_OCR_CCS) {
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("Card is SDHC\n"));
+ mmcp->card_features |= CARD_FEATURE_SDHC;
+ }
+
+ } else {
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("Card not ready: %08x\n", mmcio->cmd.resp[0]));
+ /* Send CMD55+ACMD41 once again */
+ PROBE_SET_ACTION(softc, PROBE_SEND_APP_OP_COND);
+ }
+
+ break;
+ }
+ case PROBE_GET_CID: /* XXX move to mmc_da */
+ {
+ mmcio = &done_ccb->mmcio;
+ err = mmcio->cmd.error;
+
+ if (err != MMC_ERR_NONE) {
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("PROBE_GET_CID: error %d\n", err));
+ PROBE_SET_ACTION(softc, PROBE_INVALID);
+ break;
+ }
+
+ struct mmc_params *mmcp = &path->device->mmc_ident_data;
+ memcpy(mmcp->card_cid, mmcio->cmd.resp, 4 * sizeof(uint32_t));
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("CID %08x%08x%08x%08x\n",
+ mmcp->card_cid[0],
+ mmcp->card_cid[1],
+ mmcp->card_cid[2],
+ mmcp->card_cid[3]));
+ PROBE_SET_ACTION(softc, PROBE_SEND_RELATIVE_ADDR);
+ break;
+ }
+ case PROBE_SEND_RELATIVE_ADDR: {
+ mmcio = &done_ccb->mmcio;
+ err = mmcio->cmd.error;
+ struct mmc_params *mmcp = &path->device->mmc_ident_data;
+ uint16_t rca = mmcio->cmd.resp[0] >> 16;
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("Card published RCA: %u\n", rca));
+ path->device->mmc_ident_data.card_rca = rca;
+ if (err != MMC_ERR_NONE) {
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("PROBE_SEND_RELATIVE_ADDR: error %d\n", err));
+ PROBE_SET_ACTION(softc, PROBE_INVALID);
+ break;
+ }
+
+ /* If memory is present, get CSD, otherwise select card */
+ if (mmcp->card_features & CARD_FEATURE_MEMORY)
+ PROBE_SET_ACTION(softc, PROBE_GET_CSD);
+ else
+ PROBE_SET_ACTION(softc, PROBE_SELECT_CARD);
+ break;
+ }
+ case PROBE_GET_CSD: {
+ mmcio = &done_ccb->mmcio;
+ err = mmcio->cmd.error;
+
+ if (err != MMC_ERR_NONE) {
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("PROBE_GET_CSD: error %d\n", err));
+ PROBE_SET_ACTION(softc, PROBE_INVALID);
+ break;
+ }
+
+ struct mmc_params *mmcp = &path->device->mmc_ident_data;
+ memcpy(mmcp->card_csd, mmcio->cmd.resp, 4 * sizeof(uint32_t));
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("CSD %08x%08x%08x%08x\n",
+ mmcp->card_csd[0],
+ mmcp->card_csd[1],
+ mmcp->card_csd[2],
+ mmcp->card_csd[3]));
+ PROBE_SET_ACTION(softc, PROBE_SELECT_CARD);
+ break;
+ }
+ case PROBE_SELECT_CARD: {
+ mmcio = &done_ccb->mmcio;
+ err = mmcio->cmd.error;
+ if (err != MMC_ERR_NONE) {
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("PROBE_SEND_RELATIVE_ADDR: error %d\n", err));
+ PROBE_SET_ACTION(softc, PROBE_INVALID);
+ break;
+ }
+
+ PROBE_SET_ACTION(softc, PROBE_DONE);
+ break;
+ }
+ default:
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("mmc_probedone: invalid action state 0x%x\n", softc->action));
+ panic("default: case in mmc_probe_done()");
+ }
+
+ if (softc->action == PROBE_INVALID &&
+ (path->device->flags & CAM_DEV_UNCONFIGURED) == 0) {
+ CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE,
+ ("mmc_probedone: Should send AC_LOST_DEVICE but won't for now\n"));
+ //xpt_async(AC_LOST_DEVICE, path, NULL);
+ }
+
+ xpt_release_ccb(done_ccb);
+ if (softc->action != PROBE_INVALID)
+ xpt_schedule(periph, priority);
+ /* Drop freeze taken due to CAM_DEV_QFREEZE flag set. */
+ int frozen = cam_release_devq(path, 0, 0, 0, FALSE);
+ printf("mmc_probedone: remaining freezecnt %d\n", frozen);
+
+ if (softc->action == PROBE_DONE) {
+ /* Notify the system that the device is found! */
+ if (periph->path->device->flags & CAM_DEV_UNCONFIGURED) {
+ path->device->flags &= ~CAM_DEV_UNCONFIGURED;
+ xpt_acquire_device(path->device);
+ done_ccb->ccb_h.func_code = XPT_GDEV_TYPE;
+ xpt_action(done_ccb);
+ xpt_async(AC_FOUND_DEVICE, path, done_ccb);
+ }
+
+ /* Also announce each SDIO function */
+ struct mmc_params *mmcp = &path->device->mmc_ident_data;
+
+ for (int i = 0; i < mmcp->sdio_func_count; i++) {
+ struct cam_path *newpath;
+ cam_status status;
+ status = xpt_create_path(&newpath, NULL,
+ done_ccb->ccb_h.path_id, 0, i + 1);
+ if (status != CAM_REQ_CMP)
+ printf("xpt_create_path failed"
+ " with status %#x\n",
+ status);
+ xpt_async(AC_FOUND_DEVICE, newpath, done_ccb);
+ }
+ }
+ if (softc->action == PROBE_DONE || softc->action == PROBE_INVALID) {
+ cam_periph_invalidate(periph);
+ cam_periph_release_locked(periph);
+ }
+}
diff --git a/sys/cam/scsi/scsi_pass.c b/sys/cam/scsi/scsi_pass.c
index 0941c4f4d6685..c7ead838f731e 100644
--- a/sys/cam/scsi/scsi_pass.c
+++ b/sys/cam/scsi/scsi_pass.c
@@ -2202,7 +2202,7 @@ passsendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb)
*/
fc = ccb->ccb_h.func_code;
if ((fc == XPT_SCSI_IO) || (fc == XPT_ATA_IO) || (fc == XPT_SMP_IO)
- || (fc == XPT_DEV_MATCH) || (fc == XPT_DEV_ADVINFO)) {
+ || (fc == XPT_DEV_MATCH) || (fc == XPT_DEV_ADVINFO) || (fc == XPT_MMC_IO)) {
bzero(&mapinfo, sizeof(mapinfo));
/*
diff --git a/sys/conf/files b/sys/conf/files
index a1d045ac5bd2f..61e4edcbd2872 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -110,6 +110,9 @@ cam/ctl/ctl_tpc_local.c optional ctl
cam/ctl/ctl_error.c optional ctl
cam/ctl/ctl_util.c optional ctl
cam/ctl/scsi_ctl.c optional ctl
+cam/mmc/mmc_xpt.c optional scbus mmccam
+cam/mmc/mmc_da.c optional scbus mmccam da
+cam/mmc/mmc_sdio.c optional scbus mmccam
cam/scsi/scsi_da.c optional da
cam/scsi/scsi_low.c optional ncv | nsp | stg
cam/scsi/scsi_pass.c optional pass
@@ -2240,11 +2243,12 @@ dev/mlx/mlx.c optional mlx
dev/mlx/mlx_disk.c optional mlx
dev/mlx/mlx_pci.c optional mlx pci
dev/mly/mly.c optional mly
-dev/mmc/mmc_subr.c optional mmc | mmcsd
-dev/mmc/mmc.c optional mmc
+dev/mmc/mmc_subr.c optional mmc | mmcsd !mmccam
+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
+dev/mmc/mmcsd.c optional mmcsd !mmccam
+dev/mmcnull/mmcnull.c optional mmcnull
dev/mn/if_mn.c optional mn pci
dev/mpr/mpr.c optional mpr
dev/mpr/mpr_config.c optional mpr
diff --git a/sys/conf/options b/sys/conf/options
index 71f45e1775ef2..eda2dfb8e52cd 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -996,5 +996,7 @@ UINPUT_DEBUG opt_evdev.h
# Hyper-V network driver
HN_DEBUG opt_hn.h
+# CAM-based MMC stack
+MMCCAM
# Encrypted kernel crash dumps
EKCD opt_ekcd.h
diff --git a/sys/dev/mmc/bridge.h b/sys/dev/mmc/bridge.h
index a780ffaeea413..502f433c0d5d4 100644
--- a/sys/dev/mmc/bridge.h
+++ b/sys/dev/mmc/bridge.h
@@ -174,6 +174,7 @@ struct mmc_host {
struct mmc_ios ios; /* Current state of the host */
};
+#ifdef _KERNEL
extern driver_t mmc_driver;
extern devclass_t mmc_devclass;
@@ -184,5 +185,6 @@ extern devclass_t mmc_devclass;
MODULE_DEPEND(name, mmc, MMC_VERSION, MMC_VERSION, MMC_VERSION);
#define MMC_DEPEND(name) \
MODULE_DEPEND(name, mmc, MMC_VERSION, MMC_VERSION, MMC_VERSION);
+#endif /* _KERNEL */
#endif /* DEV_MMC_BRIDGE_H */
diff --git a/sys/dev/mmc/mmcbrvar.h b/sys/dev/mmc/mmcbrvar.h
index d8f79156b81d3..47024da7696b0 100644
--- a/sys/dev/mmc/mmcbrvar.h
+++ b/sys/dev/mmc/mmcbrvar.h
@@ -56,7 +56,6 @@
#define DEV_MMC_MMCBRVAR_H
#include <dev/mmc/mmcreg.h>
-
#include "mmcbr_if.h"
enum mmcbr_device_ivars {
diff --git a/sys/dev/mmc/mmcreg.h b/sys/dev/mmc/mmcreg.h
index 359f31d50c782..671b8c23529e2 100644
--- a/sys/dev/mmc/mmcreg.h
+++ b/sys/dev/mmc/mmcreg.h
@@ -1,6 +1,7 @@
/*-
* Copyright (c) 2006 M. Warner Losh. All rights reserved.
* Copyright (c) 2017 Marius Strobl <marius@FreeBSD.org>
+ * Copyright (c) 2015-2016 Ilya Bakulin <kibab@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -156,6 +157,34 @@ struct mmc_command {
#define R1_STATE_PRG 7
#define R1_STATE_DIS 8
+/* R4 response (SDIO) */
+#define R4_IO_NUM_FUNCTIONS(ocr) (((ocr) >> 28) & 0x3)
+#define R4_IO_MEM_PRESENT (0x1<<27)
+#define R4_IO_OCR_MASK 0x00fffff0
+
+/*
+ * R5 responses
+ *
+ * Types (per SD 2.0 standard)
+ *e : error bit
+ *s : status bit
+ *r : detected and set for the actual command response
+ *x : Detected and set during command execution. The host can get
+ * the status by issuing a command with R1 response.
+ *
+ * Clear Condition (per SD 2.0 standard)
+ *a : according to the card current state.
+ *b : always related to the previous command. reception of a valid
+ * command will clear it (with a delay of one command).
+ *c : clear by read
+ */
+#define R5_COM_CRC_ERROR (1u << 15)/* er, b */
+#define R5_ILLEGAL_COMMAND (1u << 14)/* er, b */
+#define R5_IO_CURRENT_STATE_MASK (3u << 12)/* s, b */
+#define R5_IO_CURRENT_STATE(x) (((x) & R5_IO_CURRENT_STATE_MASK) >> 12)
+#define R5_ERROR (1u << 11)/* erx, c */
+#define R5_FUNCTION_NUMBER (1u << 9)/* er, c */
+#define R5_OUT_OF_RANGE (1u << 8)/* er, c */
struct mmc_data {
size_t len; /* size of the data */
size_t xfer_len;
@@ -187,6 +216,7 @@ struct mmc_request {
#define SD_SEND_RELATIVE_ADDR 3
#define MMC_SET_DSR 4
#define MMC_SLEEP_AWAKE 5
+#define IO_SEND_OP_COND 5
#define MMC_SWITCH_FUNC 6
#define MMC_SWITCH_FUNC_CMDS 0
#define MMC_SWITCH_FUNC_SET 1
@@ -269,7 +299,31 @@ struct mmc_request {
/* Class 9: I/O cards (sd) */
#define SD_IO_RW_DIRECT 52
+/* CMD52 arguments */
+#define SD_ARG_CMD52_READ (0<<31)
+#define SD_ARG_CMD52_WRITE (1<<31)
+#define SD_ARG_CMD52_FUNC_SHIFT 28
+#define SD_ARG_CMD52_FUNC_MASK 0x7
+#define SD_ARG_CMD52_EXCHANGE (1<<27)
+#define SD_ARG_CMD52_REG_SHIFT 9
+#define SD_ARG_CMD52_REG_MASK 0x1ffff
+#define SD_ARG_CMD52_DATA_SHIFT 0
+#define SD_ARG_CMD52_DATA_MASK 0xff
+#define SD_R5_DATA(resp) ((resp)[0] & 0xff)
+
#define SD_IO_RW_EXTENDED 53
+/* CMD53 arguments */
+#define SD_ARG_CMD53_READ (0<<31)
+#define SD_ARG_CMD53_WRITE (1<<31)
+#define SD_ARG_CMD53_FUNC_SHIFT 28
+#define SD_ARG_CMD53_FUNC_MASK 0x7
+#define SD_ARG_CMD53_BLOCK_MODE (1<<27)
+#define SD_ARG_CMD53_INCREMENT (1<<26)
+#define SD_ARG_CMD53_REG_SHIFT 9
+#define SD_ARG_CMD53_REG_MASK 0x1ffff
+#define SD_ARG_CMD53_LENGTH_SHIFT 0
+#define SD_ARG_CMD53_LENGTH_MASK 0x1ff
+#define SD_ARG_CMD53_LENGTH_MAX 64 /* XXX should be 511? */
/* Class 10: Switch function commands */
#define SD_SWITCH_FUNC 6
@@ -440,6 +494,54 @@ struct mmc_request {
/* Specifications require 400 kHz max. during ID phase. */
#define SD_MMC_CARD_ID_FREQUENCY 400000
+/*
+ * SDIO Direct & Extended I/O
+ */
+#define SD_IO_RW_WR (1u << 31)
+#define SD_IO_RW_FUNC(x) (((x) & 0x7) << 28)
+#define SD_IO_RW_RAW (1u << 27)
+#define SD_IO_RW_INCR (1u << 26)
+#define SD_IO_RW_ADR(x) (((x) & 0x1FFFF) << 9)
+#define SD_IO_RW_DAT(x) (((x) & 0xFF) << 0)
+#define SD_IO_RW_LEN(x) (((x) & 0xFF) << 0)
+
+#define SD_IOE_RW_LEN(x) (((x) & 0x1FF) << 0)
+#define SD_IOE_RW_BLK (1u << 27)
+
+/* Card Common Control Registers (CCCR) */
+#define SD_IO_CCCR_START 0x00000
+#define SD_IO_CCCR_SIZE 0x100
+#define SD_IO_CCCR_FN_ENABLE 0x02
+#define SD_IO_CCCR_FN_READY 0x03
+#define SD_IO_CCCR_INT_ENABLE 0x04
+#define SD_IO_CCCR_INT_PENDING 0x05
+#define SD_IO_CCCR_CTL 0x06
+#define CCCR_CTL_RES (1<<3)
+#define SD_IO_CCCR_BUS_WIDTH 0x07
+#define CCCR_BUS_WIDTH_4 (1<<1)
+#define CCCR_BUS_WIDTH_1 (1<<0)
+#define SD_IO_CCCR_CARDCAP 0x08
+#define SD_IO_CCCR_CISPTR 0x09 /* XXX 9-10, 10-11, or 9-12 */
+
+/* Function Basic Registers (FBR) */
+#define SD_IO_FBR_START 0x00100
+#define SD_IO_FBR_SIZE 0x00700
+
+/* Card Information Structure (CIS) */
+#define SD_IO_CIS_START 0x01000
+#define SD_IO_CIS_SIZE 0x17000
+
+/* CIS tuple codes (based on PC Card 16) */
+#define SD_IO_CISTPL_VERS_1 0x15
+#define SD_IO_CISTPL_MANFID 0x20
+#define SD_IO_CISTPL_FUNCID 0x21
+#define SD_IO_CISTPL_FUNCE 0x22
+#define SD_IO_CISTPL_END 0xff
+
+/* CISTPL_FUNCID codes */
+/* OpenBSD incorrectly defines 0x0c as FUNCTION_WLAN */
+/* #define SDMMC_FUNCTION_WLAN 0x0c */
+
/* OCR bits */
/*
diff --git a/sys/dev/sdhci/fsl_sdhci.c b/sys/dev/sdhci/fsl_sdhci.c
index 1b5f9371fb51d..08f5b94df83d1 100644
--- a/sys/dev/sdhci/fsl_sdhci.c
+++ b/sys/dev/sdhci/fsl_sdhci.c
@@ -33,6 +33,8 @@ __FBSDID("$FreeBSD$");
* This supports both eSDHC (earlier SoCs) and uSDHC (more recent SoCs).
*/
+#include "opt_mmccam.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/types.h>
@@ -911,7 +913,11 @@ fsl_sdhci_attach(device_t dev)
bus_generic_probe(dev);
bus_generic_attach(dev);
+#ifdef MMCCAM
+ sdhci_cam_start_slot(&sc->slot);
+#else
sdhci_start_slot(&sc->slot);
+#endif
return (0);
@@ -988,4 +994,7 @@ static driver_t fsl_sdhci_driver = {
DRIVER_MODULE(sdhci_fsl, simplebus, fsl_sdhci_driver, fsl_sdhci_devclass,
NULL, NULL);
MODULE_DEPEND(sdhci_fsl, sdhci, 1, 1, 1);
+
+#ifndef MMCCAM
MMC_DECLARE_BRIDGE(sdhci_fsl);
+#endif
diff --git a/sys/dev/sdhci/sdhci.c b/sys/dev/sdhci/sdhci.c
index 6f780cf0e057a..7504fb7bdc044 100644
--- a/sys/dev/sdhci/sdhci.c
+++ b/sys/dev/sdhci/sdhci.c
@@ -48,13 +48,21 @@ __FBSDID("$FreeBSD$");
#include <dev/mmc/mmcreg.h>
#include <dev/mmc/mmcbrvar.h>
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+
#include "mmcbr_if.h"
#include "sdhci.h"
#include "sdhci_if.h"
+#include "opt_mmccam.h"
+
SYSCTL_NODE(_hw, OID_AUTO, sdhci, CTLFLAG_RD, 0, "sdhci driver");
-static int sdhci_debug;
+static int sdhci_debug = 0;
SYSCTL_INT(_hw_sdhci, OID_AUTO, debug, CTLFLAG_RWTUN, &sdhci_debug, 0,
"Debug level");
u_int sdhci_quirk_clear = 0;
@@ -83,6 +91,14 @@ static void sdhci_start_data(struct sdhci_slot *slot, struct mmc_data *data);
static void sdhci_card_poll(void *);
static void sdhci_card_task(void *, int);
+/* CAM-related */
+int sdhci_cam_get_possible_host_clock(struct sdhci_slot *slot, int proposed_clock);
+static int sdhci_cam_update_ios(struct sdhci_slot *slot);
+static int sdhci_cam_request(struct sdhci_slot *slot, union ccb *ccb);
+static void sdhci_cam_action(struct cam_sim *sim, union ccb *ccb);
+static void sdhci_cam_poll(struct cam_sim *sim);
+static int sdhci_cam_settran_settings(struct sdhci_slot *slot, union ccb *ccb);
+
/* helper routines */
static void sdhci_dumpregs(struct sdhci_slot *slot);
static int slot_printf(struct sdhci_slot *slot, const char * fmt, ...)
@@ -252,7 +268,7 @@ sdhci_init(struct sdhci_slot *slot)
SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT |
SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL |
SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE |
- SDHCI_INT_ACMD12ERR;
+ SDHCI_INT_ACMD12ERR | SDHCI_INT_CARD_INT;
if (!(slot->quirks & SDHCI_QUIRK_POLL_CARD_PRESENT) &&
!(slot->opt & SDHCI_NON_REMOVABLE)) {
@@ -531,25 +547,87 @@ sdhci_card_task(void *arg, int pending __unused)
SDHCI_LOCK(slot);
if (SDHCI_GET_CARD_PRESENT(slot->bus, slot)) {
+#ifdef MMCCAM
+ if (slot->card_present == 0) {
+#else
if (slot->dev == NULL) {
+#endif
/* If card is present - attach mmc bus. */
if (bootverbose || sdhci_debug)
slot_printf(slot, "Card inserted\n");
+#ifdef MMCCAM
+ slot->card_present = 1;
+ union ccb *ccb;
+ uint32_t pathid;
+ pathid = cam_sim_path(slot->sim);
+ ccb = xpt_alloc_ccb_nowait();
+ if (ccb == NULL) {
+ slot_printf(slot, "Unable to alloc CCB for rescan\n");
+ SDHCI_UNLOCK(slot);
+ return;
+ }
+
+ /*
+ * We create a rescan request for BUS:0:0, since the card
+ * will be at lun 0.
+ */
+ if (xpt_create_path(&ccb->ccb_h.path, NULL, pathid,
+ /* target */ 0, /* lun */ 0) != CAM_REQ_CMP) {
+ slot_printf(slot, "Unable to create path for rescan\n");
+ SDHCI_UNLOCK(slot);
+ xpt_free_ccb(ccb);
+ return;
+ }
+ SDHCI_UNLOCK(slot);
+ xpt_rescan(ccb);
+#else
slot->dev = device_add_child(slot->bus, "mmc", -1);
device_set_ivars(slot->dev, slot);
SDHCI_UNLOCK(slot);
device_probe_and_attach(slot->dev);
+#endif
} else
SDHCI_UNLOCK(slot);
} else {
+#ifdef MMCCAM
+ if (slot->card_present == 1) {
+#else
if (slot->dev != NULL) {
+#endif
/* If no card present - detach mmc bus. */
if (bootverbose || sdhci_debug)
slot_printf(slot, "Card removed\n");
d = slot->dev;
slot->dev = NULL;
+#ifdef MMCCAM
+ slot->card_present = 0;
+ union ccb *ccb;
+ uint32_t pathid;
+ pathid = cam_sim_path(slot->sim);
+ ccb = xpt_alloc_ccb_nowait();
+ if (ccb == NULL) {
+ slot_printf(slot, "Unable to alloc CCB for rescan\n");
+ SDHCI_UNLOCK(slot);
+ return;
+ }
+
+ /*
+ * We create a rescan request for BUS:0:0, since the card
+ * will be at lun 0.
+ */
+ if (xpt_create_path(&ccb->ccb_h.path, NULL, pathid,
+ /* target */ 0, /* lun */ 0) != CAM_REQ_CMP) {
+ slot_printf(slot, "Unable to create path for rescan\n");
+ SDHCI_UNLOCK(slot);
+ xpt_free_ccb(ccb);
+ return;
+ }
+ SDHCI_UNLOCK(slot);
+ xpt_rescan(ccb);
+#else
SDHCI_UNLOCK(slot);
device_delete_child(slot->bus, d);
+#endif
} else
SDHCI_UNLOCK(slot);
}
@@ -571,7 +649,11 @@ sdhci_handle_card_present_locked(struct sdhci_slot *slot, bool is_present)
* because once power is removed, a full card re-init is needed, and
* that happens by deleting and recreating the child device.
*/
+#ifdef MMCCAM
+ was_present = slot->card_present;
+#else
was_present = slot->dev != NULL;
+#endif
if (!was_present && is_present) {
taskqueue_enqueue_timeout(taskqueue_swi_giant,
&slot->card_delayed_task, -SDHCI_INSERT_DELAY_TICKS);
@@ -607,6 +689,7 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num)
int err;
SDHCI_LOCK_INIT(slot);
+
slot->num = num;
slot->bus = dev;
@@ -938,6 +1021,10 @@ sdhci_generic_update_ios(device_t brdev, device_t reqdev)
struct sdhci_slot *slot = device_get_ivars(reqdev);
struct mmc_ios *ios = &slot->host.ios;
+ device_printf(brdev, "This is a bridge device\n");
+ device_printf(reqdev, "This is a request device\n");
+
+ slot_printf(slot, " <--- The locking slot is this\n");
SDHCI_LOCK(slot);
/* Do full reset on bus power down to clear from any state. */
if (ios->power_mode == power_off) {
@@ -1029,9 +1116,32 @@ done:
return (err);
}
+#ifdef MMCCAM
static void
sdhci_req_done(struct sdhci_slot *slot)
{
+ union ccb *ccb;
+ if (sdhci_debug > 1)
+ slot_printf(slot, "sdhci_req_done()\n");
+ if (slot->ccb != NULL && slot->curcmd != NULL) {
+ callout_stop(&slot->timeout_callout);
+ ccb = slot->ccb;
+ slot->ccb = NULL;
+ slot->curcmd = NULL;
+
+ /* Tell CAM the request is finished */
+ struct ccb_mmcio *mmcio;
+ mmcio = &ccb->mmcio;
+
+ ccb->ccb_h.status =
+ (mmcio->cmd.error == 0 ? CAM_REQ_CMP : CAM_REQ_CMP_ERR);
+ xpt_done(ccb);
+ }
+}
+#else
+static void
+sdhci_req_done(struct sdhci_slot *slot)
+{
struct mmc_request *req;
if (slot->req != NULL && slot->curcmd != NULL) {
@@ -1042,6 +1152,7 @@ sdhci_req_done(struct sdhci_slot *slot)
req->done(req);
}
}
+#endif
static void
sdhci_timeout(void *arg)
@@ -1072,8 +1183,16 @@ sdhci_set_transfer_mode(struct sdhci_slot *slot, struct mmc_data *data)
mode |= SDHCI_TRNS_MULTI;
if (data->flags & MMC_DATA_READ)
mode |= SDHCI_TRNS_READ;
+#ifdef MMCCAM
+ struct ccb_mmcio *mmcio;
+ mmcio = &slot->ccb->mmcio;
+ if (mmcio->stop.opcode == MMC_STOP_TRANSMISSION
+ && !(slot->quirks & SDHCI_QUIRK_BROKEN_AUTO_STOP))
+ mode |= SDHCI_TRNS_ACMD12;
+#else
if (slot->req->stop && !(slot->quirks & SDHCI_QUIRK_BROKEN_AUTO_STOP))
mode |= SDHCI_TRNS_ACMD12;
+#endif
if (slot->flags & SDHCI_USE_DMA)
mode |= SDHCI_TRNS_DMA;
@@ -1106,6 +1225,9 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
if (!SDHCI_GET_CARD_PRESENT(slot->bus, slot) ||
slot->power == 0 ||
slot->clock == 0) {
+ slot_printf(slot,
+ "Cannot issue a command (power=%d clock=%d)",
+ slot->power, slot->clock);
cmd->error = MMC_ERR_FAILED;
sdhci_req_done(slot);
return;
@@ -1113,11 +1235,17 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
/* Always wait for free CMD bus. */
mask = SDHCI_CMD_INHIBIT;
/* Wait for free DAT if we have data or busy signal. */
- if (cmd->data || (cmd->flags & MMC_RSP_BUSY))
+ if (cmd->data != NULL || (cmd->flags & MMC_RSP_BUSY))
mask |= SDHCI_DAT_INHIBIT;
/* We shouldn't wait for DAT for stop commands. */
+#ifdef MMCCAM
+ struct ccb_mmcio *mmcio = &slot->ccb->mmcio;
+ if (cmd == &mmcio->stop)
+ mask &= ~SDHCI_DAT_INHIBIT;
+#else
if (cmd == slot->req->stop)
mask &= ~SDHCI_DAT_INHIBIT;
+#endif
/*
* Wait for bus no more then 250 ms. Typically there will be no wait
* here at all, but when writing a crash dump we may be bypassing the
@@ -1155,7 +1283,7 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
flags |= SDHCI_CMD_CRC;
if (cmd->flags & MMC_RSP_OPCODE)
flags |= SDHCI_CMD_INDEX;
- if (cmd->data)
+ if (cmd->data != NULL)
flags |= SDHCI_CMD_DATA;
if (cmd->opcode == MMC_STOP_TRANSMISSION)
flags |= SDHCI_CMD_TYPE_ABORT;
@@ -1174,6 +1302,8 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
WR4(slot, SDHCI_ARGUMENT, cmd->arg);
/* Set data transfer mode. */
sdhci_set_transfer_mode(slot, cmd->data);
+ if (sdhci_debug > 1)
+ slot_printf(slot, "Starting command!\n");
/* Start command. */
WR2(slot, SDHCI_COMMAND_FLAGS, (cmd->opcode << 8) | (flags & 0xff));
/* Start timeout callout. */
@@ -1188,6 +1318,9 @@ sdhci_finish_command(struct sdhci_slot *slot)
uint32_t val;
uint8_t extra;
+ if (sdhci_debug > 1)
+ slot_printf(slot, "%s: called, err %d flags %d\n",
+ __func__, slot->curcmd->error, slot->curcmd->flags);
slot->cmd_done = 1;
/*
* Interrupt aggregation: Restore command interrupt.
@@ -1221,6 +1354,11 @@ sdhci_finish_command(struct sdhci_slot *slot)
} else
slot->curcmd->resp[0] = RD4(slot, SDHCI_RESPONSE);
}
+ if (sdhci_debug > 1)
+ printf("Resp: %02x %02x %02x %02x\n",
+ slot->curcmd->resp[0], slot->curcmd->resp[1],
+ slot->curcmd->resp[2], slot->curcmd->resp[3]);
+
/* If data ready - finish. */
if (slot->data_done)
sdhci_start(slot);
@@ -1301,6 +1439,11 @@ sdhci_start_data(struct sdhci_slot *slot, struct mmc_data *data)
(data->len < 512) ? data->len : 512));
/* Set block count. */
WR2(slot, SDHCI_BLOCK_COUNT, (data->len + 511) / 512);
+
+ if (sdhci_debug > 1)
+ slot_printf(slot, "Block size: %02x, count %lu\n", (unsigned int)
+ SDHCI_MAKE_BLKSZ(DMA_BOUNDARY, (data->len < 512)?data->len:512),
+ (unsigned long)(data->len + 511) / 512);
}
void
@@ -1342,6 +1485,47 @@ sdhci_finish_data(struct sdhci_slot *slot)
sdhci_start(slot);
}
+#ifdef MMCCAM
+static void
+sdhci_start(struct sdhci_slot *slot)
+{
+ union ccb *ccb;
+
+ ccb = slot->ccb;
+ if (ccb == NULL)
+ return;
+
+ struct ccb_mmcio *mmcio;
+ mmcio = &ccb->mmcio;
+
+ if (!(slot->flags & CMD_STARTED)) {
+ slot->flags |= CMD_STARTED;
+ sdhci_start_command(slot, &mmcio->cmd);
+ return;
+ }
+
+ /*
+ * Old stack doesn't use this!
+ * Enabling this code causes significant performance degradation
+ * and IRQ storms on BBB, Wandboard behaves fine.
+ * Not using this code does no harm...
+ if (!(slot->flags & STOP_STARTED) && mmcio->stop.opcode != 0) {
+ slot->flags |= STOP_STARTED;
+ sdhci_start_command(slot, &mmcio->stop);
+ return;
+ }
+ */
+ if (sdhci_debug > 1)
+ slot_printf(slot, "result: %d\n", mmcio->cmd.error);
+ if (mmcio->cmd.error == 0 &&
+ (slot->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)) {
+ sdhci_reset(slot, SDHCI_RESET_CMD);
+ sdhci_reset(slot, SDHCI_RESET_DATA);
+ }
+
+ sdhci_req_done(slot);
+}
+#else
static void
sdhci_start(struct sdhci_slot *slot)
{
@@ -1374,6 +1558,7 @@ sdhci_start(struct sdhci_slot *slot)
sdhci_req_done(slot);
}
+#endif
int
sdhci_generic_request(device_t brdev __unused, device_t reqdev,
@@ -1472,7 +1657,6 @@ static void
sdhci_data_irq(struct sdhci_slot *slot, uint32_t intmask)
{
struct mmc_data *data;
- size_t left;
if (!slot->curcmd) {
slot_printf(slot, "Got data interrupt 0x%08x, but "
@@ -1518,6 +1702,7 @@ sdhci_data_irq(struct sdhci_slot *slot, uint32_t intmask)
/* Handle DMA border. */
if (intmask & SDHCI_INT_DMA_END) {
data = slot->curcmd->data;
+ size_t left;
/* Unload DMA buffer ... */
left = data->len - slot->offset;
@@ -1641,6 +1826,10 @@ sdhci_generic_intr(struct sdhci_slot *slot)
"Card is consuming too much power!\n");
intmask &= ~SDHCI_INT_BUS_POWER;
}
+ /* Handle card interrupt. */
+ if (intmask & SDHCI_INT_CARD_INT) {
+
+ }
/* The rest is unknown. */
if (intmask) {
WR4(slot, SDHCI_INT_STATUS, intmask);
@@ -1724,6 +1913,7 @@ sdhci_generic_write_ivar(device_t bus, device_t child, int which,
uint32_t clock, max_clock;
int i;
+ slot_printf(slot, "sdhci_generic_write_ivar, var=%d\n", which);
switch (which) {
default:
return (EINVAL);
@@ -1789,4 +1979,323 @@ sdhci_generic_write_ivar(device_t bus, device_t child, int which,
return (0);
}
+/* CAM-related functions */
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+
+void
+sdhci_cam_start_slot(struct sdhci_slot *slot)
+{
+ if ((slot->devq = cam_simq_alloc(1)) == NULL) {
+ goto fail;
+ }
+
+ mtx_init(&slot->sim_mtx, "sdhcisim", NULL, MTX_DEF);
+ slot->sim = cam_sim_alloc(sdhci_cam_action, sdhci_cam_poll,
+ "sdhci_slot", slot, device_get_unit(slot->bus),
+ &slot->sim_mtx, 1, 1, slot->devq);
+
+ if (slot->sim == NULL) {
+ cam_simq_free(slot->devq);
+ slot_printf(slot, "cannot allocate CAM SIM\n");
+ goto fail;
+ }
+
+ mtx_lock(&slot->sim_mtx);
+ if (xpt_bus_register(slot->sim, slot->bus, 0) != 0) {
+ slot_printf(slot,
+ "cannot register SCSI pass-through bus\n");
+ cam_sim_free(slot->sim, FALSE);
+ cam_simq_free(slot->devq);
+ mtx_unlock(&slot->sim_mtx);
+ goto fail;
+ }
+
+ mtx_unlock(&slot->sim_mtx);
+ /* End CAM-specific init */
+ slot->card_present = 0;
+ sdhci_card_task(slot, 0);
+ return;
+
+fail:
+ if (slot->sim != NULL) {
+ mtx_lock(&slot->sim_mtx);
+ xpt_bus_deregister(cam_sim_path(slot->sim));
+ cam_sim_free(slot->sim, FALSE);
+ mtx_unlock(&slot->sim_mtx);
+ }
+
+ if (slot->devq != NULL)
+ cam_simq_free(slot->devq);
+}
+
+static void
+sdhci_cam_handle_mmcio(struct cam_sim *sim, union ccb *ccb)
+{
+ struct sdhci_slot *slot;
+
+ slot = cam_sim_softc(sim);
+
+ sdhci_cam_request(slot, ccb);
+}
+
+void
+sdhci_cam_action(struct cam_sim *sim, union ccb *ccb)
+{
+ struct sdhci_slot *slot;
+
+ slot = cam_sim_softc(sim);
+ if (slot == NULL) {
+ ccb->ccb_h.status = CAM_SEL_TIMEOUT;
+ xpt_done(ccb);
+ return;
+ }
+
+ mtx_assert(&slot->sim_mtx, MA_OWNED);
+
+ switch (ccb->ccb_h.func_code) {
+ case XPT_PATH_INQ:
+ {
+ struct ccb_pathinq *cpi;
+
+ cpi = &ccb->cpi;
+ cpi->version_num = 1;
+ cpi->hba_inquiry = 0;
+ cpi->target_sprt = 0;
+ cpi->hba_misc = PIM_NOBUSRESET | PIM_SEQSCAN;
+ cpi->hba_eng_cnt = 0;
+ cpi->max_target = 0;
+ cpi->max_lun = 0;
+ cpi->initiator_id = 1;
+ cpi->maxio = MAXPHYS;
+ strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
+ strncpy(cpi->hba_vid, "Deglitch Networks", HBA_IDLEN);
+ strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
+ cpi->unit_number = cam_sim_unit(sim);
+ cpi->bus_id = cam_sim_bus(sim);
+ cpi->base_transfer_speed = 100; /* XXX WTF? */
+ cpi->protocol = PROTO_MMCSD;
+ cpi->protocol_version = SCSI_REV_0;
+ cpi->transport = XPORT_MMCSD;
+ cpi->transport_version = 0;
+
+ cpi->ccb_h.status = CAM_REQ_CMP;
+ break;
+ }
+ case XPT_GET_TRAN_SETTINGS:
+ {
+ struct ccb_trans_settings *cts = &ccb->cts;
+
+ if (sdhci_debug > 1)
+ slot_printf(slot, "Got XPT_GET_TRAN_SETTINGS\n");
+
+ cts->protocol = PROTO_MMCSD;
+ cts->protocol_version = 1;
+ cts->transport = XPORT_MMCSD;
+ cts->transport_version = 1;
+ cts->xport_specific.valid = 0;
+ cts->proto_specific.mmc.host_ocr = slot->host.host_ocr;
+ cts->proto_specific.mmc.host_f_min = slot->host.f_min;
+ cts->proto_specific.mmc.host_f_max = slot->host.f_max;
+ cts->proto_specific.mmc.host_caps = slot->host.caps;
+ memcpy(&cts->proto_specific.mmc.ios, &slot->host.ios, sizeof(struct mmc_ios));
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ break;
+ }
+ case XPT_SET_TRAN_SETTINGS:
+ {
+ if (sdhci_debug > 1)
+ slot_printf(slot, "Got XPT_SET_TRAN_SETTINGS\n");
+ sdhci_cam_settran_settings(slot, ccb);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ break;
+ }
+ case XPT_RESET_BUS:
+ if (sdhci_debug > 1)
+ slot_printf(slot, "Got XPT_RESET_BUS, ACK it...\n");
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ break;
+ case XPT_MMC_IO:
+ /*
+ * Here is the HW-dependent part of
+ * sending the command to the underlying h/w
+ * At some point in the future an interrupt comes.
+ * Then the request will be marked as completed.
+ */
+ if (sdhci_debug > 1)
+ slot_printf(slot, "Got XPT_MMC_IO\n");
+ ccb->ccb_h.status = CAM_REQ_INPROG;
+
+ sdhci_cam_handle_mmcio(sim, ccb);
+ return;
+ /* NOTREACHED */
+ break;
+ default:
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ break;
+ }
+ xpt_done(ccb);
+ return;
+}
+
+void
+sdhci_cam_poll(struct cam_sim *sim)
+{
+ return;
+}
+
+int sdhci_cam_get_possible_host_clock(struct sdhci_slot *slot, int proposed_clock) {
+ int max_clock, clock, i;
+
+ if (proposed_clock == 0)
+ return 0;
+ max_clock = slot->max_clk;
+ clock = max_clock;
+
+ if (slot->version < SDHCI_SPEC_300) {
+ for (i = 0; i < SDHCI_200_MAX_DIVIDER;
+ i <<= 1) {
+ if (clock <= proposed_clock)
+ break;
+ clock >>= 1;
+ }
+ } else {
+ for (i = 0; i < SDHCI_300_MAX_DIVIDER;
+ i += 2) {
+ if (clock <= proposed_clock)
+ break;
+ clock = max_clock / (i + 2);
+ }
+ }
+ return clock;
+}
+
+int
+sdhci_cam_settran_settings(struct sdhci_slot *slot, union ccb *ccb)
+{
+ struct mmc_ios *ios;
+ struct mmc_ios *new_ios;
+ struct ccb_trans_settings_mmc *cts;
+
+ ios = &slot->host.ios;
+
+ cts = &ccb->cts.proto_specific.mmc;
+ new_ios = &cts->ios;
+
+ /* Update only requested fields */
+ if (cts->ios_valid & MMC_CLK) {
+ ios->clock = sdhci_cam_get_possible_host_clock(slot, new_ios->clock);
+ slot_printf(slot, "Clock => %d\n", ios->clock);
+ }
+ if (cts->ios_valid & MMC_VDD) {
+ ios->vdd = new_ios->vdd;
+ slot_printf(slot, "VDD => %d\n", ios->vdd);
+ }
+ if (cts->ios_valid & MMC_CS) {
+ ios->chip_select = new_ios->chip_select;
+ slot_printf(slot, "CS => %d\n", ios->chip_select);
+ }
+ if (cts->ios_valid & MMC_BW) {
+ ios->bus_width = new_ios->bus_width;
+ slot_printf(slot, "Bus width => %d\n", ios->bus_width);
+ }
+ if (cts->ios_valid & MMC_PM) {
+ ios->power_mode = new_ios->power_mode;
+ slot_printf(slot, "Power mode => %d\n", ios->power_mode);
+ }
+ if (cts->ios_valid & MMC_BT) {
+ ios->timing = new_ios->timing;
+ slot_printf(slot, "Timing => %d\n", ios->timing);
+ }
+ if (cts->ios_valid & MMC_BM) {
+ ios->bus_mode = new_ios->bus_mode;
+ slot_printf(slot, "Bus mode => %d\n", ios->bus_mode);
+ }
+
+ /* XXX Provide a way to call a chip-specific IOS update, required for TI */
+ return (sdhci_cam_update_ios(slot));
+}
+
+int
+sdhci_cam_update_ios(struct sdhci_slot *slot)
+{
+ struct mmc_ios *ios = &slot->host.ios;
+
+ slot_printf(slot, "%s: power_mode=%d, clk=%d, bus_width=%d, timing=%d\n",
+ __func__, ios->power_mode, ios->clock, ios->bus_width, ios->timing);
+ SDHCI_LOCK(slot);
+ /* Do full reset on bus power down to clear from any state. */
+ if (ios->power_mode == power_off) {
+ WR4(slot, SDHCI_SIGNAL_ENABLE, 0);
+ sdhci_init(slot);
+ }
+ /* Configure the bus. */
+ sdhci_set_clock(slot, ios->clock);
+ sdhci_set_power(slot, (ios->power_mode == power_off) ? 0 : ios->vdd);
+ if (ios->bus_width == bus_width_8) {
+ slot->hostctrl |= SDHCI_CTRL_8BITBUS;
+ slot->hostctrl &= ~SDHCI_CTRL_4BITBUS;
+ } else if (ios->bus_width == bus_width_4) {
+ slot->hostctrl &= ~SDHCI_CTRL_8BITBUS;
+ slot->hostctrl |= SDHCI_CTRL_4BITBUS;
+ } else if (ios->bus_width == bus_width_1) {
+ slot->hostctrl &= ~SDHCI_CTRL_8BITBUS;
+ slot->hostctrl &= ~SDHCI_CTRL_4BITBUS;
+ } else {
+ panic("Invalid bus width: %d", ios->bus_width);
+ }
+ if (ios->timing == bus_timing_hs &&
+ !(slot->quirks & SDHCI_QUIRK_DONT_SET_HISPD_BIT))
+ slot->hostctrl |= SDHCI_CTRL_HISPD;
+ else
+ slot->hostctrl &= ~SDHCI_CTRL_HISPD;
+ WR1(slot, SDHCI_HOST_CONTROL, slot->hostctrl);
+ /* Some controllers like reset after bus changes. */
+ if(slot->quirks & SDHCI_QUIRK_RESET_ON_IOS)
+ sdhci_reset(slot, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+
+ SDHCI_UNLOCK(slot);
+ return (0);
+}
+
+int
+sdhci_cam_request(struct sdhci_slot *slot, union ccb *ccb)
+{
+ struct ccb_mmcio *mmcio;
+
+ mmcio = &ccb->mmcio;
+
+ SDHCI_LOCK(slot);
+/* if (slot->req != NULL) {
+ SDHCI_UNLOCK(slot);
+ return (EBUSY);
+ }
+*/
+ if (sdhci_debug > 1) {
+ slot_printf(slot, "CMD%u arg %#x flags %#x dlen %u dflags %#x\n",
+ mmcio->cmd.opcode, mmcio->cmd.arg, mmcio->cmd.flags,
+ mmcio->cmd.data != NULL ? (unsigned int) mmcio->cmd.data->len : 0,
+ mmcio->cmd.data != NULL ? mmcio->cmd.data->flags: 0);
+ }
+ if (mmcio->cmd.data != NULL) {
+ if (mmcio->cmd.data->len == 0 || mmcio->cmd.data->flags == 0)
+ panic("data->len = %d, data->flags = %d -- something is b0rked",
+ (int)mmcio->cmd.data->len, mmcio->cmd.data->flags);
+ }
+ slot->ccb = ccb;
+ slot->flags = 0;
+ sdhci_start(slot);
+ SDHCI_UNLOCK(slot);
+ if (dumping) {
+ while (slot->ccb != NULL) {
+ sdhci_generic_intr(slot);
+ DELAY(10);
+ }
+ }
+ return (0);
+}
+
MODULE_VERSION(sdhci, 1);
diff --git a/sys/dev/sdhci/sdhci.h b/sys/dev/sdhci/sdhci.h
index 814f81ed54071..7be4be2632ee1 100644
--- a/sys/dev/sdhci/sdhci.h
+++ b/sys/dev/sdhci/sdhci.h
@@ -367,6 +367,13 @@ struct sdhci_slot {
#define SDHCI_USE_DMA 4 /* Use DMA for this req. */
#define PLATFORM_DATA_STARTED 8 /* Data xfer is handled by platform */
struct mtx mtx; /* Slot mutex */
+
+ /* CAM stuff */
+ union ccb *ccb;
+ struct cam_devq *devq;
+ struct cam_sim *sim;
+ struct mtx sim_mtx;
+ u_char card_present; /* XXX Maybe derive this from elsewhere? */
};
int sdhci_generic_read_ivar(device_t bus, device_t child, int which,
@@ -393,4 +400,6 @@ bool sdhci_generic_get_card_present(device_t brdev, struct sdhci_slot *slot);
void sdhci_generic_set_uhs_timing(device_t brdev, struct sdhci_slot *slot);
void sdhci_handle_card_present(struct sdhci_slot *slot, bool is_present);
+/* CAM-related */
+void sdhci_cam_start_slot(struct sdhci_slot *slot);
#endif /* __SDHCI_H__ */
diff --git a/sys/dev/sdhci/sdhci_acpi.c b/sys/dev/sdhci/sdhci_acpi.c
index ea5ed70b08773..e6c63824baf91 100644
--- a/sys/dev/sdhci/sdhci_acpi.c
+++ b/sys/dev/sdhci/sdhci_acpi.c
@@ -400,4 +400,7 @@ static devclass_t sdhci_acpi_devclass;
DRIVER_MODULE(sdhci_acpi, acpi, sdhci_acpi_driver, sdhci_acpi_devclass, NULL,
NULL);
MODULE_DEPEND(sdhci_acpi, sdhci, 1, 1, 1);
+
+#ifndef MMCCAM
MMC_DECLARE_BRIDGE(sdhci_acpi);
+#endif
diff --git a/sys/dev/sdhci/sdhci_pci.c b/sys/dev/sdhci/sdhci_pci.c
index 9c59d322d6e66..de798cc29e180 100644
--- a/sys/dev/sdhci/sdhci_pci.c
+++ b/sys/dev/sdhci/sdhci_pci.c
@@ -26,6 +26,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include "opt_mmccam.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
@@ -396,8 +398,13 @@ sdhci_pci_attach(device_t dev)
device_printf(dev, "Can't setup IRQ\n");
pci_enable_busmaster(dev);
/* Process cards detection. */
- for (i = 0; i < sc->num_slots; i++)
+ for (i = 0; i < sc->num_slots; i++) {
+#ifdef MMCCAM
+ sdhci_cam_start_slot(&sc->slots[i]);
+#else
sdhci_start_slot(&sc->slots[i]);
+#endif
+ }
return (0);
}
@@ -518,4 +525,7 @@ static devclass_t sdhci_pci_devclass;
DRIVER_MODULE(sdhci_pci, pci, sdhci_pci_driver, sdhci_pci_devclass, NULL,
NULL);
MODULE_DEPEND(sdhci_pci, sdhci, 1, 1, 1);
+
+#ifndef MMCCAM
MMC_DECLARE_BRIDGE(sdhci_pci);
+#endif
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index 21d68575da468..88a9fdd8ba516 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -251,8 +251,6 @@ SUBDIR= \
${_mlx5} \
${_mlx5en} \
${_mly} \
- mmc \
- mmcsd \
mpr \
mps \
mpt \