123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- From 8851ac5dfa652434a8e031883314a8fd0226fecf Mon Sep 17 00:00:00 2001
- From: Phil Elwell <phil@raspberrypi.org>
- Date: Mon, 4 Apr 2016 16:03:18 +0100
- Subject: [PATCH] bcm2835-sdhost: Firmware manages the clock divisor
- The bcm2835-sdhost driver hands control of the CDIV clock divisor
- register to matching firmware, allowing it to adjust to a changing
- core clock. This removes the need to use the performance governor or
- to enable io_is_busy on the on-demand governor in order to get the
- best SD performance.
- N.B. As SD clocks must be an integer divisor of the core clock, it is
- possible that the SD clock for "turbo" mode can be different (even
- lower) than "normal" mode.
- Signed-off-by: Phil Elwell <phil@raspberrypi.org>
- ---
- drivers/mmc/host/bcm2835-sdhost.c | 120 ++++++++++++++++++-----------
- include/soc/bcm2835/raspberrypi-firmware.h | 1 +
- 2 files changed, 74 insertions(+), 47 deletions(-)
- --- a/drivers/mmc/host/bcm2835-sdhost.c
- +++ b/drivers/mmc/host/bcm2835-sdhost.c
- @@ -50,6 +50,7 @@
- #include <linux/of_dma.h>
- #include <linux/time.h>
- #include <linux/workqueue.h>
- +#include <soc/bcm2835/raspberrypi-firmware.h>
-
- #define DRIVER_NAME "sdhost-bcm2835"
-
- @@ -183,6 +184,7 @@ struct bcm2835_host {
- unsigned int use_sbc:1; /* Send CMD23 */
-
- unsigned int debug:1; /* Enable debug output */
- + unsigned int firmware_sets_cdiv:1; /* Let the firmware manage the clock */
-
- /*DMA part*/
- struct dma_chan *dma_chan_rxtx; /* DMA channel for reads and writes */
- @@ -430,7 +432,7 @@ static void bcm2835_sdhost_reset_interna
- host->clock = 0;
- host->sectors = 0;
- bcm2835_sdhost_write(host, host->hcfg, SDHCFG);
- - bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
- + bcm2835_sdhost_write(host, SDCDIV_MAX_CDIV, SDCDIV);
- mmiowb();
- }
-
- @@ -1534,62 +1536,75 @@ void bcm2835_sdhost_set_clock(struct bcm
-
- host->mmc->actual_clock = 0;
-
- - if (clock < 100000) {
- - /* Can't stop the clock, but make it as slow as possible
- - * to show willing
- - */
- - host->cdiv = SDCDIV_MAX_CDIV;
- - bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
- - return;
- - }
- -
- - div = host->max_clk / clock;
- - if (div < 2)
- - div = 2;
- - if ((host->max_clk / div) > clock)
- - div++;
- - div -= 2;
- + if (host->firmware_sets_cdiv) {
- + u32 msg[3] = { clock, 0, 0 };
-
- - if (div > SDCDIV_MAX_CDIV)
- - div = SDCDIV_MAX_CDIV;
- + rpi_firmware_property(rpi_firmware_get(NULL),
- + RPI_FIRMWARE_SET_SDHOST_CLOCK,
- + &msg, sizeof(msg));
-
- - clock = host->max_clk / (div + 2);
- - host->mmc->actual_clock = clock;
- + clock = max(msg[1], msg[2]);
- + } else {
- + if (clock < 100000) {
- + /* Can't stop the clock, but make it as slow as
- + * possible to show willing
- + */
- + host->cdiv = SDCDIV_MAX_CDIV;
- + bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
- + return;
- + }
- +
- + 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;
- +
- + clock = host->max_clk / (div + 2);
- +
- + host->cdiv = div;
- + bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
- +
- + if (host->debug)
- + pr_info("%s: clock=%d -> max_clk=%d, cdiv=%x "
- + "(actual clock %d)\n",
- + mmc_hostname(host->mmc), input_clock,
- + host->max_clk, host->cdiv,
- + clock);
- + }
-
- /* Calibrate some delays */
-
- host->ns_per_fifo_word = (1000000000/clock) *
- ((host->mmc->caps & MMC_CAP_4_BIT_DATA) ? 8 : 32);
-
- - if (clock > input_clock) {
- - /* Save the closest value, to make it easier
- - to reduce in the event of error */
- - host->overclock_50 = (clock/MHZ);
- -
- - if (clock != host->overclock) {
- - pr_warn("%s: overclocking to %dHz\n",
- - mmc_hostname(host->mmc), clock);
- - host->overclock = clock;
- + if (input_clock == 50 * MHZ) {
- + if (clock > input_clock) {
- + /* Save the closest value, to make it easier
- + to reduce in the event of error */
- + host->overclock_50 = (clock/MHZ);
- +
- + if (clock != host->overclock) {
- + pr_warn("%s: overclocking to %dHz\n",
- + mmc_hostname(host->mmc), clock);
- + host->overclock = clock;
- + }
- + } else if (host->overclock) {
- + host->overclock = 0;
- + if (clock == 50 * MHZ)
- + pr_warn("%s: cancelling overclock\n",
- + mmc_hostname(host->mmc));
- }
- }
- - else if (host->overclock)
- - {
- - host->overclock = 0;
- - if (clock == 50 * MHZ)
- - pr_warn("%s: cancelling overclock\n",
- - mmc_hostname(host->mmc));
- - }
- -
- - host->cdiv = div;
- - bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
-
- /* Set the timeout to 500ms */
- - bcm2835_sdhost_write(host, host->mmc->actual_clock/2, SDTOUT);
- + bcm2835_sdhost_write(host, clock/2, SDTOUT);
-
- - if (host->debug)
- - pr_info("%s: clock=%d -> max_clk=%d, cdiv=%x (actual clock %d)\n",
- - mmc_hostname(host->mmc), input_clock,
- - host->max_clk, host->cdiv, host->mmc->actual_clock);
- + host->mmc->actual_clock = clock;
- }
-
- static void bcm2835_sdhost_request(struct mmc_host *mmc, struct mmc_request *mrq)
- @@ -1704,11 +1719,6 @@ static void bcm2835_sdhost_set_ios(struc
-
- log_event("IOS<", ios->clock, 0);
-
- - if (!ios->clock || ios->clock != host->clock) {
- - bcm2835_sdhost_set_clock(host, ios->clock);
- - host->clock = ios->clock;
- - }
- -
- /* set bus width */
- host->hcfg &= ~SDHCFG_WIDE_EXT_BUS;
- if (ios->bus_width == MMC_BUS_WIDTH_4)
- @@ -1721,6 +1731,11 @@ static void bcm2835_sdhost_set_ios(struc
-
- bcm2835_sdhost_write(host, host->hcfg, SDHCFG);
-
- + if (!ios->clock || ios->clock != host->clock) {
- + bcm2835_sdhost_set_clock(host, ios->clock);
- + host->clock = ios->clock;
- + }
- +
- mmiowb();
-
- spin_unlock_irqrestore(&host->lock, flags);
- @@ -1953,6 +1968,7 @@ static int bcm2835_sdhost_probe(struct p
- struct bcm2835_host *host;
- struct mmc_host *mmc;
- const __be32 *addr;
- + u32 msg[3];
- int ret;
-
- pr_debug("bcm2835_sdhost_probe\n");
- @@ -2058,6 +2074,16 @@ static int bcm2835_sdhost_probe(struct p
- else
- mmc->caps |= MMC_CAP_4_BIT_DATA;
-
- + msg[0] = 0;
- + msg[1] = ~0;
- + msg[2] = ~0;
- +
- + rpi_firmware_property(rpi_firmware_get(NULL),
- + RPI_FIRMWARE_SET_SDHOST_CLOCK,
- + &msg, sizeof(msg));
- +
- + host->firmware_sets_cdiv = (msg[1] != ~0);
- +
- ret = bcm2835_sdhost_add_host(host);
- if (ret)
- goto err;
- --- a/include/soc/bcm2835/raspberrypi-firmware.h
- +++ b/include/soc/bcm2835/raspberrypi-firmware.h
- @@ -79,6 +79,7 @@ enum rpi_firmware_property_tag {
- RPI_FIRMWARE_SET_VOLTAGE = 0x00038003,
- RPI_FIRMWARE_SET_TURBO = 0x00038009,
- RPI_FIRMWARE_SET_CUSTOMER_OTP = 0x00038021,
- + RPI_FIRMWARE_SET_SDHOST_CLOCK = 0x00038042,
-
- /* Dispmanx TAGS */
- RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE = 0x00040001,
|