Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
7694 lines (7548 sloc) 242 KB
From 7ad116121c7170f69e4d85f3443374f870a2a5d2 Mon Sep 17 00:00:00 2001
From: Udit kumar agarwal <dev.madaarigmail.com>
Date: Mon, 2 Jul 2018 04:43:35 +0530
Subject: [PATCH] Added MMCCAM/SDIO stack to freebsd-head 642b174dadd
Backported MMCCAM stack from freebsd-head d6756a3ac8 to freebsd-head 642b174dadd
---
etc/mtree/BSD.include.dist | 2 +
include/Makefile | 2 +-
lib/libcam/Makefile | 3 +-
sys/amd64/conf/MMCCAM | 36 +
sys/arm/broadcom/bcm2835/bcm2835_sdhci.c | 4 +
sys/arm/conf/BEAGLEBONE-MMCCAM | 21 +
sys/arm/conf/GENERIC-MMCCAM | 22 +
sys/arm/nvidia/tegra_sdhci.c | 4 +
sys/arm/ti/ti_sdhci.c | 29 +-
sys/cam/ata/ata_all.c | 2 +-
sys/cam/ata/ata_da.c | 6 +-
sys/cam/ata/ata_pmp.c | 2 +-
sys/cam/ata/ata_xpt.c | 4 +-
sys/cam/cam.h | 2 +-
sys/cam/cam_ccb.h | 78 +-
sys/cam/cam_periph.c | 16 +-
sys/cam/cam_periph.h | 2 +-
sys/cam/cam_xpt.c | 18 +-
sys/cam/cam_xpt.h | 13 +
sys/cam/cam_xpt_internal.h | 1 +
sys/cam/mmc/mmc.h | 104 ++
sys/cam/mmc/mmc_all.h | 70 ++
sys/cam/mmc/mmc_bus.h | 5 +
sys/cam/mmc/mmc_da.c | 1898 ++++++++++++++++++++++++++++++
sys/cam/mmc/mmc_xpt.c | 1107 +++++++++++++++++
sys/cam/nvme/nvme_da.c | 6 +-
sys/cam/scsi/scsi_all.c | 2 +-
sys/cam/scsi/scsi_cd.c | 3 +-
sys/cam/scsi/scsi_ch.c | 3 +-
sys/cam/scsi/scsi_da.c | 7 +-
sys/cam/scsi/scsi_enc.c | 4 +-
sys/cam/scsi/scsi_pass.c | 5 +-
sys/cam/scsi/scsi_pt.c | 3 +-
sys/cam/scsi/scsi_sa.c | 6 +-
sys/cam/scsi/scsi_sg.c | 3 +-
sys/cam/scsi/scsi_xpt.c | 32 +-
sys/conf/files | 9 +-
sys/conf/options | 2 +
sys/ddb/db_sym.c | 2 +-
sys/dev/aic7xxx/aic79xx_osm.h | 2 +-
sys/dev/aic7xxx/aic7xxx_osm.h | 2 +-
sys/dev/drm/drmP.h | 2 +-
sys/dev/isci/environment.h | 2 +-
sys/dev/mmc/bridge.h | 2 +
sys/dev/mmc/host/dwmmc.c | 4 +
sys/dev/mmc/host/dwmmc_altera.c | 95 ++
sys/dev/mmc/host/dwmmc_rockchip.c | 152 +++
sys/dev/mmc/host/dwmmc_samsung.c | 132 +++
sys/dev/mmc/mmcbrvar.h | 1 -
sys/dev/mmc/mmcreg.h | 102 ++
sys/dev/sdhci/fsl_sdhci.c | 5 +
sys/dev/sdhci/sdhci.c | 563 ++++++++-
sys/dev/sdhci/sdhci.h | 13 +
sys/dev/sdhci/sdhci_acpi.c | 3 +
sys/dev/sdhci/sdhci_fdt.c | 50 +-
sys/dev/sdhci/sdhci_pci.c | 8 +-
sys/modules/sdhci/Makefile | 2 +-
usr.bin/sdiotool/Makefile | 9 +
usr.bin/sdiotool/brcmfmac_bus.h | 28 +
usr.bin/sdiotool/brcmfmac_sdio.h | 162 +++
usr.bin/sdiotool/cam_sdio.c | 440 +++++++
usr.bin/sdiotool/cam_sdio.h | 97 ++
usr.bin/sdiotool/linux_compat.h | 60 +
usr.bin/sdiotool/linux_sdio_compat.c | 104 ++
usr.bin/sdiotool/linux_sdio_compat.h | 65 +
usr.bin/sdiotool/sdiotool.c | 649 ++++++++++
66 files changed, 6190 insertions(+), 102 deletions(-)
create mode 100644 sys/amd64/conf/MMCCAM
create mode 100644 sys/arm/conf/BEAGLEBONE-MMCCAM
create mode 100644 sys/arm/conf/GENERIC-MMCCAM
create mode 100644 sys/cam/mmc/mmc.h
create mode 100644 sys/cam/mmc/mmc_all.h
create mode 100644 sys/cam/mmc/mmc_bus.h
create mode 100644 sys/cam/mmc/mmc_da.c
create mode 100644 sys/cam/mmc/mmc_xpt.c
create mode 100644 sys/dev/mmc/host/dwmmc_altera.c
create mode 100644 sys/dev/mmc/host/dwmmc_rockchip.c
create mode 100644 sys/dev/mmc/host/dwmmc_samsung.c
create mode 100644 usr.bin/sdiotool/Makefile
create mode 100644 usr.bin/sdiotool/brcmfmac_bus.h
create mode 100644 usr.bin/sdiotool/brcmfmac_sdio.h
create mode 100644 usr.bin/sdiotool/cam_sdio.c
create mode 100644 usr.bin/sdiotool/cam_sdio.h
create mode 100644 usr.bin/sdiotool/linux_compat.h
create mode 100644 usr.bin/sdiotool/linux_sdio_compat.c
create mode 100644 usr.bin/sdiotool/linux_sdio_compat.h
create mode 100644 usr.bin/sdiotool/sdiotool.c
diff --git a/etc/mtree/BSD.include.dist b/etc/mtree/BSD.include.dist
index 15e9807ea73..5383841c3b2 100644
--- a/etc/mtree/BSD.include.dist
+++ b/etc/mtree/BSD.include.dist
@@ -90,6 +90,8 @@
cam
ata
..
+ mmc
+ ..
nvme
..
scsi
diff --git a/include/Makefile b/include/Makefile
index e8ad0030a7c..170d6fa35de 100644
--- a/include/Makefile
+++ b/include/Makefile
@@ -42,7 +42,7 @@ LHDRS= aio.h errno.h fcntl.h linker_set.h poll.h stdatomic.h stdint.h \
LDIRS= bsm cam geom net net80211 netgraph netinet netinet6 \
netipsec netnatm netsmb nfs nfsclient nfsserver sys vm
-LSUBDIRS= cam/ata cam/nvme cam/scsi \
+LSUBDIRS= cam/ata cam/mmc cam/nvme cam/scsi \
dev/acpica dev/agp dev/an dev/bktr dev/ciss dev/filemon dev/firewire \
dev/hwpmc dev/hyperv \
dev/ic dev/iicbus dev/io dev/lmc dev/mfi dev/mmc dev/nvme \
diff --git a/lib/libcam/Makefile b/lib/libcam/Makefile
index 3cf1a0d71db..8aac81c60e1 100644
--- a/lib/libcam/Makefile
+++ b/lib/libcam/Makefile
@@ -37,7 +37,8 @@ MLINKS+= cam.3 cam_open_device.3 \
cam_cdbparse.3 buff_encode_visit.3
.PATH: ${SRCTOP}/sys/cam/scsi ${SRCTOP}/sys/cam/ata \
- ${SRCTOP}/sys/cam
+ ${SRCTOP}/sys/cam \
+ ${SRCTOP}/sys/cam/mmc
CFLAGS+= -I${.CURDIR} -I${SRCTOP}/sys
diff --git a/sys/amd64/conf/MMCCAM b/sys/amd64/conf/MMCCAM
new file mode 100644
index 00000000000..c8bbeb817f5
--- /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 1954b7d40a6..e39d4e6445b 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"
@@ -679,4 +681,6 @@ static driver_t bcm_sdhci_driver = {
DRIVER_MODULE(sdhci_bcm, simplebus, bcm_sdhci_driver, bcm_sdhci_devclass,
NULL, NULL);
MODULE_DEPEND(sdhci_bcm, sdhci, 1, 1, 1);
+#ifndef MMCCAM
MMC_DECLARE_BRIDGE(sdhci_bcm);
+#endif
diff --git a/sys/arm/conf/BEAGLEBONE-MMCCAM b/sys/arm/conf/BEAGLEBONE-MMCCAM
new file mode 100644
index 00000000000..3d83352e79c
--- /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/conf/GENERIC-MMCCAM b/sys/arm/conf/GENERIC-MMCCAM
new file mode 100644
index 00000000000..20ca990d1c3
--- /dev/null
+++ b/sys/arm/conf/GENERIC-MMCCAM
@@ -0,0 +1,22 @@
+#
+# GEMERIC-MMCCAM
+#
+# Custom kernel for GENERIC plus MMCCAM as opposed to the prior MMC stack.
+#
+# $FreeBSD$
+
+include GENERIC
+
+ident GENERIC-MMCCAM
+
+options MMCCAM
+
+# Add CAMDEBUG stuff
+options CAMDEBUG
+options CAM_DEBUG_FLAGS=(CAM_DEBUG_INFO|CAM_DEBUG_PROBE|CAM_DEBUG_PERIPH)
+
+# pass(4) device
+device pass
+
+nodevice mmc
+nodevice mmcsd
diff --git a/sys/arm/nvidia/tegra_sdhci.c b/sys/arm/nvidia/tegra_sdhci.c
index 0e9921fe2d6..9693ea8101e 100644
--- a/sys/arm/nvidia/tegra_sdhci.c
+++ b/sys/arm/nvidia/tegra_sdhci.c
@@ -62,6 +62,8 @@ __FBSDID("$FreeBSD$");
#include "sdhci_if.h"
+#include "opt_mmccam.h"
+
/* Tegra SDHOST controller vendor register definitions */
#define SDMMC_VENDOR_CLOCK_CNTRL 0x100
#define VENDOR_CLOCK_CNTRL_CLK_SHIFT 8
@@ -463,5 +465,7 @@ static DEFINE_CLASS_0(sdhci, tegra_sdhci_driver, tegra_sdhci_methods,
sizeof(struct tegra_sdhci_softc));
DRIVER_MODULE(sdhci_tegra, simplebus, tegra_sdhci_driver, tegra_sdhci_devclass,
NULL, NULL);
+#ifndef MMCCAM
MODULE_DEPEND(sdhci_tegra, sdhci, 1, 1, 1);
MMC_DECLARE_BRIDGE(sdhci);
+#endif
diff --git a/sys/arm/ti/ti_sdhci.c b/sys/arm/ti/ti_sdhci.c
index 6b188ddd072..cb7e9da65c8 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);
@@ -659,7 +684,6 @@ ti_sdhci_attach(device_t dev)
bus_generic_attach(dev);
sdhci_start_slot(&sc->slot);
-
return (0);
fail:
@@ -730,4 +754,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/ata/ata_all.c b/sys/cam/ata/ata_all.c
index 34aa704cfb1..1aba70e76a2 100644
--- a/sys/cam/ata/ata_all.c
+++ b/sys/cam/ata/ata_all.c
@@ -30,7 +30,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#ifdef _KERNEL
-#include <opt_scsi.h>
+#include "opt_scsi.h"
#include <sys/systm.h>
#include <sys/libkern.h>
diff --git a/sys/cam/ata/ata_da.c b/sys/cam/ata/ata_da.c
index a07a7cd8c18..a11b0447919 100644
--- a/sys/cam/ata/ata_da.c
+++ b/sys/cam/ata/ata_da.c
@@ -1060,7 +1060,7 @@ adadump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t len
xpt_polled_action(&ccb);
error = cam_periph_error(&ccb,
- 0, SF_NO_RECOVERY | SF_NO_RETRY, NULL);
+ 0, SF_NO_RECOVERY | SF_NO_RETRY);
if ((ccb.ccb_h.status & CAM_DEV_QFRZN) != 0)
cam_release_devq(ccb.ccb_h.path, /*relsim_flags*/0,
/*reduction*/0, /*timeout*/0, /*getcount_only*/0);
@@ -1096,7 +1096,7 @@ adadump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t len
xpt_polled_action(&ccb);
error = cam_periph_error(&ccb,
- 0, SF_NO_RECOVERY | SF_NO_RETRY, NULL);
+ 0, SF_NO_RECOVERY | SF_NO_RETRY);
if ((ccb.ccb_h.status & CAM_DEV_QFRZN) != 0)
cam_release_devq(ccb.ccb_h.path, /*relsim_flags*/0,
/*reduction*/0, /*timeout*/0, /*getcount_only*/0);
@@ -3309,7 +3309,7 @@ adaerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
}
#endif
- return(cam_periph_error(ccb, cam_flags, sense_flags, NULL));
+ return(cam_periph_error(ccb, cam_flags, sense_flags));
}
static void
diff --git a/sys/cam/ata/ata_pmp.c b/sys/cam/ata/ata_pmp.c
index 1b28fdd0338..acd92f7aa74 100644
--- a/sys/cam/ata/ata_pmp.c
+++ b/sys/cam/ata/ata_pmp.c
@@ -595,7 +595,7 @@ pmpdone(struct cam_periph *periph, union ccb *done_ccb)
priority = done_ccb->ccb_h.pinfo.priority;
if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
- if (cam_periph_error(done_ccb, 0, 0, NULL) == ERESTART) {
+ if (cam_periph_error(done_ccb, 0, 0) == ERESTART) {
return;
} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
cam_release_devq(done_ccb->ccb_h.path,
diff --git a/sys/cam/ata/ata_xpt.c b/sys/cam/ata/ata_xpt.c
index 71445e65331..3cf8b65ec9e 100644
--- a/sys/cam/ata/ata_xpt.c
+++ b/sys/cam/ata/ata_xpt.c
@@ -749,8 +749,8 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
if (cam_periph_error(done_ccb,
- 0, softc->restart ? (SF_NO_RECOVERY | SF_NO_RETRY) : 0,
- NULL) == ERESTART) {
+ 0, softc->restart ? (SF_NO_RECOVERY | SF_NO_RETRY) : 0
+ ) == ERESTART) {
out:
/* Drop freeze taken due to CAM_DEV_QFREEZE flag set. */
cam_release_devq(path, 0, 0, 0, FALSE);
diff --git a/sys/cam/cam.h b/sys/cam/cam.h
index b0c55e7075f..1308bbedc59 100644
--- a/sys/cam/cam.h
+++ b/sys/cam/cam.h
@@ -32,7 +32,7 @@
#define _CAM_CAM_H 1
#ifdef _KERNEL
-#include <opt_cam.h>
+#include "opt_cam.h"
#endif
#include <sys/cdefs.h>
diff --git a/sys/cam/cam_ccb.h b/sys/cam/cam_ccb.h
index 4cfe2e4fa34..8786f3b6d15 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)
@@ -773,6 +776,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 +1018,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 +1053,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 */
@@ -1227,6 +1262,7 @@ struct ccb_dev_advinfo {
#define CDAI_TYPE_PHYS_PATH 3
#define CDAI_TYPE_RCAPLONG 4
#define CDAI_TYPE_EXT_INQ 5
+#define CDAI_TYPE_MMC_PARAMS 8
off_t bufsiz; /* IN: Size of external buffer */
#define CAM_SCSI_DEVID_MAXLEN 65536 /* length in buffer is an uint16_t */
off_t provsiz; /* OUT: Size required/used */
@@ -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) \
@@ -1326,6 +1363,13 @@ cam_fill_smpio(struct ccb_smpio *smpio, uint32_t retries,
uint8_t *smp_response, int smp_response_len,
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 *),
@@ -1414,6 +1458,34 @@ cam_fill_smpio(struct ccb_smpio *smpio, uint32_t retries,
smpio->smp_response_len = smp_response_len;
}
+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)
{
diff --git a/sys/cam/cam_periph.c b/sys/cam/cam_periph.c
index b3cbf59e334..c9b0568ef3d 100644
--- a/sys/cam/cam_periph.c
+++ b/sys/cam/cam_periph.c
@@ -810,6 +810,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;
@@ -1251,7 +1263,7 @@ camperiphdone(struct cam_periph *periph, union ccb *done_ccb)
}
}
if (cam_periph_error(done_ccb,
- 0, SF_RETRY_UA | SF_NO_PRINT, NULL) == ERESTART)
+ 0, SF_RETRY_UA | SF_NO_PRINT) == ERESTART)
goto out;
if (done_ccb->ccb_h.status & CAM_DEV_QFRZN) {
cam_release_devq(done_ccb->ccb_h.path, 0, 0, 0, 0);
@@ -1665,7 +1677,7 @@ sense_error_done:
*/
int
cam_periph_error(union ccb *ccb, cam_flags camflags,
- u_int32_t sense_flags, union ccb *save_ccb)
+ u_int32_t sense_flags)
{
struct cam_path *newpath;
union ccb *orig_ccb, *scan_ccb;
diff --git a/sys/cam/cam_periph.h b/sys/cam/cam_periph.h
index 87f153c3213..e20a7776f28 100644
--- a/sys/cam/cam_periph.h
+++ b/sys/cam/cam_periph.h
@@ -195,7 +195,7 @@ void cam_periph_freeze_after_event(struct cam_periph *periph,
struct timeval* event_time,
u_int duration_ms);
int cam_periph_error(union ccb *ccb, cam_flags camflags,
- u_int32_t sense_flags, union ccb *save_ccb);
+ u_int32_t sense_flags);
static __inline struct mtx *
cam_periph_mtx(struct cam_periph *periph)
diff --git a/sys/cam/cam_xpt.c b/sys/cam/cam_xpt.c
index 6be86ce65b8..cd3dac64b88 100644
--- a/sys/cam/cam_xpt.c
+++ b/sys/cam/cam_xpt.c
@@ -326,7 +326,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
@@ -409,7 +408,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)
{
@@ -817,6 +816,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,
@@ -1388,7 +1389,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);
@@ -2575,6 +2576,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:
@@ -2686,11 +2689,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;
@@ -5425,7 +5429,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" },
@@ -5441,7 +5445,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 8e6027e5644..a6523099c83 100644
--- a/sys/cam/cam_xpt.h
+++ b/sys/cam/cam_xpt.h
@@ -34,6 +34,7 @@
#ifdef _KERNEL
#include <sys/cdefs.h>
+#include <cam/cam_ccb.h>
#endif
/* Forward Declarations */
@@ -141,6 +142,18 @@ 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);
+
+static inline void
+xpt_path_inq(struct ccb_pathinq *cpi, struct cam_path *path)
+{
+
+ bzero(cpi, sizeof(*cpi));
+ xpt_setup_ccb(&cpi->ccb_h, path, CAM_PRIORITY_NORMAL);
+ cpi->ccb_h.func_code = XPT_PATH_INQ;
+ xpt_action((union ccb *)cpi);
+}
+
#endif /* _KERNEL */
#endif /* _CAM_CAM_XPT_H */
diff --git a/sys/cam/cam_xpt_internal.h b/sys/cam/cam_xpt_internal.h
index b573c81e0b0..b2b84f3997e 100644
--- a/sys/cam/cam_xpt_internal.h
+++ b/sys/cam/cam_xpt_internal.h
@@ -120,6 +120,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 00000000000..809c0382d66
--- /dev/null
+++ b/sys/cam/mmc/mmc.h
@@ -0,0 +1,104 @@
+/*-
+ * 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
+#define CARD_FEATURE_18V 0x1 << 5
+
+ uint8_t sdio_func_count;
+} __packed;
+
+/*
+ * Only one MMC card on bus is supported now.
+ * If we ever want to support multiple MMC cards on the same bus,
+ * mmc_xpt needs to be extended to issue new RCAs based on number
+ * of already probed cards. Furthermore, retuning and high-speed
+ * settings should also take all cards into account.
+ */
+#define MMC_PROPOSED_RCA 2
+
+#endif
diff --git a/sys/cam/mmc/mmc_all.h b/sys/cam/mmc/mmc_all.h
new file mode 100644
index 00000000000..c2494894ca2
--- /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 00000000000..db77da51078
--- /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 00000000000..8f7dd8d3662
--- /dev/null
+++ b/sys/cam/mmc/mmc_da.c
@@ -0,0 +1,1898 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * 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_PART_SWITCH,
+} sdda_state;
+
+#define SDDA_FMT_BOOT "sdda%dboot"
+#define SDDA_FMT_GP "sdda%dgp"
+#define SDDA_FMT_RPMB "sdda%drpmb"
+#define SDDA_LABEL_ENH "enh"
+
+#define SDDA_PART_NAMELEN (16 + 1)
+
+struct sdda_softc;
+
+struct sdda_part {
+ struct disk *disk;
+ struct bio_queue_head bio_queue;
+ sdda_flags flags;
+ struct sdda_softc *sc;
+ u_int cnt;
+ u_int type;
+ bool ro;
+ char name[SDDA_PART_NAMELEN];
+};
+
+struct sdda_softc {
+ int outstanding_cmds; /* Number of active commands */
+ int refcount; /* Active xpt_action() calls */
+ sdda_state state;
+ struct mmc_data *mmcdata;
+ struct cam_periph *periph;
+// sdda_quirks quirks;
+ struct task start_init_task;
+ 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;
+
+ /* Generic switch timeout */
+ uint32_t cmd6_time;
+ /* MMC partitions support */
+ struct sdda_part *part[MMC_PART_MAX];
+ uint8_t part_curr; /* Partition currently switched to */
+ uint8_t part_requested; /* What partition we're currently switching to */
+ uint32_t part_time; /* Partition switch timeout [us] */
+ off_t enh_base; /* Enhanced user data area slice base ... */
+ off_t enh_size; /* ... and size [bytes] */
+ int log_count;
+ struct timeval log_time;
+};
+
+#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 void sdda_start_init(void *context, union ccb *start_ccb);
+static void sdda_start_init_task(void *context, int pending);
+static void sdda_process_mmc_partitions(struct cam_periph *periph, union ccb *start_ccb);
+static uint32_t sdda_get_host_caps(struct cam_periph *periph, union ccb *ccb);
+static void sdda_init_switch_part(struct cam_periph *periph, union ccb *start_ccb, u_int part);
+static int mmc_select_card(struct cam_periph *periph, union ccb *ccb, uint32_t rca);
+static inline uint32_t mmc_get_sector_size(struct cam_periph *periph) {return MMC_SECTOR_SIZE;}
+
+/* TODO: actually issue GET_TRAN_SETTINGS to get R/O status */
+static inline bool sdda_get_read_only(struct cam_periph *periph, union ccb *start_ccb)
+{
+
+ return (false);
+}
+
+static uint32_t mmc_get_spec_vers(struct cam_periph *periph);
+static uint64_t mmc_get_media_size(struct cam_periph *periph);
+static uint32_t mmc_get_cmd6_timeout(struct cam_periph *periph);
+static void sdda_add_part(struct cam_periph *periph, u_int type,
+ const char *name, u_int cnt, off_t media_size, bool ro);
+
+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 sdda_part *part;
+ struct cam_periph *periph;
+ struct sdda_softc *softc;
+ int error;
+
+ part = (struct sdda_part *)dp->d_drv1;
+ softc = part->sc;
+ periph = softc->periph;
+ 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, ("sddaopen\n"));
+
+ part->flags |= SDDA_FLAG_OPEN;
+
+ cam_periph_unhold(periph);
+ cam_periph_unlock(periph);
+ return (0);
+}
+
+static int
+sddaclose(struct disk *dp)
+{
+ struct sdda_part *part;
+ struct cam_periph *periph;
+ struct sdda_softc *softc;
+
+ part = (struct sdda_part *)dp->d_drv1;
+ softc = part->sc;
+ periph = softc->periph;
+ part->flags &= ~SDDA_FLAG_OPEN;
+
+ cam_periph_lock(periph);
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("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;
+ struct sdda_part *part;
+ struct bio *bp;
+ int i;
+
+ /* Check if we have more work to do. */
+ /* Find partition that has outstanding commands. Prefer current partition. */
+ bp = bioq_first(&softc->part[softc->part_curr]->bio_queue);
+ if (bp == NULL) {
+ for (i = 0; i < MMC_PART_MAX; i++) {
+ if ((part = softc->part[i]) != NULL &&
+ (bp = bioq_first(&softc->part[i]->bio_queue)) != NULL)
+ break;
+ }
+ }
+ if (bp != NULL) {
+ 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_part *part;
+ struct sdda_softc *softc;
+
+ part = (struct sdda_part *)bp->bio_disk->d_drv1;
+ softc = part->sc;
+ periph = softc->periph;
+
+ 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(&part->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;
+ struct sdda_part *part;
+
+ part = (struct sdda_part *)dp->d_drv1;
+ periph = part->sc->periph;
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sddadiskgonecb\n"));
+
+ cam_periph_release(periph);
+}
+
+static void
+sddaoninvalidate(struct cam_periph *periph)
+{
+ struct sdda_softc *softc;
+ struct sdda_part *part;
+
+ 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"));
+ for (int i = 0; i < MMC_PART_MAX; i++) {
+ if ((part = softc->part[i]) != NULL) {
+ bioq_flush(&part->bio_queue, NULL, ENXIO);
+ disk_gone(part->disk);
+ }
+ }
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("bioq_flush end\n"));
+
+}
+
+static void
+sddacleanup(struct cam_periph *periph)
+{
+ struct sdda_softc *softc;
+ struct sdda_part *part;
+ int i;
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sddacleanup\n"));
+ softc = (struct sdda_softc *)periph->softc;
+
+ cam_periph_unlock(periph);
+
+ for (i = 0; i < MMC_PART_MAX; i++) {
+ if ((part = softc->part[i]) != NULL) {
+ disk_destroy(part->disk);
+ free(part, M_DEVBUF);
+ softc->part[i] = NULL;
+ }
+ }
+ 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;
+ int i;
+
+ CAM_DEBUG(path, CAM_DEBUG_TRACE, ("=> AC_ADVINFO_CHANGED\n"));
+ buftype = (uintptr_t)arg;
+ if (buftype == CDAI_TYPE_PHYS_PATH) {
+ struct sdda_softc *softc;
+ struct sdda_part *part;
+
+ softc = periph->softc;
+ for (i = 0; i < MMC_PART_MAX; i++) {
+ if ((part = softc->part[i]) != NULL) {
+ disk_attr_changed(part->disk, "GEOM::physpath",
+ M_NOWAIT);
+ }
+ }
+ }
+ break;
+ }
+ default:
+ CAM_DEBUG(path, CAM_DEBUG_TRACE, ("=> default?!\n"));
+ cam_periph_async(periph, code, path, arg);
+ break;
+ }
+}
+
+
+static int
+sddagetattr(struct bio *bp)
+{
+ struct cam_periph *periph;
+ struct sdda_softc *softc;
+ struct sdda_part *part;
+ int ret;
+
+ part = (struct sdda_part *)bp->bio_disk->d_drv1;
+ softc = part->sc;
+ periph = softc->periph;
+ 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_getdev *cgd;
+ 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);
+ }
+
+ softc->state = SDDA_STATE_INIT;
+ softc->mmcdata =
+ (struct mmc_data *)malloc(sizeof(struct mmc_data), M_DEVBUF, M_NOWAIT|M_ZERO);
+ periph->softc = softc;
+ softc->periph = periph;
+
+ 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 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(&d, 0, sizeof(d));
+
+ 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);
+ 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 inline void
+mmc_switch_fill_mmcio(union ccb *ccb,
+ uint8_t set, uint8_t index, uint8_t value, u_int timeout)
+{
+ 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*/ timeout);
+}
+
+static int
+mmc_select_card(struct cam_periph *periph, union ccb *ccb, uint32_t rca)
+{
+ int flags;
+
+ flags = (rca ? MMC_RSP_R1B : MMC_RSP_NONE) | MMC_CMD_AC;
+ cam_fill_mmcio(&ccb->mmcio,
+ /*retries*/ 0,
+ /*cbfcnp*/ NULL,
+ /*flags*/ CAM_DIR_IN,
+ /*mmc_opcode*/ MMC_SELECT_CARD,
+ /*mmc_arg*/ rca << 16,
+ /*mmc_flags*/ flags,
+ /*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_SELECT 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_switch(struct cam_periph *periph, union ccb *ccb,
+ uint8_t set, uint8_t index, uint8_t value, u_int timeout)
+{
+
+ mmc_switch_fill_mmcio(ccb, set, index, value, timeout);
+ 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 uint32_t
+mmc_get_spec_vers(struct cam_periph *periph) {
+ struct sdda_softc *softc = (struct sdda_softc *)periph->softc;
+
+ return (softc->csd.spec_vers);
+}
+
+static uint64_t
+mmc_get_media_size(struct cam_periph *periph) {
+ struct sdda_softc *softc = (struct sdda_softc *)periph->softc;
+
+ return (softc->mediasize);
+}
+
+static uint32_t
+mmc_get_cmd6_timeout(struct cam_periph *periph)
+{
+ struct sdda_softc *softc = (struct sdda_softc *)periph->softc;
+
+ if (mmc_get_spec_vers(periph) >= 6)
+ return (softc->raw_ext_csd[EXT_CSD_GEN_CMD6_TIME] * 10);
+ return (500 * 1000);
+}
+
+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;
+ uint32_t arg;
+
+ memset(res, 0, 64);
+ mmc_d.len = 64;
+ mmc_d.data = res;
+ mmc_d.flags = MMC_DATA_READ;
+
+ arg = mode << 31; /* 0 - check, 1 - set */
+ arg |= 0x00FFFFFF;
+ arg &= ~(0xF << (grp * 4));
+ arg |= value << (grp * 4);
+
+ cam_fill_mmcio(&ccb->mmcio,
+ /*retries*/ 0,
+ /*cbfcnp*/ NULL,
+ /*flags*/ CAM_DIR_IN,
+ /*mmc_opcode*/ SD_SWITCH_FUNC,
+ /*mmc_arg*/ arg,
+ /*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 sdda_softc *softc = (struct sdda_softc *)periph->softc;
+ 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, softc->cmd6_time);
+ } 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 sdda_softc *softc = (struct sdda_softc *)periph->softc;
+ 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, softc->cmd6_time);
+ } 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
+*part_type(u_int type)
+{
+
+ switch (type) {
+ case EXT_CSD_PART_CONFIG_ACC_RPMB:
+ return ("RPMB");
+ case EXT_CSD_PART_CONFIG_ACC_DEFAULT:
+ return ("default");
+ case EXT_CSD_PART_CONFIG_ACC_BOOT0:
+ return ("boot0");
+ case EXT_CSD_PART_CONFIG_ACC_BOOT1:
+ return ("boot1");
+ case EXT_CSD_PART_CONFIG_ACC_GP0:
+ case EXT_CSD_PART_CONFIG_ACC_GP1:
+ case EXT_CSD_PART_CONFIG_ACC_GP2:
+ case EXT_CSD_PART_CONFIG_ACC_GP3:
+ return ("general purpose");
+ default:
+ return ("(unknown type)");
+ }
+}
+
+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 uint32_t
+sdda_get_host_caps(struct cam_periph *periph, union ccb *ccb)
+{
+ struct ccb_trans_settings_mmc *cts;
+
+ cts = &ccb->cts.proto_specific.mmc;
+
+ ccb->ccb_h.func_code = XPT_GET_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;
+ xpt_action(ccb);
+
+ if (ccb->ccb_h.status != CAM_REQ_CMP)
+ panic("Cannot get host caps");
+ return (cts->host_caps);
+}
+
+static void
+sdda_start_init(void *context, union ccb *start_ccb)
+{
+ struct cam_periph *periph = (struct cam_periph *)context;
+ struct ccb_trans_settings_mmc *cts;
+ uint32_t host_caps;
+ uint32_t sec_count;
+ int err;
+ int host_f_max;
+
+ 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 (mmc_get_spec_vers(periph) >= 4) {
+ err = mmc_send_ext_csd(periph, start_ccb,
+ (uint8_t *)&softc->raw_ext_csd,
+ sizeof(softc->raw_ext_csd));
+ if (err != 0) {
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH,
+ ("Cannot read EXT_CSD, err %d", err));
+ return;
+ }
+ }
+ } 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;
+ softc->cmd6_time = mmc_get_cmd6_timeout(periph);
+
+ /* MMC >= 4.x have EXT_CSD that has its own opinion about capacity */
+ if (mmc_get_spec_vers(periph) >= 4) {
+ 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 */
+ 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");
+ host_f_max = cts->host_f_max;
+ 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[2];
+ 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;
+ }
+
+ /*
+ * We deselect then reselect the card here. Some cards
+ * become unselected and timeout with the above two
+ * commands, although the state tables / diagrams in the
+ * standard suggest they go back to the transfer state.
+ * Other cards don't become deselected, and if we
+ * attempt to blindly re-select them, we get timeout
+ * errors from some controllers. So we deselect then
+ * reselect to handle all situations.
+ */
+ mmc_select_card(periph, start_ccb, 0);
+ mmc_select_card(periph, start_ccb, get_rca(periph));
+ } else {
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, ("Not trying the switch\n"));
+ goto finish_hs_tests;
+ }
+ }
+
+ if (mmcp->card_features & CARD_FEATURE_MMC && mmc_get_spec_vers(periph) >= 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));
+
+ /* Enable high-speed timing on the card */
+ 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"));
+ f_max = 25000000;
+ }
+ }
+ /* Set frequency on the controller */
+ 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);
+
+ softc->state = SDDA_STATE_NORMAL;
+
+ /* MMC partitions support */
+ if (mmcp->card_features & CARD_FEATURE_MMC && mmc_get_spec_vers(periph) >= 4) {
+ sdda_process_mmc_partitions(periph, start_ccb);
+ } else if (mmcp->card_features & CARD_FEATURE_SD20) {
+ /* For SD[HC] cards, just add one partition that is the whole card */
+ sdda_add_part(periph, 0, "sdda",
+ periph->unit_number,
+ mmc_get_media_size(periph),
+ sdda_get_read_only(periph, start_ccb));
+ softc->part_curr = 0;
+ }
+
+ 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_LOST_DEVICE | AC_GETDEV_CHANGED |
+ AC_ADVINFO_CHANGED, sddaasync, periph, periph->path);
+}
+
+static void
+sdda_add_part(struct cam_periph *periph, u_int type, const char *name,
+ u_int cnt, off_t media_size, bool ro)
+{
+ struct sdda_softc *sc = (struct sdda_softc *)periph->softc;
+ struct sdda_part *part;
+ struct ccb_pathinq cpi;
+ u_int maxio;
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH,
+ ("Partition type '%s', size %ju %s\n",
+ part_type(type),
+ media_size,
+ ro ? "(read-only)" : ""));
+
+ part = sc->part[type] = malloc(sizeof(*part), M_DEVBUF,
+ M_WAITOK | M_ZERO);
+
+ part->cnt = cnt;
+ part->type = type;
+ part->ro = ro;
+ part->sc = sc;
+ snprintf(part->name, sizeof(part->name), name, periph->unit_number);
+
+ /*
+ * Due to the nature of RPMB partition it doesn't make much sense
+ * to add it as a disk. It would be more appropriate to create a
+ * userland tool to operate on the partition or leverage the existing
+ * tools from sysutils/mmc-utils.
+ */
+ if (type == EXT_CSD_PART_CONFIG_ACC_RPMB) {
+ /* TODO: Create device, assign IOCTL handler */
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH,
+ ("Don't know what to do with RPMB partitions yet\n"));
+ return;
+ }
+
+ bioq_init(&part->bio_queue);
+
+ 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);
+
+ /*
+ * Register this media as a disk
+ */
+ (void)cam_periph_hold(periph, PRIBIO);
+ cam_periph_unlock(periph);
+
+ part->disk = disk_alloc();
+ part->disk->d_rotation_rate = DISK_RR_NON_ROTATING;
+ part->disk->d_devstat = devstat_new_entry(part->name,
+ cnt, 512,
+ DEVSTAT_ALL_SUPPORTED,
+ DEVSTAT_TYPE_DIRECT | XPORT_DEVSTAT_TYPE(cpi.transport),
+ DEVSTAT_PRIORITY_DISK);
+
+ part->disk->d_open = sddaopen;
+ part->disk->d_close = sddaclose;
+ part->disk->d_strategy = sddastrategy;
+ part->disk->d_getattr = sddagetattr;
+// sc->disk->d_dump = sddadump;
+ part->disk->d_gone = sddadiskgonecb;
+ part->disk->d_name = part->name;
+ part->disk->d_drv1 = part;
+ 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 */
+ part->disk->d_maxsize = maxio;
+ part->disk->d_unit = cnt;
+ part->disk->d_flags = 0;
+ strlcpy(part->disk->d_descr, sc->card_id_string,
+ MIN(sizeof(part->disk->d_descr), sizeof(sc->card_id_string)));
+ strlcpy(part->disk->d_ident, sc->card_sn_string,
+ MIN(sizeof(part->disk->d_ident), sizeof(sc->card_sn_string)));
+ part->disk->d_hba_vendor = cpi.hba_vendor;
+ part->disk->d_hba_device = cpi.hba_device;
+ part->disk->d_hba_subvendor = cpi.hba_subvendor;
+ part->disk->d_hba_subdevice = cpi.hba_subdevice;
+
+ part->disk->d_sectorsize = mmc_get_sector_size(periph);
+ part->disk->d_mediasize = media_size;
+ part->disk->d_stripesize = 0;
+ part->disk->d_fwsectors = 0;
+ part->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;
+ }
+ disk_create(part->disk, DISK_VERSION);
+ cam_periph_lock(periph);
+ cam_periph_unhold(periph);
+}
+
+/*
+ * For MMC cards, process EXT_CSD and add partitions that are supported by
+ * this device.
+ */
+static void
+sdda_process_mmc_partitions(struct cam_periph *periph, union ccb *ccb)
+{
+ struct sdda_softc *sc = (struct sdda_softc *)periph->softc;
+ struct mmc_params *mmcp = &periph->path->device->mmc_ident_data;
+ off_t erase_size, sector_size, size, wp_size;
+ int i;
+ const uint8_t *ext_csd;
+ uint8_t rev;
+ bool comp, ro;
+
+ ext_csd = sc->raw_ext_csd;
+
+ /*
+ * Enhanced user data area and general purpose partitions are only
+ * supported in revision 1.4 (EXT_CSD_REV == 4) and later, the RPMB
+ * partition in revision 1.5 (MMC v4.41, EXT_CSD_REV == 5) and later.
+ */
+ rev = ext_csd[EXT_CSD_REV];
+
+ /*
+ * Ignore user-creatable enhanced user data area and general purpose
+ * partitions partitions as long as partitioning hasn't been finished.
+ */
+ comp = (ext_csd[EXT_CSD_PART_SET] & EXT_CSD_PART_SET_COMPLETED) != 0;
+
+ /*
+ * Add enanced user data area slice, unless it spans the entirety of
+ * the user data area. The enhanced area is of a multiple of high
+ * capacity write protect groups ((ERASE_GRP_SIZE + HC_WP_GRP_SIZE) *
+ * 512 KB) and its offset given in either sectors or bytes, depending
+ * on whether it's a high capacity device or not.
+ * NB: The slicer and its slices need to be registered before adding
+ * the disk for the corresponding user data area as re-tasting is
+ * racy.
+ */
+ sector_size = mmc_get_sector_size(periph);
+ size = ext_csd[EXT_CSD_ENH_SIZE_MULT] +
+ (ext_csd[EXT_CSD_ENH_SIZE_MULT + 1] << 8) +
+ (ext_csd[EXT_CSD_ENH_SIZE_MULT + 2] << 16);
+ if (rev >= 4 && comp == TRUE && size > 0 &&
+ (ext_csd[EXT_CSD_PART_SUPPORT] &
+ EXT_CSD_PART_SUPPORT_ENH_ATTR_EN) != 0 &&
+ (ext_csd[EXT_CSD_PART_ATTR] & (EXT_CSD_PART_ATTR_ENH_USR)) != 0) {
+ erase_size = ext_csd[EXT_CSD_ERASE_GRP_SIZE] * 1024 *
+ MMC_SECTOR_SIZE;
+ wp_size = ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
+ size *= erase_size * wp_size;
+ if (size != mmc_get_media_size(periph) * sector_size) {
+ sc->enh_size = size;
+ sc->enh_base = (ext_csd[EXT_CSD_ENH_START_ADDR] +
+ (ext_csd[EXT_CSD_ENH_START_ADDR + 1] << 8) +
+ (ext_csd[EXT_CSD_ENH_START_ADDR + 2] << 16) +
+ (ext_csd[EXT_CSD_ENH_START_ADDR + 3] << 24)) *
+ ((mmcp->card_features & CARD_FEATURE_SDHC) ? 1: MMC_SECTOR_SIZE);
+ } else
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH,
+ ("enhanced user data area spans entire device"));
+ }
+
+ /*
+ * Add default partition. This may be the only one or the user
+ * data area in case partitions are supported.
+ */
+ ro = sdda_get_read_only(periph, ccb);
+ sdda_add_part(periph, EXT_CSD_PART_CONFIG_ACC_DEFAULT, "sdda",
+ periph->unit_number, mmc_get_media_size(periph), ro);
+ sc->part_curr = EXT_CSD_PART_CONFIG_ACC_DEFAULT;
+
+ if (mmc_get_spec_vers(periph) < 3)
+ return;
+
+ /* Belatedly announce enhanced user data slice. */
+ if (sc->enh_size != 0) {
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH,
+ ("enhanced user data area off 0x%jx size %ju bytes\n",
+ sc->enh_base, sc->enh_size));
+ }
+
+ /*
+ * Determine partition switch timeout (provided in units of 10 ms)
+ * and ensure it's at least 300 ms as some eMMC chips lie.
+ */
+ sc->part_time = max(ext_csd[EXT_CSD_PART_SWITCH_TO] * 10 * 1000,
+ 300 * 1000);
+
+ /* Add boot partitions, which are of a fixed multiple of 128 KB. */
+ size = ext_csd[EXT_CSD_BOOT_SIZE_MULT] * MMC_BOOT_RPMB_BLOCK_SIZE;
+ if (size > 0 && (sdda_get_host_caps(periph, ccb) & MMC_CAP_BOOT_NOACC) == 0) {
+ sdda_add_part(periph, EXT_CSD_PART_CONFIG_ACC_BOOT0,
+ SDDA_FMT_BOOT, 0, size,
+ ro | ((ext_csd[EXT_CSD_BOOT_WP_STATUS] &
+ EXT_CSD_BOOT_WP_STATUS_BOOT0_MASK) != 0));
+ sdda_add_part(periph, EXT_CSD_PART_CONFIG_ACC_BOOT1,
+ SDDA_FMT_BOOT, 1, size,
+ ro | ((ext_csd[EXT_CSD_BOOT_WP_STATUS] &
+ EXT_CSD_BOOT_WP_STATUS_BOOT1_MASK) != 0));
+ }
+
+ /* Add RPMB partition, which also is of a fixed multiple of 128 KB. */
+ size = ext_csd[EXT_CSD_RPMB_MULT] * MMC_BOOT_RPMB_BLOCK_SIZE;
+ if (rev >= 5 && size > 0)
+ sdda_add_part(periph, EXT_CSD_PART_CONFIG_ACC_RPMB,
+ SDDA_FMT_RPMB, 0, size, ro);
+
+ if (rev <= 3 || comp == FALSE)
+ return;
+
+ /*
+ * Add general purpose partitions, which are of a multiple of high
+ * capacity write protect groups, too.
+ */
+ if ((ext_csd[EXT_CSD_PART_SUPPORT] & EXT_CSD_PART_SUPPORT_EN) != 0) {
+ erase_size = ext_csd[EXT_CSD_ERASE_GRP_SIZE] * 1024 *
+ MMC_SECTOR_SIZE;
+ wp_size = ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
+ for (i = 0; i < MMC_PART_GP_MAX; i++) {
+ size = ext_csd[EXT_CSD_GP_SIZE_MULT + i * 3] +
+ (ext_csd[EXT_CSD_GP_SIZE_MULT + i * 3 + 1] << 8) +
+ (ext_csd[EXT_CSD_GP_SIZE_MULT + i * 3 + 2] << 16);
+ if (size == 0)
+ continue;
+ sdda_add_part(periph, EXT_CSD_PART_CONFIG_ACC_GP0 + i,
+ SDDA_FMT_GP, i, size * erase_size * wp_size, ro);
+ }
+ }
+}
+
+/*
+ * We cannot just call mmc_switch() since it will sleep, and we are in
+ * GEOM context and cannot sleep. Instead, create an MMCIO request to switch
+ * partitions and send it to h/w, and upon completion resume processing
+ * the I/O que This function cannot fail, instead check switch errors in sddadone().
+ */
+static void
+sdda_init_switch_part(struct cam_periph *periph, union ccb *start_ccb, u_int part) {
+ struct sdda_softc *sc = (struct sdda_softc *)periph->softc;
+ uint8_t value;
+
+ sc->part_requested = part;
+
+ value = (sc->raw_ext_csd[EXT_CSD_PART_CONFIG] &
+ ~EXT_CSD_PART_CONFIG_ACC_MASK) | part;
+
+ mmc_switch_fill_mmcio(start_ccb, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_PART_CONFIG, value, sc->part_time);
+ start_ccb->ccb_h.cbfcnp = sddadone;
+
+ sc->outstanding_cmds++;
+ cam_periph_unlock(periph);
+ xpt_action(start_ccb);
+ cam_periph_lock(periph);
+}
+
+/* Called with periph lock held! */
+static void
+sddastart(struct cam_periph *periph, union ccb *start_ccb)
+{
+ struct bio *bp;
+ struct sdda_softc *softc = (struct sdda_softc *)periph->softc;
+ struct sdda_part *part;
+ struct mmc_params *mmcp = &periph->path->device->mmc_ident_data;
+ int part_index;
+
+ 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\n"));
+ xpt_release_ccb(start_ccb);
+ return;
+ }
+
+ /* Find partition that has outstanding commands. Prefer current partition. */
+ part = softc->part[softc->part_curr];
+ bp = bioq_first(&part->bio_queue);
+ if (bp == NULL) {
+ for (part_index = 0; part_index < MMC_PART_MAX; part_index++) {
+ if ((part = softc->part[part_index]) != NULL &&
+ (bp = bioq_first(&softc->part[part_index]->bio_queue)) != NULL)
+ break;
+ }
+ }
+ if (bp == NULL) {
+ xpt_release_ccb(start_ccb);
+ return;
+ }
+ if (part_index != softc->part_curr) {
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH,
+ ("Partition %d -> %d\n", softc->part_curr, part_index));
+ /*
+ * According to section "6.2.2 Command restrictions" of the eMMC
+ * specification v5.1, CMD19/CMD21 aren't allowed to be used with
+ * RPMB partitions. So we pause re-tuning along with triggering
+ * it up-front to decrease the likelihood of re-tuning becoming
+ * necessary while accessing an RPMB partition. Consequently, an
+ * RPMB partition should immediately be switched away from again
+ * after an access in order to allow for re-tuning to take place
+ * anew.
+ */
+ /* TODO: pause retune if switching to RPMB partition */
+ softc->state = SDDA_STATE_PART_SWITCH;
+ sdda_init_switch_part(periph, start_ccb, part_index);
+ return;
+ }
+
+ bioq_remove(&part->bio_queue, bp);
+
+ switch (bp->bio_cmd) {
+ case BIO_WRITE:
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("BIO_WRITE\n"));
+ part->flags |= SDDA_FLAG_DIRTY;
+ /* FALLTHROUGH */
+ case BIO_READ:
+ {
+ struct ccb_mmcio *mmcio;
+ uint64_t blockno = bp->bio_pblkno;
+ uint16_t count = bp->bio_bcount / 512;
+ uint16_t opcode;
+
+ if (bp->bio_cmd == BIO_READ)
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("BIO_READ\n"));
+ 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;
+
+ 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->cmd.data->flags |= MMC_DATA_MULTI;
+ 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);
+
+ /* 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 bio *bp;
+ struct sdda_softc *softc;
+ struct ccb_mmcio *mmcio;
+ struct cam_path *path;
+ uint32_t card_status;
+ int error = 0;
+
+ softc = (struct sdda_softc *)periph->softc;
+ mmcio = &done_ccb->mmcio;
+ path = done_ccb->ccb_h.path;
+
+ CAM_DEBUG(path, CAM_DEBUG_TRACE, ("sddadone\n"));
+// 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;
+ }
+
+ 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)));
+
+ /* Process result of switching MMC partitions */
+ if (softc->state == SDDA_STATE_PART_SWITCH) {
+ CAM_DEBUG(path, CAM_DEBUG_TRACE,
+ ("Compteting partition switch to %d\n", softc->part_requested));
+ softc->outstanding_cmds--;
+ /* Complete partition switch */
+ softc->state = SDDA_STATE_NORMAL;
+ if (error != MMC_ERR_NONE) {
+ /* TODO: Unpause retune if accessing RPMB */
+ xpt_release_ccb(done_ccb);
+ xpt_schedule(periph, CAM_PRIORITY_NORMAL);
+ return;
+ }
+
+ softc->raw_ext_csd[EXT_CSD_PART_CONFIG] =
+ (softc->raw_ext_csd[EXT_CSD_PART_CONFIG] &
+ ~EXT_CSD_PART_CONFIG_ACC_MASK) | softc->part_requested;
+ /* TODO: Unpause retune if accessing RPMB */
+ softc->part_curr = softc->part_requested;
+ xpt_release_ccb(done_ccb);
+
+ /* Return to processing BIO requests */
+ xpt_schedule(periph, CAM_PRIORITY_NORMAL);
+ return;
+ }
+
+ 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;
+ }
+
+ softc->outstanding_cmds--;
+ xpt_release_ccb(done_ccb);
+ /*
+ * Release the periph refcount taken in sddastart() for each CCB.
+ */
+ KASSERT(softc->refcount >= 1, ("sddadone softc %p refcount %d", softc, softc->refcount));
+ softc->refcount--;
+ 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));
+}
+#endif /* _KERNEL */
diff --git a/sys/cam/mmc/mmc_xpt.c b/sys/cam/mmc/mmc_xpt.c
new file mode 100644
index 00000000000..4f43582e4a8
--- /dev/null
+++ b/sys/cam/mmc/mmc_xpt.c
@@ -0,0 +1,1107 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * 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 <machine/stdarg.h> /* for xpt_print below */
+#include <machine/_inttypes.h> /* for PRIu64 */
+#include "opt_cam.h"
+
+FEATURE(mmccam, "CAM-based MMC/SD/SDIO stack");
+
+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_MMC_SET_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_MMC_SET_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 */
+ uint8_t acmd41_count; /* how many times ACMD41 has been issued */
+ 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 if (async_code == AC_UNIT_ATTENTION) {
+ printf("Got interrupt generated by the card and ignored it\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_path_inq(&cpi, path);
+
+ 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_TRACE,
+ ("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;
+ case CDAI_TYPE_MMC_PARAMS:
+ cdai->provsiz = sizeof(struct mmc_params);
+ amt = MIN(cdai->provsiz, cdai->bufsiz);
+ memcpy(cdai->buf, &device->mmc_ident_data, amt);
+ 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_path_inq(&cpi, periph->path);
+ 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)
+{
+ mmcprobe_softc *softc;
+ union ccb *request_ccb; /* CCB representing the probe request */
+ cam_status status;
+
+ 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;
+ softc->acmd41_count = 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:
+ xpt_path_inq(&start_ccb->cpi, periph->path);
+
+ CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("Start with PROBE_RESET\n"));
+ init_standard_ccb(start_ccb, XPT_GET_TRAN_SETTINGS);
+ xpt_action(start_ccb);
+ if(cts->ios.power_mode != power_off) {
+ 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.
+ */
+ uint32_t cmd_arg = MMC_OCR_CCS | mmcp->card_ocr; /* CCS + ocr */
+ if (softc->acmd41_count < 10 && mmcp->card_ocr != 0 )
+ cmd_arg |= MMC_OCR_S18R;
+ mmcio->cmd.arg = cmd_arg;
+ mmcio->cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
+ softc->acmd41_count++;
+ } 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_MMC_SET_RELATIVE_ADDR:
+ init_standard_ccb(start_ccb, XPT_MMC_IO);
+ mmcio->cmd.opcode = MMC_SET_RELATIVE_ADDR;
+ mmcio->cmd.arg = MMC_PROPOSED_RCA << 16;
+ mmcio->cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ 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);
+ break;
+ }
+ 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;
+ }