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;
+
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to