From patchwork Thu Jul 20 03:44:44 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chen-Yu Tsai X-Patchwork-Id: 9853699 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 2CCAE60388 for ; Thu, 20 Jul 2017 03:47:11 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1A1B4286D6 for ; Thu, 20 Jul 2017 03:47:11 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 0EF5928750; Thu, 20 Jul 2017 03:47:11 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A7AA2286D6 for ; Thu, 20 Jul 2017 03:47:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933532AbdGTDpA (ORCPT ); Wed, 19 Jul 2017 23:45:00 -0400 Received: from mirror2.csie.ntu.edu.tw ([140.112.30.76]:59856 "EHLO wens.csie.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S933271AbdGTDo6 (ORCPT ); Wed, 19 Jul 2017 23:44:58 -0400 Received: by wens.csie.org (Postfix, from userid 1000) id 4BC635FFB2; Thu, 20 Jul 2017 11:44:53 +0800 (CST) From: Chen-Yu Tsai To: Maxime Ripard , Ulf Hansson , Michael Turquette , Stephen Boyd , Rob Herring , Mark Rutland Cc: Chen-Yu Tsai , linux-arm-kernel@lists.infradead.org, linux-mmc@vger.kernel.org, linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-sunxi@googlegroups.com Subject: [PATCH v2 02/10] clk: sunxi-ng: Add MP_MMC clocks that support MMC timing modes switching Date: Thu, 20 Jul 2017 11:44:44 +0800 Message-Id: <20170720034452.15920-3-wens@csie.org> X-Mailer: git-send-email 2.13.2 In-Reply-To: <20170720034452.15920-1-wens@csie.org> References: <20170720034452.15920-1-wens@csie.org> Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP All of our MMC clocks are of the MP clock type. A few MMC clocks on some SoCs, such as MMC2 on the A83T, support new/old timing mode switching. From a clock rate point of view, when the new timing mode is active. the output clock rate is halved. This patch adds a special wrapper class of clocks, MP_MMC, around the generic MP type clocks. The rate related callbacks in ccu_mp_mmc_ops for this class look at the timing mode bit and apply the /2 post-divider when needed, before passing it through to the generic class ops, ccu_mp_ops. Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu_mp.c | 80 +++++++++++++++++++++++++++++++++++++++++++ drivers/clk/sunxi-ng/ccu_mp.h | 24 +++++++++++++ 2 files changed, 104 insertions(+) diff --git a/drivers/clk/sunxi-ng/ccu_mp.c b/drivers/clk/sunxi-ng/ccu_mp.c index b917ad7a386c..688855e7dc8c 100644 --- a/drivers/clk/sunxi-ng/ccu_mp.c +++ b/drivers/clk/sunxi-ng/ccu_mp.c @@ -172,3 +172,83 @@ const struct clk_ops ccu_mp_ops = { .recalc_rate = ccu_mp_recalc_rate, .set_rate = ccu_mp_set_rate, }; + +/* + * Support for MMC timing mode switching + * + * The MMC clocks on some SoCs support switching between old and + * new timing modes. A platform specific API is provided to query + * and set the timing mode on supported SoCs. + * + * In addition, a special class of ccu_mp_ops is provided, which + * takes in to account the timing mode switch. When the new timing + * mode is active, the clock output rate is halved. This new class + * is a wrapper around the generic ccu_mp_ops. When clock rates + * are passed through to ccu_mp_ops callbacks, they are doubled + * if the new timing mode bit is set, to account for the post + * divider. Conversely, when clock rates are passed back, they + * are halved if the mode bit is set. + */ + +static unsigned long ccu_mp_mmc_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned long rate = ccu_mp_recalc_rate(hw, parent_rate); + struct ccu_common *cm = hw_to_ccu_common(hw); + u32 val = readl(cm->base + cm->reg); + + if (val & CCU_MMC_NEW_TIMING_MODE) + return rate / 2; + return rate; +} + +static int ccu_mp_mmc_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct ccu_common *cm = hw_to_ccu_common(hw); + u32 val = readl(cm->base + cm->reg); + int ret; + + /* adjust the requested clock rate */ + if (val & CCU_MMC_NEW_TIMING_MODE) { + req->rate *= 2; + req->min_rate *= 2; + req->max_rate *= 2; + } + + ret = ccu_mp_determine_rate(hw, req); + + /* re-adjust the requested clock rate back */ + if (val & CCU_MMC_NEW_TIMING_MODE) { + req->rate /= 2; + req->min_rate /= 2; + req->max_rate /= 2; + } + + return ret; +} + +static int ccu_mp_mmc_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct ccu_common *cm = hw_to_ccu_common(hw); + u32 val = readl(cm->base + cm->reg); + + if (val & CCU_MMC_NEW_TIMING_MODE) + rate *= 2; + + return ccu_mp_set_rate(hw, rate, parent_rate); +} + +const struct clk_ops ccu_mp_mmc_ops = { + .disable = ccu_mp_disable, + .enable = ccu_mp_enable, + .is_enabled = ccu_mp_is_enabled, + + .get_parent = ccu_mp_get_parent, + .set_parent = ccu_mp_set_parent, + + .determine_rate = ccu_mp_mmc_determine_rate, + .recalc_rate = ccu_mp_mmc_recalc_rate, + .set_rate = ccu_mp_mmc_set_rate, +}; diff --git a/drivers/clk/sunxi-ng/ccu_mp.h b/drivers/clk/sunxi-ng/ccu_mp.h index 915625e97d98..e5e90cec648e 100644 --- a/drivers/clk/sunxi-ng/ccu_mp.h +++ b/drivers/clk/sunxi-ng/ccu_mp.h @@ -74,4 +74,28 @@ static inline struct ccu_mp *hw_to_ccu_mp(struct clk_hw *hw) extern const struct clk_ops ccu_mp_ops; +/* Special class of M-P clock that supports MMC timing modes */ + +#define SUNXI_CCU_MP_MMC_WITH_MUX_GATE(_struct, _name, _parents, _reg, \ + _mshift, _mwidth, \ + _pshift, _pwidth, \ + _muxshift, _muxwidth, \ + _gate, _flags) \ + struct ccu_mp _struct = { \ + .enable = _gate, \ + .m = _SUNXI_CCU_DIV(_mshift, _mwidth), \ + .p = _SUNXI_CCU_DIV(_pshift, _pwidth), \ + .mux = _SUNXI_CCU_MUX(_muxshift, _muxwidth), \ + .common = { \ + .reg = _reg, \ + .features = CCU_FEATURE_MMC_TIMING_SWITCH, \ + .hw.init = CLK_HW_INIT_PARENTS(_name, \ + _parents, \ + &ccu_mp_mmc_ops, \ + _flags), \ + } \ + } + +extern const struct clk_ops ccu_mp_mmc_ops; + #endif /* _CCU_MP_H_ */