Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 15 Jan 2017 22:10:32 +0000 (UTC)
From:      Ian Lepore <ian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org
Subject:   svn commit: r312244 - stable/11/sys/dev/sdhci
Message-ID:  <201701152210.v0FMAWad084564@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ian
Date: Sun Jan 15 22:10:32 2017
New Revision: 312244
URL: https://svnweb.freebsd.org/changeset/base/312244

Log:
  MFC r308187, r311660, r311693, r311727, r311797:
  
  Toggle card insert/remove interrupt enable bits on events.
  
  Add a new sdhci interface method, get_card_present().
  
  Now that the PRESENT_STATE register is only used for the inhibit bits loop
  in this function, sdhci_start_command(), eliminate the state variable and
  restructure the loop to read the register just once at the top of the loop.
  
  Add support for non-removable media, and a quirk to use polling to detect
  card insert/remove events on controllers that don't implement the insert
  and remove interrupts.
  
  Add sdhci_handle_card_present_locked() that can be called from the interrupt
  handler which already holds the mutex, and have sdhci_handle_card_present()
  be just a tiny wrapper that does the locking for external callers.

Modified:
  stable/11/sys/dev/sdhci/sdhci.c
  stable/11/sys/dev/sdhci/sdhci.h
  stable/11/sys/dev/sdhci/sdhci_if.m
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sys/dev/sdhci/sdhci.c
==============================================================================
--- stable/11/sys/dev/sdhci/sdhci.c	Sun Jan 15 22:00:59 2017	(r312243)
+++ stable/11/sys/dev/sdhci/sdhci.c	Sun Jan 15 22:10:32 2017	(r312244)
@@ -73,6 +73,7 @@ static void sdhci_set_clock(struct sdhci
 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, uin
 	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 *sl
 	}
 }
 
-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 sdh
 	    "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 *sl
 	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, s
 		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 *s
 		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 *s
 	 *  (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 *s
 		}
 		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 *sl
 
 	/* 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. */

Modified: stable/11/sys/dev/sdhci/sdhci.h
==============================================================================
--- stable/11/sys/dev/sdhci/sdhci.h	Sun Jan 15 22:00:59 2017	(r312243)
+++ stable/11/sys/dev/sdhci/sdhci.h	Sun Jan 15 22:10:32 2017	(r312244)
@@ -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 
 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__ */

Modified: stable/11/sys/dev/sdhci/sdhci_if.m
==============================================================================
--- stable/11/sys/dev/sdhci/sdhci_if.m	Sun Jan 15 22:00:59 2017	(r312243)
+++ stable/11/sys/dev/sdhci/sdhci_if.m	Sun Jan 15 22:10:32 2017	(r312244)
@@ -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;
+



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201701152210.v0FMAWad084564>