Skip to content

Commit dff460c

Browse files
author
Phil Elwell
committed
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 <[email protected]>
1 parent a2fb5ae commit dff460c

File tree

2 files changed

+87
-49
lines changed

2 files changed

+87
-49
lines changed

drivers/mmc/host/bcm2835-sdhost.c

Lines changed: 86 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include <linux/of_dma.h>
5151
#include <linux/time.h>
5252
#include <linux/workqueue.h>
53+
#include <soc/bcm2835/raspberrypi-firmware.h>
5354

5455
#define DRIVER_NAME "sdhost-bcm2835"
5556

@@ -190,6 +191,8 @@ struct bcm2835_host {
190191
unsigned int use_sbc:1; /* Send CMD23 */
191192

192193
unsigned int debug:1; /* Enable debug output */
194+
unsigned int firmware_sets_cdiv:1; /* Let the firmware manage the clock */
195+
unsigned int reset_clock:1; /* Reset the clock fore the next request */
193196

194197
/*DMA part*/
195198
struct dma_chan *dma_chan_rxtx; /* DMA channel for reads and writes */
@@ -437,7 +440,7 @@ static void bcm2835_sdhost_reset_internal(struct bcm2835_host *host)
437440
host->clock = 0;
438441
host->sectors = 0;
439442
bcm2835_sdhost_write(host, host->hcfg, SDHCFG);
440-
bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
443+
bcm2835_sdhost_write(host, SDCDIV_MAX_CDIV, SDCDIV);
441444
mmiowb();
442445
}
443446

@@ -1510,6 +1513,7 @@ void bcm2835_sdhost_set_clock(struct bcm2835_host *host, unsigned int clock)
15101513
{
15111514
int div = 0; /* Initialized for compiler warning */
15121515
unsigned int input_clock = clock;
1516+
unsigned long flags;
15131517

15141518
if (host->debug)
15151519
pr_info("%s: set_clock(%d)\n", mmc_hostname(host->mmc), clock);
@@ -1541,62 +1545,84 @@ void bcm2835_sdhost_set_clock(struct bcm2835_host *host, unsigned int clock)
15411545

15421546
host->mmc->actual_clock = 0;
15431547

1544-
if (clock < 100000) {
1545-
/* Can't stop the clock, but make it as slow as possible
1546-
* to show willing
1547-
*/
1548-
host->cdiv = SDCDIV_MAX_CDIV;
1549-
bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
1550-
return;
1551-
}
1548+
if (host->firmware_sets_cdiv) {
1549+
u32 msg[3] = { clock, 0, 0 };
15521550

1553-
div = host->max_clk / clock;
1554-
if (div < 2)
1555-
div = 2;
1556-
if ((host->max_clk / div) > clock)
1557-
div++;
1558-
div -= 2;
1551+
rpi_firmware_property(rpi_firmware_get(NULL),
1552+
RPI_FIRMWARE_SET_SDHOST_CLOCK,
1553+
&msg, sizeof(msg));
15591554

1560-
if (div > SDCDIV_MAX_CDIV)
1561-
div = SDCDIV_MAX_CDIV;
1555+
clock = max(msg[1], msg[2]);
1556+
spin_lock_irqsave(&host->lock, flags);
1557+
} else {
1558+
spin_lock_irqsave(&host->lock, flags);
1559+
if (clock < 100000) {
1560+
/* Can't stop the clock, but make it as slow as
1561+
* possible to show willing
1562+
*/
1563+
host->cdiv = SDCDIV_MAX_CDIV;
1564+
bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
1565+
mmiowb();
1566+
spin_unlock_irqrestore(&host->lock, flags);
1567+
return;
1568+
}
15621569

1563-
clock = host->max_clk / (div + 2);
1564-
host->mmc->actual_clock = clock;
1570+
div = host->max_clk / clock;
1571+
if (div < 2)
1572+
div = 2;
1573+
if ((host->max_clk / div) > clock)
1574+
div++;
1575+
div -= 2;
1576+
1577+
if (div > SDCDIV_MAX_CDIV)
1578+
div = SDCDIV_MAX_CDIV;
1579+
1580+
clock = host->max_clk / (div + 2);
1581+
1582+
host->cdiv = div;
1583+
bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
1584+
1585+
if (host->debug)
1586+
pr_info("%s: clock=%d -> max_clk=%d, cdiv=%x "
1587+
"(actual clock %d)\n",
1588+
mmc_hostname(host->mmc), input_clock,
1589+
host->max_clk, host->cdiv,
1590+
clock);
1591+
}
15651592

15661593
/* Calibrate some delays */
15671594

15681595
host->ns_per_fifo_word = (1000000000/clock) *
15691596
((host->mmc->caps & MMC_CAP_4_BIT_DATA) ? 8 : 32);
15701597

1571-
if (clock > input_clock) {
1572-
/* Save the closest value, to make it easier
1573-
to reduce in the event of error */
1574-
host->overclock_50 = (clock/MHZ);
1598+
if (input_clock == 50 * MHZ) {
1599+
if (clock > input_clock) {
1600+
/* Save the closest value, to make it easier
1601+
to reduce in the event of error */
1602+
host->overclock_50 = (clock/MHZ);
15751603

1576-
if (clock != host->overclock) {
1577-
pr_warn("%s: overclocking to %dHz\n",
1578-
mmc_hostname(host->mmc), clock);
1579-
host->overclock = clock;
1604+
if (clock != host->overclock) {
1605+
pr_warn("%s: overclocking to %dHz\n",
1606+
mmc_hostname(host->mmc), clock);
1607+
host->overclock = clock;
1608+
}
1609+
} else if (host->overclock) {
1610+
host->overclock = 0;
1611+
if (clock == 50 * MHZ)
1612+
pr_warn("%s: cancelling overclock\n",
1613+
mmc_hostname(host->mmc));
15801614
}
15811615
}
1582-
else if (host->overclock)
1583-
{
1584-
host->overclock = 0;
1585-
if (clock == 50 * MHZ)
1586-
pr_warn("%s: cancelling overclock\n",
1587-
mmc_hostname(host->mmc));
1588-
}
1589-
1590-
host->cdiv = div;
1591-
bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
15921616

15931617
/* Set the timeout to 500ms */
1594-
bcm2835_sdhost_write(host, host->mmc->actual_clock/2, SDTOUT);
1618+
bcm2835_sdhost_write(host, clock/2, SDTOUT);
15951619

1596-
if (host->debug)
1597-
pr_info("%s: clock=%d -> max_clk=%d, cdiv=%x (actual clock %d)\n",
1598-
mmc_hostname(host->mmc), input_clock,
1599-
host->max_clk, host->cdiv, host->mmc->actual_clock);
1620+
host->mmc->actual_clock = clock;
1621+
host->clock = input_clock;
1622+
host->reset_clock = 0;
1623+
1624+
mmiowb();
1625+
spin_unlock_irqrestore(&host->lock, flags);
16001626
}
16011627

16021628
static void bcm2835_sdhost_request(struct mmc_host *mmc, struct mmc_request *mrq)
@@ -1645,6 +1671,9 @@ static void bcm2835_sdhost_request(struct mmc_host *mmc, struct mmc_request *mrq
16451671
(mrq->data->blocks > host->pio_limit))
16461672
bcm2835_sdhost_prepare_dma(host, mrq->data);
16471673

1674+
if (host->reset_clock)
1675+
bcm2835_sdhost_set_clock(host, host->clock);
1676+
16481677
spin_lock_irqsave(&host->lock, flags);
16491678

16501679
WARN_ON(host->mrq != NULL);
@@ -1711,11 +1740,6 @@ static void bcm2835_sdhost_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
17111740

17121741
log_event("IOS<", ios->clock, 0);
17131742

1714-
if (!ios->clock || ios->clock != host->clock) {
1715-
bcm2835_sdhost_set_clock(host, ios->clock);
1716-
host->clock = ios->clock;
1717-
}
1718-
17191743
/* set bus width */
17201744
host->hcfg &= ~SDHCFG_WIDE_EXT_BUS;
17211745
if (ios->bus_width == MMC_BUS_WIDTH_4)
@@ -1731,6 +1755,9 @@ static void bcm2835_sdhost_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
17311755
mmiowb();
17321756

17331757
spin_unlock_irqrestore(&host->lock, flags);
1758+
1759+
if (!ios->clock || ios->clock != host->clock)
1760+
bcm2835_sdhost_set_clock(host, ios->clock);
17341761
}
17351762

17361763
static struct mmc_host_ops bcm2835_sdhost_ops = {
@@ -1802,7 +1829,7 @@ static void bcm2835_sdhost_tasklet_finish(unsigned long param)
18021829
host->overclock_50--;
18031830
pr_warn("%s: reducing overclock due to errors\n",
18041831
mmc_hostname(host->mmc));
1805-
bcm2835_sdhost_set_clock(host,50*MHZ);
1832+
host->reset_clock = 1;
18061833
mrq->cmd->error = -EILSEQ;
18071834
mrq->cmd->retries = 1;
18081835
}
@@ -1959,6 +1986,7 @@ static int bcm2835_sdhost_probe(struct platform_device *pdev)
19591986
struct resource *iomem;
19601987
struct bcm2835_host *host;
19611988
struct mmc_host *mmc;
1989+
u32 msg[3];
19621990
int ret;
19631991

19641992
pr_debug("bcm2835_sdhost_probe\n");
@@ -1970,7 +1998,6 @@ static int bcm2835_sdhost_probe(struct platform_device *pdev)
19701998
mmc->ops = &bcm2835_sdhost_ops;
19711999
host = mmc_priv(mmc);
19722000
host->mmc = mmc;
1973-
host->cmd_quick_poll_retries = 0;
19742001
host->pio_timeout = msecs_to_jiffies(500);
19752002
host->pio_limit = 1;
19762003
host->max_delay = 1; /* Warn if over 1ms */
@@ -2059,6 +2086,16 @@ static int bcm2835_sdhost_probe(struct platform_device *pdev)
20592086
else
20602087
mmc->caps |= MMC_CAP_4_BIT_DATA;
20612088

2089+
msg[0] = 0;
2090+
msg[1] = ~0;
2091+
msg[2] = ~0;
2092+
2093+
rpi_firmware_property(rpi_firmware_get(NULL),
2094+
RPI_FIRMWARE_SET_SDHOST_CLOCK,
2095+
&msg, sizeof(msg));
2096+
2097+
host->firmware_sets_cdiv = (msg[1] != ~0);
2098+
20622099
ret = bcm2835_sdhost_add_host(host);
20632100
if (ret)
20642101
goto err;

include/soc/bcm2835/raspberrypi-firmware.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ enum rpi_firmware_property_tag {
7676
RPI_FIRMWARE_SET_VOLTAGE = 0x00038003,
7777
RPI_FIRMWARE_SET_TURBO = 0x00038009,
7878
RPI_FIRMWARE_SET_CUSTOMER_OTP = 0x00038021,
79+
RPI_FIRMWARE_SET_SDHOST_CLOCK = 0x00038042,
7980

8081
/* Dispmanx TAGS */
8182
RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE = 0x00040001,

0 commit comments

Comments
 (0)