summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/dev/sdhci/sdhci.c
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2018-08-07 14:56:50 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2018-09-21 10:29:37 +0200
commitc37f9fba70085fedc8eede7559489d2321393005 (patch)
tree042455ebf1fa89a277a825f72e1ed805d0b4d296 /freebsd/sys/dev/sdhci/sdhci.c
parentUpdate to FreeBSD head 2017-06-01 (diff)
downloadrtems-libbsd-c37f9fba70085fedc8eede7559489d2321393005.tar.bz2
Update to FreeBSD head 2017-08-01
Git mirror commit f5002f5e5f78cae9f0269d812dc0aedb0339312c. Update #3472.
Diffstat (limited to 'freebsd/sys/dev/sdhci/sdhci.c')
-rw-r--r--freebsd/sys/dev/sdhci/sdhci.c1021
1 files changed, 976 insertions, 45 deletions
diff --git a/freebsd/sys/dev/sdhci/sdhci.c b/freebsd/sys/dev/sdhci/sdhci.c
index c87199a8..f1616a6e 100644
--- a/freebsd/sys/dev/sdhci/sdhci.c
+++ b/freebsd/sys/dev/sdhci/sdhci.c
@@ -2,6 +2,7 @@
/*-
* Copyright (c) 2008 Alexander Motin <mav@FreeBSD.org>
+ * Copyright (c) 2017 Marius Strobl <marius@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -34,7 +35,9 @@ __FBSDID("$FreeBSD$");
#include <sys/callout.h>
#include <sys/conf.h>
#include <sys/kernel.h>
+#include <sys/kobj.h>
#include <sys/lock.h>
+#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <rtems/bsd/sys/resource.h>
@@ -50,13 +53,22 @@ __FBSDID("$FreeBSD$");
#include <dev/mmc/mmcreg.h>
#include <dev/mmc/mmcbrvar.h>
+#include <dev/sdhci/sdhci.h>
+
+#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>
+
#include <rtems/bsd/local/mmcbr_if.h>
-#include "sdhci.h"
#include <rtems/bsd/local/sdhci_if.h>
+#include <rtems/bsd/local/opt_mmccam.h>
+
SYSCTL_NODE(_hw, OID_AUTO, sdhci, CTLFLAG_RD, 0, "sdhci driver");
-static int sdhci_debug;
+static int sdhci_debug = 0;
SYSCTL_INT(_hw_sdhci, OID_AUTO, debug, CTLFLAG_RWTUN, &sdhci_debug, 0,
"Debug level");
u_int sdhci_quirk_clear = 0;
@@ -78,17 +90,30 @@ SYSCTL_INT(_hw_sdhci, OID_AUTO, quirk_set, CTLFLAG_RWTUN, &sdhci_quirk_set, 0,
#define WR_MULTI_4(slot, off, ptr, count) \
SDHCI_WRITE_MULTI_4((slot)->bus, (slot), (off), (ptr), (count))
+static void sdhci_card_poll(void *arg);
+static void sdhci_card_task(void *arg, int pending);
+static int sdhci_exec_tuning(struct sdhci_slot *slot, bool reset);
+static void sdhci_req_wakeup(struct mmc_request *req);
+static void sdhci_retune(void *arg);
static void sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock);
static void sdhci_start(struct sdhci_slot *slot);
static void sdhci_start_data(struct sdhci_slot *slot, struct mmc_data *data);
-static void sdhci_card_poll(void *);
-static void sdhci_card_task(void *, int);
+#ifdef MMCCAM
+/* CAM-related */
+int sdhci_cam_get_possible_host_clock(struct sdhci_slot *slot, int proposed_clock);
+static int sdhci_cam_update_ios(struct sdhci_slot *slot);
+static int sdhci_cam_request(struct sdhci_slot *slot, union ccb *ccb);
+static void sdhci_cam_action(struct cam_sim *sim, union ccb *ccb);
+static void sdhci_cam_poll(struct cam_sim *sim);
+static int sdhci_cam_settran_settings(struct sdhci_slot *slot, union ccb *ccb);
+#endif
/* helper routines */
static void sdhci_dumpregs(struct sdhci_slot *slot);
static int slot_printf(struct sdhci_slot *slot, const char * fmt, ...)
__printflike(2, 3);
+static uint32_t sdhci_tuning_intmask(struct sdhci_slot *slot);
#define SDHCI_LOCK(_slot) mtx_lock(&(_slot)->mtx)
#define SDHCI_UNLOCK(_slot) mtx_unlock(&(_slot)->mtx)
@@ -169,13 +194,13 @@ sdhci_dumpregs(struct sdhci_slot *slot)
RD1(slot, SDHCI_TIMEOUT_CONTROL), RD4(slot, SDHCI_INT_STATUS));
slot_printf(slot, "Int enab: 0x%08x | Sig enab: 0x%08x\n",
RD4(slot, SDHCI_INT_ENABLE), RD4(slot, SDHCI_SIGNAL_ENABLE));
- slot_printf(slot, "AC12 err: 0x%08x | Host ctl2: 0x%08x\n",
+ slot_printf(slot, "AC12 err: 0x%08x | Host ctl2:0x%08x\n",
RD2(slot, SDHCI_ACMD12_ERR), RD2(slot, SDHCI_HOST_CONTROL2));
slot_printf(slot, "Caps: 0x%08x | Caps2: 0x%08x\n",
RD4(slot, SDHCI_CAPABILITIES), RD4(slot, SDHCI_CAPABILITIES2));
slot_printf(slot, "Max curr: 0x%08x | ADMA err: 0x%08x\n",
RD4(slot, SDHCI_MAX_CURRENT), RD1(slot, SDHCI_ADMA_ERR));
- slot_printf(slot, "ADMA addr: 0x%08x | Slot int: 0x%08x\n",
+ slot_printf(slot, "ADMA addr:0x%08x | Slot int: 0x%08x\n",
RD4(slot, SDHCI_ADMA_ADDRESS_LO), RD2(slot, SDHCI_SLOT_INT_STATUS));
slot_printf(slot,
@@ -242,6 +267,21 @@ sdhci_reset(struct sdhci_slot *slot, uint8_t mask)
}
}
+static uint32_t
+sdhci_tuning_intmask(struct sdhci_slot *slot)
+{
+ uint32_t intmask;
+
+ intmask = 0;
+ if (slot->opt & SDHCI_TUNING_SUPPORTED) {
+ intmask |= SDHCI_INT_TUNEERR;
+ if (slot->retune_mode == SDHCI_RETUNE_MODE_2 ||
+ slot->retune_mode == SDHCI_RETUNE_MODE_3)
+ intmask |= SDHCI_INT_RETUNE;
+ }
+ return (intmask);
+}
+
static void
sdhci_init(struct sdhci_slot *slot)
{
@@ -261,7 +301,7 @@ sdhci_init(struct sdhci_slot *slot)
slot->intmask |= SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT;
}
- WR4(slot, SDHCI_INT_ENABLE, slot->intmask);
+ WR4(slot, SDHCI_INT_ENABLE, slot->intmask | sdhci_tuning_intmask(slot));
WR4(slot, SDHCI_SIGNAL_ENABLE, slot->intmask);
}
@@ -368,6 +408,7 @@ sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock)
static void
sdhci_set_power(struct sdhci_slot *slot, u_char power)
{
+ int i;
uint8_t pwr;
if (slot->power == power)
@@ -396,9 +437,20 @@ sdhci_set_power(struct sdhci_slot *slot, u_char power)
break;
}
WR1(slot, SDHCI_POWER_CONTROL, pwr);
- /* Turn on the power. */
+ /*
+ * Turn on VDD1 power. Note that at least some Intel controllers can
+ * fail to enable bus power on the first try after transiting from D3
+ * to D0, so we give them up to 2 ms.
+ */
pwr |= SDHCI_POWER_ON;
- WR1(slot, SDHCI_POWER_CONTROL, pwr);
+ for (i = 0; i < 20; i++) {
+ WR1(slot, SDHCI_POWER_CONTROL, pwr);
+ if (RD1(slot, SDHCI_POWER_CONTROL) & SDHCI_POWER_ON)
+ break;
+ DELAY(100);
+ }
+ if (!(RD1(slot, SDHCI_POWER_CONTROL) & SDHCI_POWER_ON))
+ slot_printf(slot, "Bus power failed to enable");
if (slot->quirks & SDHCI_QUIRK_INTEL_POWER_UP_RESET) {
WR1(slot, SDHCI_POWER_CONTROL, pwr | 0x10);
@@ -521,25 +573,93 @@ sdhci_card_task(void *arg, int pending __unused)
SDHCI_LOCK(slot);
if (SDHCI_GET_CARD_PRESENT(slot->bus, slot)) {
+#ifdef MMCCAM
+ if (slot->card_present == 0) {
+#else
if (slot->dev == NULL) {
+#endif
/* If card is present - attach mmc bus. */
if (bootverbose || sdhci_debug)
slot_printf(slot, "Card inserted\n");
- slot->dev = device_add_child(slot->bus, "mmc", -1);
- device_set_ivars(slot->dev, slot);
+#ifdef MMCCAM
+ slot->card_present = 1;
+ union ccb *ccb;
+ uint32_t pathid;
+ pathid = cam_sim_path(slot->sim);
+ ccb = xpt_alloc_ccb_nowait();
+ if (ccb == NULL) {
+ slot_printf(slot, "Unable to alloc CCB for rescan\n");
+ SDHCI_UNLOCK(slot);
+ return;
+ }
+
+ /*
+ * We create a rescan request for BUS:0:0, since the card
+ * will be at lun 0.
+ */
+ if (xpt_create_path(&ccb->ccb_h.path, NULL, pathid,
+ /* target */ 0, /* lun */ 0) != CAM_REQ_CMP) {
+ slot_printf(slot, "Unable to create path for rescan\n");
+ SDHCI_UNLOCK(slot);
+ xpt_free_ccb(ccb);
+ return;
+ }
+ SDHCI_UNLOCK(slot);
+ xpt_rescan(ccb);
+#else
+ d = slot->dev = device_add_child(slot->bus, "mmc", -1);
SDHCI_UNLOCK(slot);
- device_probe_and_attach(slot->dev);
+ if (d) {
+ device_set_ivars(d, slot);
+ (void)device_probe_and_attach(d);
+ }
+#endif
} else
SDHCI_UNLOCK(slot);
} else {
+#ifdef MMCCAM
+ if (slot->card_present == 1) {
+#else
if (slot->dev != NULL) {
+#endif
/* If no card present - detach mmc bus. */
if (bootverbose || sdhci_debug)
slot_printf(slot, "Card removed\n");
d = slot->dev;
slot->dev = NULL;
+#ifdef MMCCAM
+ slot->card_present = 0;
+ union ccb *ccb;
+ uint32_t pathid;
+ pathid = cam_sim_path(slot->sim);
+ ccb = xpt_alloc_ccb_nowait();
+ if (ccb == NULL) {
+ slot_printf(slot, "Unable to alloc CCB for rescan\n");
+ SDHCI_UNLOCK(slot);
+ return;
+ }
+
+ /*
+ * We create a rescan request for BUS:0:0, since the card
+ * will be at lun 0.
+ */
+ if (xpt_create_path(&ccb->ccb_h.path, NULL, pathid,
+ /* target */ 0, /* lun */ 0) != CAM_REQ_CMP) {
+ slot_printf(slot, "Unable to create path for rescan\n");
+ SDHCI_UNLOCK(slot);
+ xpt_free_ccb(ccb);
+ return;
+ }
SDHCI_UNLOCK(slot);
+ xpt_rescan(ccb);
+#else
+ slot->intmask &= ~sdhci_tuning_intmask(slot);
+ WR4(slot, SDHCI_SIGNAL_ENABLE, slot->intmask);
+ slot->opt &= ~SDHCI_TUNING_ENABLED;
+ SDHCI_UNLOCK(slot);
+ callout_drain(&slot->retune_callout);
device_delete_child(slot->bus, d);
+#endif
} else
SDHCI_UNLOCK(slot);
}
@@ -561,7 +681,11 @@ sdhci_handle_card_present_locked(struct sdhci_slot *slot, bool is_present)
* because once power is removed, a full card re-init is needed, and
* that happens by deleting and recreating the child device.
*/
+#ifdef MMCCAM
+ was_present = slot->card_present;
+#else
was_present = slot->dev != NULL;
+#endif
if (!was_present && is_present) {
taskqueue_enqueue_timeout(taskqueue_swi_giant,
&slot->card_delayed_task, -SDHCI_INSERT_DELAY_TICKS);
@@ -593,10 +717,13 @@ sdhci_card_poll(void *arg)
int
sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num)
{
+ kobjop_desc_t kobj_desc;
+ kobj_method_t *kobj_method;
uint32_t caps, caps2, freq, host_caps;
int err;
SDHCI_LOCK_INIT(slot);
+
slot->num = num;
slot->bus = dev;
@@ -617,6 +744,7 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num)
BUS_DMA_NOWAIT, &slot->dmamap);
if (err != 0) {
device_printf(dev, "Can't alloc DMA memory\n");
+ bus_dma_tag_destroy(slot->dmatag);
SDHCI_LOCK_DESTROY(slot);
return (err);
}
@@ -626,6 +754,8 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num)
sdhci_getaddr, &slot->paddr, 0);
if (err != 0 || slot->paddr == 0) {
device_printf(dev, "Can't load DMA memory\n");
+ bus_dmamem_free(slot->dmatag, slot->dmamem, slot->dmamap);
+ bus_dma_tag_destroy(slot->dmatag);
SDHCI_LOCK_DESTROY(slot);
if (err)
return (err);
@@ -633,8 +763,6 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num)
return (EFAULT);
}
- /* Initialize slot. */
- sdhci_init(slot);
slot->version = (RD2(slot, SDHCI_HOST_VERSION)
>> SDHCI_SPEC_VER_SHIFT) & SDHCI_SPEC_VER_MASK;
if (slot->quirks & SDHCI_QUIRK_MISSING_CAPS) {
@@ -647,6 +775,22 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num)
else
caps2 = 0;
}
+ if (slot->version >= SDHCI_SPEC_300) {
+ if ((caps & SDHCI_SLOTTYPE_MASK) != SDHCI_SLOTTYPE_REMOVABLE &&
+ (caps & SDHCI_SLOTTYPE_MASK) != SDHCI_SLOTTYPE_EMBEDDED) {
+ device_printf(dev,
+ "Driver doesn't support shared bus slots\n");
+ bus_dmamap_unload(slot->dmatag, slot->dmamap);
+ bus_dmamem_free(slot->dmatag, slot->dmamem,
+ slot->dmamap);
+ bus_dma_tag_destroy(slot->dmatag);
+ SDHCI_LOCK_DESTROY(slot);
+ return (ENXIO);
+ } else if ((caps & SDHCI_SLOTTYPE_MASK) ==
+ SDHCI_SLOTTYPE_EMBEDDED) {
+ slot->opt |= SDHCI_SLOT_EMBEDDED | SDHCI_NON_REMOVABLE;
+ }
+ }
/* Calculate base clock frequency. */
if (slot->version >= SDHCI_SPEC_300)
freq = (caps & SDHCI_CLOCK_V3_BASE_MASK) >>
@@ -696,12 +840,14 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num)
slot->host.host_ocr |= MMC_OCR_320_330 | MMC_OCR_330_340;
if (caps & SDHCI_CAN_VDD_300)
slot->host.host_ocr |= MMC_OCR_290_300 | MMC_OCR_300_310;
- if (caps & SDHCI_CAN_VDD_180)
+ /* 1.8V VDD is not supposed to be used for removable cards. */
+ if ((caps & SDHCI_CAN_VDD_180) && (slot->opt & SDHCI_SLOT_EMBEDDED))
slot->host.host_ocr |= MMC_OCR_LOW_VOLTAGE;
if (slot->host.host_ocr == 0) {
device_printf(dev, "Hardware doesn't report any "
"support voltages.\n");
}
+
host_caps = MMC_CAP_4_BIT_DATA;
if (caps & SDHCI_CAN_DO_8BITBUS)
host_caps |= MMC_CAP_8_BIT_DATA;
@@ -711,6 +857,8 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num)
host_caps |= MMC_CAP_BOOT_NOACC;
if (slot->quirks & SDHCI_QUIRK_WAIT_WHILE_BUSY)
host_caps |= MMC_CAP_WAIT_WHILE_BUSY;
+
+ /* Determine supported UHS-I and eMMC modes. */
if (caps2 & (SDHCI_CAN_SDR50 | SDHCI_CAN_SDR104 | SDHCI_CAN_DDR50))
host_caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
if (caps2 & SDHCI_CAN_SDR104) {
@@ -727,12 +875,91 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num)
if (slot->quirks & SDHCI_QUIRK_CAPS_BIT63_FOR_MMC_HS400 &&
caps2 & SDHCI_CAN_MMC_HS400)
host_caps |= MMC_CAP_MMC_HS400;
+
+ /*
+ * Disable UHS-I and eMMC modes if the set_uhs_timing method is the
+ * default NULL implementation.
+ */
+ kobj_desc = &sdhci_set_uhs_timing_desc;
+ kobj_method = kobj_lookup_method(((kobj_t)dev)->ops->cls, NULL,
+ kobj_desc);
+ if (kobj_method == &kobj_desc->deflt)
+ host_caps &= ~(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
+ MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_DDR50 | MMC_CAP_UHS_SDR104 |
+ MMC_CAP_MMC_DDR52 | MMC_CAP_MMC_HS200 | MMC_CAP_MMC_HS400);
+
+#define SDHCI_CAP_MODES_TUNING(caps2) \
+ (((caps2) & SDHCI_TUNE_SDR50 ? MMC_CAP_UHS_SDR50 : 0) | \
+ MMC_CAP_UHS_DDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_MMC_HS200 | \
+ MMC_CAP_MMC_HS400)
+
+ /*
+ * Disable UHS-I and eMMC modes that require (re-)tuning if either
+ * the tune or re-tune method is the default NULL implementation.
+ */
+ kobj_desc = &mmcbr_tune_desc;
+ kobj_method = kobj_lookup_method(((kobj_t)dev)->ops->cls, NULL,
+ kobj_desc);
+ if (kobj_method == &kobj_desc->deflt)
+ goto no_tuning;
+ kobj_desc = &mmcbr_retune_desc;
+ kobj_method = kobj_lookup_method(((kobj_t)dev)->ops->cls, NULL,
+ kobj_desc);
+ if (kobj_method == &kobj_desc->deflt) {
+no_tuning:
+ host_caps &= ~(SDHCI_CAP_MODES_TUNING(caps2));
+ }
+
+ /* Allocate tuning structures and determine tuning parameters. */
+ if (host_caps & SDHCI_CAP_MODES_TUNING(caps2)) {
+ slot->opt |= SDHCI_TUNING_SUPPORTED;
+ slot->tune_req = malloc(sizeof(*slot->tune_req), M_DEVBUF,
+ M_WAITOK);
+ slot->tune_cmd = malloc(sizeof(*slot->tune_cmd), M_DEVBUF,
+ M_WAITOK);
+ slot->tune_data = malloc(sizeof(*slot->tune_data), M_DEVBUF,
+ M_WAITOK);
+ if (caps2 & SDHCI_TUNE_SDR50)
+ slot->opt |= SDHCI_SDR50_NEEDS_TUNING;
+ slot->retune_mode = (caps2 & SDHCI_RETUNE_MODES_MASK) >>
+ SDHCI_RETUNE_MODES_SHIFT;
+ if (slot->retune_mode == SDHCI_RETUNE_MODE_1) {
+ slot->retune_count = (caps2 & SDHCI_RETUNE_CNT_MASK) >>
+ SDHCI_RETUNE_CNT_SHIFT;
+ if (slot->retune_count > 0xb) {
+ device_printf(dev, "Unknown re-tuning count "
+ "%x, using 1 sec\n", slot->retune_count);
+ slot->retune_count = 1;
+ } else if (slot->retune_count != 0)
+ slot->retune_count =
+ 1 << (slot->retune_count - 1);
+ }
+ }
+
+#undef SDHCI_CAP_MODES_TUNING
+
+ /* Determine supported VCCQ signaling levels. */
host_caps |= MMC_CAP_SIGNALING_330;
if (host_caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
- MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50 |
+ MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_DDR50 | MMC_CAP_UHS_SDR104 |
MMC_CAP_MMC_DDR52_180 | MMC_CAP_MMC_HS200_180 |
MMC_CAP_MMC_HS400_180))
- host_caps |= MMC_CAP_SIGNALING_180;
+ host_caps |= MMC_CAP_SIGNALING_120 | MMC_CAP_SIGNALING_180;
+
+ /*
+ * Disable 1.2 V and 1.8 V signaling if the switch_vccq method is the
+ * default NULL implementation. Disable 1.2 V support if it's the
+ * generic SDHCI implementation.
+ */
+ kobj_desc = &mmcbr_switch_vccq_desc;
+ kobj_method = kobj_lookup_method(((kobj_t)dev)->ops->cls, NULL,
+ kobj_desc);
+ if (kobj_method == &kobj_desc->deflt)
+ host_caps &= ~(MMC_CAP_SIGNALING_120 | MMC_CAP_SIGNALING_180);
+ else if (kobj_method->func == (kobjop_t)sdhci_generic_switch_vccq)
+ host_caps &= ~MMC_CAP_SIGNALING_120;
+
+ /* Determine supported driver types (type B is always mandatory). */
if (caps2 & SDHCI_CAN_DRIVE_TYPE_A)
host_caps |= MMC_CAP_DRIVER_TYPE_A;
if (caps2 & SDHCI_CAN_DRIVE_TYPE_C)
@@ -761,20 +988,24 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num)
if (bootverbose || sdhci_debug) {
slot_printf(slot,
- "%uMHz%s %s VDD:%s%s%s VCCQ: 3.3V%s%s DRV: B%s%s%s %s\n",
+ "%uMHz%s %s VDD:%s%s%s VCCQ: 3.3V%s%s DRV: B%s%s%s %s %s\n",
slot->max_clk / 1000000,
(caps & SDHCI_CAN_DO_HISPD) ? " HS" : "",
(host_caps & MMC_CAP_8_BIT_DATA) ? "8bits" :
((host_caps & MMC_CAP_4_BIT_DATA) ? "4bits" : "1bit"),
(caps & SDHCI_CAN_VDD_330) ? " 3.3V" : "",
(caps & SDHCI_CAN_VDD_300) ? " 3.0V" : "",
- (caps & SDHCI_CAN_VDD_180) ? " 1.8V" : "",
+ ((caps & SDHCI_CAN_VDD_180) &&
+ (slot->opt & SDHCI_SLOT_EMBEDDED)) ? " 1.8V" : "",
(host_caps & MMC_CAP_SIGNALING_180) ? " 1.8V" : "",
(host_caps & MMC_CAP_SIGNALING_120) ? " 1.2V" : "",
- (caps2 & SDHCI_CAN_DRIVE_TYPE_A) ? "A" : "",
- (caps2 & SDHCI_CAN_DRIVE_TYPE_C) ? "C" : "",
- (caps2 & SDHCI_CAN_DRIVE_TYPE_D) ? "D" : "",
- (slot->opt & SDHCI_HAVE_DMA) ? "DMA" : "PIO");
+ (host_caps & MMC_CAP_DRIVER_TYPE_A) ? "A" : "",
+ (host_caps & MMC_CAP_DRIVER_TYPE_C) ? "C" : "",
+ (host_caps & MMC_CAP_DRIVER_TYPE_D) ? "D" : "",
+ (slot->opt & SDHCI_HAVE_DMA) ? "DMA" : "PIO",
+ (slot->opt & SDHCI_SLOT_EMBEDDED) ? "embedded" :
+ (slot->opt & SDHCI_NON_REMOVABLE) ? "non-removable" :
+ "removable");
if (host_caps & (MMC_CAP_MMC_DDR52 | MMC_CAP_MMC_HS200 |
MMC_CAP_MMC_HS400 | MMC_CAP_MMC_ENH_STROBE))
slot_printf(slot, "eMMC:%s%s%s%s\n",
@@ -793,6 +1024,9 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num)
(host_caps & MMC_CAP_UHS_SDR50) ? " SDR50" : "",
(host_caps & MMC_CAP_UHS_SDR104) ? " SDR104" : "",
(host_caps & MMC_CAP_UHS_DDR50) ? " DDR50" : "");
+ if (slot->opt & SDHCI_TUNING_SUPPORTED)
+ slot_printf(slot, "Re-tuning count %d secs, mode %d\n",
+ slot->retune_count, slot->retune_mode + 1);
sdhci_dumpregs(slot);
}
@@ -806,6 +1040,7 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num)
sdhci_card_task, slot);
callout_init(&slot->card_poll_callout, 1);
callout_init_mtx(&slot->timeout_callout, &slot->mtx, 0);
+ callout_init_mtx(&slot->retune_callout, &slot->mtx, 0);
if ((slot->quirks & SDHCI_QUIRK_POLL_CARD_PRESENT) &&
!(slot->opt & SDHCI_NON_REMOVABLE)) {
@@ -813,6 +1048,8 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num)
SDHCI_CARD_PRESENT_TICKS, sdhci_card_poll, slot);
}
+ sdhci_init(slot);
+
return (0);
}
@@ -830,6 +1067,7 @@ sdhci_cleanup_slot(struct sdhci_slot *slot)
callout_drain(&slot->timeout_callout);
callout_drain(&slot->card_poll_callout);
+ callout_drain(&slot->retune_callout);
taskqueue_drain(taskqueue_swi_giant, &slot->card_task);
taskqueue_drain_timeout(taskqueue_swi_giant, &slot->card_delayed_task);
@@ -846,6 +1084,11 @@ sdhci_cleanup_slot(struct sdhci_slot *slot)
bus_dmamap_unload(slot->dmatag, slot->dmamap);
bus_dmamem_free(slot->dmatag, slot->dmamem, slot->dmamap);
bus_dma_tag_destroy(slot->dmatag);
+ if (slot->opt & SDHCI_TUNING_SUPPORTED) {
+ free(slot->tune_req, M_DEVBUF);
+ free(slot->tune_cmd, M_DEVBUF);
+ free(slot->tune_data, M_DEVBUF);
+ }
SDHCI_LOCK_DESTROY(slot);
@@ -856,7 +1099,16 @@ int
sdhci_generic_suspend(struct sdhci_slot *slot)
{
+ /*
+ * We expect the MMC layer to issue initial tuning after resume.
+ * Otherwise, we'd need to indicate re-tuning including circuit reset
+ * being required at least for re-tuning modes 1 and 2 ourselves.
+ */
+ callout_drain(&slot->retune_callout);
+ SDHCI_LOCK(slot);
+ slot->opt &= ~SDHCI_TUNING_ENABLED;
sdhci_reset(slot, SDHCI_RESET_ALL);
+ SDHCI_UNLOCK(slot);
return (0);
}
@@ -865,7 +1117,9 @@ int
sdhci_generic_resume(struct sdhci_slot *slot)
{
+ SDHCI_LOCK(slot);
sdhci_init(slot);
+ SDHCI_UNLOCK(slot);
return (0);
}
@@ -899,15 +1153,18 @@ sdhci_generic_set_uhs_timing(device_t brdev __unused, struct sdhci_slot *slot)
if (slot->version < SDHCI_SPEC_300)
return;
+ SDHCI_ASSERT_LOCKED(slot);
ios = &slot->host.ios;
sdhci_set_clock(slot, 0);
hostctrl2 = RD2(slot, SDHCI_HOST_CONTROL2);
hostctrl2 &= ~SDHCI_CTRL2_UHS_MASK;
- if (ios->timing == bus_timing_mmc_hs400 ||
- ios->timing == bus_timing_mmc_hs400es)
- hostctrl2 |= SDHCI_CTRL2_MMC_HS400;
- else if (ios->clock > SD_SDR50_MAX)
- hostctrl2 |= SDHCI_CTRL2_UHS_SDR104;
+ if (ios->clock > SD_SDR50_MAX) {
+ if (ios->timing == bus_timing_mmc_hs400 ||
+ ios->timing == bus_timing_mmc_hs400es)
+ hostctrl2 |= SDHCI_CTRL2_MMC_HS400;
+ else
+ hostctrl2 |= SDHCI_CTRL2_UHS_SDR104;
+ }
else if (ios->clock > SD_SDR25_MAX)
hostctrl2 |= SDHCI_CTRL2_UHS_SDR50;
else if (ios->clock > SD_SDR12_MAX) {
@@ -1019,6 +1276,222 @@ done:
return (err);
}
+int
+sdhci_generic_tune(device_t brdev __unused, device_t reqdev, bool hs400)
+{
+ struct sdhci_slot *slot = device_get_ivars(reqdev);
+ struct mmc_ios *ios = &slot->host.ios;
+ struct mmc_command *tune_cmd;
+ struct mmc_data *tune_data;
+ uint32_t opcode;
+ int err;
+
+ if (!(slot->opt & SDHCI_TUNING_SUPPORTED))
+ return (0);
+
+ slot->retune_ticks = slot->retune_count * hz;
+ opcode = MMC_SEND_TUNING_BLOCK;
+ SDHCI_LOCK(slot);
+ switch (ios->timing) {
+ case bus_timing_mmc_hs400:
+ slot_printf(slot, "HS400 must be tuned in HS200 mode\n");
+ SDHCI_UNLOCK(slot);
+ return (EINVAL);
+ case bus_timing_mmc_hs200:
+ /*
+ * In HS400 mode, controllers use the data strobe line to
+ * latch data from the devices so periodic re-tuning isn't
+ * expected to be required.
+ */
+ if (hs400)
+ slot->retune_ticks = 0;
+ opcode = MMC_SEND_TUNING_BLOCK_HS200;
+ break;
+ case bus_timing_uhs_ddr50:
+ case bus_timing_uhs_sdr104:
+ break;
+ case bus_timing_uhs_sdr50:
+ if (slot->opt & SDHCI_SDR50_NEEDS_TUNING)
+ break;
+ /* FALLTHROUGH */
+ default:
+ SDHCI_UNLOCK(slot);
+ return (0);
+ }
+
+ tune_cmd = slot->tune_cmd;
+ memset(tune_cmd, 0, sizeof(*tune_cmd));
+ tune_cmd->opcode = opcode;
+ tune_cmd->flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+ tune_data = tune_cmd->data = slot->tune_data;
+ memset(tune_data, 0, sizeof(*tune_data));
+ tune_data->len = (opcode == MMC_SEND_TUNING_BLOCK_HS200 &&
+ ios->bus_width == bus_width_8) ? MMC_TUNING_LEN_HS200 :
+ MMC_TUNING_LEN;
+ tune_data->flags = MMC_DATA_READ;
+ tune_data->mrq = tune_cmd->mrq = slot->tune_req;
+
+ slot->opt &= ~SDHCI_TUNING_ENABLED;
+ err = sdhci_exec_tuning(slot, true);
+ if (err == 0) {
+ slot->opt |= SDHCI_TUNING_ENABLED;
+ slot->intmask |= sdhci_tuning_intmask(slot);
+ WR4(slot, SDHCI_SIGNAL_ENABLE, slot->intmask);
+ if (slot->retune_ticks) {
+ callout_reset(&slot->retune_callout, slot->retune_ticks,
+ sdhci_retune, slot);
+ }
+ }
+ SDHCI_UNLOCK(slot);
+ return (err);
+}
+
+int
+sdhci_generic_retune(device_t brdev __unused, device_t reqdev, bool reset)
+{
+ struct sdhci_slot *slot = device_get_ivars(reqdev);
+ int err;
+
+ if (!(slot->opt & SDHCI_TUNING_ENABLED))
+ return (0);
+
+ /* HS400 must be tuned in HS200 mode. */
+ if (slot->host.ios.timing == bus_timing_mmc_hs400)
+ return (EINVAL);
+
+ SDHCI_LOCK(slot);
+ err = sdhci_exec_tuning(slot, reset);
+ /*
+ * There are two ways sdhci_exec_tuning() can fail:
+ * EBUSY should not actually happen when requests are only issued
+ * with the host properly acquired, and
+ * EIO re-tuning failed (but it did work initially).
+ *
+ * In both cases, we should retry at later point if periodic re-tuning
+ * is enabled. Note that due to slot->retune_req not being cleared in
+ * these failure cases, the MMC layer should trigger another attempt at
+ * re-tuning with the next request anyway, though.
+ */
+ if (slot->retune_ticks) {
+ callout_reset(&slot->retune_callout, slot->retune_ticks,
+ sdhci_retune, slot);
+ }
+ SDHCI_UNLOCK(slot);
+ return (err);
+}
+
+static int
+sdhci_exec_tuning(struct sdhci_slot *slot, bool reset)
+{
+ struct mmc_request *tune_req;
+ struct mmc_command *tune_cmd;
+ int i;
+ uint32_t intmask;
+ uint16_t hostctrl2;
+ u_char opt;
+
+ SDHCI_ASSERT_LOCKED(slot);
+ if (slot->req != NULL)
+ return (EBUSY);
+
+ /* Tuning doesn't work with DMA enabled. */
+ opt = slot->opt;
+ slot->opt = opt & ~SDHCI_HAVE_DMA;
+
+ /*
+ * Ensure that as documented, SDHCI_INT_DATA_AVAIL is the only
+ * kind of interrupt we receive in response to a tuning request.
+ */
+ intmask = slot->intmask;
+ slot->intmask = SDHCI_INT_DATA_AVAIL;
+ WR4(slot, SDHCI_SIGNAL_ENABLE, SDHCI_INT_DATA_AVAIL);
+
+ hostctrl2 = RD2(slot, SDHCI_HOST_CONTROL2);
+ if (reset)
+ hostctrl2 &= ~SDHCI_CTRL2_SAMPLING_CLOCK;
+ else
+ hostctrl2 |= SDHCI_CTRL2_SAMPLING_CLOCK;
+ WR2(slot, SDHCI_HOST_CONTROL2, hostctrl2 | SDHCI_CTRL2_EXEC_TUNING);
+
+ tune_req = slot->tune_req;
+ tune_cmd = slot->tune_cmd;
+ for (i = 0; i < MMC_TUNING_MAX; i++) {
+ memset(tune_req, 0, sizeof(*tune_req));
+#ifdef __rtems__
+ rtems_binary_semaphore_init(&tune_req->req_done,
+ "sdhci_req_done");
+#endif /* __rtems__ */
+ tune_req->cmd = tune_cmd;
+ tune_req->done = sdhci_req_wakeup;
+ tune_req->done_data = slot;
+ slot->req = tune_req;
+ slot->flags = 0;
+ sdhci_start(slot);
+#ifndef __rtems__
+ while (!(tune_req->flags & MMC_REQ_DONE))
+ msleep(tune_req, &slot->mtx, 0, "sdhciet", 0);
+#else /* __rtems__ */
+ rtems_binary_semaphore_wait(&tune_req->req_done);
+ rtems_binary_semaphore_destroy(&tune_req->req_done);
+#endif /* __rtems__ */
+ if (!(tune_req->flags & MMC_TUNE_DONE))
+ break;
+ hostctrl2 = RD2(slot, SDHCI_HOST_CONTROL2);
+ if (!(hostctrl2 & SDHCI_CTRL2_EXEC_TUNING))
+ break;
+ if (tune_cmd->opcode == MMC_SEND_TUNING_BLOCK)
+ DELAY(1000);
+ }
+
+ slot->opt = opt;
+ slot->intmask = intmask;
+ WR4(slot, SDHCI_SIGNAL_ENABLE, intmask);
+
+ if ((hostctrl2 & (SDHCI_CTRL2_EXEC_TUNING |
+ SDHCI_CTRL2_SAMPLING_CLOCK)) == SDHCI_CTRL2_SAMPLING_CLOCK) {
+ slot->retune_req = 0;
+ return (0);
+ }
+
+ slot_printf(slot, "Tuning failed, using fixed sampling clock\n");
+ WR2(slot, SDHCI_HOST_CONTROL2, hostctrl2 & ~(SDHCI_CTRL2_EXEC_TUNING |
+ SDHCI_CTRL2_SAMPLING_CLOCK));
+ sdhci_reset(slot, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+ return (EIO);
+}
+
+static void
+sdhci_retune(void *arg)
+{
+ struct sdhci_slot *slot = arg;
+
+ slot->retune_req |= SDHCI_RETUNE_REQ_NEEDED;
+}
+
+#ifdef MMCCAM
+static void
+sdhci_req_done(struct sdhci_slot *slot)
+{
+ union ccb *ccb;
+
+ if (__predict_false(sdhci_debug > 1))
+ slot_printf(slot, "%s\n", __func__);
+ if (slot->ccb != NULL && slot->curcmd != NULL) {
+ callout_stop(&slot->timeout_callout);
+ ccb = slot->ccb;
+ slot->ccb = NULL;
+ slot->curcmd = NULL;
+
+ /* Tell CAM the request is finished */
+ struct ccb_mmcio *mmcio;
+ mmcio = &ccb->mmcio;
+
+ ccb->ccb_h.status =
+ (mmcio->cmd.error == 0 ? CAM_REQ_CMP : CAM_REQ_CMP_ERR);
+ xpt_done(ccb);
+ }
+}
+#else
static void
sdhci_req_done(struct sdhci_slot *slot)
{
@@ -1032,6 +1505,21 @@ sdhci_req_done(struct sdhci_slot *slot)
req->done(req);
}
}
+#endif
+
+static void
+sdhci_req_wakeup(struct mmc_request *req)
+{
+#ifndef __rtems__
+ struct sdhci_slot *slot;
+
+ slot = req->done_data;
+ req->flags |= MMC_REQ_DONE;
+ wakeup(req);
+#else /* __rtems__ */
+ rtems_binary_semaphore_post(&req->req_done);
+#endif /* __rtems__ */
+}
static void
sdhci_timeout(void *arg)
@@ -1039,13 +1527,13 @@ sdhci_timeout(void *arg)
struct sdhci_slot *slot = arg;
if (slot->curcmd != NULL) {
- slot_printf(slot, " Controller timeout\n");
+ slot_printf(slot, "Controller timeout\n");
sdhci_dumpregs(slot);
sdhci_reset(slot, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
slot->curcmd->error = MMC_ERR_TIMEOUT;
sdhci_req_done(slot);
} else {
- slot_printf(slot, " Spurious timeout - no active command\n");
+ slot_printf(slot, "Spurious timeout - no active command\n");
}
}
@@ -1062,8 +1550,16 @@ sdhci_set_transfer_mode(struct sdhci_slot *slot, struct mmc_data *data)
mode |= SDHCI_TRNS_MULTI;
if (data->flags & MMC_DATA_READ)
mode |= SDHCI_TRNS_READ;
+#ifdef MMCCAM
+ struct ccb_mmcio *mmcio;
+ mmcio = &slot->ccb->mmcio;
+ if (mmcio->stop.opcode == MMC_STOP_TRANSMISSION
+ && !(slot->quirks & SDHCI_QUIRK_BROKEN_AUTO_STOP))
+ mode |= SDHCI_TRNS_ACMD12;
+#else
if (slot->req->stop && !(slot->quirks & SDHCI_QUIRK_BROKEN_AUTO_STOP))
mode |= SDHCI_TRNS_ACMD12;
+#endif
if (slot->flags & SDHCI_USE_DMA)
mode |= SDHCI_TRNS_DMA;
@@ -1096,6 +1592,9 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
if (!SDHCI_GET_CARD_PRESENT(slot->bus, slot) ||
slot->power == 0 ||
slot->clock == 0) {
+ slot_printf(slot,
+ "Cannot issue a command (power=%d clock=%d)",
+ slot->power, slot->clock);
cmd->error = MMC_ERR_FAILED;
sdhci_req_done(slot);
return;
@@ -1103,10 +1602,20 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
/* Always wait for free CMD bus. */
mask = SDHCI_CMD_INHIBIT;
/* Wait for free DAT if we have data or busy signal. */
- if (cmd->data || (cmd->flags & MMC_RSP_BUSY))
+ if (cmd->data != NULL || (cmd->flags & MMC_RSP_BUSY))
mask |= SDHCI_DAT_INHIBIT;
- /* We shouldn't wait for DAT for stop commands. */
- if (cmd == slot->req->stop)
+ /*
+ * We shouldn't wait for DAT for stop commands or CMD19/CMD21. Note
+ * that these latter are also special in that SDHCI_CMD_DATA should
+ * be set below but no actual data is ever read from the controller.
+ */
+#ifdef MMCCAM
+ if (cmd == &slot->ccb->mmcio.stop ||
+#else
+ if (cmd == slot->req->stop ||
+#endif
+ __predict_false(cmd->opcode == MMC_SEND_TUNING_BLOCK ||
+ cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))
mask &= ~SDHCI_DAT_INHIBIT;
/*
* Wait for bus no more then 250 ms. Typically there will be no wait
@@ -1145,7 +1654,7 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
flags |= SDHCI_CMD_CRC;
if (cmd->flags & MMC_RSP_OPCODE)
flags |= SDHCI_CMD_INDEX;
- if (cmd->data)
+ if (cmd->data != NULL)
flags |= SDHCI_CMD_DATA;
if (cmd->opcode == MMC_STOP_TRANSMISSION)
flags |= SDHCI_CMD_TYPE_ABORT;
@@ -1164,6 +1673,8 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
WR4(slot, SDHCI_ARGUMENT, cmd->arg);
/* Set data transfer mode. */
sdhci_set_transfer_mode(slot, cmd->data);
+ if (__predict_false(sdhci_debug > 1))
+ slot_printf(slot, "Starting command!\n");
/* Start command. */
WR2(slot, SDHCI_COMMAND_FLAGS, (cmd->opcode << 8) | (flags & 0xff));
/* Start timeout callout. */
@@ -1178,15 +1689,23 @@ sdhci_finish_command(struct sdhci_slot *slot)
uint32_t val;
uint8_t extra;
+ if (__predict_false(sdhci_debug > 1))
+ slot_printf(slot, "%s: called, err %d flags %d\n",
+ __func__, slot->curcmd->error, slot->curcmd->flags);
slot->cmd_done = 1;
/*
* Interrupt aggregation: Restore command interrupt.
* Main restore point for the case when command interrupt
* happened first.
*/
- WR4(slot, SDHCI_SIGNAL_ENABLE, slot->intmask |= SDHCI_INT_RESPONSE);
+ if (__predict_true(slot->curcmd->opcode != MMC_SEND_TUNING_BLOCK &&
+ slot->curcmd->opcode != MMC_SEND_TUNING_BLOCK_HS200))
+ WR4(slot, SDHCI_SIGNAL_ENABLE, slot->intmask |=
+ SDHCI_INT_RESPONSE);
/* In case of error - reset host and return. */
if (slot->curcmd->error) {
+ if (slot->curcmd->error == MMC_ERR_BADCRC)
+ slot->retune_req |= SDHCI_RETUNE_REQ_RESET;
sdhci_reset(slot, SDHCI_RESET_CMD);
sdhci_reset(slot, SDHCI_RESET_DATA);
sdhci_start(slot);
@@ -1211,6 +1730,11 @@ sdhci_finish_command(struct sdhci_slot *slot)
} else
slot->curcmd->resp[0] = RD4(slot, SDHCI_RESPONSE);
}
+ if (__predict_false(sdhci_debug > 1))
+ printf("Resp: %02x %02x %02x %02x\n",
+ slot->curcmd->resp[0], slot->curcmd->resp[1],
+ slot->curcmd->resp[2], slot->curcmd->resp[3]);
+
/* If data ready - finish. */
if (slot->data_done)
sdhci_start(slot);
@@ -1291,6 +1815,11 @@ sdhci_start_data(struct sdhci_slot *slot, struct mmc_data *data)
(data->len < 512) ? data->len : 512));
/* Set block count. */
WR2(slot, SDHCI_BLOCK_COUNT, (data->len + 511) / 512);
+
+ if (__predict_false(sdhci_debug > 1))
+ slot_printf(slot, "Block size: %02x, count %lu\n",
+ (unsigned int)SDHCI_MAKE_BLKSZ(DMA_BOUNDARY, (data->len < 512) ? data->len : 512),
+ (unsigned long)(data->len + 511) / 512);
}
void
@@ -1322,6 +1851,8 @@ sdhci_finish_data(struct sdhci_slot *slot)
slot->data_done = 1;
/* If there was error - reset the host. */
if (slot->curcmd->error) {
+ if (slot->curcmd->error == MMC_ERR_BADCRC)
+ slot->retune_req |= SDHCI_RETUNE_REQ_RESET;
sdhci_reset(slot, SDHCI_RESET_CMD);
sdhci_reset(slot, SDHCI_RESET_DATA);
sdhci_start(slot);
@@ -1332,6 +1863,47 @@ sdhci_finish_data(struct sdhci_slot *slot)
sdhci_start(slot);
}
+#ifdef MMCCAM
+static void
+sdhci_start(struct sdhci_slot *slot)
+{
+ union ccb *ccb;
+
+ ccb = slot->ccb;
+ if (ccb == NULL)
+ return;
+
+ struct ccb_mmcio *mmcio;
+ mmcio = &ccb->mmcio;
+
+ if (!(slot->flags & CMD_STARTED)) {
+ slot->flags |= CMD_STARTED;
+ sdhci_start_command(slot, &mmcio->cmd);
+ return;
+ }
+
+ /*
+ * Old stack doesn't use this!
+ * Enabling this code causes significant performance degradation
+ * and IRQ storms on BBB, Wandboard behaves fine.
+ * Not using this code does no harm...
+ if (!(slot->flags & STOP_STARTED) && mmcio->stop.opcode != 0) {
+ slot->flags |= STOP_STARTED;
+ sdhci_start_command(slot, &mmcio->stop);
+ return;
+ }
+ */
+ if (__predict_false(sdhci_debug > 1))
+ slot_printf(slot, "result: %d\n", mmcio->cmd.error);
+ if (mmcio->cmd.error == 0 &&
+ (slot->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)) {
+ sdhci_reset(slot, SDHCI_RESET_CMD);
+ sdhci_reset(slot, SDHCI_RESET_DATA);
+ }
+
+ sdhci_req_done(slot);
+}
+#else
static void
sdhci_start(struct sdhci_slot *slot)
{
@@ -1352,7 +1924,7 @@ sdhci_start(struct sdhci_slot *slot)
sdhci_start_command(slot, req->stop);
return;
}
- if (sdhci_debug > 1)
+ if (__predict_false(sdhci_debug > 1))
slot_printf(slot, "result: %d\n", req->cmd->error);
if (!req->cmd->error &&
((slot->curcmd == req->stop &&
@@ -1364,6 +1936,7 @@ sdhci_start(struct sdhci_slot *slot)
sdhci_req_done(slot);
}
+#endif
int
sdhci_generic_request(device_t brdev __unused, device_t reqdev,
@@ -1376,7 +1949,7 @@ sdhci_generic_request(device_t brdev __unused, device_t reqdev,
SDHCI_UNLOCK(slot);
return (EBUSY);
}
- if (sdhci_debug > 1) {
+ if (__predict_false(sdhci_debug > 1)) {
slot_printf(slot,
"CMD%u arg %#x flags %#x dlen %u dflags %#x\n",
req->cmd->opcode, req->cmd->arg, req->cmd->flags,
@@ -1495,6 +2068,15 @@ sdhci_data_irq(struct sdhci_slot *slot, uint32_t intmask)
goto done;
}
+ /* Handle tuning completion interrupt. */
+ if (__predict_false((intmask & SDHCI_INT_DATA_AVAIL) &&
+ (slot->curcmd->opcode == MMC_SEND_TUNING_BLOCK ||
+ slot->curcmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))) {
+ slot->req->flags |= MMC_TUNE_DONE;
+ sdhci_finish_command(slot);
+ sdhci_finish_data(slot);
+ return;
+ }
/* Handle PIO interrupt. */
if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) {
if ((slot->opt & SDHCI_PLATFORM_TRANSFER) &&
@@ -1587,9 +2169,21 @@ sdhci_generic_intr(struct sdhci_slot *slot)
SDHCI_UNLOCK(slot);
return;
}
- if (sdhci_debug > 2)
+ if (__predict_false(sdhci_debug > 2))
slot_printf(slot, "Interrupt %#x\n", intmask);
+ /* Handle tuning error interrupt. */
+ if (__predict_false(intmask & SDHCI_INT_TUNEERR)) {
+ slot_printf(slot, "Tuning error indicated\n");
+ slot->retune_req |= SDHCI_RETUNE_REQ_RESET;
+ if (slot->curcmd) {
+ slot->curcmd->error = MMC_ERR_BADCRC;
+ sdhci_finish_command(slot);
+ }
+ }
+ /* Handle re-tuning interrupt. */
+ if (__predict_false(intmask & SDHCI_INT_RETUNE))
+ slot->retune_req |= SDHCI_RETUNE_REQ_NEEDED;
/* Handle card presence interrupts. */
if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
present = (intmask & SDHCI_INT_CARD_INSERT) != 0;
@@ -1602,7 +2196,6 @@ sdhci_generic_intr(struct sdhci_slot *slot)
WR4(slot, SDHCI_INT_STATUS, intmask &
(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE));
sdhci_handle_card_present_locked(slot, present);
- intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
}
/* Handle command interrupts. */
if (intmask & SDHCI_INT_CMD_MASK) {
@@ -1621,16 +2214,14 @@ sdhci_generic_intr(struct sdhci_slot *slot)
WR4(slot, SDHCI_INT_STATUS, SDHCI_INT_ACMD12ERR);
sdhci_acmd_irq(slot);
}
- intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK);
- intmask &= ~SDHCI_INT_ACMD12ERR;
- intmask &= ~SDHCI_INT_ERROR;
/* Handle bus power interrupt. */
if (intmask & SDHCI_INT_BUS_POWER) {
WR4(slot, SDHCI_INT_STATUS, SDHCI_INT_BUS_POWER);
- slot_printf(slot,
- "Card is consuming too much power!\n");
- intmask &= ~SDHCI_INT_BUS_POWER;
+ slot_printf(slot, "Card is consuming too much power!\n");
}
+ intmask &= ~(SDHCI_INT_ERROR | SDHCI_INT_TUNEERR | SDHCI_INT_RETUNE |
+ SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE | SDHCI_INT_CMD_MASK |
+ SDHCI_INT_DATA_MASK | SDHCI_INT_ACMD12ERR | SDHCI_INT_BUS_POWER);
/* The rest is unknown. */
if (intmask) {
WR4(slot, SDHCI_INT_STATUS, intmask);
@@ -1684,6 +2275,19 @@ sdhci_generic_read_ivar(device_t bus, device_t child, int which,
case MMCBR_IVAR_VDD:
*result = slot->host.ios.vdd;
break;
+ case MMCBR_IVAR_RETUNE_REQ:
+ if (slot->opt & SDHCI_TUNING_ENABLED) {
+ if (slot->retune_req & SDHCI_RETUNE_REQ_RESET) {
+ *result = retune_req_reset;
+ break;
+ }
+ if (slot->retune_req & SDHCI_RETUNE_REQ_NEEDED) {
+ *result = retune_req_normal;
+ break;
+ }
+ }
+ *result = retune_req_none;
+ break;
case MMCBR_IVAR_VCCQ:
*result = slot->host.ios.vccq;
break;
@@ -1694,6 +2298,16 @@ sdhci_generic_read_ivar(device_t bus, device_t child, int which,
*result = slot->host.ios.timing;
break;
case MMCBR_IVAR_MAX_DATA:
+ /*
+ * Re-tuning modes 1 and 2 restrict the maximum data length
+ * per read/write command to 4 MiB.
+ */
+ if (slot->opt & SDHCI_TUNING_ENABLED &&
+ (slot->retune_mode == SDHCI_RETUNE_MODE_1 ||
+ slot->retune_mode == SDHCI_RETUNE_MODE_2)) {
+ *result = 4 * 1024 * 1024 / MMC_SECTOR_SIZE;
+ break;
+ }
*result = 65535;
break;
case MMCBR_IVAR_MAX_BUSY_TIMEOUT:
@@ -1714,6 +2328,8 @@ sdhci_generic_write_ivar(device_t bus, device_t child, int which,
uint32_t clock, max_clock;
int i;
+ if (sdhci_debug > 1)
+ slot_printf(slot, "%s: var=%d\n", __func__, which);
switch (which) {
default:
return (EINVAL);
@@ -1774,9 +2390,324 @@ sdhci_generic_write_ivar(device_t bus, device_t child, int which,
case MMCBR_IVAR_F_MIN:
case MMCBR_IVAR_F_MAX:
case MMCBR_IVAR_MAX_DATA:
+ case MMCBR_IVAR_RETUNE_REQ:
return (EINVAL);
}
return (0);
}
+#ifdef MMCCAM
+void
+sdhci_cam_start_slot(struct sdhci_slot *slot)
+{
+ if ((slot->devq = cam_simq_alloc(1)) == NULL) {
+ goto fail;
+ }
+
+ mtx_init(&slot->sim_mtx, "sdhcisim", NULL, MTX_DEF);
+ slot->sim = cam_sim_alloc(sdhci_cam_action, sdhci_cam_poll,
+ "sdhci_slot", slot, device_get_unit(slot->bus),
+ &slot->sim_mtx, 1, 1, slot->devq);
+
+ if (slot->sim == NULL) {
+ cam_simq_free(slot->devq);
+ slot_printf(slot, "cannot allocate CAM SIM\n");
+ goto fail;
+ }
+
+ mtx_lock(&slot->sim_mtx);
+ if (xpt_bus_register(slot->sim, slot->bus, 0) != 0) {
+ slot_printf(slot,
+ "cannot register SCSI pass-through bus\n");
+ cam_sim_free(slot->sim, FALSE);
+ cam_simq_free(slot->devq);
+ mtx_unlock(&slot->sim_mtx);
+ goto fail;
+ }
+
+ mtx_unlock(&slot->sim_mtx);
+ /* End CAM-specific init */
+ slot->card_present = 0;
+ sdhci_card_task(slot, 0);
+ return;
+
+fail:
+ if (slot->sim != NULL) {
+ mtx_lock(&slot->sim_mtx);
+ xpt_bus_deregister(cam_sim_path(slot->sim));
+ cam_sim_free(slot->sim, FALSE);
+ mtx_unlock(&slot->sim_mtx);
+ }
+
+ if (slot->devq != NULL)
+ cam_simq_free(slot->devq);
+}
+
+static void
+sdhci_cam_handle_mmcio(struct cam_sim *sim, union ccb *ccb)
+{
+ struct sdhci_slot *slot;
+
+ slot = cam_sim_softc(sim);
+
+ sdhci_cam_request(slot, ccb);
+}
+
+void
+sdhci_cam_action(struct cam_sim *sim, union ccb *ccb)
+{
+ struct sdhci_slot *slot;
+
+ slot = cam_sim_softc(sim);
+ if (slot == NULL) {
+ ccb->ccb_h.status = CAM_SEL_TIMEOUT;
+ xpt_done(ccb);
+ return;
+ }
+
+ mtx_assert(&slot->sim_mtx, MA_OWNED);
+
+ switch (ccb->ccb_h.func_code) {
+ case XPT_PATH_INQ:
+ {
+ struct ccb_pathinq *cpi;
+
+ cpi = &ccb->cpi;
+ cpi->version_num = 1;
+ cpi->hba_inquiry = 0;
+ cpi->target_sprt = 0;
+ cpi->hba_misc = PIM_NOBUSRESET | PIM_SEQSCAN;
+ cpi->hba_eng_cnt = 0;
+ cpi->max_target = 0;
+ cpi->max_lun = 0;
+ cpi->initiator_id = 1;
+ cpi->maxio = MAXPHYS;
+ strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
+ strncpy(cpi->hba_vid, "Deglitch Networks", HBA_IDLEN);
+ strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
+ cpi->unit_number = cam_sim_unit(sim);
+ cpi->bus_id = cam_sim_bus(sim);
+ cpi->base_transfer_speed = 100; /* XXX WTF? */
+ cpi->protocol = PROTO_MMCSD;
+ cpi->protocol_version = SCSI_REV_0;
+ cpi->transport = XPORT_MMCSD;
+ cpi->transport_version = 0;
+
+ cpi->ccb_h.status = CAM_REQ_CMP;
+ break;
+ }
+ case XPT_GET_TRAN_SETTINGS:
+ {
+ struct ccb_trans_settings *cts = &ccb->cts;
+
+ if (sdhci_debug > 1)
+ slot_printf(slot, "Got XPT_GET_TRAN_SETTINGS\n");
+
+ cts->protocol = PROTO_MMCSD;
+ cts->protocol_version = 1;
+ cts->transport = XPORT_MMCSD;
+ cts->transport_version = 1;
+ cts->xport_specific.valid = 0;
+ cts->proto_specific.mmc.host_ocr = slot->host.host_ocr;
+ cts->proto_specific.mmc.host_f_min = slot->host.f_min;
+ cts->proto_specific.mmc.host_f_max = slot->host.f_max;
+ cts->proto_specific.mmc.host_caps = slot->host.caps;
+ memcpy(&cts->proto_specific.mmc.ios, &slot->host.ios, sizeof(struct mmc_ios));
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ break;
+ }
+ case XPT_SET_TRAN_SETTINGS:
+ {
+ if (sdhci_debug > 1)
+ slot_printf(slot, "Got XPT_SET_TRAN_SETTINGS\n");
+ sdhci_cam_settran_settings(slot, ccb);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ break;
+ }
+ case XPT_RESET_BUS:
+ if (sdhci_debug > 1)
+ slot_printf(slot, "Got XPT_RESET_BUS, ACK it...\n");
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ break;
+ case XPT_MMC_IO:
+ /*
+ * Here is the HW-dependent part of
+ * sending the command to the underlying h/w
+ * At some point in the future an interrupt comes.
+ * Then the request will be marked as completed.
+ */
+ if (__predict_false(sdhci_debug > 1))
+ slot_printf(slot, "Got XPT_MMC_IO\n");
+ ccb->ccb_h.status = CAM_REQ_INPROG;
+
+ sdhci_cam_handle_mmcio(sim, ccb);
+ return;
+ /* NOTREACHED */
+ break;
+ default:
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ break;
+ }
+ xpt_done(ccb);
+ return;
+}
+
+void
+sdhci_cam_poll(struct cam_sim *sim)
+{
+ return;
+}
+
+int sdhci_cam_get_possible_host_clock(struct sdhci_slot *slot, int proposed_clock) {
+ int max_clock, clock, i;
+
+ if (proposed_clock == 0)
+ return 0;
+ max_clock = slot->max_clk;
+ clock = max_clock;
+
+ if (slot->version < SDHCI_SPEC_300) {
+ for (i = 0; i < SDHCI_200_MAX_DIVIDER;
+ i <<= 1) {
+ if (clock <= proposed_clock)
+ break;
+ clock >>= 1;
+ }
+ } else {
+ for (i = 0; i < SDHCI_300_MAX_DIVIDER;
+ i += 2) {
+ if (clock <= proposed_clock)
+ break;
+ clock = max_clock / (i + 2);
+ }
+ }
+ return clock;
+}
+
+int
+sdhci_cam_settran_settings(struct sdhci_slot *slot, union ccb *ccb)
+{
+ struct mmc_ios *ios;
+ struct mmc_ios *new_ios;
+ struct ccb_trans_settings_mmc *cts;
+
+ ios = &slot->host.ios;
+
+ cts = &ccb->cts.proto_specific.mmc;
+ new_ios = &cts->ios;
+
+ /* Update only requested fields */
+ if (cts->ios_valid & MMC_CLK) {
+ ios->clock = sdhci_cam_get_possible_host_clock(slot, new_ios->clock);
+ slot_printf(slot, "Clock => %d\n", ios->clock);
+ }
+ if (cts->ios_valid & MMC_VDD) {
+ ios->vdd = new_ios->vdd;
+ slot_printf(slot, "VDD => %d\n", ios->vdd);
+ }
+ if (cts->ios_valid & MMC_CS) {
+ ios->chip_select = new_ios->chip_select;
+ slot_printf(slot, "CS => %d\n", ios->chip_select);
+ }
+ if (cts->ios_valid & MMC_BW) {
+ ios->bus_width = new_ios->bus_width;
+ slot_printf(slot, "Bus width => %d\n", ios->bus_width);
+ }
+ if (cts->ios_valid & MMC_PM) {
+ ios->power_mode = new_ios->power_mode;
+ slot_printf(slot, "Power mode => %d\n", ios->power_mode);
+ }
+ if (cts->ios_valid & MMC_BT) {
+ ios->timing = new_ios->timing;
+ slot_printf(slot, "Timing => %d\n", ios->timing);
+ }
+ if (cts->ios_valid & MMC_BM) {
+ ios->bus_mode = new_ios->bus_mode;
+ slot_printf(slot, "Bus mode => %d\n", ios->bus_mode);
+ }
+
+ /* XXX Provide a way to call a chip-specific IOS update, required for TI */
+ return (sdhci_cam_update_ios(slot));
+}
+
+int
+sdhci_cam_update_ios(struct sdhci_slot *slot)
+{
+ struct mmc_ios *ios = &slot->host.ios;
+
+ slot_printf(slot, "%s: power_mode=%d, clk=%d, bus_width=%d, timing=%d\n",
+ __func__, ios->power_mode, ios->clock, ios->bus_width, ios->timing);
+ SDHCI_LOCK(slot);
+ /* Do full reset on bus power down to clear from any state. */
+ if (ios->power_mode == power_off) {
+ WR4(slot, SDHCI_SIGNAL_ENABLE, 0);
+ sdhci_init(slot);
+ }
+ /* Configure the bus. */
+ sdhci_set_clock(slot, ios->clock);
+ sdhci_set_power(slot, (ios->power_mode == power_off) ? 0 : ios->vdd);
+ if (ios->bus_width == bus_width_8) {
+ slot->hostctrl |= SDHCI_CTRL_8BITBUS;
+ slot->hostctrl &= ~SDHCI_CTRL_4BITBUS;
+ } else if (ios->bus_width == bus_width_4) {
+ slot->hostctrl &= ~SDHCI_CTRL_8BITBUS;
+ slot->hostctrl |= SDHCI_CTRL_4BITBUS;
+ } else if (ios->bus_width == bus_width_1) {
+ slot->hostctrl &= ~SDHCI_CTRL_8BITBUS;
+ slot->hostctrl &= ~SDHCI_CTRL_4BITBUS;
+ } else {
+ panic("Invalid bus width: %d", ios->bus_width);
+ }
+ if (ios->timing == bus_timing_hs &&
+ !(slot->quirks & SDHCI_QUIRK_DONT_SET_HISPD_BIT))
+ slot->hostctrl |= SDHCI_CTRL_HISPD;
+ else
+ slot->hostctrl &= ~SDHCI_CTRL_HISPD;
+ WR1(slot, SDHCI_HOST_CONTROL, slot->hostctrl);
+ /* Some controllers like reset after bus changes. */
+ if(slot->quirks & SDHCI_QUIRK_RESET_ON_IOS)
+ sdhci_reset(slot, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+
+ SDHCI_UNLOCK(slot);
+ return (0);
+}
+
+int
+sdhci_cam_request(struct sdhci_slot *slot, union ccb *ccb)
+{
+ struct ccb_mmcio *mmcio;
+
+ mmcio = &ccb->mmcio;
+
+ SDHCI_LOCK(slot);
+/* if (slot->req != NULL) {
+ SDHCI_UNLOCK(slot);
+ return (EBUSY);
+ }
+*/
+ if (__predict_false(sdhci_debug > 1)) {
+ slot_printf(slot, "CMD%u arg %#x flags %#x dlen %u dflags %#x\n",
+ mmcio->cmd.opcode, mmcio->cmd.arg, mmcio->cmd.flags,
+ mmcio->cmd.data != NULL ? (unsigned int) mmcio->cmd.data->len : 0,
+ mmcio->cmd.data != NULL ? mmcio->cmd.data->flags: 0);
+ }
+ if (mmcio->cmd.data != NULL) {
+ if (mmcio->cmd.data->len == 0 || mmcio->cmd.data->flags == 0)
+ panic("data->len = %d, data->flags = %d -- something is b0rked",
+ (int)mmcio->cmd.data->len, mmcio->cmd.data->flags);
+ }
+ slot->ccb = ccb;
+ slot->flags = 0;
+ sdhci_start(slot);
+ SDHCI_UNLOCK(slot);
+ if (dumping) {
+ while (slot->ccb != NULL) {
+ sdhci_generic_intr(slot);
+ DELAY(10);
+ }
+ }
+ return (0);
+}
+#endif /* MMCCAM */
+
MODULE_VERSION(sdhci, 1);