dwmmc: Handle the card detect interrupt

The driver used to always add the mmc device as it's child even
it no card was detected. Add a function that will detect if the
card is present or not and that will attach/detach the mmc device.
The function is either call on attach (as we won't have the interrupt
fired) or from two taskqueues. The first taskqueue will directly call
the function when the sdcard was present and is now removed and the other
one will delay a bit the attach when we didn't had a card and now have one.
This is mostly based on comments from the sdhci driver where it describe
a situation when the CD pin is detected before the others pins are connected.

MFC after:	1 month
This commit is contained in:
Emmanuel Vadot 2019-12-11 18:50:23 +00:00
parent 1a37b8cdde
commit 87d4212b1d
6 changed files with 81 additions and 4 deletions

View File

@ -45,6 +45,8 @@ __FBSDID("$FreeBSD$");
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/rman.h>
#include <sys/queue.h>
#include <sys/taskqueue.h>
#include <dev/mmc/bridge.h>
#include <dev/mmc/mmcbrvar.h>
@ -127,6 +129,7 @@ static int dma_done(struct dwmmc_softc *, struct mmc_command *);
static int dma_stop(struct dwmmc_softc *);
static void pio_read(struct dwmmc_softc *, struct mmc_command *);
static void pio_write(struct dwmmc_softc *, struct mmc_command *);
static void dwmmc_handle_card_present(struct dwmmc_softc *sc, bool is_present);
static struct resource_spec dwmmc_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
@ -374,7 +377,8 @@ dwmmc_intr(void *arg)
sc->dto_rcvd = 1;
if (reg & SDMMC_INTMASK_CD) {
/* XXX: Handle card detect */
dwmmc_handle_card_present(sc,
READ4(sc, SDMMC_CDETECT) == 0 ? true : false);
}
}
@ -407,6 +411,56 @@ dwmmc_intr(void *arg)
DWMMC_UNLOCK(sc);
}
static void
dwmmc_handle_card_present(struct dwmmc_softc *sc, bool is_present)
{
bool was_present;
was_present = sc->child != NULL;
if (!was_present && is_present) {
taskqueue_enqueue_timeout(taskqueue_swi_giant,
&sc->card_delayed_task, -(hz / 2));
} else if (was_present && !is_present) {
taskqueue_enqueue(taskqueue_swi_giant, &sc->card_task);
}
}
static void
dwmmc_card_task(void *arg, int pending __unused)
{
struct dwmmc_softc *sc = arg;
DWMMC_LOCK(sc);
if (READ4(sc, SDMMC_CDETECT) == 0) {
if (sc->child == NULL) {
if (bootverbose)
device_printf(sc->dev, "Card inserted\n");
sc->child = device_add_child(sc->dev, "mmc", -1);
DWMMC_UNLOCK(sc);
if (sc->child) {
device_set_ivars(sc->child, sc);
(void)device_probe_and_attach(sc->child);
}
} else
DWMMC_UNLOCK(sc);
} else {
/* Card isn't present, detach if necessary */
if (sc->child != NULL) {
if (bootverbose)
device_printf(sc->dev, "Card removed\n");
DWMMC_UNLOCK(sc);
device_delete_child(sc->dev, sc->child);
sc->child = NULL;
} else
DWMMC_UNLOCK(sc);
}
}
static int
parse_fdt(struct dwmmc_softc *sc)
{
@ -677,8 +731,17 @@ dwmmc_attach(device_t dev)
sc->host.caps |= MMC_CAP_HSPEED;
sc->host.caps |= MMC_CAP_SIGNALING_330;
sc->child = device_add_child(dev, "mmc", -1);
return (bus_generic_attach(dev));
TASK_INIT(&sc->card_task, 0, dwmmc_card_task, sc);
TIMEOUT_TASK_INIT(taskqueue_swi_giant, &sc->card_delayed_task, 0,
dwmmc_card_task, sc);
/*
* Schedule a card detection as we won't get an interrupt
* if the card is inserted when we attach
*/
dwmmc_card_task(sc, 0);
return (0);
}
int
@ -693,7 +756,9 @@ dwmmc_detach(device_t dev)
if (ret != 0)
return (ret);
DWMMC_LOCK_DESTROY(sc);
taskqueue_drain(taskqueue_swi_giant, &sc->card_task);
taskqueue_drain_timeout(taskqueue_swi_giant, &sc->card_delayed_task);
if (sc->intr_cookie != NULL) {
ret = bus_teardown_intr(dev, sc->res[1], sc->intr_cookie);
if (ret != 0)
@ -707,6 +772,8 @@ dwmmc_detach(device_t dev)
return (ret);
}
DWMMC_LOCK_DESTROY(sc);
#ifdef EXT_RESOURCES
if (sc->hwreset != NULL && hwreset_deassert(sc->hwreset) != 0)
device_printf(sc->dev, "cannot deassert reset\n");

View File

@ -31,6 +31,8 @@ __FBSDID("$FreeBSD$");
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/module.h>
#include <sys/queue.h>
#include <sys/taskqueue.h>
#include <machine/bus.h>

View File

@ -32,6 +32,8 @@ __FBSDID("$FreeBSD$");
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/module.h>
#include <sys/queue.h>
#include <sys/taskqueue.h>
#include <machine/bus.h>

View File

@ -31,6 +31,8 @@ __FBSDID("$FreeBSD$");
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/module.h>
#include <sys/queue.h>
#include <sys/taskqueue.h>
#include <machine/bus.h>

View File

@ -31,6 +31,8 @@ __FBSDID("$FreeBSD$");
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/module.h>
#include <sys/queue.h>
#include <sys/taskqueue.h>
#include <machine/bus.h>

View File

@ -62,6 +62,8 @@ struct dwmmc_softc {
uint32_t pwren_inverted;
u_int desc_count;
device_t child;
struct task card_task; /* Card presence check task */
struct timeout_task card_delayed_task;/* Card insert delayed task */
int (*update_ios)(struct dwmmc_softc *sc, struct mmc_ios *ios);