mmc: tmio, sh_mobile_sdhi: Add support for variable input clock frequency
Currently tmio_mmc assumes that the input clock frequency is fixed and only its own clock divider can be changed. This is not true in the case of sh_mobile_sdhi; we can use the clock API to change it. In tmio_mmc: - Delegate setting of f_min from tmio to the clk_enable operation (if implemented), as it can be smaller than f_max / 512 - Add an optional clk_update operation called from tmio_mmc_set_clock() that updates the input clock frequency - Rename tmio_mmc_clk_update() to tmio_mmc_clk_enable(), to avoid confusion with the clk_update operation In sh_mobile_sdhi: - Make the setting of f_max conditional; it should be set through the max-frequency property in the device tree in future - Set f_min based on the input clock's minimum frequency - Implement the clk_update operation, selecting the best input clock frequency for the bus frequency that's wanted sh_mobile_sdhi_clk_update() is loosely based on Kuninori Morimoto's work in sh_mmcif. Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk> Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
parent
0ea28210c1
commit
2fb55956ce
|
@ -139,7 +139,20 @@ static int sh_mobile_sdhi_clk_enable(struct tmio_mmc_host *host)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mmc->f_max = clk_get_rate(priv->clk);
|
||||
/*
|
||||
* The clock driver may not know what maximum frequency
|
||||
* actually works, so it should be set with the max-frequency
|
||||
* property which will already have been read to f_max. If it
|
||||
* was missing, assume the current frequency is the maximum.
|
||||
*/
|
||||
if (!mmc->f_max)
|
||||
mmc->f_max = clk_get_rate(priv->clk);
|
||||
|
||||
/*
|
||||
* Minimum frequency is the minimum input clock frequency
|
||||
* divided by our maximum divider.
|
||||
*/
|
||||
mmc->f_min = max(clk_round_rate(priv->clk, 1) / 512, 1L);
|
||||
|
||||
/* enable 16bit data access on SDBUF as default */
|
||||
sh_mobile_sdhi_sdbuf_width(host, 16);
|
||||
|
@ -147,6 +160,44 @@ static int sh_mobile_sdhi_clk_enable(struct tmio_mmc_host *host)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int sh_mobile_sdhi_clk_update(struct tmio_mmc_host *host,
|
||||
unsigned int new_clock)
|
||||
{
|
||||
struct sh_mobile_sdhi *priv = host_to_priv(host);
|
||||
unsigned int freq, best_freq, diff_min, diff;
|
||||
int i;
|
||||
|
||||
diff_min = ~0;
|
||||
best_freq = 0;
|
||||
|
||||
/*
|
||||
* We want the bus clock to be as close as possible to, but no
|
||||
* greater than, new_clock. As we can divide by 1 << i for
|
||||
* any i in [0, 9] we want the input clock to be as close as
|
||||
* possible, but no greater than, new_clock << i.
|
||||
*/
|
||||
for (i = min(9, ilog2(UINT_MAX / new_clock)); i >= 0; i--) {
|
||||
freq = clk_round_rate(priv->clk, new_clock << i);
|
||||
if (freq > (new_clock << i)) {
|
||||
/* Too fast; look for a slightly slower option */
|
||||
freq = clk_round_rate(priv->clk,
|
||||
(new_clock << i) / 4 * 3);
|
||||
if (freq > (new_clock << i))
|
||||
continue;
|
||||
}
|
||||
|
||||
diff = new_clock - (freq >> i);
|
||||
if (diff <= diff_min) {
|
||||
best_freq = freq;
|
||||
diff_min = diff;
|
||||
}
|
||||
}
|
||||
|
||||
clk_set_rate(priv->clk, best_freq);
|
||||
|
||||
return best_freq;
|
||||
}
|
||||
|
||||
static void sh_mobile_sdhi_clk_disable(struct tmio_mmc_host *host)
|
||||
{
|
||||
struct sh_mobile_sdhi *priv = host_to_priv(host);
|
||||
|
@ -265,6 +316,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
|||
host->dma = dma_priv;
|
||||
host->write16_hook = sh_mobile_sdhi_write16_hook;
|
||||
host->clk_enable = sh_mobile_sdhi_clk_enable;
|
||||
host->clk_update = sh_mobile_sdhi_clk_update;
|
||||
host->clk_disable = sh_mobile_sdhi_clk_disable;
|
||||
host->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk;
|
||||
|
||||
|
@ -362,7 +414,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n",
|
||||
dev_info(&pdev->dev, "%s base at 0x%08lx max clock rate %u MHz\n",
|
||||
mmc_hostname(host->mmc), (unsigned long)
|
||||
(platform_get_resource(pdev, IORESOURCE_MEM, 0)->start),
|
||||
host->mmc->f_max / 1000000);
|
||||
|
|
|
@ -96,6 +96,8 @@ struct tmio_mmc_host {
|
|||
|
||||
int (*write16_hook)(struct tmio_mmc_host *host, int addr);
|
||||
int (*clk_enable)(struct tmio_mmc_host *host);
|
||||
unsigned int (*clk_update)(struct tmio_mmc_host *host,
|
||||
unsigned int new_clock);
|
||||
void (*clk_disable)(struct tmio_mmc_host *host);
|
||||
int (*multi_io_quirk)(struct mmc_card *card,
|
||||
unsigned int direction, int blk_size);
|
||||
|
|
|
@ -160,9 +160,12 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host,
|
|||
u32 clk = 0, clock;
|
||||
|
||||
if (new_clock) {
|
||||
for (clock = host->mmc->f_min, clk = 0x80000080;
|
||||
new_clock >= (clock << 1);
|
||||
clk >>= 1)
|
||||
if (host->clk_update)
|
||||
clock = host->clk_update(host, new_clock) / 512;
|
||||
else
|
||||
clock = host->mmc->f_min;
|
||||
|
||||
for (clk = 0x80000080; new_clock >= (clock << 1); clk >>= 1)
|
||||
clock <<= 1;
|
||||
|
||||
/* 1/1 clock is option */
|
||||
|
@ -837,19 +840,12 @@ fail:
|
|||
pm_runtime_put_autosuspend(mmc_dev(mmc));
|
||||
}
|
||||
|
||||
static int tmio_mmc_clk_update(struct tmio_mmc_host *host)
|
||||
static int tmio_mmc_clk_enable(struct tmio_mmc_host *host)
|
||||
{
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
int ret;
|
||||
|
||||
if (!host->clk_enable)
|
||||
return -ENOTSUPP;
|
||||
|
||||
ret = host->clk_enable(host);
|
||||
if (!ret)
|
||||
mmc->f_min = mmc->f_max / 512;
|
||||
|
||||
return ret;
|
||||
return host->clk_enable(host);
|
||||
}
|
||||
|
||||
static void tmio_mmc_power_on(struct tmio_mmc_host *host, unsigned short vdd)
|
||||
|
@ -1135,7 +1131,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
|
|||
mmc->caps & MMC_CAP_NONREMOVABLE ||
|
||||
mmc->slot.cd_irq >= 0);
|
||||
|
||||
if (tmio_mmc_clk_update(_host) < 0) {
|
||||
if (tmio_mmc_clk_enable(_host) < 0) {
|
||||
mmc->f_max = pdata->hclk;
|
||||
mmc->f_min = mmc->f_max / 512;
|
||||
}
|
||||
|
@ -1263,7 +1259,7 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
|
|||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
|
||||
tmio_mmc_reset(host);
|
||||
tmio_mmc_clk_update(host);
|
||||
tmio_mmc_clk_enable(host);
|
||||
|
||||
if (host->clk_cache) {
|
||||
tmio_mmc_set_clock(host, host->clk_cache);
|
||||
|
|
Loading…
Reference in New Issue