svn commit: r335476 - in head/sys: arm/allwinner conf
Ilya Bakulin
kibab at FreeBSD.org
Thu Jun 21 11:49:23 UTC 2018
Author: kibab
Date: Thu Jun 21 11:49:21 2018
New Revision: 335476
URL: https://svnweb.freebsd.org/changeset/base/335476
Log:
Add MMCCAM support to AllWinner MMC driver
Using MMCCAM on AllWinner boards is now possible, reaching highest
possible data transfer speed.
For now, MMCCAM doesn't scan cards on boot. This means that scanning
has to be done manually and that it's not possible to mount root FS
from MMC/SD card since there is no block device at the boot time.
For manually scanning the cards, run:
# camcontrol rescan X:0:0
Where X is the bus number (look at camcontrol devlist to determine
bus number assigned to the MMC controller).
Reviewed by: manu
Approved by: imp (mentor)
Differential Revision: https://reviews.freebsd.org/D15891
Modified:
head/sys/arm/allwinner/aw_mmc.c
head/sys/arm/allwinner/files.allwinner
head/sys/conf/files.arm64
Modified: head/sys/arm/allwinner/aw_mmc.c
==============================================================================
--- head/sys/arm/allwinner/aw_mmc.c Thu Jun 21 11:43:54 2018 (r335475)
+++ head/sys/arm/allwinner/aw_mmc.c Thu Jun 21 11:49:21 2018 (r335476)
@@ -55,6 +55,16 @@ __FBSDID("$FreeBSD$");
#include <dev/extres/hwreset/hwreset.h>
#include <dev/extres/regulator/regulator.h>
+#include "opt_mmccam.h"
+
+#ifdef MMCCAM
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+#endif
+
#define AW_MMC_MEMRES 0
#define AW_MMC_IRQRES 1
#define AW_MMC_RESSZ 2
@@ -112,7 +122,14 @@ struct aw_mmc_softc {
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 int aw_mmc_init(struct aw_mmc_softc *);
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,7 +186,201 @@ static int aw_mmc_release_host(device_t, device_t);
#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);
+
+#ifdef DEBUG
+ 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);
+ }
+#endif
+ 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 +411,9 @@ aw_mmc_attach(device_t dev)
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 +516,35 @@ aw_mmc_attach(device_t dev)
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 +555,7 @@ aw_mmc_attach(device_t dev)
device_delete_child(dev, child);
goto fail;
}
-
+#endif /* MMCCAM */
return (0);
fail:
@@ -314,6 +564,17 @@ fail:
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 +698,11 @@ aw_mmc_prepare_dma(struct aw_mmc_softc *sc)
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 +814,25 @@ static void
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
+#ifdef DEBUG
+ if (bootverbose) {
+ device_printf(sc->aw_dev, "%s: cmd %d err %d\n", __func__, cmd->opcode, cmd->error);
+ }
+#endif
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 +852,21 @@ aw_mmc_req_done(struct aw_mmc_softc *sc)
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 +883,11 @@ aw_mmc_req_ok(struct aw_mmc_softc *sc)
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,15 +908,30 @@ aw_mmc_req_ok(struct aw_mmc_softc *sc)
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,
@@ -634,6 +939,28 @@ aw_mmc_timeout(void *arg)
}
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)
{
bus_dmasync_op_t sync_op;
@@ -654,31 +981,41 @@ aw_mmc_intr(void *arg)
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 +1049,29 @@ aw_mmc_request(device_t bus, device_t child, struct mm
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;
+
+#ifdef DEBUG
+ 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
+#endif
cmdreg = AW_MMC_CMDR_LOAD;
imask = AW_MMC_INT_ERR_BIT;
sc->aw_intr_wait = 0;
@@ -1163,4 +1516,6 @@ static driver_t aw_mmc_driver = {
DRIVER_MODULE(aw_mmc, simplebus, aw_mmc_driver, aw_mmc_devclass, NULL,
NULL);
+#ifndef MMCCAM
MMC_DECLARE_BRIDGE(aw_mmc);
+#endif
Modified: head/sys/arm/allwinner/files.allwinner
==============================================================================
--- head/sys/arm/allwinner/files.allwinner Thu Jun 21 11:43:54 2018 (r335475)
+++ head/sys/arm/allwinner/files.allwinner Thu Jun 21 11:49:21 2018 (r335476)
@@ -10,7 +10,7 @@ arm/allwinner/a10_sramc.c standard
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
Modified: head/sys/conf/files.arm64
==============================================================================
--- head/sys/conf/files.arm64 Thu Jun 21 11:43:54 2018 (r335475)
+++ head/sys/conf/files.arm64 Thu Jun 21 11:49:21 2018 (r335476)
@@ -27,7 +27,7 @@ cloudabi64_vdso_blob.o optional compat_cloudabi64 \
# Allwinner common files
arm/allwinner/a10_ehci.c optional ehci aw_ehci fdt
arm/allwinner/aw_gpio.c optional gpio aw_gpio fdt
-arm/allwinner/aw_mmc.c optional mmc aw_mmc fdt
+arm/allwinner/aw_mmc.c optional mmc aw_mmc fdt | mmccam aw_mmc fdt
arm/allwinner/aw_nmi.c optional aw_nmi fdt \
compile-with "${NORMAL_C} -I$S/gnu/dts/include"
arm/allwinner/aw_rsb.c optional aw_rsb fdt
More information about the svn-src-head
mailing list