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 \