summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/sdhci/sdhci.c129
-rw-r--r--sys/dev/sdhci/sdhci.h13
-rw-r--r--sys/dev/sdhci/sdhci_if.m6
3 files changed, 110 insertions, 38 deletions
diff --git a/sys/dev/sdhci/sdhci.c b/sys/dev/sdhci/sdhci.c
index e625d4f..36e2774 100644
--- a/sys/dev/sdhci/sdhci.c
+++ b/sys/dev/sdhci/sdhci.c
@@ -73,6 +73,7 @@ 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);
/* helper routines */
@@ -89,6 +90,9 @@ static void sdhci_card_task(void *, int);
#define SDHCI_200_MAX_DIVIDER 256
#define SDHCI_300_MAX_DIVIDER 2046
+#define SDHCI_CARD_PRESENT_TICKS (hz / 5)
+#define SDHCI_INSERT_DELAY_TICKS (hz / 2)
+
/*
* Broadcom BCM577xx Controller Constants
*/
@@ -164,8 +168,7 @@ sdhci_reset(struct sdhci_slot *slot, uint8_t mask)
int timeout;
if (slot->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
- if (!(RD4(slot, SDHCI_PRESENT_STATE) &
- SDHCI_CARD_PRESENT))
+ if (!SDHCI_GET_CARD_PRESENT(slot->bus, slot))
return;
}
@@ -230,10 +233,15 @@ sdhci_init(struct sdhci_slot *slot)
slot->intmask = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX |
SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT |
- SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT |
SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL |
SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE |
SDHCI_INT_ACMD12ERR;
+
+ if (!(slot->quirks & SDHCI_QUIRK_POLL_CARD_PRESENT) &&
+ !(slot->opt & SDHCI_NON_REMOVABLE)) {
+ slot->intmask |= SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT;
+ }
+
WR4(slot, SDHCI_INT_ENABLE, slot->intmask);
WR4(slot, SDHCI_SIGNAL_ENABLE, slot->intmask);
}
@@ -475,23 +483,17 @@ sdhci_transfer_pio(struct sdhci_slot *slot)
}
}
-static void
-sdhci_card_delay(void *arg)
-{
- struct sdhci_slot *slot = arg;
-
- taskqueue_enqueue(taskqueue_swi_giant, &slot->card_task);
-}
-
static void
sdhci_card_task(void *arg, int pending)
{
struct sdhci_slot *slot = arg;
SDHCI_LOCK(slot);
- if (RD4(slot, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT) {
+ if (SDHCI_GET_CARD_PRESENT(slot->bus, slot)) {
if (slot->dev == NULL) {
/* 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);
SDHCI_UNLOCK(slot);
@@ -501,6 +503,8 @@ sdhci_card_task(void *arg, int pending)
} else {
if (slot->dev != NULL) {
/* If no card present - detach mmc bus. */
+ if (bootverbose || sdhci_debug)
+ slot_printf(slot, "Card removed\n");
device_t d = slot->dev;
slot->dev = NULL;
SDHCI_UNLOCK(slot);
@@ -510,6 +514,51 @@ sdhci_card_task(void *arg, int pending)
}
}
+static void
+sdhci_handle_card_present_locked(struct sdhci_slot *slot, bool is_present)
+{
+ bool was_present;
+
+ /*
+ * If there was no card and now there is one, schedule the task to
+ * create the child device after a short delay. The delay is to
+ * debounce the card insert (sometimes the card detect pin stabilizes
+ * before the other pins have made good contact).
+ *
+ * If there was a card present and now it's gone, immediately schedule
+ * the task to delete the child device. No debouncing -- gone is gone,
+ * because once power is removed, a full card re-init is needed, and
+ * that happens by deleting and recreating the child device.
+ */
+ was_present = slot->dev != NULL;
+ if (!was_present && is_present) {
+ taskqueue_enqueue_timeout(taskqueue_swi_giant,
+ &slot->card_delayed_task, -SDHCI_INSERT_DELAY_TICKS);
+ } else if (was_present && !is_present) {
+ taskqueue_enqueue(taskqueue_swi_giant, &slot->card_task);
+ }
+}
+
+void
+sdhci_handle_card_present(struct sdhci_slot *slot, bool is_present)
+{
+
+ SDHCI_LOCK(slot);
+ sdhci_handle_card_present_locked(slot, is_present);
+ SDHCI_UNLOCK(slot);
+}
+
+static void
+sdhci_card_poll(void *arg)
+{
+ struct sdhci_slot *slot = arg;
+
+ sdhci_handle_card_present(slot,
+ SDHCI_GET_CARD_PRESENT(slot->bus, slot));
+ callout_reset(&slot->card_poll_callout, SDHCI_CARD_PRESENT_TICKS,
+ sdhci_card_poll, slot);
+}
+
int
sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num)
{
@@ -653,9 +702,17 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num)
"timeout", CTLFLAG_RW, &slot->timeout, 0,
"Maximum timeout for SDHCI transfers (in secs)");
TASK_INIT(&slot->card_task, 0, sdhci_card_task, slot);
- callout_init(&slot->card_callout, 1);
+ TIMEOUT_TASK_INIT(taskqueue_swi_giant, &slot->card_delayed_task, 0,
+ sdhci_card_task, slot);
+ callout_init(&slot->card_poll_callout, 1);
callout_init_mtx(&slot->timeout_callout, &slot->mtx, 0);
+ if ((slot->quirks & SDHCI_QUIRK_POLL_CARD_PRESENT) &&
+ !(slot->opt & SDHCI_NON_REMOVABLE)) {
+ callout_reset(&slot->card_poll_callout,
+ SDHCI_CARD_PRESENT_TICKS, sdhci_card_poll, slot);
+ }
+
return (0);
}
@@ -671,8 +728,9 @@ sdhci_cleanup_slot(struct sdhci_slot *slot)
device_t d;
callout_drain(&slot->timeout_callout);
- callout_drain(&slot->card_callout);
+ callout_drain(&slot->card_poll_callout);
taskqueue_drain(taskqueue_swi_giant, &slot->card_task);
+ taskqueue_drain_timeout(taskqueue_swi_giant, &slot->card_delayed_task);
SDHCI_LOCK(slot);
d = slot->dev;
@@ -718,6 +776,16 @@ sdhci_generic_min_freq(device_t brdev, struct sdhci_slot *slot)
return (slot->max_clk / SDHCI_200_MAX_DIVIDER);
}
+bool
+sdhci_generic_get_card_present(device_t brdev, struct sdhci_slot *slot)
+{
+
+ if (slot->opt & SDHCI_NON_REMOVABLE)
+ return true;
+
+ return (RD4(slot, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT);
+}
+
int
sdhci_generic_update_ios(device_t brdev, device_t reqdev)
{
@@ -815,7 +883,7 @@ static void
sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
{
int flags, timeout;
- uint32_t mask, state;
+ uint32_t mask;
slot->curcmd = cmd;
slot->cmd_done = 0;
@@ -830,11 +898,9 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
return;
}
- /* Read controller present state. */
- state = RD4(slot, SDHCI_PRESENT_STATE);
/* Do not issue command if there is no card, clock or power.
* Controller will not detect timeout without clock active. */
- if ((state & SDHCI_CARD_PRESENT) == 0 ||
+ if (!SDHCI_GET_CARD_PRESENT(slot->bus, slot) ||
slot->power == 0 ||
slot->clock == 0) {
cmd->error = MMC_ERR_FAILED;
@@ -860,7 +926,7 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
* (It's usually more like 20-30ms in the real world.)
*/
timeout = 250;
- while (state & mask) {
+ while (mask & RD4(slot, SDHCI_PRESENT_STATE)) {
if (timeout == 0) {
slot_printf(slot, "Controller never released "
"inhibit bit(s).\n");
@@ -871,7 +937,6 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
}
timeout--;
DELAY(1000);
- state = RD4(slot, SDHCI_PRESENT_STATE);
}
/* Prepare command flags. */
@@ -1309,7 +1374,7 @@ sdhci_acmd_irq(struct sdhci_slot *slot)
void
sdhci_generic_intr(struct sdhci_slot *slot)
{
- uint32_t intmask;
+ uint32_t intmask, present;
SDHCI_LOCK(slot);
/* Read slot interrupt status. */
@@ -1323,22 +1388,16 @@ sdhci_generic_intr(struct sdhci_slot *slot)
/* Handle card presence interrupts. */
if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
+ present = (intmask & SDHCI_INT_CARD_INSERT) != 0;
+ slot->intmask &=
+ ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
+ slot->intmask |= present ? SDHCI_INT_CARD_REMOVE :
+ SDHCI_INT_CARD_INSERT;
+ WR4(slot, SDHCI_INT_ENABLE, slot->intmask);
+ WR4(slot, SDHCI_SIGNAL_ENABLE, slot->intmask);
WR4(slot, SDHCI_INT_STATUS, intmask &
(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE));
-
- if (intmask & SDHCI_INT_CARD_REMOVE) {
- if (bootverbose || sdhci_debug)
- slot_printf(slot, "Card removed\n");
- callout_stop(&slot->card_callout);
- taskqueue_enqueue(taskqueue_swi_giant,
- &slot->card_task);
- }
- if (intmask & SDHCI_INT_CARD_INSERT) {
- if (bootverbose || sdhci_debug)
- slot_printf(slot, "Card inserted\n");
- callout_reset(&slot->card_callout, hz / 2,
- sdhci_card_delay, slot);
- }
+ sdhci_handle_card_present_locked(slot, present);
intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
}
/* Handle command interrupts. */
diff --git a/sys/dev/sdhci/sdhci.h b/sys/dev/sdhci/sdhci.h
index 2638b4f..3f2f73a 100644
--- a/sys/dev/sdhci/sdhci.h
+++ b/sys/dev/sdhci/sdhci.h
@@ -65,6 +65,8 @@
#define SDHCI_QUIRK_DONT_SET_HISPD_BIT (1<<15)
/* Alternate clock source is required when supplying a 400 KHz clock. */
#define SDHCI_QUIRK_BCM577XX_400KHZ_CLKSRC (1<<16)
+/* Card insert/remove interrupts don't work, polling required. */
+#define SDHCI_QUIRK_POLL_CARD_PRESENT (1<<17)
/*
* Controller registers
@@ -273,8 +275,9 @@ struct sdhci_slot {
device_t dev; /* Slot device */
u_char num; /* Slot number */
u_char opt; /* Slot options */
-#define SDHCI_HAVE_DMA 1
-#define SDHCI_PLATFORM_TRANSFER 2
+#define SDHCI_HAVE_DMA 0x01
+#define SDHCI_PLATFORM_TRANSFER 0x02
+#define SDHCI_NON_REMOVABLE 0x04
u_char version;
int timeout; /* Transfer timeout */
uint32_t max_clk; /* Max possible freq */
@@ -284,7 +287,9 @@ struct sdhci_slot {
u_char *dmamem;
bus_addr_t paddr; /* DMA buffer address */
struct task card_task; /* Card presence check task */
- struct callout card_callout; /* Card insert delay callout */
+ struct timeout_task
+ card_delayed_task;/* Card insert delayed task */
+ struct callout card_poll_callout;/* Card present polling callout */
struct callout timeout_callout;/* Card command/data response timeout */
struct mmc_host host; /* Host parameters */
struct mmc_request *req; /* Current request */
@@ -322,5 +327,7 @@ int sdhci_generic_acquire_host(device_t brdev, device_t reqdev);
int sdhci_generic_release_host(device_t brdev, device_t reqdev);
void sdhci_generic_intr(struct sdhci_slot *slot);
uint32_t sdhci_generic_min_freq(device_t brdev, struct sdhci_slot *slot);
+bool sdhci_generic_get_card_present(device_t brdev, struct sdhci_slot *slot);
+void sdhci_handle_card_present(struct sdhci_slot *slot, bool is_present);
#endif /* __SDHCI_H__ */
diff --git a/sys/dev/sdhci/sdhci_if.m b/sys/dev/sdhci/sdhci_if.m
index b33cdcf..da02d31 100644
--- a/sys/dev/sdhci/sdhci_if.m
+++ b/sys/dev/sdhci/sdhci_if.m
@@ -152,3 +152,9 @@ METHOD uint32_t min_freq {
device_t brdev;
struct sdhci_slot *slot;
} DEFAULT sdhci_generic_min_freq;
+
+METHOD bool get_card_present {
+ device_t brdev;
+ struct sdhci_slot *slot;
+} DEFAULT sdhci_generic_get_card_present;
+
OpenPOWER on IntegriCloud