Index: etc/mtree/BSD.include.dist =================================================================== --- etc/mtree/BSD.include.dist +++ etc/mtree/BSD.include.dist @@ -90,6 +90,8 @@ .. scsi .. + mmc + .. .. clang 3.4.1 @@ -230,7 +232,7 @@ gssapi .. infiniband - complib + complib .. iba .. Index: include/Makefile =================================================================== --- include/Makefile +++ include/Makefile @@ -39,7 +39,7 @@ LDIRS= bsm cam geom net net80211 netgraph netinet netinet6 \ netipsec netnatm netsmb nfs nfsclient nfsserver sys vm -LSUBDIRS= cam/ata cam/scsi \ +LSUBDIRS= cam/ata cam/scsi cam/mmc \ dev/acpica dev/agp dev/an dev/bktr dev/ciss dev/filemon dev/firewire \ dev/hwpmc \ dev/ic dev/iicbus ${_dev_ieee488} dev/io dev/lmc dev/mfi dev/nvme \ Index: lib/libcam/Makefile =================================================================== --- lib/libcam/Makefile +++ lib/libcam/Makefile @@ -37,7 +37,7 @@ cam_cdbparse.3 buff_encode_visit.3 .PATH: ${.CURDIR}/../../sys/cam/scsi ${.CURDIR}/../../sys/cam/ata \ - ${.CURDIR}/../../sys/cam + ${.CURDIR}/../../sys/cam ${.CURDIR}/../../sys/cam/mmc SDIR= ${.CURDIR}/../../sys CFLAGS+= -I${.CURDIR} -I${SDIR} Index: sys/Makefile =================================================================== --- sys/Makefile +++ sys/Makefile @@ -56,7 +56,7 @@ # You need the devel/global and one of editor/emacs* ports for that. TAGS ${.CURDIR}/TAGS: ${.CURDIR}/cscope.files rm -f ${.CURDIR}/TAGS - cd ${.CURDIR}; xargs etags -a < ${.CURDIR}/cscope.files + cd ${.CURDIR}; xargs exctags -ea < ${.CURDIR}/cscope.files # You need the textproc/glimpse ports for this. glimpse: Index: sys/amd64/conf/GENERIC =================================================================== --- sys/amd64/conf/GENERIC +++ sys/amd64/conf/GENERIC @@ -356,4 +356,5 @@ device xenpci # Xen HVM Hypervisor services driver # VMware support +options CAM_DEBUG_COMPILE=65535 device vmx # VMware VMXNET3 Ethernet Index: sys/arm/conf/BEAGLEBONE =================================================================== --- sys/arm/conf/BEAGLEBONE +++ sys/arm/conf/BEAGLEBONE @@ -72,12 +72,12 @@ options NFSLOCKD # Uncomment this for NFS root -#options NFS_ROOT # NFS usable as /, requires NFSCL -#options BOOTP_NFSROOT -#options BOOTP_COMPAT -#options BOOTP -#options BOOTP_NFSV3 -#options BOOTP_WIRED_TO=cpsw0 +options NFS_ROOT # NFS usable as /, requires NFSCL +options BOOTP_NFSROOT +options BOOTP_COMPAT +options BOOTP +options BOOTP_NFSV3 +options BOOTP_WIRED_TO=cpsw0 # MMC/SD/SDIO Card slot support device mmc # mmc/sd bus @@ -139,3 +139,6 @@ options FDT options FDT_DTB_STATIC makeoptions FDT_DTS_FILE=beaglebone.dts + +options CAMDEBUG +options CAM_DEBUG_FLAGS=(CAM_DEBUG_INFO|CAM_DEBUG_PROBE|CAM_DEBUG_PERIPH|CAM_DEBUG_TRACE) Index: sys/arm/ti/ti_sdhci.c =================================================================== --- sys/arm/ti/ti_sdhci.c +++ sys/arm/ti/ti_sdhci.c @@ -39,6 +39,8 @@ #include #include #include +#include +#include #include #include @@ -52,6 +54,13 @@ #include #include +/* CAM includes */ +#include +#include +#include +#include +#include + #include #include "sdhci_if.h" @@ -75,6 +84,11 @@ uint32_t sdhci_clkdiv; boolean_t disable_highspeed; boolean_t force_card_present; + + struct mtx sc_mtx; + struct cam_devq *devq; + struct cam_sim *sim; + struct cam_path *path; }; /* @@ -120,6 +134,11 @@ #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) { @@ -607,7 +626,39 @@ bus_generic_probe(dev); bus_generic_attach(dev); - sdhci_start_slot(&sc->slot); +// sdhci_start_slot(&sc->slot); + + /* CAM-specific init */ + mtx_init(&sc->sc_mtx, "tisdhcimtx", NULL, MTX_DEF); + + if ((sc->devq = cam_simq_alloc(1)) == NULL) { + err = ENOMEM; + goto fail; + } + + sc->sim = cam_sim_alloc(ti_sdhci_cam_action, ti_sdhci_cam_poll, + "ti_sdhci", sc, device_get_unit(dev), + &sc->sc_mtx, 1, 1, sc->devq); + + if (sc->sim == NULL) { + cam_simq_free(sc->devq); + device_printf(dev, "cannot allocate CAM SIM\n"); + err = EINVAL; + goto fail; + } + + mtx_lock(&sc->sc_mtx); + if (xpt_bus_register(sc->sim, dev, 0) != 0) { + device_printf(dev, + "cannot register SCSI pass-through bus\n"); + cam_sim_free(sc->sim, FALSE); + cam_simq_free(sc->devq); + mtx_unlock(&sc->sc_mtx); + err = EINVAL; + goto fail; + } + mtx_unlock(&sc->sc_mtx); + /* End CAM-specific init */ return (0); @@ -619,6 +670,16 @@ if (sc->mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + if (sc->sim != NULL) { + mtx_lock(&sc->sc_mtx); + xpt_bus_deregister(cam_sim_path(sc->sim)); + cam_sim_free(sc->sim, FALSE); + mtx_unlock(&sc->sc_mtx); + } + + if (sc->devq != NULL) + cam_simq_free(sc->devq); + return (err); } @@ -637,6 +698,160 @@ return (ENXIO); } +static void +ti_sdhci_handle_mmcio(struct cam_sim *sim, union ccb *ccb) +{ + struct ti_sdhci_softc *sc; + + sc = cam_sim_softc(sim); + + sdhci_request_cam(&sc->slot, ccb); +} + +static void +ti_sdhci_cam_action(struct cam_sim *sim, union ccb *ccb) +{ + struct ti_sdhci_softc *sc; + + sc = cam_sim_softc(sim); + if (sc == NULL) { + ccb->ccb_h.status = CAM_SEL_TIMEOUT; + xpt_done(ccb); + return; + } + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + device_printf(sc->dev, "action: func_code %0x\n", ccb->ccb_h.func_code); + + switch (ccb->ccb_h.func_code) { + case XPT_PATH_INQ: + { + struct ccb_pathinq *cpi; + + cpi = &ccb->cpi; + cpi->version_num = 1; + cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE | PI_WIDE_16; + 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; + 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; + + device_printf(sc->dev, "Got XPT_GET_TRAN_SETTINGS\n"); + + cts->protocol = PROTO_MMCSD; + cts->protocol_version = 0; + cts->transport = XPORT_MMCSD; + cts->transport_version = 0; + cts->xport_specific.valid = 0; + cts->proto_specific.mmc.host_ocr = sc->slot.host.host_ocr; + ccb->ccb_h.status = CAM_REQ_CMP; + break; + } + case XPT_SET_TRAN_SETTINGS: + device_printf(sc->dev, "Got XPT_SET_TRAN_SETTINGS, should update IOS...\n"); + ti_sdhci_cam_settran_settings(sc, ccb); + + ccb->ccb_h.status = CAM_REQ_CMP; + break; + case XPT_RESET_BUS: + device_printf(sc->dev, "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. + */ + device_printf(sc->dev, "Got XPT_MMC_IO\n"); + ccb->ccb_h.status = CAM_REQ_INPROG; + + ti_sdhci_handle_mmcio(sim, ccb); + return; + /* NOTREACHED */ + break; + default: + ccb->ccb_h.status = CAM_REQ_INVALID; + break; + } + xpt_done(ccb); + return; +} + +static void +ti_sdhci_cam_poll(struct cam_sim *sim) +{ + return; +} + +static int +ti_sdhci_cam_settran_settings(struct ti_sdhci_softc *sc, union ccb *ccb) +{ + struct sdhci_slot *slot; + struct mmc_ios *ios; + struct mmc_ios *new_ios; + uint32_t val32; + struct ccb_trans_settings_mmc *cts; + + slot = &sc->slot; + 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 = new_ios->clock; + if (cts->ios_valid & MMC_VDD) { + ios->vdd = new_ios->vdd; + device_printf(sc->dev, "VDD => %d\n", ios->vdd); + } + if (cts->ios_valid & MMC_CS) + ios->chip_select = new_ios->chip_select; + if (cts->ios_valid & MMC_BW) + ios->bus_width = new_ios->bus_width; + if (cts->ios_valid & MMC_PM) + ios->power_mode = new_ios->power_mode; + if (cts->ios_valid & MMC_BT) + ios->timing = new_ios->timing; + if (cts->ios_valid & MMC_BM) + ios->bus_mode = new_ios->bus_mode; + /* + * There is an 8-bit-bus bit in the MMCHS control register which, when + * set, overrides the 1 vs 4 bit setting in the standard SDHCI + * registers. Set that bit first according to whether an 8-bit bus is + * requested, then let the standard driver handle everything else. + */ + val32 = ti_mmchs_read_4(sc, MMCHS_CON); + if (ios->bus_width == bus_width_8) + ti_mmchs_write_4(sc, MMCHS_CON, val32 | MMCHS_CON_DW8); + else + ti_mmchs_write_4(sc, MMCHS_CON, val32 & ~MMCHS_CON_DW8); + + return (sdhci_update_ios_cam(slot)); +} + static device_method_t ti_sdhci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ti_sdhci_probe), Index: sys/cam/cam_ccb.h =================================================================== --- sys/cam/cam_ccb.h +++ sys/cam/cam_ccb.h @@ -41,6 +41,7 @@ #include #include #include +#include /* General allocation length definitions for CCB structures */ #define IOCDBLEN CAM_MAX_CDBLEN /* Space for CDB bytes/pointer */ @@ -223,6 +224,8 @@ /* Notify Host Target driver of event */ XPT_NOTIFY_ACKNOWLEDGE = 0x37 | XPT_FC_QUEUED | XPT_FC_USER_CCB, /* Acknowledgement of event */ + XPT_MMC_IO = 0x38 | XPT_FC_DEV_QUEUED, + /* Execute the requested ATA I/O operation */ /* Vendor Unique codes: 0x80->0x8F */ XPT_VUNIQUE = 0x80 @@ -249,6 +252,7 @@ PROTO_ATAPI, /* AT Attachment Packetized Interface */ PROTO_SATAPM, /* SATA Port Multiplier */ PROTO_SEMB, /* SATA Enclosure Management Bridge */ + PROTO_MMCSD, /* MMC, SD, SDIO */ } cam_proto; typedef enum { @@ -264,6 +268,7 @@ XPORT_SATA, /* Serial AT Attachment */ XPORT_ISCSI, /* iSCSI */ XPORT_SRP, /* SCSI RDMA Protocol */ + XPORT_MMCSD, /* MMC, SD, SDIO card */ } cam_xport; #define XPORT_IS_ATA(t) ((t) == XPORT_ATA || (t) == XPORT_SATA) @@ -739,6 +744,15 @@ u_int init_id; /* initiator id of who selected */ }; +/* + * 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 ccb_accept_tio { struct ccb_hdr ccb_h; cdb_t cdb_io; /* Union for CDB bytes/pointer */ @@ -940,6 +954,26 @@ #define CTS_SATA_CAPS_D_APST 0x00020000 }; +/* + * Basically, what earlier was done with updating IOS + * now can be done using SET_TRAN_SETTINGS + */ +#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; +}; + /* Get/Set transfer rate/width/disconnection/tag queueing settings */ struct ccb_trans_settings { struct ccb_hdr ccb_h; @@ -952,6 +986,7 @@ u_int valid; /* Which fields to honor */ struct ccb_trans_settings_ata ata; struct ccb_trans_settings_scsi scsi; + struct ccb_trans_settings_mmc mmc; } proto_specific; union { u_int valid; /* Which fields to honor */ @@ -1203,6 +1238,7 @@ struct ccb_ataio ataio; struct ccb_dev_advinfo cdai; struct ccb_async casync; + struct ccb_mmcio mmcio; }; __BEGIN_DECLS Index: sys/cam/cam_xpt.c =================================================================== --- sys/cam/cam_xpt.c +++ sys/cam/cam_xpt.c @@ -385,7 +385,7 @@ } return (error); } - + static int xptdoioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) { @@ -1022,6 +1022,8 @@ else if (path->device->protocol == PROTO_SEMB) semb_print_ident( (struct sep_identify_data *)&path->device->ident_data); + else if (path->device->protocol == PROTO_MMCSD) + mmc_print_ident(&path->device->mmc_ident_data); else printf("Unknown protocol device\n"); if (path->device->serial_num_len > 0) { @@ -1075,6 +1077,8 @@ else if (path->device->protocol == PROTO_SEMB) semb_print_ident_short( (struct sep_identify_data *)&path->device->ident_data); + else if (path->device->protocol == PROTO_MMCSD) + mmc_print_ident(&path->device->mmc_ident_data); else printf("Unknown protocol device"); if (path->device->serial_num_len > 0) @@ -1315,7 +1319,7 @@ 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); @@ -2494,6 +2498,7 @@ if (start_ccb->ccb_h.func_code == XPT_ATA_IO) start_ccb->ataio.resid = 0; /* FALLTHROUGH */ + case XPT_MMC_IO: case XPT_RESET_DEV: case XPT_ENG_EXEC: case XPT_SMP_IO: @@ -3898,6 +3903,9 @@ case XPORT_SATA: new_bus->xport = ata_get_xport(); break; + case XPORT_MMCSD: + new_bus->xport = mmc_get_xport(); + break; default: new_bus->xport = &xport_default; break; Index: sys/cam/cam_xpt_internal.h =================================================================== --- sys/cam/cam_xpt_internal.h +++ sys/cam/cam_xpt_internal.h @@ -88,6 +88,7 @@ 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 @@ -165,6 +166,7 @@ struct xpt_xport * scsi_get_xport(void); struct xpt_xport * ata_get_xport(void); +struct xpt_xport * mmc_get_xport(void); struct cam_ed * xpt_alloc_device(struct cam_eb *bus, struct cam_et *target, Index: sys/cam/mmc/mmc.h =================================================================== --- /dev/null +++ sys/cam/mmc/mmc.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$ + */ + +#ifndef CAM_MMC_H +#define CAM_MMC_H + +/* + * This structure describes an MMC/SD card + */ +struct mmc_params { + u_int8_t model[40]; /* Card model */ +} __packed; + +#endif Index: sys/cam/mmc/mmc_all.h =================================================================== --- /dev/null +++ sys/cam/mmc/mmc_all.h @@ -0,0 +1,70 @@ +/*- + * 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$ + */ + +/* + * 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 Index: sys/cam/mmc/mmc_bus.h =================================================================== --- /dev/null +++ sys/cam/mmc/mmc_bus.h @@ -0,0 +1,138 @@ +/*- + * Copyright (c) 2006 M. Warner Losh. 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$ + */ + +#ifndef DEV_MMC_BRIDGE_H +#define DEV_MMC_BRIDGE_H + +/* + * This file defines interfaces for the mmc bridge. The names chosen + * are similar to or the same as the names used in Linux to allow for + * easy porting of what Linux calls mmc host drivers. I use the + * FreeBSD terminology of bridge and bus for consistancy with other + * drivers in the system. This file corresponds roughly to the Linux + * linux/mmc/host.h file. + * + * A mmc bridge is a chipset that can have one or more mmc and/or sd + * cards attached to it. mmc cards are attached on a bus topology, + * while sd and sdio cards are attached using a star topology (meaning + * in practice each sd card has its own, independent slot). Each + * mmcbr is assumed to be derived from the mmcbr. This is done to + * allow for easier addition of bridges (as each bridge does not need + * to be added to the mmcbus file). + * + * Attached to the mmc bridge is an mmcbus. The mmcbus is described + * in dev/mmc/bus.h. + */ + + +/* + * mmc_ios is a structure that is used to store the state of the mmc/sd + * bus configuration. This include the bus' clock speed, its voltage, + * the bus mode for command output, the SPI chip select, some power + * states and the bus width. + */ +enum mmc_vdd { + vdd_150 = 0, vdd_155, vdd_160, vdd_165, vdd_170, vdd_180, + vdd_190, vdd_200, vdd_210, vdd_220, vdd_230, vdd_240, vdd_250, + vdd_260, vdd_270, vdd_280, vdd_290, vdd_300, vdd_310, vdd_320, + vdd_330, vdd_340, vdd_350, vdd_360 +}; + +enum mmc_power_mode { + power_off = 0, power_up, power_on +}; + +enum mmc_bus_mode { + opendrain = 1, pushpull +}; + +enum mmc_chip_select { + cs_dontcare = 0, cs_high, cs_low +}; + +enum mmc_bus_width { + bus_width_1 = 0, bus_width_4 = 2, bus_width_8 = 3 +}; + +enum mmc_bus_timing { + bus_timing_normal = 0, bus_timing_hs +}; + +struct mmc_ios { + uint32_t clock; /* Speed of the clock in Hz to move data */ + enum mmc_vdd vdd; /* Voltage to apply to the power pins/ */ + enum mmc_bus_mode bus_mode; + enum mmc_chip_select chip_select; + enum mmc_bus_width bus_width; + enum mmc_power_mode power_mode; + enum mmc_bus_timing timing; +}; + +enum mmc_card_mode { + mode_mmc, mode_sd +}; + +struct mmc_host { + int f_min; + int f_max; + uint32_t host_ocr; + uint32_t ocr; + uint32_t caps; +#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 */ + enum mmc_card_mode mode; + struct mmc_ios ios; /* Current state of the host */ +}; + +#endif /* DEV_MMC_BRIDGE_H */ Index: sys/cam/mmc/mmc_xpt.c =================================================================== --- /dev/null +++ sys/cam/mmc/mmc_xpt.c @@ -0,0 +1,1116 @@ +/*- + * 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 /* for xpt_print below */ +#include /* for PRIu64 */ +#include "opt_cam.h" + +struct xpt_xport *mmc_get_xport(void); +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_bus(struct cam_periph *periph, union ccb *ccb); +static void mmc_scan_lun(struct cam_periph *periph, + struct cam_path *path, cam_flags flags, + union ccb *ccb); +static void xptscandone(struct cam_periph *periph, union ccb *done_ccb); + +/* Supplementary methods */ +static void mmc_build_app_op_cond(union ccb *ccb, uint32_t ocr); + +/* 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 const char * get_action_desc(int action); + +static struct xpt_xport mmc_xport = { + .alloc_device = mmc_alloc_device, + .action = mmc_action, + .async = mmc_dev_async, + .announce = mmc_announce_periph, +}; + +struct xpt_xport * +mmc_get_xport(void) +{ + return (&mmc_xport); +} + +static void +xptscandone(struct cam_periph *periph, union ccb *done_ccb) +{ + + xpt_free_path(done_ccb->ccb_h.path); + xpt_free_ccb(done_ccb); +} + +/* 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) +{ + cam_status status; + struct cam_path newpath; + + printf("mmc_dev_async(async_code=0x%x, path_id=%d, target_id=%x, lun_id=%" PRIu64 "\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; + + /* + * We need our own path with wildcards expanded to + * handle certain types of events. + */ + if ((async_code == AC_SENT_BDR) + || (async_code == AC_BUS_RESET) + || (async_code == AC_INQ_CHANGED)) + status = xpt_compile_path(&newpath, NULL, + bus->path_id, + target->target_id, + device->lun_id); + else + status = CAM_REQ_CMP_ERR; + + printf("mmc_async: status = 0x%x\n", status); + + if (status == CAM_REQ_CMP) { + if (async_code == AC_INQ_CHANGED) { + /* + * We've sent a start unit command, or + * something similar to a device that + * may have caused its inquiry data to + * change. So we re-scan the device to + * refresh the inquiry data for it. + */ + mmc_scan_lun(newpath.periph, &newpath, + CAM_EXPECT_INQ_CHANGE, NULL); + } else { + /* We need to reinitialize device after reset. */ + mmc_scan_lun(newpath.periph, &newpath, + 0, NULL); + } + xpt_release_path(&newpath); + } else if (async_code == AC_LOST_DEVICE && + (device->flags & CAM_DEV_UNCONFIGURED) == 0) { + device->flags |= CAM_DEV_UNCONFIGURED; + xpt_release_device(device); + } else if (async_code == AC_TRANSFER_NEG) { + struct ccb_trans_settings *settings; + struct cam_path path; + + printf("AC_TRANSFER_NEG\n"); + settings = (struct ccb_trans_settings *)async_arg; + xpt_compile_path(&path, NULL, bus->path_id, target->target_id, + device->lun_id); +/* ata_set_transfer_settings(settings, &path, TRUE); */ + xpt_release_path(&path); + } +} + +typedef struct { + union ccb *request_ccb; + struct ccb_pathinq *cpi; + int counter; +} mmc_scan_bus_info; + +static void +mmc_scan_bus(struct cam_periph *periph, union ccb *request_ccb) +{ + struct cam_path *path; + union ccb *work_ccb, *reset_ccb; + mmc_scan_bus_info *scan_info; + struct mtx *mtx; + cam_status status; + + CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_INFO, + ("mmc_scan_bus! func_code=%x\n", request_ccb->ccb_h.func_code)); + + switch (request_ccb->ccb_h.func_code) { + case XPT_SCAN_BUS: + CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("XPT_SCAN_BUS\n")); + case XPT_SCAN_TGT: + CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("XPT_SCAN_TGT\n")); + /* Find out the characteristics of the bus */ + work_ccb = xpt_alloc_ccb_nowait(); + if (work_ccb == NULL) { + request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; + xpt_done(request_ccb); + return; + } + xpt_setup_ccb(&work_ccb->ccb_h, request_ccb->ccb_h.path, + request_ccb->ccb_h.pinfo.priority); + work_ccb->ccb_h.func_code = XPT_PATH_INQ; + xpt_action(work_ccb); + if (work_ccb->ccb_h.status != CAM_REQ_CMP) { + request_ccb->ccb_h.status = work_ccb->ccb_h.status; + xpt_free_ccb(work_ccb); + xpt_done(request_ccb); + return; + } + + /* We may need to reset bus first, if we haven't done it yet. */ + if (!(work_ccb->cpi.hba_misc & PIM_NOBUSRESET) && + !timevalisset(&request_ccb->ccb_h.path->bus->last_reset)) { + reset_ccb = xpt_alloc_ccb_nowait(); + if (reset_ccb == NULL) { + request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; + xpt_free_ccb(work_ccb); + xpt_done(request_ccb); + return; + } + xpt_setup_ccb(&reset_ccb->ccb_h, request_ccb->ccb_h.path, + CAM_PRIORITY_NONE); + reset_ccb->ccb_h.func_code = XPT_RESET_BUS; + xpt_action(reset_ccb); + if (reset_ccb->ccb_h.status != CAM_REQ_CMP) { + request_ccb->ccb_h.status = reset_ccb->ccb_h.status; + xpt_free_ccb(reset_ccb); + xpt_free_ccb(work_ccb); + xpt_done(request_ccb); + return; + } + xpt_free_ccb(reset_ccb); + } + + /* Save some state for use while we probe for devices */ + scan_info = (mmc_scan_bus_info *) + malloc(sizeof(mmc_scan_bus_info), M_CAMXPT, M_NOWAIT); + if (scan_info == NULL) { + request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; + xpt_free_ccb(work_ccb); + xpt_done(request_ccb); + return; + } + scan_info->request_ccb = request_ccb; + scan_info->cpi = &work_ccb->cpi; + scan_info->counter = 0; + + work_ccb = xpt_alloc_ccb_nowait(); + if (work_ccb == NULL) { + free(scan_info, M_CAMXPT); + request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; + xpt_done(request_ccb); + break; + } + mtx = xpt_path_mtx(scan_info->request_ccb->ccb_h.path); + goto scan_next; + + case XPT_SCAN_LUN: + CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("XPT_SCAN_LUN\n")); + work_ccb = request_ccb; + /* Reuse the same CCB to query if a device was really found */ + scan_info = (mmc_scan_bus_info *)work_ccb->ccb_h.ppriv_ptr0; + mtx = xpt_path_mtx(scan_info->request_ccb->ccb_h.path); + mtx_lock(mtx); + /* Free the current request path- we're done with it. */ + xpt_free_path(work_ccb->ccb_h.path); + mtx_unlock(mtx); + xpt_free_ccb(work_ccb); + xpt_free_ccb((union ccb *)scan_info->cpi); + request_ccb = scan_info->request_ccb; + free(scan_info, M_CAMXPT); + request_ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(request_ccb); + CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("SCAN_LUN -- done with it\n")); + return; + +scan_next: + status = xpt_create_path(&path, NULL, + scan_info->request_ccb->ccb_h.path_id, 0, 0); + if (status != CAM_REQ_CMP) { + if (request_ccb->ccb_h.func_code == XPT_SCAN_LUN) + mtx_unlock(mtx); + printf("xpt_scan_bus: xpt_create_path failed" + " with status %#x, bus scan halted\n", + status); + xpt_free_ccb(work_ccb); + xpt_free_ccb((union ccb *)scan_info->cpi); + request_ccb = scan_info->request_ccb; + free(scan_info, M_CAMXPT); + request_ccb->ccb_h.status = status; + xpt_done(request_ccb); + break; + } + xpt_setup_ccb(&work_ccb->ccb_h, path, + scan_info->request_ccb->ccb_h.pinfo.priority); + work_ccb->ccb_h.func_code = XPT_SCAN_LUN; + work_ccb->ccb_h.cbfcnp = mmc_scan_bus; + work_ccb->ccb_h.flags |= CAM_UNLOCKED; + work_ccb->ccb_h.ppriv_ptr0 = scan_info; + work_ccb->crcn.flags = scan_info->request_ccb->crcn.flags; + mtx_unlock(mtx); + xpt_action(work_ccb); + if (mtx != NULL) + mtx_lock(mtx); + break; + + default: + break; + } +} + +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_path *new_path; + struct cam_periph *old_periph; + int lock; + + CAM_DEBUG(path, CAM_DEBUG_PROBE, ("xpt_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 (request_ccb == NULL) { + request_ccb = xpt_alloc_ccb_nowait(); + if (request_ccb == NULL) { + xpt_print(path, "xpt_scan_lun: can't allocate CCB, " + "can't continue\n"); + return; + } + status = xpt_create_path(&new_path, NULL, + path->bus->path_id, + path->target->target_id, + path->device->lun_id); + if (status != CAM_REQ_CMP) { + xpt_print(path, "xpt_scan_lun: can't create path, " + "can't continue\n"); + xpt_free_ccb(request_ccb); + return; + } + xpt_setup_ccb(&request_ccb->ccb_h, new_path, CAM_PRIORITY_XPT); + request_ccb->ccb_h.cbfcnp = xptscandone; + request_ccb->ccb_h.flags |= CAM_UNLOCKED; + request_ccb->ccb_h.func_code = XPT_SCAN_LUN; + request_ccb->crcn.flags = flags; + } + + lock = (xpt_path_owned(path) == 0); + if (lock) + xpt_path_lock(path); + if ((old_periph = cam_periph_find(path, "mmcprobe")) != NULL) { + xpt_print(path, " Found mmcprobe device at path\n"); + if ((old_periph->flags & CAM_PERIPH_INVALID) == 0) { + xpt_print(path, " Periph is OK, insert request CCB into tailq\n"); + } else { + xpt_print(path, " Old periph is invalid\n"); + 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, + request_ccb->ccb_h.path, NULL, 0, + request_ccb); + 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, + get_action_desc(start_ccb->ccb_h.func_code))); + switch (start_ccb->ccb_h.func_code) { + + case XPT_SCAN_BUS: + CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO, + ("mmc_action! XPT_SCAN_BUS\n")); + mmc_scan_bus(start_ccb->ccb_h.path->periph, start_ccb); + break; + + case XPT_SCAN_TGT: + CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO, + ("mmc_action! XPT_SCAN_TGT\n")); + mmc_scan_bus(start_ccb->ccb_h.path->periph, start_ccb); + break; + + case XPT_SCAN_LUN: + CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO, + ("mmc_action! XPT_SCAN_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; + } +} + +/* + * XXX: What's the point in having this function per-XPT? + */ +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_INFO, + ("mmc_dev_advinfo: request %x\n", cdai->buftype)); + switch(cdai->buftype) { + case CDAI_TYPE_SCSI_DEVID: + if (cdai->flags & CDAI_FLAG_STORE) + return; + cdai->provsiz = device->device_id_len; + if (device->device_id_len == 0) + break; + amt = device->device_id_len; + if (cdai->provsiz > cdai->bufsiz) + amt = cdai->bufsiz; + memcpy(cdai->buf, device->device_id, amt); + break; + case CDAI_TYPE_SERIAL_NUM: + if (cdai->flags & CDAI_FLAG_STORE) + return; + cdai->provsiz = device->serial_num_len; + if (device->serial_num_len == 0) + break; + amt = device->serial_num_len; + if (cdai->provsiz > cdai->bufsiz) + amt = cdai->bufsiz; + memcpy(cdai->buf, device->serial_num, amt); + break; + case CDAI_TYPE_PHYS_PATH: + if (cdai->flags & CDAI_FLAG_STORE) { + if (device->physpath != NULL) + free(device->physpath, M_CAMXPT); + device->physpath_len = cdai->bufsiz; + /* Clear existing buffer if zero length */ + if (cdai->bufsiz == 0) + break; + device->physpath = malloc(cdai->bufsiz, M_CAMXPT, M_NOWAIT); + if (device->physpath == NULL) { + start_ccb->ccb_h.status = CAM_REQ_ABORTED; + return; + } + memcpy(device->physpath, cdai->buf, cdai->bufsiz); + } else { + cdai->provsiz = device->physpath_len; + if (device->physpath_len == 0) + break; + amt = device->physpath_len; + if (cdai->provsiz > cdai->bufsiz) + amt = cdai->bufsiz; + memcpy(cdai->buf, device->physpath, amt); + } + break; + default: + return; + } + start_ccb->ccb_h.status = CAM_REQ_CMP; + + if (cdai->flags & CDAI_FLAG_STORE) { + xpt_async(AC_ADVINFO_CHANGED, start_ccb->ccb_h.path, + (void *)(uintptr_t)cdai->buftype); + } +} + +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("SIM infromation should appear here!\n"); +} + +void +mmc_print_ident(struct mmc_params *ident_data) +{ + printf("(Some information about this MMC/SD device here)\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. */ + +typedef enum { + PROBE_RESET, + PROBE_IDENTIFY, + PROBE_SEND_IF_COND, + PROBE_SEND_APP_OP_COND_APPCMD, + PROBE_SEND_APP_OP_COND_ACMD41, + PROBE_DONE, + PROBE_INVALID +} probe_action; + +static char *probe_action_text[] = { + "PROBE_RESET", + "PROBE_IDENTIFY", + "PROBE_SEND_IF_COND", + "PROBE_SEND_APP_OP_COND_APPCMD", + "PROBE_SEND_APP_OP_COND_ACMD41", + "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) + +typedef enum { + PROBE_WTF = 0x01 +} probe_flags; + +typedef struct { + TAILQ_HEAD(, ccb_hdr) request_ccbs; + probe_action action; + int restart; + union ccb saved_ccb; + probe_flags flags; + struct cam_periph *periph; + + /* MMC Bus settings */ + uint32_t host_ocr; +} mmcprobe_softc; + +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); + 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")); +/* + * ATA XPT then sends the following CCB to the SIM: + cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS; + cts.type = CTS_TYPE_CURRENT_SETTINGS; +... + * ... but now there is nothing to send yet, I think +*/ + +/* + * ATA XPT then calls probeschedule() here, but we dont have + * so much work to do to write a separate function :) +*/ + 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 +mmc_build_app_op_cond(union ccb *ccb, uint32_t ocr) +{ + struct ccb_mmcio *mmcio; + + mmcio = &ccb->mmcio; + + init_standard_ccb(ccb, XPT_MMC_IO); + mmcio->cmd.opcode = ACMD_SD_SEND_OP_COND; + mmcio->cmd.arg = ocr; + mmcio->cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; + mmcio->cmd.data = NULL; +} + +static void +mmcprobe_start(struct cam_periph *periph, union ccb *start_ccb) +{ + mmcprobe_softc *softc; + struct cam_path *path; + struct ccb_mmcio *mmcio; + struct ccb_trans_settings_mmc *cts; + struct mtx *p_mtx = cam_periph_mtx(periph); + + 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; + + 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: + 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); + + init_standard_ccb(start_ccb, XPT_MMC_IO); + mmcio->cmd.opcode = MMC_GO_IDLE_STATE; + mmcio->cmd.arg = 0; + mmcio->cmd.flags = MMC_RSP_NONE | MMC_CMD_BC; + mmcio->cmd.data = NULL; + 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->cmd.data = NULL; + break; + + case PROBE_SEND_APP_OP_COND_APPCMD: + init_standard_ccb(start_ccb, XPT_MMC_IO); + mmcio->cmd.opcode = MMC_APP_CMD; /* CMD 55 */ + mmcio->cmd.arg = 0; /* rca << 16 */ + mmcio->cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + mmcio->cmd.data = NULL; + break; + + case PROBE_SEND_APP_OP_COND_ACMD41: + init_standard_ccb(start_ccb, XPT_MMC_IO); + mmcio->cmd.opcode = ACMD_SD_SEND_OP_COND; /* CMD 55 */ + /* XXX: Don't set HCS if no response to CMD8 */ + mmcio->cmd.arg = 1 << 30; /* HCS + ocr */ + mmcio->cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;; + mmcio->cmd.data = NULL; + break; + + case PROBE_DONE: + CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("Start with PROBE_DONE\n")); + return; + /* NOTREACHED */ + break; + default: + CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_PROBE, ("probestart: invalid action state 0x%x\n", softc->action)); + panic("ciao"); + } + + 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 found = 1; + 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; + + if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + /* Call generic error handler */ + if (cam_periph_error(done_ccb, + 0, softc->restart ? (SF_NO_RECOVERY | SF_NO_RETRY) : 0, + NULL) == ERESTART) { + /* Drop freeze taken due to CAM_DEV_QFREEZE flag set. */ + cam_release_devq(path, 0, 0, 0, FALSE); + return; + } + if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { + /* Don't wedge the queue */ + xpt_release_devq(path, /*count*/1, /*run_queue*/TRUE); + } + + if ((path->device->flags & CAM_DEV_UNCONFIGURED) == 0) + xpt_async(AC_LOST_DEVICE, path, NULL); + found = 0; + PROBE_SET_ACTION(softc, PROBE_INVALID); + + xpt_release_ccb(done_ccb); + /* Drop freeze taken due to CAM_DEV_QFREEZE flag set. */ + cam_release_devq(path, 0, 0, 0, FALSE); + cam_periph_invalidate(periph); + cam_periph_release_locked(periph); + return; + } + + 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")); + 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; + + 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])); + PROBE_SET_ACTION(softc, PROBE_INVALID); + } else { + PROBE_SET_ACTION(softc, PROBE_SEND_APP_OP_COND_APPCMD); + CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE, + ("SD 2.0 interface conditions: OK\n")); + } + break; + } + case PROBE_SEND_APP_OP_COND_APPCMD: + { + mmcio = &done_ccb->mmcio; + err = mmcio->cmd.error; + + if (err != MMC_ERR_NONE || !(mmcio->cmd.resp[0] & R1_APP_CMD)) { + 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_INVALID); + } else + PROBE_SET_ACTION(softc, PROBE_SEND_APP_OP_COND_ACMD41); + break; + } + case PROBE_SEND_APP_OP_COND_ACMD41: + { + 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\n", err)); + PROBE_SET_ACTION(softc, PROBE_INVALID); + } else { + if ((mmcio->cmd.resp[0] & MMC_OCR_CARD_BUSY) || + (mmcio->cmd.arg & MMC_OCR_VOLTAGE) == 0) { + CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE, + ("Card OCR: %08x\n", mmcio->cmd.resp[0])); + PROBE_SET_ACTION(softc, PROBE_DONE); + } 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_APPCMD); + } + } + break; + } + default: + CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_PROBE, + ("mmc_probedone: invalid action state 0x%x\n", softc->action)); + panic("ciao"); + } + + xpt_release_ccb(done_ccb); + xpt_schedule(periph, priority); + /* Drop freeze taken due to CAM_DEV_QFREEZE flag set. */ + cam_release_devq(path, 0, 0, 0, FALSE); +} + + +/* Helper stuff */ +static const char * +get_action_desc(int action) +{ + switch (action) { + case XPT_FC_QUEUED: + return "XPT_FC_QUEUED"; + ;break; + case XPT_FC_USER_CCB: + return "XPT_FC_USER_CCB"; + ;break; + case XPT_FC_XPT_ONLY: + return "XPT_FC_XPT_ONLY"; + ;break; + case XPT_FC_DEV_QUEUED: + return "XPT_FC_DEV_QUEUED"; + ;break; + case XPT_NOOP: + return "XPT_NOOP"; + ;break; + case XPT_SCSI_IO: + return "XPT_SCSI_IO"; + ;break; + case XPT_GDEV_TYPE: + return "XPT_GDEV_TYPE"; + ;break; + case XPT_GDEVLIST: + return "XPT_GDEVLIST"; + ;break; + case XPT_PATH_INQ: + return "XPT_PATH_INQ"; + ;break; + case XPT_REL_SIMQ: + return "XPT_REL_SIMQ"; + ;break; + case XPT_SASYNC_CB: + return "XPT_SASYNC_CB"; + ;break; + case XPT_SDEV_TYPE: + return "XPT_SDEV_TYPE"; + ;break; + case XPT_SCAN_BUS: + return "XPT_SCAN_BUS"; + ;break; + case XPT_DEV_MATCH: + return "XPT_DEV_MATCH"; + ;break; + case XPT_DEBUG: + return "XPT_DEBUG"; + ;break; + case XPT_PATH_STATS: + return "XPT_PATH_STATS"; + ;break; + case XPT_GDEV_STATS: + return "XPT_GDEV_STATS"; + ;break; + case XPT_DEV_ADVINFO: + return "XPT_DEV_ADVINFO"; + ;break; + case XPT_ASYNC: + return "XPT_ASYNC"; + ;break; + case XPT_ABORT: + return "XPT_ABORT"; + ;break; + case XPT_RESET_BUS: + return "XPT_RESET_BUS"; + ;break; + case XPT_RESET_DEV: + return "XPT_RESET_DEV"; + ;break; + case XPT_TERM_IO: + return "XPT_TERM_IO"; + ;break; + case XPT_SCAN_LUN: + return "XPT_SCAN_LUN"; + ;break; + case XPT_GET_TRAN_SETTINGS: + return "XPT_GET_TRAN_SETTINGS"; + ;break; + case XPT_SET_TRAN_SETTINGS: + return "XPT_SET_TRAN_SETTINGS"; + ;break; + case XPT_CALC_GEOMETRY: + return "XPT_CALC_GEOMETRY"; + ;break; + case XPT_ATA_IO: + return "XPT_ATA_IO"; + ;break; + case XPT_GET_SIM_KNOB: + return "XPT_GET_SIM_KNOB"; + ;break; + case XPT_SET_SIM_KNOB: + return "XPT_SET_SIM_KNOB"; + ;break; + case XPT_SMP_IO: + return "XPT_SMP_IO"; + ;break; + case XPT_SCAN_TGT: + return "XPT_SCAN_TGT"; + ;break; + case XPT_ENG_INQ: + return "XPT_ENG_INQ"; + ;break; + case XPT_ENG_EXEC: + return "XPT_ENG_EXEC"; + ;break; + case XPT_EN_LUN: + return "XPT_EN_LUN"; + ;break; + case XPT_TARGET_IO: + return "XPT_TARGET_IO"; + ;break; + case XPT_ACCEPT_TARGET_IO: + return "XPT_ACCEPT_TARGET_IO"; + ;break; + case XPT_CONT_TARGET_IO: + return "XPT_CONT_TARGET_IO"; + ;break; + case XPT_IMMED_NOTIFY: + return "XPT_IMMED_NOTIFY"; + ;break; + case XPT_NOTIFY_ACK: + return "XPT_NOTIFY_ACK"; + ;break; + case XPT_IMMEDIATE_NOTIFY: + return "XPT_IMMEDIATE_NOTIFY"; + ;break; + case XPT_NOTIFY_ACKNOWLEDGE: + return "XPT_NOTIFY_ACKNOWLEDGE"; + ;break; + case XPT_MMC_IO: + return "XPT_MMC_IO"; + ;break; + case XPT_VUNIQUE: + return "XPT_VUNIQUE"; + ;break; + default: + return ""; + } +} Index: sys/cam/mmc/mmcreg.h =================================================================== --- /dev/null +++ sys/cam/mmc/mmcreg.h @@ -0,0 +1,445 @@ +/*- + * Copyright (c) 2006 M. Warner Losh. 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$ + */ + +#ifndef DEV_MMC_MMCREG_H +#define DEV_MMC_MMCREG_H + +/* + * This file contains the register definitions for the mmc and sd busses. + * They are taken from publicly available sources. + */ + +struct mmc_data; +struct mmc_request; + +struct mmc_command { + uint32_t opcode; + uint32_t arg; + uint32_t resp[4]; + uint32_t flags; /* Expected responses */ +#define MMC_RSP_PRESENT (1ul << 0) /* Response */ +#define MMC_RSP_136 (1ul << 1) /* 136 bit response */ +#define MMC_RSP_CRC (1ul << 2) /* Expect valid crc */ +#define MMC_RSP_BUSY (1ul << 3) /* Card may send busy */ +#define MMC_RSP_OPCODE (1ul << 4) /* Response include opcode */ +#define MMC_RSP_MASK 0x1ful +#define MMC_CMD_AC (0ul << 5) /* Addressed Command, no data */ +#define MMC_CMD_ADTC (1ul << 5) /* Addressed Data transfer cmd */ +#define MMC_CMD_BC (2ul << 5) /* Broadcast command, no response */ +#define MMC_CMD_BCR (3ul << 5) /* Broadcast command with response */ +#define MMC_CMD_MASK (3ul << 5) + +/* Possible response types defined in the standard: */ +#define MMC_RSP_NONE (0) +#define MMC_RSP_R1 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE) +#define MMC_RSP_R1B (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE | MMC_RSP_BUSY) +#define MMC_RSP_R2 (MMC_RSP_PRESENT | MMC_RSP_136 | MMC_RSP_CRC) +#define MMC_RSP_R3 (MMC_RSP_PRESENT) +#define MMC_RSP_R6 (MMC_RSP_PRESENT | MMC_RSP_CRC) +#define MMC_RSP_R7 (MMC_RSP_PRESENT | MMC_RSP_CRC) +#define MMC_RSP(x) ((x) & MMC_RSP_MASK) + uint32_t retries; + uint32_t error; +#define MMC_ERR_NONE 0 +#define MMC_ERR_TIMEOUT 1 +#define MMC_ERR_BADCRC 2 +#define MMC_ERR_FIFO 3 +#define MMC_ERR_FAILED 4 +#define MMC_ERR_INVALID 5 +#define MMC_ERR_NO_MEMORY 6 +#define MMC_ERR_MAX 6 + struct mmc_data *data; /* Data segment with cmd */ + struct mmc_request *mrq; /* backpointer to request */ +}; + +/* + * R1 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 R1_OUT_OF_RANGE (1u << 31) /* erx, c */ +#define R1_ADDRESS_ERROR (1u << 30) /* erx, c */ +#define R1_BLOCK_LEN_ERROR (1u << 29) /* erx, c */ +#define R1_ERASE_SEQ_ERROR (1u << 28) /* er, c */ +#define R1_ERASE_PARAM (1u << 27) /* erx, c */ +#define R1_WP_VIOLATION (1u << 26) /* erx, c */ +#define R1_CARD_IS_LOCKED (1u << 25) /* sx, a */ +#define R1_LOCK_UNLOCK_FAILED (1u << 24) /* erx, c */ +#define R1_COM_CRC_ERROR (1u << 23) /* er, b */ +#define R1_ILLEGAL_COMMAND (1u << 22) /* er, b */ +#define R1_CARD_ECC_FAILED (1u << 21) /* erx, c */ +#define R1_CC_ERROR (1u << 20) /* erx, c */ +#define R1_ERROR (1u << 19) /* erx, c */ +#define R1_CSD_OVERWRITE (1u << 16) /* erx, c */ +#define R1_WP_ERASE_SKIP (1u << 15) /* erx, c */ +#define R1_CARD_ECC_DISABLED (1u << 14) /* sx, a */ +#define R1_ERASE_RESET (1u << 13) /* sr, c */ +#define R1_CURRENT_STATE_MASK (0xfu << 9) /* sx, b */ +#define R1_READY_FOR_DATA (1u << 8) /* sx, a */ +#define R1_APP_CMD (1u << 5) /* sr, c */ +#define R1_AKE_SEQ_ERROR (1u << 3) /* er, c */ +#define R1_STATUS(x) ((x) & 0xFFFFE000) +#define R1_CURRENT_STATE(x) (((x) & R1_CURRENT_STATE_MASK) >> 9) +#define R1_STATE_IDLE 0 +#define R1_STATE_READY 1 +#define R1_STATE_IDENT 2 +#define R1_STATE_STBY 3 +#define R1_STATE_TRAN 4 +#define R1_STATE_DATA 5 +#define R1_STATE_RCV 6 +#define R1_STATE_PRG 7 +#define R1_STATE_DIS 8 + +struct mmc_data { + size_t len; /* size of the data */ + size_t xfer_len; + void *data; /* data buffer */ + uint32_t flags; +#define MMC_DATA_WRITE (1UL << 0) +#define MMC_DATA_READ (1UL << 1) +#define MMC_DATA_STREAM (1UL << 2) +#define MMC_DATA_MULTI (1UL << 3) + struct mmc_request *mrq; +}; + +struct mmc_request { + struct mmc_command *cmd; + struct mmc_command *stop; + void (*done)(struct mmc_request *); /* Completion function */ + void *done_data; /* requestor set data */ + uint32_t flags; +#define MMC_REQ_DONE 1 +}; + +/* Command definitions */ + +/* Class 0 and 1: Basic commands & read stream commands */ +#define MMC_GO_IDLE_STATE 0 +#define MMC_SEND_OP_COND 1 +#define MMC_ALL_SEND_CID 2 +#define MMC_SET_RELATIVE_ADDR 3 +#define SD_SEND_RELATIVE_ADDR 3 +#define MMC_SET_DSR 4 + /* reserved: 5 */ +#define MMC_SWITCH_FUNC 6 +#define MMC_SWITCH_FUNC_CMDS 0 +#define MMC_SWITCH_FUNC_SET 1 +#define MMC_SWITCH_FUNC_CLR 2 +#define MMC_SWITCH_FUNC_WR 3 +#define MMC_SELECT_CARD 7 +#define MMC_DESELECT_CARD 7 +#define MMC_SEND_EXT_CSD 8 +#define SD_SEND_IF_COND 8 +#define MMC_SEND_CSD 9 +#define MMC_SEND_CID 10 +#define MMC_READ_DAT_UNTIL_STOP 11 +#define MMC_STOP_TRANSMISSION 12 +#define MMC_SEND_STATUS 13 +#define MMC_BUSTEST_R 14 +#define MMC_GO_INACTIVE_STATE 15 +#define MMC_BUSTEST_W 19 + +/* Class 2: Block oriented read commands */ +#define MMC_SET_BLOCKLEN 16 +#define MMC_READ_SINGLE_BLOCK 17 +#define MMC_READ_MULTIPLE_BLOCK 18 + /* reserved: 19 */ + +/* Class 3: Stream write commands */ +#define MMC_WRITE_DAT_UNTIL_STOP 20 + /* reserved: 21 */ + /* reserved: 22 */ + +/* Class 4: Block oriented write commands */ +#define MMC_SET_BLOCK_COUNT 23 +#define MMC_WRITE_BLOCK 24 +#define MMC_WRITE_MULTIPLE_BLOCK 25 +#define MMC_PROGARM_CID 26 +#define MMC_PROGRAM_CSD 27 + +/* Class 6: Block oriented write protection commands */ +#define MMC_SET_WRITE_PROT 28 +#define MMC_CLR_WRITE_PROT 29 +#define MMC_SEND_WRITE_PROT 30 + /* reserved: 31 */ + +/* Class 5: Erase commands */ +#define SD_ERASE_WR_BLK_START 32 +#define SD_ERASE_WR_BLK_END 33 + /* 34 -- reserved old command */ +#define MMC_ERASE_GROUP_START 35 +#define MMC_ERASE_GROUP_END 36 + /* 37 -- reserved old command */ +#define MMC_ERASE 38 + +/* Class 9: I/O mode commands */ +#define MMC_FAST_IO 39 +#define MMC_GO_IRQ_STATE 40 + /* reserved: 41 */ + +/* Class 7: Lock card */ +#define MMC_LOCK_UNLOCK 42 + /* reserved: 43 */ + /* reserved: 44 */ + /* reserved: 45 */ + /* reserved: 46 */ + /* reserved: 47 */ + /* reserved: 48 */ + /* reserved: 49 */ + /* reserved: 50 */ + /* reserved: 51 */ + /* reserved: 54 */ + +/* Class 8: Application specific commands */ +#define MMC_APP_CMD 55 +#define MMC_GEN_CMD 56 + /* reserved: 57 */ + /* reserved: 58 */ + /* reserved: 59 */ + /* reserved for mfg: 60 */ + /* reserved for mfg: 61 */ + /* reserved for mfg: 62 */ + /* reserved for mfg: 63 */ + +/* Class 9: I/O cards (sd) */ +#define SD_IO_RW_DIRECT 52 +#define SD_IO_RW_EXTENDED 53 + +/* Class 10: Switch function commands */ +#define SD_SWITCH_FUNC 6 + /* reserved: 34 */ + /* reserved: 35 */ + /* reserved: 36 */ + /* reserved: 37 */ + /* reserved: 50 */ + /* reserved: 57 */ + + +/* Application specific commands for SD */ +#define ACMD_SET_BUS_WIDTH 6 +#define ACMD_SD_STATUS 13 +#define ACMD_SEND_NUM_WR_BLOCKS 22 +#define ACMD_SET_WR_BLK_ERASE_COUNT 23 +#define ACMD_SD_SEND_OP_COND 41 +#define ACMD_SET_CLR_CARD_DETECT 42 +#define ACMD_SEND_SCR 51 + +/* + * EXT_CSD fields + */ +#define EXT_CSD_ERASE_GRP_DEF 175 /* R/W */ +#define EXT_CSD_BUS_WIDTH 183 /* R/W */ +#define EXT_CSD_HS_TIMING 185 /* R/W */ +#define EXT_CSD_CARD_TYPE 196 /* RO */ +#define EXT_CSD_REV 192 /* RO */ +#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ +#define EXT_CSD_ERASE_TO_MULT 223 /* RO */ +#define EXT_CSD_ERASE_GRP_SIZE 224 /* RO */ + +/* + * EXT_CSD field definitions + */ +#define EXT_CSD_CMD_SET_NORMAL 1 +#define EXT_CSD_CMD_SET_SECURE 2 +#define EXT_CSD_CMD_SET_CPSECURE 4 + +#define EXT_CSD_CARD_TYPE_26 1 +#define EXT_CSD_CARD_TYPE_52 2 + +#define EXT_CSD_BUS_WIDTH_1 0 +#define EXT_CSD_BUS_WIDTH_4 1 +#define EXT_CSD_BUS_WIDTH_8 2 + +#define MMC_TYPE_26_MAX_HS 26000000 +#define MMC_TYPE_52_MAX_HS 52000000 + +/* + * SD bus widths + */ +#define SD_BUS_WIDTH_1 0 +#define SD_BUS_WIDTH_4 2 + +/* + * SD Switch + */ +#define SD_SWITCH_MODE_CHECK 0 +#define SD_SWITCH_MODE_SET 1 +#define SD_SWITCH_GROUP1 0 +#define SD_SWITCH_NORMAL_MODE 0 +#define SD_SWITCH_HS_MODE 1 +#define SD_SWITCH_NOCHANGE 0xF + +#define SD_CLR_CARD_DETECT 0 +#define SD_SET_CARD_DETECT 1 + +#define SD_MAX_HS 50000000 + +/* OCR bits */ + +/* + * in SD 2.0 spec, bits 8-14 are now marked reserved + * Low voltage in SD2.0 spec is bit 7, TBD voltage + * Low voltage in MC 3.31 spec is bit 7, 1.65-1.95V + * Specs prior to MMC 3.31 defined bits 0-7 as voltages down to 1.5V. + * 3.31 redefined them to be reserved and also said that cards had to + * support the 2.7-3.6V and fixed the OCR to be 0xfff8000 for high voltage + * cards. MMC 4.0 says that a dual voltage card responds with 0xfff8080. + * Looks like the fine-grained control of the voltage tolerance ranges + * was abandoned. + * + * The MMC_OCR_CCS appears to be valid for only SD cards. + */ +#define MMC_OCR_VOLTAGE 0x3fffffffU /* Vdd Voltage mask */ +#define MMC_OCR_LOW_VOLTAGE (1u << 7) /* Low Voltage Range -- tbd */ +#define MMC_OCR_200_210 (1U << 8) /* Vdd voltage 2.00 ~ 2.10 */ +#define MMC_OCR_MIN_VOLTAGE_SHIFT 8 +#define MMC_OCR_210_220 (1U << 9) /* Vdd voltage 2.10 ~ 2.20 */ +#define MMC_OCR_220_230 (1U << 10) /* Vdd voltage 2.20 ~ 2.30 */ +#define MMC_OCR_230_240 (1U << 11) /* Vdd voltage 2.30 ~ 2.40 */ +#define MMC_OCR_240_250 (1U << 12) /* Vdd voltage 2.40 ~ 2.50 */ +#define MMC_OCR_250_260 (1U << 13) /* Vdd voltage 2.50 ~ 2.60 */ +#define MMC_OCR_260_270 (1U << 14) /* Vdd voltage 2.60 ~ 2.70 */ +#define MMC_OCR_270_280 (1U << 15) /* Vdd voltage 2.70 ~ 2.80 */ +#define MMC_OCR_280_290 (1U << 16) /* Vdd voltage 2.80 ~ 2.90 */ +#define MMC_OCR_290_300 (1U << 17) /* Vdd voltage 2.90 ~ 3.00 */ +#define MMC_OCR_300_310 (1U << 18) /* Vdd voltage 3.00 ~ 3.10 */ +#define MMC_OCR_310_320 (1U << 19) /* Vdd voltage 3.10 ~ 3.20 */ +#define MMC_OCR_320_330 (1U << 20) /* Vdd voltage 3.20 ~ 3.30 */ +#define MMC_OCR_330_340 (1U << 21) /* Vdd voltage 3.30 ~ 3.40 */ +#define MMC_OCR_340_350 (1U << 22) /* Vdd voltage 3.40 ~ 3.50 */ +#define MMC_OCR_350_360 (1U << 23) /* Vdd voltage 3.50 ~ 3.60 */ +#define MMC_OCR_MAX_VOLTAGE_SHIFT 23 +#define MMC_OCR_CCS (1u << 30) /* Card Capacity status (SD vs SDHC) */ +#define MMC_OCR_CARD_BUSY (1U << 31) /* Card Power up status */ + +/* CSD -- decoded structure */ +struct mmc_cid { + uint32_t mid; + char pnm[8]; + uint32_t psn; + uint16_t oid; + uint16_t mdt_year; + uint8_t mdt_month; + uint8_t prv; + uint8_t fwrev; +}; + +struct mmc_csd +{ + uint8_t csd_structure; + uint8_t spec_vers; + uint16_t ccc; + uint16_t tacc; + uint32_t nsac; + uint32_t r2w_factor; + uint32_t tran_speed; + uint32_t read_bl_len; + uint32_t write_bl_len; + uint32_t vdd_r_curr_min; + uint32_t vdd_r_curr_max; + uint32_t vdd_w_curr_min; + uint32_t vdd_w_curr_max; + uint32_t wp_grp_size; + uint32_t erase_sector; + uint64_t capacity; + unsigned int read_bl_partial:1, + read_blk_misalign:1, + write_bl_partial:1, + write_blk_misalign:1, + dsr_imp:1, + erase_blk_en:1, + wp_grp_enable:1; +}; + +struct mmc_scr +{ + unsigned char sda_vsn; + unsigned char bus_widths; +#define SD_SCR_BUS_WIDTH_1 (1<<0) +#define SD_SCR_BUS_WIDTH_4 (1<<2) +}; + +struct mmc_sd_status +{ + uint8_t bus_width; + uint8_t secured_mode; + uint16_t card_type; + uint16_t prot_area; + uint8_t speed_class; + uint8_t perf_move; + uint8_t au_size; + uint16_t erase_size; + uint8_t erase_timeout; + uint8_t erase_offset; +}; + +/* + * Older versions of the MMC standard had a variable sector size. However, + * I've been able to find no old MMC or SD cards that have a non 512 + * byte sector size anywhere, so we assume that such cards are very rare + * and only note their existance in passing here... + */ +#define MMC_SECTOR_SIZE 512 + +#endif /* DEV_MMCREG_H */ Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -91,6 +91,7 @@ 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 cam/scsi/scsi_da.c optional da cam/scsi/scsi_low.c optional ct | ncv | nsp | stg cam/scsi/scsi_pass.c optional pass @@ -1879,6 +1880,7 @@ dev/mmc/mmcbr_if.m standard dev/mmc/mmcbus_if.m standard dev/mmc/mmcsd.c optional mmcsd +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 Index: sys/dev/mmcnull/Makefile =================================================================== --- /dev/null +++ sys/dev/mmcnull/Makefile @@ -0,0 +1,4 @@ +KMOD= mmcnull +SRCS= mmcnull.c + +.include Index: sys/dev/mmcnull/mmcnull.c =================================================================== --- /dev/null +++ sys/dev/mmcnull/mmcnull.c @@ -0,0 +1,328 @@ +/*- + * Copyright (c) 2013 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +struct mmcnull_softc { + device_t dev; + struct mtx sc_mtx; + + struct cam_devq *devq; + struct cam_sim *sim; + struct cam_path *path; + + struct callout tick; + union ccb *cur_ccb; +}; + +static void mmcnull_identify(driver_t *, device_t); +static int mmcnull_probe(device_t); +static int mmcnull_attach(device_t); +static int mmcnull_detach(device_t); +static void mmcnull_action(struct cam_sim *, union ccb *); +static void mmcnull_poll(struct cam_sim *); + +static void +mmcnull_callout(void *arg) +{ + struct mmcnull_softc *sc; + + sc = (struct mmcnull_softc *)arg; + + callout_reset(&sc->tick, 1, mmcnull_callout, sc); +} + +static void +mmcnull_identify(driver_t *driver, device_t parent) +{ + device_t child; + + if (resource_disabled("mmcnull", 0)) + return; + + if (device_get_unit(parent) != 0) + return; + + /* Avoid duplicates. */ + if (device_find_child(parent, "mmcnull", -1)) + return; + + child = BUS_ADD_CHILD(parent, 20, "mmcnull", 0); + if (child == NULL) { + device_printf(parent, "add MMCNULL child failed\n"); + return; + } +} + + +static int +mmcnull_probe(device_t dev) +{ + device_set_desc(dev, "Emulated MMC controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +mmcnull_attach(device_t dev) +{ + struct mmcnull_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + + mtx_init(&sc->sc_mtx, "mmcnullmtx", NULL, MTX_DEF); + + if ((sc->devq = cam_simq_alloc(1)) == NULL) + return (ENOMEM); + + sc->sim = cam_sim_alloc(mmcnull_action, mmcnull_poll, "mmcnull", sc, + device_get_unit(dev), &sc->sc_mtx, 1, 1, + sc->devq); + + if (sc->sim == NULL) { + cam_simq_free(sc->devq); + device_printf(dev, "cannot allocate CAM SIM\n"); + return (EINVAL); + } + + mtx_lock(&sc->sc_mtx); + if (xpt_bus_register(sc->sim, dev, 0) != 0) { + device_printf(dev, + "cannot register SCSI pass-through bus\n"); + cam_sim_free(sc->sim, FALSE); + cam_simq_free(sc->devq); + mtx_unlock(&sc->sc_mtx); + return (EINVAL); + } + mtx_unlock(&sc->sc_mtx); + + callout_init_mtx(&sc->tick, &sc->sc_mtx, 0); /* Callout to emulate interrupts */ + + device_printf(dev, "attached OK\n"); + + return (0); +} + +static int +mmcnull_detach(device_t dev) +{ + struct mmcnull_softc *sc; + + sc = device_get_softc(dev); + + if (sc == NULL) + return (EINVAL); + + if (sc->sim != NULL) { + mtx_lock(&sc->sc_mtx); + xpt_bus_deregister(cam_sim_path(sc->sim)); + cam_sim_free(sc->sim, FALSE); + mtx_unlock(&sc->sc_mtx); + } + + if (sc->devq != NULL) + cam_simq_free(sc->devq); + + callout_drain(&sc->tick); + mtx_destroy(&sc->sc_mtx); + + device_printf(dev, "detached OK\n"); + return (0); +} + +/* + * The interrupt handler + * This implementation calls it via callout(9) + * with the mutex already taken + */ +static void +mmcnull_intr(void *xsc) +{ + struct mmcnull_softc *sc; + union ccb *ccb; + struct ccb_mmcio *mmcio; + + sc = (struct mmcnull_softc *) xsc; + mtx_assert(&sc->sc_mtx, MA_OWNED); + + ccb = sc->cur_ccb; + mmcio = &ccb->mmcio; + device_printf(sc->dev, "mmcnull_intr: MMC command = %d\n", + mmcio->mmc_command); + + ccb->ccb_h.status = CAM_REQ_CMP; + + sc->cur_ccb = NULL; + xpt_done(ccb); +} + +/* + * This is a MMC IO handler + * It extracts MMC command from CCB and sends it + * to the h/w + */ +static void +mmcnull_handle_mmcio(struct cam_sim *sim, union ccb *ccb) +{ + struct mmcnull_softc *sc; + struct ccb_mmcio *mmcio; + + sc = cam_sim_softc(sim); + mmcio = &ccb->mmcio; + ccb->ccb_h.status = CAM_REQ_INPROG; + sc->cur_ccb = ccb; + + /* Real h/w will wait for the interrupt */ + callout_reset(&sc->tick, hz * 10, mmcnull_intr, sc); +} + +static void +mmcnull_action(struct cam_sim *sim, union ccb *ccb) +{ + struct mmcnull_softc *sc; + + sc = cam_sim_softc(sim); + if (sc == NULL) { + ccb->ccb_h.status = CAM_SEL_TIMEOUT; + xpt_done(ccb); + return; + } + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + device_printf(sc->dev, "action: func_code %0x\n", ccb->ccb_h.func_code); + + switch (ccb->ccb_h.func_code) { + case XPT_PATH_INQ: + { + struct ccb_pathinq *cpi; + + cpi = &ccb->cpi; + cpi->version_num = 1; + cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE | PI_WIDE_16; + 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; + 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; + + device_printf(sc->dev, "Got XPT_GET_TRAN_SETTINGS\n"); + + cts->protocol = PROTO_MMCSD; + cts->protocol_version = 0; + cts->transport = XPORT_MMCSD; + cts->transport_version = 0; + cts->xport_specific.valid = 0; + + ccb->ccb_h.status = CAM_REQ_CMP; + break; + } + case XPT_SET_TRAN_SETTINGS: + device_printf(sc->dev, "Got XPT_SET_TRAN_SETTINGS, should update IOS...\n"); + ccb->ccb_h.status = CAM_REQ_CMP; + break; + case XPT_RESET_BUS: + device_printf(sc->dev, "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. + */ + device_printf(sc->dev, "Got XPT_MMC_IO\n"); + mmcnull_handle_mmcio(sim, ccb); + return; + break; + default: + ccb->ccb_h.status = CAM_REQ_INVALID; + break; + } + xpt_done(ccb); + return; +} + +static void +mmcnull_poll(struct cam_sim *sim) +{ + return; +} + + +static device_method_t mmcnull_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, mmcnull_identify), + DEVMETHOD(device_probe, mmcnull_probe), + DEVMETHOD(device_attach, mmcnull_attach), + DEVMETHOD(device_detach, mmcnull_detach), + DEVMETHOD_END +}; + +static driver_t mmcnull_driver = { + "mmcnull", mmcnull_methods, sizeof(struct mmcnull_softc) +}; + +static devclass_t mmcnull_devclass; + +DRIVER_MODULE(mmcnull, cpu, mmcnull_driver, mmcnull_devclass, 0, 0); Index: sys/dev/sdhci/sdhci.h =================================================================== --- sys/dev/sdhci/sdhci.h +++ sys/dev/sdhci/sdhci.h @@ -247,7 +247,10 @@ struct mmc_host host; /* Host parameters */ struct mmc_request *req; /* Current request */ struct mmc_command *curcmd; /* Current command of current request */ - + + /* CAM stuff */ + union ccb *ccb; + uint32_t intmask; /* Current interrupt mask */ uint32_t clock; /* Current clock freq. */ size_t offset; /* Data buffer offset */ @@ -281,4 +284,7 @@ void sdhci_generic_intr(struct sdhci_slot *slot); uint32_t sdhci_generic_min_freq(device_t brdev, struct sdhci_slot *slot); +/* CAM-related */ +int sdhci_update_ios_cam(struct sdhci_slot *slot); +int sdhci_request_cam(struct sdhci_slot *slot, union ccb *ccb); #endif /* __SDHCI_H__ */ Index: sys/dev/sdhci/sdhci.c =================================================================== --- sys/dev/sdhci/sdhci.c +++ sys/dev/sdhci/sdhci.c @@ -48,13 +48,19 @@ #include #include +#include +#include +#include +#include +#include + #include "mmcbr_if.h" #include "sdhci.h" #include "sdhci_if.h" SYSCTL_NODE(_hw, OID_AUTO, sdhci, CTLFLAG_RD, 0, "sdhci driver"); -static int sdhci_debug; +static int sdhci_debug = 10; SYSCTL_INT(_hw_sdhci, OID_AUTO, debug, CTLFLAG_RWTUN, &sdhci_debug, 0, "Debug level"); #define RD1(slot, off) SDHCI_READ_1((slot)->bus, (slot), (off)) @@ -470,6 +476,7 @@ int err; SDHCI_LOCK_INIT(slot); + slot->num = num; slot->bus = dev; @@ -599,6 +606,7 @@ TASK_INIT(&slot->card_task, 0, sdhci_card_task, slot); callout_init(&slot->card_callout, 1); callout_init_mtx(&slot->timeout_callout, &slot->mtx, 0); + return (0); } @@ -661,12 +669,23 @@ return (slot->max_clk / SDHCI_200_MAX_DIVIDER); } +/* + * When called from the old stack: + sdhci_ti0: This is a bridge device + mmc0: This is a request device + sdhci_ti0-slot0: <--- The locking slot is this + * + */ int 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) { @@ -767,6 +786,9 @@ if ((state & SDHCI_CARD_PRESENT) == 0 || slot->power == 0 || slot->clock == 0) { + slot_printf(slot, + "Cannot issue a command (card=%d power=%d clock=%d)", + state, slot->power, slot->clock); cmd->error = MMC_ERR_FAILED; sdhci_req_done(slot); return; @@ -777,8 +799,9 @@ if (cmd->data || (cmd->flags & MMC_RSP_BUSY)) mask |= SDHCI_DAT_INHIBIT; /* We shouldn't wait for DAT for stop commands. */ - if (cmd == slot->req->stop) +/* if (cmd == slot->req->stop) mask &= ~SDHCI_DAT_INHIBIT; +*/ /* * 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 @@ -836,6 +859,7 @@ WR4(slot, SDHCI_ARGUMENT, cmd->arg); /* Set data transfer mode. */ sdhci_set_transfer_mode(slot, cmd->data); + slot_printf(slot, "Starting command!\n"); /* Start command. */ WR2(slot, SDHCI_COMMAND_FLAGS, (cmd->opcode << 8) | (flags & 0xff)); /* Start timeout callout. */ @@ -847,6 +871,8 @@ { int i; + 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. * Main restore point for the case when command interrupt @@ -877,6 +903,10 @@ } else slot->curcmd->resp[0] = RD4(slot, SDHCI_RESPONSE); } + 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); @@ -1300,6 +1330,11 @@ } SDHCI_UNLOCK(slot); + + /* Tell CAM the request is finished */ + slot->ccb->ccb_h.status = CAM_REQ_CMP; + + xpt_done(slot->ccb); } int @@ -1361,6 +1396,7 @@ { struct sdhci_slot *slot = device_get_ivars(child); + slot_printf(slot, "sdhci_generic_write_ivar, var=%d\n", which); switch (which) { default: return (EINVAL); @@ -1428,4 +1464,75 @@ return (0); } +/* CAM-related functions */ +#include +#include +#include +#include +#include + +int +sdhci_update_ios_cam(struct sdhci_slot *slot) +{ + struct mmc_ios *ios = &slot->host.ios; + + 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) { + 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_4) + slot->hostctrl |= SDHCI_CTRL_4BITBUS; + else + slot->hostctrl &= ~SDHCI_CTRL_4BITBUS; + if (ios->timing == bus_timing_hs) + 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_request_cam(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)?(u_int)mmcio->cmd.data->len:0, + (mmcio->cmd.data)?mmcio->cmd.data->flags:0); + } +// slot->req = req; + slot->ccb = ccb; + slot->flags = 0; + sdhci_start_command(slot, &mmcio->cmd); + SDHCI_UNLOCK(slot); + if (dumping) { + while (slot->req != NULL) { + sdhci_generic_intr(slot); + DELAY(10); + } + } + return (0); +} + MODULE_VERSION(sdhci, 1); Index: sys/modules/mmcnull/Makefile =================================================================== --- /dev/null +++ sys/modules/mmcnull/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/mmcnull + +KMOD= mmcnull +SRCS= mmcnull.c device_if.h bus_if.h + +.include