From patchwork Fri Feb 19 20:16:39 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wolfram Sang X-Patchwork-Id: 8363721 Return-Path: X-Original-To: patchwork-linux-mmc@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 27DCC9F727 for ; Fri, 19 Feb 2016 20:17:22 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 228352047D for ; Fri, 19 Feb 2016 20:17:21 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id EFE212047B for ; Fri, 19 Feb 2016 20:17:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2992679AbcBSURS (ORCPT ); Fri, 19 Feb 2016 15:17:18 -0500 Received: from sauhun.de ([89.238.76.85]:40793 "EHLO pokefinder.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2993498AbcBSURR (ORCPT ); Fri, 19 Feb 2016 15:17:17 -0500 Received: from p4fe2573c.dip0.t-ipconnect.de ([79.226.87.60]:49132 helo=localhost) by pokefinder.org with esmtpsa (TLS1.2:RSA_AES_128_CBC_SHA1:128) (Exim 4.80) (envelope-from ) id 1aWrUJ-0007i4-Tf; Fri, 19 Feb 2016 21:17:16 +0100 From: Wolfram Sang To: linux-renesas-soc@vger.kernel.org Cc: linux-mmc@vger.kernel.org, Ben Hutchings , Dirk Behme , Wolfram Sang Subject: [RFC 3/7] mmc: tmio, sh_mobile_sdhi: Add support for variable input clock frequency Date: Fri, 19 Feb 2016 21:16:39 +0100 Message-Id: <1455913003-6140-4-git-send-email-wsa@the-dreams.de> X-Mailer: git-send-email 2.7.0 In-Reply-To: <1455913003-6140-1-git-send-email-wsa@the-dreams.de> References: <1455913003-6140-1-git-send-email-wsa@the-dreams.de> Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Ben Hutchings 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 Signed-off-by: Wolfram Sang --- drivers/mmc/host/sh_mobile_sdhi.c | 56 +++++++++++++++++++++++++++++++++++++-- drivers/mmc/host/tmio_mmc.h | 2 ++ drivers/mmc/host/tmio_mmc_pio.c | 24 +++++++---------- 3 files changed, 66 insertions(+), 16 deletions(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 675562653fdbe6..4e2ac0e5e5d415 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -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); diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 68fd8d791358c1..b44b5890290622 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -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); diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index d1160156678ade..ae81b34f17a5a5 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -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);