]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
mmc: bcm2835-host: let firmware manage the clock divisor
authorVincent Fazio <vfazio@xes-inc.com>
Tue, 14 Sep 2021 18:19:19 +0000 (13:19 -0500)
committerPeter Robinson <pbrobinson@gmail.com>
Fri, 24 Mar 2023 14:43:20 +0000 (14:43 +0000)
Newer firmware can manage the SDCDIV clock divisor register, allowing
the divisor to scale with the core as necessary.

Leverage this ability if the firmware supports it.

Adapted from the following raspberrypi Linux kernel commit:

  bcm2835-sdhost: Firmware manages the clock divisor
  https://github.com/raspberrypi/linux/commit/08532d242d7702ae0add95096aa49c5e96e066e2

Signed-off-by: Vincent Fazio <vfazio@xes-inc.com>
Signed-off-by: Peter Robinson <pbrobinson@gmail.com>
arch/arm/mach-bcm283x/include/mach/mbox.h
arch/arm/mach-bcm283x/include/mach/msg.h
arch/arm/mach-bcm283x/msg.c
drivers/mmc/bcm2835_sdhost.c

index 7dcac583cc43c97d784f43435de4df53e8e554bd..490664f878feece68638f2aa64c9015f9cd58800 100644 (file)
@@ -252,6 +252,22 @@ struct bcm2835_mbox_tag_get_clock_rate {
        } body;
 };
 
+#define BCM2835_MBOX_TAG_SET_SDHOST_CLOCK      0x00038042
+
+struct bcm2835_mbox_tag_set_sdhost_clock {
+       struct bcm2835_mbox_tag_hdr tag_hdr;
+       union {
+               struct {
+                       u32 rate_hz;
+               } req;
+               struct {
+                       u32 rate_hz;
+                       u32 rate_1;
+                       u32 rate_2;
+               } resp;
+       } body;
+};
+
 #define BCM2835_MBOX_TAG_ALLOCATE_BUFFER       0x00040001
 
 struct bcm2835_mbox_tag_allocate_buffer {
index eb3da93cc2f1d9d6db49c5a03b41ab6c746e94f2..e54da86e3530d75a94e3358b8f7c6cb38f5cad33 100644 (file)
@@ -22,6 +22,16 @@ int bcm2835_power_on_module(u32 module);
  */
 int bcm2835_get_mmc_clock(u32 clock_id);
 
+/**
+ * bcm2835_set_sdhost_clock() - determine if firmware controls sdhost cdiv
+ *
+ * @rate_hz: Input clock frequency
+ * @rate_1: Returns a clock frequency
+ * @rate_2: Returns a clock frequency
+ * @return 0 of OK, -EIO on error
+ */
+int bcm2835_set_sdhost_clock(u32 rate_hz, u32 *rate_1, u32 *rate_2);
+
 /**
  * bcm2835_get_video_size() - get the current display size
  *
index 68b2773875ee8dd2863f39d4bc0bb64ba36e3f30..2188b38d84b4a098c96e449bdd96d45400d30f26 100644 (file)
@@ -21,6 +21,12 @@ struct msg_get_clock_rate {
        u32 end_tag;
 };
 
+struct msg_set_sdhost_clock {
+       struct bcm2835_mbox_hdr hdr;
+       struct bcm2835_mbox_tag_set_sdhost_clock set_sdhost_clock;
+       u32 end_tag;
+};
+
 struct msg_query {
        struct bcm2835_mbox_hdr hdr;
        struct bcm2835_mbox_tag_physical_w_h physical_w_h;
@@ -110,6 +116,28 @@ int bcm2835_get_mmc_clock(u32 clock_id)
        return clock_rate;
 }
 
+int bcm2835_set_sdhost_clock(u32 rate_hz, u32 *rate_1, u32 *rate_2)
+{
+       ALLOC_CACHE_ALIGN_BUFFER(struct msg_set_sdhost_clock, msg_sdhost_clk, 1);
+       int ret;
+
+       BCM2835_MBOX_INIT_HDR(msg_sdhost_clk);
+       BCM2835_MBOX_INIT_TAG(&msg_sdhost_clk->set_sdhost_clock, SET_SDHOST_CLOCK);
+
+       msg_sdhost_clk->set_sdhost_clock.body.req.rate_hz = rate_hz;
+
+       ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &msg_sdhost_clk->hdr);
+       if (ret) {
+               printf("bcm2835: Could not query sdhost clock rate\n");
+               return -EIO;
+       }
+
+       *rate_1 = msg_sdhost_clk->set_sdhost_clock.body.resp.rate_1;
+       *rate_2 = msg_sdhost_clk->set_sdhost_clock.body.resp.rate_2;
+
+       return 0;
+}
+
 int bcm2835_get_video_size(int *widthp, int *heightp)
 {
        ALLOC_CACHE_ALIGN_BUFFER(struct msg_query, msg_query, 1);
index 894dbdd6861277e50cb3f27213b920edd63ad566..5c23c03d10d750beacba23f0ed8a9939c65bb269 100644 (file)
@@ -181,6 +181,7 @@ struct bcm2835_host {
        struct udevice          *dev;
        struct mmc              *mmc;
        struct bcm2835_plat     *plat;
+       unsigned int            firmware_sets_cdiv:1;
 };
 
 static void bcm2835_dumpregs(struct bcm2835_host *host)
@@ -233,7 +234,7 @@ static void bcm2835_reset_internal(struct bcm2835_host *host)
        msleep(20);
        host->clock = 0;
        writel(host->hcfg, host->ioaddr + SDHCFG);
-       writel(host->cdiv, host->ioaddr + SDCDIV);
+       writel(SDCDIV_MAX_CDIV, host->ioaddr + SDCDIV);
 }
 
 static int bcm2835_wait_transfer_complete(struct bcm2835_host *host)
@@ -598,6 +599,7 @@ static int bcm2835_transmit(struct bcm2835_host *host)
 static void bcm2835_set_clock(struct bcm2835_host *host, unsigned int clock)
 {
        int div;
+       u32 clock_rate[2] = { 0 };
 
        /* The SDCDIV register has 11 bits, and holds (div - 2).  But
         * in data mode the max is 50MHz wihout a minimum, and only
@@ -620,26 +622,34 @@ static void bcm2835_set_clock(struct bcm2835_host *host, unsigned int clock)
         * clock divisor at all times.
         */
 
-       if (clock < 100000) {
-               /* Can't stop the clock, but make it as slow as possible
-                * to show willing
-                */
-               host->cdiv = SDCDIV_MAX_CDIV;
-               writel(host->cdiv, host->ioaddr + SDCDIV);
-               return;
-       }
+       if (host->firmware_sets_cdiv) {
+               bcm2835_set_sdhost_clock(clock, &clock_rate[0], &clock_rate[1]);
+               clock = max(clock_rate[0], clock_rate[1]);
+       } else {
+               if (clock < 100000) {
+                       /* Can't stop the clock, but make it as slow as possible
+                       * to show willing
+                       */
+                       host->cdiv = SDCDIV_MAX_CDIV;
+                       writel(host->cdiv, host->ioaddr + SDCDIV);
+                       return;
+               }
 
-       div = host->max_clk / clock;
-       if (div < 2)
-               div = 2;
-       if ((host->max_clk / div) > clock)
-               div++;
-       div -= 2;
+               div = host->max_clk / clock;
+               if (div < 2)
+                       div = 2;
+               if ((host->max_clk / div) > clock)
+                       div++;
+               div -= 2;
 
-       if (div > SDCDIV_MAX_CDIV)
-               div = SDCDIV_MAX_CDIV;
+               if (div > SDCDIV_MAX_CDIV)
+                       div = SDCDIV_MAX_CDIV;
+
+               clock = host->max_clk / (div + 2);
+               host->cdiv = div;
+               writel(host->cdiv, host->ioaddr + SDCDIV);
+       }
 
-       clock = host->max_clk / (div + 2);
        host->mmc->clock = clock;
 
        /* Calibrate some delays */
@@ -647,9 +657,6 @@ static void bcm2835_set_clock(struct bcm2835_host *host, unsigned int clock)
        host->ns_per_fifo_word = (1000000000 / clock) *
                ((host->mmc->card_caps & MMC_MODE_4BIT) ? 8 : 32);
 
-       host->cdiv = div;
-       writel(host->cdiv, host->ioaddr + SDCDIV);
-
        /* Set the timeout to 500ms */
        writel(host->mmc->clock / 2, host->ioaddr + SDTOUT);
 }
@@ -759,6 +766,7 @@ static int bcm2835_probe(struct udevice *dev)
        struct bcm2835_host *host = dev_get_priv(dev);
        struct mmc *mmc = mmc_get_mmc_dev(dev);
        struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+       u32 clock_rate[2] = { ~0 };
 
        host->dev = dev;
        host->mmc = mmc;
@@ -776,6 +784,9 @@ static int bcm2835_probe(struct udevice *dev)
 
        host->max_clk = bcm2835_get_mmc_clock(BCM2835_MBOX_CLOCK_ID_CORE);
 
+       bcm2835_set_sdhost_clock(0, &clock_rate[0], &clock_rate[1]);
+       host->firmware_sets_cdiv = (clock_rate[0] != ~0);
+
        bcm2835_add_host(host);
 
        dev_dbg(dev, "%s -> OK\n", __func__);