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