Index: sys/arm/allwinner/aw_mmc.c =================================================================== --- sys/arm/allwinner/aw_mmc.c +++ sys/arm/allwinner/aw_mmc.c @@ -55,6 +55,16 @@ #include #include +#include "opt_mmccam.h" + +#ifdef MMCCAM +#include +#include +#include +#include +#include +#endif + #define AW_MMC_MEMRES 0 #define AW_MMC_IRQRES 1 #define AW_MMC_RESSZ 2 @@ -112,7 +122,14 @@ int aw_timeout; struct callout aw_timeoutc; struct mmc_host aw_host; +#ifdef MMCCAM + union ccb * ccb; + struct cam_devq * devq; + struct cam_sim * sim; + struct mtx sim_mtx; +#else struct mmc_request * aw_req; +#endif struct mtx aw_mtx; struct resource * aw_res[AW_MMC_RESSZ]; struct aw_mmc_conf * aw_mmc_conf; @@ -148,11 +165,19 @@ static void aw_mmc_intr(void *); static int aw_mmc_update_clock(struct aw_mmc_softc *, uint32_t); +static void aw_mmc_print_error(uint32_t); static int aw_mmc_update_ios(device_t, device_t); static int aw_mmc_request(device_t, device_t, struct mmc_request *); static int aw_mmc_get_ro(device_t, device_t); static int aw_mmc_acquire_host(device_t, device_t); static int aw_mmc_release_host(device_t, device_t); +#ifdef MMCCAM +static void aw_mmc_cam_action(struct cam_sim *, union ccb *); +static void aw_mmc_cam_poll(struct cam_sim *); +static int aw_mmc_cam_settran_settings(struct aw_mmc_softc *, union ccb *); +static int aw_mmc_cam_request(struct aw_mmc_softc *, union ccb *); +static void aw_mmc_cam_handle_mmcio(struct cam_sim *, union ccb *); +#endif #define AW_MMC_LOCK(_sc) mtx_lock(&(_sc)->aw_mtx) #define AW_MMC_UNLOCK(_sc) mtx_unlock(&(_sc)->aw_mtx) @@ -161,6 +186,198 @@ #define AW_MMC_WRITE_4(_sc, _reg, _value) \ bus_write_4((_sc)->aw_res[AW_MMC_MEMRES], _reg, _value) +#ifdef MMCCAM +static void +aw_mmc_cam_handle_mmcio(struct cam_sim *sim, union ccb *ccb) +{ + struct aw_mmc_softc *sc; + + sc = cam_sim_softc(sim); + + aw_mmc_cam_request(sc, ccb); +} + +static void +aw_mmc_cam_action(struct cam_sim *sim, union ccb *ccb) +{ + struct aw_mmc_softc *sc; + + sc = cam_sim_softc(sim); + if (sc == NULL) { + ccb->ccb_h.status = CAM_SEL_TIMEOUT; + xpt_done(ccb); + return; + } + + mtx_assert(&sc->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 = (sc->aw_mmc_conf->dma_xferlen * + AW_MMC_DMA_SEGS) / MMC_SECTOR_SIZE; + 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->protocol = PROTO_MMCSD; + cpi->protocol_version = SCSI_REV_0; + cpi->transport = XPORT_MMCSD; + cpi->transport_version = 1; + + cpi->ccb_h.status = CAM_REQ_CMP; + break; + } + case XPT_GET_TRAN_SETTINGS: + { + struct ccb_trans_settings *cts = &ccb->cts; + + if (bootverbose) + device_printf(sc->aw_dev, "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 = sc->aw_host.host_ocr; + cts->proto_specific.mmc.host_f_min = sc->aw_host.f_min; + cts->proto_specific.mmc.host_f_max = sc->aw_host.f_max; + cts->proto_specific.mmc.host_caps = sc->aw_host.caps; + memcpy(&cts->proto_specific.mmc.ios, &sc->aw_host.ios, sizeof(struct mmc_ios)); + ccb->ccb_h.status = CAM_REQ_CMP; + break; + } + case XPT_SET_TRAN_SETTINGS: + { + if (bootverbose) + device_printf(sc->aw_dev, "Got XPT_SET_TRAN_SETTINGS\n"); + aw_mmc_cam_settran_settings(sc, ccb); + ccb->ccb_h.status = CAM_REQ_CMP; + break; + } + case XPT_RESET_BUS: + if (bootverbose) + device_printf(sc->aw_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. + */ + ccb->ccb_h.status = CAM_REQ_INPROG; + + aw_mmc_cam_handle_mmcio(sim, ccb); + return; + /* NOTREACHED */ + break; + default: + ccb->ccb_h.status = CAM_REQ_INVALID; + break; + } + xpt_done(ccb); + return; +} + +static void +aw_mmc_cam_poll(struct cam_sim *sim) +{ + return; +} + +static int +aw_mmc_cam_settran_settings(struct aw_mmc_softc *sc, union ccb *ccb) +{ + struct mmc_ios *ios; + struct mmc_ios *new_ios; + struct ccb_trans_settings_mmc *cts; + + ios = &sc->aw_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; + device_printf(sc->aw_dev, "Clock => %d\n", ios->clock); + } + if (cts->ios_valid & MMC_VDD) { + ios->vdd = new_ios->vdd; + device_printf(sc->aw_dev, "VDD => %d\n", ios->vdd); + } + if (cts->ios_valid & MMC_CS) { + ios->chip_select = new_ios->chip_select; + device_printf(sc->aw_dev, "CS => %d\n", ios->chip_select); + } + if (cts->ios_valid & MMC_BW) { + ios->bus_width = new_ios->bus_width; + device_printf(sc->aw_dev, "Bus width => %d\n", ios->bus_width); + } + if (cts->ios_valid & MMC_PM) { + ios->power_mode = new_ios->power_mode; + device_printf(sc->aw_dev, "Power mode => %d\n", ios->power_mode); + } + if (cts->ios_valid & MMC_BT) { + ios->timing = new_ios->timing; + device_printf(sc->aw_dev, "Timing => %d\n", ios->timing); + } + if (cts->ios_valid & MMC_BM) { + ios->bus_mode = new_ios->bus_mode; + device_printf(sc->aw_dev, "Bus mode => %d\n", ios->bus_mode); + } + + return (aw_mmc_update_ios(sc->aw_dev, NULL)); +} + +static int +aw_mmc_cam_request(struct aw_mmc_softc *sc, union ccb *ccb) +{ + struct ccb_mmcio *mmcio; + + mmcio = &ccb->mmcio; + + AW_MMC_LOCK(sc); + + if (__predict_false(bootverbose)) { + device_printf(sc->aw_dev, "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); + } + if (sc->ccb != NULL) { + device_printf(sc->aw_dev, "Controller still has an active command\n"); + return (EBUSY); + } + sc->ccb = ccb; + /* aw_mmc_request locks again */ + AW_MMC_UNLOCK(sc); + aw_mmc_request(sc->aw_dev, NULL, NULL); + + return (0); +} +#endif /* MMCCAM */ + static int aw_mmc_probe(device_t dev) { @@ -192,7 +409,9 @@ sc->aw_mmc_conf = (struct aw_mmc_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; +#ifndef MMCCAM sc->aw_req = NULL; +#endif if (bus_alloc_resources(dev, aw_mmc_res_spec, sc->aw_res) != 0) { device_printf(dev, "cannot allocate device resources\n"); return (ENXIO); @@ -295,6 +514,36 @@ if (bus_width >= 8) sc->aw_host.caps |= MMC_CAP_8_BIT_DATA; +#ifdef MMCCAM + child = NULL; /* Not used by MMCCAM, need to silence compiler warnings */ + sc->ccb = NULL; + if ((sc->devq = cam_simq_alloc(1)) == NULL) { + goto fail; + } + + mtx_init(&sc->sim_mtx, "awmmcsim", NULL, MTX_DEF); + sc->sim = cam_sim_alloc(aw_mmc_cam_action, aw_mmc_cam_poll, + "aw_mmc_sim", sc, device_get_unit(dev), + &sc->sim_mtx, 1, 1, sc->devq); + + if (sc->sim == NULL) { + cam_simq_free(sc->devq); + device_printf(dev, "cannot allocate CAM SIM\n"); + goto fail; + } + + mtx_lock(&sc->sim_mtx); + if (xpt_bus_register(sc->sim, sc->aw_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->sim_mtx); + goto fail; + } + + mtx_unlock(&sc->sim_mtx); +#else /* !MMCCAM */ child = device_add_child(dev, "mmc", -1); if (child == NULL) { device_printf(dev, "attaching MMC bus failed!\n"); @@ -305,7 +554,7 @@ device_delete_child(dev, child); goto fail; } - +#endif /* MMCCAM */ return (0); fail: @@ -314,6 +563,17 @@ bus_teardown_intr(dev, sc->aw_res[AW_MMC_IRQRES], sc->aw_intrhand); bus_release_resources(dev, aw_mmc_res_spec, sc->aw_res); +#ifdef MMCCAM + if (sc->sim != NULL) { + mtx_lock(&sc->sim_mtx); + xpt_bus_deregister(cam_sim_path(sc->sim)); + cam_sim_free(sc->sim, FALSE); + mtx_unlock(&sc->sim_mtx); + } + + if (sc->devq != NULL) + cam_simq_free(sc->devq); +#endif return (ENXIO); } @@ -437,7 +697,11 @@ struct mmc_command *cmd; uint32_t val; +#ifdef MMCCAM + cmd = &sc->ccb->mmcio.cmd; +#else cmd = sc->aw_req->cmd; +#endif if (cmd->data->len > (sc->aw_mmc_conf->dma_xferlen * AW_MMC_DMA_SEGS)) return (EFBIG); error = bus_dmamap_load(sc->aw_dma_buf_tag, sc->aw_dma_buf_map, @@ -549,11 +813,23 @@ aw_mmc_req_done(struct aw_mmc_softc *sc) { struct mmc_command *cmd; +#ifdef MMCCAM + union ccb *ccb; +#else struct mmc_request *req; +#endif uint32_t val, mask; int retry; +#ifdef MMCCAM + ccb = sc->ccb; + cmd = &ccb->mmcio.cmd; +#else cmd = sc->aw_req->cmd; +#endif + if (bootverbose) { + device_printf(sc->aw_dev, "%s: cmd %d err %d\n", __func__, cmd->opcode, cmd->error); + } if (cmd->error != MMC_ERR_NONE) { /* Reset the FIFO and DMA engines. */ mask = AW_MMC_GCTL_FIFO_RST | AW_MMC_GCTL_DMA_RST; @@ -573,14 +849,21 @@ aw_mmc_update_clock(sc, 1); } - req = sc->aw_req; callout_stop(&sc->aw_timeoutc); - sc->aw_req = NULL; sc->aw_intr = 0; sc->aw_resid = 0; sc->aw_dma_map_err = 0; sc->aw_intr_wait = 0; +#ifdef MMCCAM + sc->ccb = NULL; + ccb->ccb_h.status = + (ccb->mmcio.cmd.error == 0 ? CAM_REQ_CMP : CAM_REQ_CMP_ERR); + xpt_done(ccb); +#else + req = sc->aw_req; + sc->aw_req = NULL; req->done(req); +#endif } static void @@ -597,7 +880,11 @@ break; DELAY(1000); } +#ifdef MMCCAM + cmd = &sc->ccb->mmcio.cmd; +#else cmd = sc->aw_req->cmd; +#endif if (timeout == 0) { cmd->error = MMC_ERR_FAILED; aw_mmc_req_done(sc); @@ -618,21 +905,58 @@ aw_mmc_req_done(sc); } + +static inline void +set_mmc_error(struct aw_mmc_softc *sc, int error_code) +{ +#ifdef MMCCAM + sc->ccb->mmcio.cmd.error = error_code; +#else + sc->aw_req->cmd->error = error_code; +#endif +} + static void aw_mmc_timeout(void *arg) { struct aw_mmc_softc *sc; sc = (struct aw_mmc_softc *)arg; +#ifdef MMCCAM + if (sc->ccb != NULL) { +#else if (sc->aw_req != NULL) { +#endif device_printf(sc->aw_dev, "controller timeout\n"); - sc->aw_req->cmd->error = MMC_ERR_TIMEOUT; + set_mmc_error(sc, MMC_ERR_TIMEOUT); aw_mmc_req_done(sc); } else device_printf(sc->aw_dev, "Spurious timeout - no active request\n"); } +static void +aw_mmc_print_error(uint32_t err) +{ + if(err & AW_MMC_INT_RESP_ERR) + printf("AW_MMC_INT_RESP_ERR "); + if (err & AW_MMC_INT_RESP_CRC_ERR) + printf("AW_MMC_INT_RESP_CRC_ERR "); + if (err & AW_MMC_INT_DATA_CRC_ERR) + printf("AW_MMC_INT_DATA_CRC_ERR "); + if (err & AW_MMC_INT_RESP_TIMEOUT) + printf("AW_MMC_INT_RESP_TIMEOUT "); + if (err & AW_MMC_INT_FIFO_RUN_ERR) + printf("AW_MMC_INT_FIFO_RUN_ERR "); + if (err & AW_MMC_INT_CMD_BUSY) + printf("AW_MMC_INT_CMD_BUSY "); + if (err & AW_MMC_INT_DATA_START_ERR) + printf("AW_MMC_INT_DATA_START_ERR "); + if (err & AW_MMC_INT_DATA_END_BIT_ERR) + printf("AW_MMC_INT_DATA_END_BIT_ERR"); + printf("\n"); +} + static void aw_mmc_intr(void *arg) { @@ -654,31 +978,41 @@ device_printf(sc->aw_dev, "idst: %#x, imask: %#x, rint: %#x\n", idst, imask, rint); #endif +#ifdef MMCCAM + if (sc->ccb == NULL) { +#else if (sc->aw_req == NULL) { +#endif device_printf(sc->aw_dev, "Spurious interrupt - no active request, rint: 0x%08X\n", rint); + aw_mmc_print_error(rint); goto end; } if (rint & AW_MMC_INT_ERR_BIT) { if (bootverbose) device_printf(sc->aw_dev, "error rint: 0x%08X\n", rint); + aw_mmc_print_error(rint); if (rint & AW_MMC_INT_RESP_TIMEOUT) - sc->aw_req->cmd->error = MMC_ERR_TIMEOUT; + set_mmc_error(sc, MMC_ERR_TIMEOUT); else - sc->aw_req->cmd->error = MMC_ERR_FAILED; + set_mmc_error(sc, MMC_ERR_FAILED); aw_mmc_req_done(sc); goto end; } if (idst & AW_MMC_IDST_ERROR) { device_printf(sc->aw_dev, "error idst: 0x%08x\n", idst); - sc->aw_req->cmd->error = MMC_ERR_FAILED; + set_mmc_error(sc, MMC_ERR_FAILED); aw_mmc_req_done(sc); goto end; } sc->aw_intr |= rint; +#ifdef MMCCAM + data = sc->ccb->mmcio.cmd.data; +#else data = sc->aw_req->cmd->data; +#endif if (data != NULL && (idst & AW_MMC_IDST_COMPLETE) != 0) { if (data->flags & MMC_DATA_WRITE) sync_op = BUS_DMASYNC_POSTWRITE; @@ -712,13 +1046,27 @@ sc = device_get_softc(bus); AW_MMC_LOCK(sc); +#ifdef MMCCAM + KASSERT(req == NULL, ("req should be NULL in MMCCAM case!")); + /* + * For MMCCAM, sc->ccb has been NULL-checked and populated + * by aw_mmc_cam_request() already. + */ + cmd = &sc->ccb->mmcio.cmd; +#else if (sc->aw_req) { AW_MMC_UNLOCK(sc); return (EBUSY); } - sc->aw_req = req; cmd = req->cmd; + + if (bootverbose) + device_printf(sc->aw_dev, "CMD%u arg %#x flags %#x dlen %u dflags %#x\n", + cmd->opcode, cmd->arg, cmd->flags, + cmd->data != NULL ? (unsigned int)cmd->data->len : 0, + cmd->data != NULL ? cmd->data->flags: 0); +#endif cmdreg = AW_MMC_CMDR_LOAD; imask = AW_MMC_INT_ERR_BIT; sc->aw_intr_wait = 0; @@ -1163,4 +1511,6 @@ DRIVER_MODULE(aw_mmc, simplebus, aw_mmc_driver, aw_mmc_devclass, NULL, NULL); +#ifndef MMCCAM MMC_DECLARE_BRIDGE(aw_mmc); +#endif Index: sys/arm/allwinner/files.allwinner =================================================================== --- sys/arm/allwinner/files.allwinner +++ sys/arm/allwinner/files.allwinner @@ -10,7 +10,7 @@ arm/allwinner/aw_gpio.c optional gpio arm/allwinner/aw_if_dwc.c optional dwc arm/allwinner/aw_machdep.c standard -arm/allwinner/aw_mmc.c optional mmc +arm/allwinner/aw_mmc.c optional mmc | mmccam arm/allwinner/aw_mp.c optional smp arm/allwinner/aw_nmi.c optional intrng arm/allwinner/aw_rsb.c optional rsb | p2wi