Author: adrian
Date: Thu Oct 15 04:22:56 2015
New Revision: 289359
URL: https://svnweb.freebsd.org/changeset/base/289359

Log:
  Add support for the BCM57765 card reader.
  
  This patch adds support for the BCM57765[2] card reader function included in
  Broadcom's BCM57766 ethernet/sd3.0 controller. This controller is commonly
  found in laptops and Apple hardware (MBP, iMac, etc).
  
  The BCM57765 chipset is almost fully compatible with the SD3.0 spec, but
  does not support deriving a frequency below 781KHz from its default base
  clock via the standard SD3.0-configured 10-bit clock divisor.
  
  If such a divisor is set, card identification (which requires a 400KHz
  clock frequency) will time out[1].
  
  As a work-around, I've made use of an undocumented device-specific clock
  control register to switch the controller to a 63MHz clock source when
  targeting clock speeds below 781KHz; the clock source is likewise switched
  back to the 200MHz clock when targeting speeds greater than 781KHz.
  
  Additionally, this patch fixes a small sdhci_pci bug; the
  sdhci_pci_softc->quirks flag was not copied to the sdhci_slot, resulting in
  `quirk` behavior not being applied by sdhci.c.
  
  [1] A number of Linux/FreeBSD users have noted that bringing up the chipsets'
  associated ethernet interface will allow SD cards to enumerate (slowly).
  This is a controller implementation side-effect triggered by the ethernet
  driver's reading of the hardware statistics registers.
  
  [2] This may also fix card detection when using the BCM57785 chipset, but I
  don't have access to the BCM57785 chipset and can't verify.
  
  I actually snagged some BCM57785 hardware recently (2012 Retina MacBook Pro)
  and can confirm that this also fixes card enumeration with the BCM57785
  chipset; with the patch, I can boot off of the internal sdcard reader.
  
  PR:           kern/203385
  Submitted by: Landon Fuller <[email protected]>

Modified:
  head/sys/dev/sdhci/sdhci.c
  head/sys/dev/sdhci/sdhci.h
  head/sys/dev/sdhci/sdhci_pci.c

Modified: head/sys/dev/sdhci/sdhci.c
==============================================================================
--- head/sys/dev/sdhci/sdhci.c  Thu Oct 15 03:48:03 2015        (r289358)
+++ head/sys/dev/sdhci/sdhci.c  Thu Oct 15 04:22:56 2015        (r289359)
@@ -89,6 +89,19 @@ static void sdhci_card_task(void *, int)
 #define        SDHCI_200_MAX_DIVIDER   256
 #define        SDHCI_300_MAX_DIVIDER   2046
 
+/*
+ * Broadcom BCM577xx Controller Constants
+ */
+#define BCM577XX_DEFAULT_MAX_DIVIDER   256             /* Maximum divider 
supported by the default clock source. */
+#define BCM577XX_ALT_CLOCK_BASE                63000000        /* Alternative 
clock's base frequency. */
+
+#define BCM577XX_HOST_CONTROL          0x198
+#define BCM577XX_CTRL_CLKSEL_MASK      0xFFFFCFFF
+#define BCM577XX_CTRL_CLKSEL_SHIFT     12
+#define BCM577XX_CTRL_CLKSEL_DEFAULT   0x0
+#define BCM577XX_CTRL_CLKSEL_64MHZ     0x3
+
+
 static void
 sdhci_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
 {
@@ -228,6 +241,8 @@ sdhci_init(struct sdhci_slot *slot)
 static void
 sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock)
 {
+       uint32_t clk_base;
+       uint32_t clk_sel;
        uint32_t res;
        uint16_t clk;
        uint16_t div;
@@ -243,6 +258,22 @@ sdhci_set_clock(struct sdhci_slot *slot,
        /* If no clock requested - left it so. */
        if (clock == 0)
                return;
+       
+       /* Determine the clock base frequency */
+       clk_base = slot->max_clk;
+       if (slot->quirks & SDHCI_QUIRK_BCM577XX_400KHZ_CLKSRC) {
+               clk_sel = RD2(slot, BCM577XX_HOST_CONTROL) & 
BCM577XX_CTRL_CLKSEL_MASK;
+
+               /* Select clock source appropriate for the requested frequency. 
*/
+               if ((clk_base / BCM577XX_DEFAULT_MAX_DIVIDER) > clock) {
+                       clk_base = BCM577XX_ALT_CLOCK_BASE;
+                       clk_sel |= (BCM577XX_CTRL_CLKSEL_64MHZ << 
BCM577XX_CTRL_CLKSEL_SHIFT);
+               } else {
+                       clk_sel |= (BCM577XX_CTRL_CLKSEL_DEFAULT << 
BCM577XX_CTRL_CLKSEL_SHIFT);
+               }
+               
+               WR2(slot, BCM577XX_HOST_CONTROL, clk_sel);
+       }
 
        /* Recalculate timeout clock frequency based on the new sd clock. */
        if (slot->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)
@@ -250,7 +281,7 @@ sdhci_set_clock(struct sdhci_slot *slot,
 
        if (slot->version < SDHCI_SPEC_300) {
                /* Looking for highest freq <= clock. */
-               res = slot->max_clk;
+               res = clk_base;
                for (div = 1; div < SDHCI_200_MAX_DIVIDER; div <<= 1) {
                        if (res <= clock)
                                break;
@@ -261,11 +292,11 @@ sdhci_set_clock(struct sdhci_slot *slot,
        }
        else {
                /* Version 3.0 divisors are multiples of two up to 1023*2 */
-               if (clock >= slot->max_clk)
+               if (clock >= clk_base)
                        div = 0;
                else {
                        for (div = 2; div < SDHCI_300_MAX_DIVIDER; div += 2) { 
-                               if ((slot->max_clk / div) <= clock) 
+                               if ((clk_base / div) <= clock) 
                                        break;
                        }
                }
@@ -273,8 +304,8 @@ sdhci_set_clock(struct sdhci_slot *slot,
        }
 
        if (bootverbose || sdhci_debug)
-               slot_printf(slot, "Divider %d for freq %d (max %d)\n", 
-                       div, clock, slot->max_clk);
+               slot_printf(slot, "Divider %d for freq %d (base %d)\n", 
+                       div, clock, clk_base);
 
        /* Now we have got divider, set it. */
        clk = (div & SDHCI_DIVIDER_MASK) << SDHCI_DIVIDER_SHIFT;

Modified: head/sys/dev/sdhci/sdhci.h
==============================================================================
--- head/sys/dev/sdhci/sdhci.h  Thu Oct 15 03:48:03 2015        (r289358)
+++ head/sys/dev/sdhci/sdhci.h  Thu Oct 15 04:22:56 2015        (r289359)
@@ -63,6 +63,8 @@
 #define        SDHCI_QUIRK_WAITFOR_RESET_ASSERTED              (1<<14)
 /* Leave controller in standard mode when putting card in HS mode. */
 #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)
 
 /*
  * Controller registers

Modified: head/sys/dev/sdhci/sdhci_pci.c
==============================================================================
--- head/sys/dev/sdhci/sdhci_pci.c      Thu Oct 15 03:48:03 2015        
(r289358)
+++ head/sys/dev/sdhci/sdhci_pci.c      Thu Oct 15 04:22:56 2015        
(r289359)
@@ -105,6 +105,8 @@ static const struct sdhci_device {
        { 0x2381197B,   0xffff, "JMicron JMB38X SD",
            SDHCI_QUIRK_32BIT_DMA_SIZE |
            SDHCI_QUIRK_RESET_AFTER_REQUEST },
+       { 0x16bc14e4,   0xffff, "Broadcom BCM577xx SDXC/MMC Card Reader",
+           SDHCI_QUIRK_BCM577XX_400KHZ_CLKSRC },
        { 0,            0xffff, NULL,
            0 }
 };
@@ -334,6 +336,8 @@ sdhci_pci_attach(device_t dev)
                        device_printf(dev, "Can't allocate memory for slot 
%d\n", i);
                        continue;
                }
+               
+               slot->quirks = sc->quirks;
 
                if (sdhci_init_slot(dev, slot, i) != 0)
                        continue;
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to