From patchwork Tue Nov 18 21:08:28 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandru M Stan X-Patchwork-Id: 5333331 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id DD31C9F2ED for ; Tue, 18 Nov 2014 21:11:32 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id BBE9720107 for ; Tue, 18 Nov 2014 21:11:31 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 8B12720127 for ; Tue, 18 Nov 2014 21:11:30 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1Xqq1k-0000M3-Cd; Tue, 18 Nov 2014 21:09:32 +0000 Received: from mail-ig0-x230.google.com ([2607:f8b0:4001:c05::230]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1Xqq1T-0000FZ-Ay for linux-arm-kernel@lists.infradead.org; Tue, 18 Nov 2014 21:09:18 +0000 Received: by mail-ig0-f176.google.com with SMTP id l13so1888814iga.9 for ; Tue, 18 Nov 2014 13:08:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=tofUBQRTcSyB55g7942ClRZGtGA99L0UZahwFIGia5o=; b=RGkacgAECJNtaAf0rj+qL8mYFcLCGqUCX55i4sLcX0o4TxEFUmY1RG81c0Xw4dwMUT nFlDEq+R0rVuVet8Xt5R56LaMGCvjnPv//8i20R7n77Ns5KmBssXLItEjYHuBFnCC9Yu LndVm4/nsxVTyYyP7556kSmv5p5T9lJMOKINg= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=tofUBQRTcSyB55g7942ClRZGtGA99L0UZahwFIGia5o=; b=Se0pPKNGZQF04gt/VuO9uGwyjwv4ZV7so1LIEerj+h8wlwhJDwfmuFR9l9uanTBCMh 0FO/V7KLIMU5dvbUtDkj3k+61+oGbpYjUGsDy3NKJqkNDW0eblL/HPNMaqgERSnwIZHS gFp5W/0iK90ihcTFerHzZqwmIKuU/P6KsGC4Aw+HjApoLFI4zxxmXMwJahzq89bxPZZZ 0qloxf5dRRWkrc8nyHFi4oxmICxfaR/gbpc1tlMAd8b4FT5i0EkEaTeE1WIuztBK4G7Y os9Vmc0PVRqc3S1g60BKbOe4o0wpWyRv1l56oZim9pBguwxCcTDT7tZHH/L9qw+3ihYo fbMA== X-Gm-Message-State: ALoCoQkc0KL8ss4vHVJfbFepEKEFQhvc5UAl3aqTgJFHmS0DEk8DBnzTTUhmCXQAMGt3B37yhHnu X-Received: by 10.50.134.9 with SMTP id pg9mr36395277igb.28.1416344934432; Tue, 18 Nov 2014 13:08:54 -0800 (PST) Received: from amstan.mtv.corp.google.com ([172.22.65.92]) by mx.google.com with ESMTPSA id y9sm177003igp.22.2014.11.18.13.08.53 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 18 Nov 2014 13:08:54 -0800 (PST) From: Alexandru M Stan To: Mike Turquette , Heiko Stuebner , Doug Anderson , addy ke Subject: [PATCH v3 2/2] clk: rockchip: Add support for the mmc clock phases using the framework Date: Tue, 18 Nov 2014 13:08:28 -0800 Message-Id: <1416344908-5975-3-git-send-email-amstan@chromium.org> X-Mailer: git-send-email 2.1.0.rc2.206.gedb03e5 In-Reply-To: <1416344908-5975-1-git-send-email-amstan@chromium.org> References: <1416344908-5975-1-git-send-email-amstan@chromium.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20141118_130915_513112_7FA3651A X-CRM114-Status: GOOD ( 21.80 ) X-Spam-Score: -0.8 (/) Cc: devicetree@vger.kernel.org, Alexandru M Stan , Kever Yang , linux-kernel@vger.kernel.org, linux-rockchip@lists.infradead.org, Sonny Rao , linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-2.5 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_LOW, T_DKIM_INVALID, T_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 This patch adds the 2 physical clocks for the mmc (drive and sample). They're mostly there for the phase properties, but they also show the true clock (by dividing by RK3288_MMC_CLKGEN_DIV). The drive and sample phases are generated by dividing an upstream parent clock by 2, this allows us to adjust the phase by 90 deg. There's also an option to have up to 255 delay elements (40-80 picoseconds long). This driver uses those elements (under the assumption that they're 60ps long) to generate a rough 45deg (which might be from 33deg to 66deg) setting. Suggested-by: Heiko Stuebner Signed-off-by: Alexandru M Stan --- Changes in v3: - renamed everything internal from phase to just mmc_clock or mmc - added RK3288_MMC_CLKGEN_DIV instead of the magic number - added new paragraph to commit message Changes in v2: - fixed my cc/to list - removed dangling #DEFINE DEBUG drivers/clk/rockchip/Makefile | 1 + drivers/clk/rockchip/clk-mmc-phase.c | 151 +++++++++++++++++++++++++++++++++++ drivers/clk/rockchip/clk-rk3288.c | 12 +++ drivers/clk/rockchip/clk.c | 8 ++ drivers/clk/rockchip/clk.h | 23 ++++++ 5 files changed, 195 insertions(+) create mode 100644 drivers/clk/rockchip/clk-mmc-phase.c diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index bd8514d..2714097 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -6,6 +6,7 @@ obj-y += clk-rockchip.o obj-y += clk.o obj-y += clk-pll.o obj-y += clk-cpu.o +obj-y += clk-mmc-phase.o obj-$(CONFIG_RESET_CONTROLLER) += softrst.o obj-y += clk-rk3188.o diff --git a/drivers/clk/rockchip/clk-mmc-phase.c b/drivers/clk/rockchip/clk-mmc-phase.c new file mode 100644 index 0000000..9da4fd6 --- /dev/null +++ b/drivers/clk/rockchip/clk-mmc-phase.c @@ -0,0 +1,151 @@ +/* + * Copyright 2014 Google, Inc + * Author: Alexandru M Stan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include "clk.h" + +struct rockchip_mmc_clock { + struct clk_hw hw; + void __iomem *reg; + int id; + int shift; +}; + +#define to_mmc_clock(_hw) container_of(_hw, struct rockchip_mmc_clock, hw) + +#define RK3288_MMC_CLKGEN_DIV 2 + +static unsigned long rockchip_mmc_recalc(struct clk_hw *hw, + unsigned long parent_rate) +{ + return parent_rate / RK3288_MMC_CLKGEN_DIV; +} + +#define ROCKCHIP_MMC_DELAY_SEL BIT(10) +#define ROCKCHIP_MMC_DEGREE_MASK 0x3 +#define ROCKCHIP_MMC_DELAYNUM_OFFSET 2 +#define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET) + +#define PSECS_PER_SEC 1000000000000LL + +/* + * Each fine delay is between 40ps-80ps. Assume each fine delay is 60ps to + * simplify calculations. So 45degs could be anywhere between 33deg and 66deg. + */ +#define DELAY_ELEMENT_PSEC 60 + +static int rockchip_mmc_get_phase(struct clk_hw *hw) +{ + struct rockchip_mmc_clock *mmc_clock = to_mmc_clock(hw); + unsigned long rate = clk_get_rate(hw->clk); + u32 raw_value; + u16 degrees; + u8 delay_num = 0; + + raw_value = readl(mmc_clock->reg) >> (mmc_clock->shift); + + degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90; + + if (raw_value & ROCKCHIP_MMC_DELAY_SEL) { + /* degrees/delaynum * 10000 */ + unsigned long factor = (DELAY_ELEMENT_PSEC / 10) * 36 * + (rate / 1000000); + + delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK); + delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET; + degrees += delay_num * factor / 10000; + } + + return degrees % 360; +} + +static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees) +{ + struct rockchip_mmc_clock *mmc_clock = to_mmc_clock(hw); + unsigned long rate = clk_get_rate(hw->clk); + u8 nineties, is_fortyfive; + u8 delay_num = 0; + u32 raw_value; + + degrees -= degrees % 45; + + nineties = degrees / 90; + is_fortyfive = (degrees % 90) == 45; + + if (is_fortyfive) { + u64 delay; + + delay = PSECS_PER_SEC; + do_div(delay, rate); + do_div(delay, 360 / 45); + do_div(delay, DELAY_ELEMENT_PSEC); + + delay_num = (u8) min(delay, 255ULL); + } + + raw_value = ROCKCHIP_MMC_DELAY_SEL; + raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET; + raw_value |= nineties; + writel(HIWORD_UPDATE(raw_value, 0xffff, mmc_clock->shift), mmc_clock->reg); + + pr_debug("%s->set_phase(%d) delay_nums=%u reg[0x%p]=0x%x actual_degrees=%d\n", + __clk_get_name(hw->clk), degrees, delay_num, + mmc_clock->reg, raw_value>>(mmc_clock->shift), + rockchip_mmc_get_phase(hw) + ); + + return 0; +} + +static const struct clk_ops rockchip_mmc_clk_ops = { + .recalc_rate = rockchip_mmc_recalc, + .get_phase = rockchip_mmc_get_phase, + .set_phase = rockchip_mmc_set_phase, +}; + +struct clk *rockchip_clk_register_mmc(const char *name, + const char **parent_names, u8 num_parents, + void __iomem *reg, int shift) +{ + struct clk_init_data init; + struct rockchip_mmc_clock *mmc_clock; + struct clk *clk; + + mmc_clock = kmalloc(sizeof(*mmc_clock), GFP_KERNEL); + if (!mmc_clock) + return NULL; + + init.num_parents = num_parents; + init.parent_names = parent_names; + init.ops = &rockchip_mmc_clk_ops; + + mmc_clock->hw.init = &init; + mmc_clock->reg = reg; + mmc_clock->shift = shift; + + if (name) + init.name = name; + + clk = clk_register(NULL, &mmc_clock->hw); + if (IS_ERR(clk)) + goto err_free; + + return clk; + +err_free: + kfree(mmc_clock); + return NULL; +} diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c index 2327829..f415f84 100644 --- a/drivers/clk/rockchip/clk-rk3288.c +++ b/drivers/clk/rockchip/clk-rk3288.c @@ -483,6 +483,18 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { RK3288_CLKSEL_CON(12), 14, 2, MFLAGS, 8, 6, DFLAGS, RK3288_CLKGATE_CON(13), 3, GFLAGS), + MMC(SCLK_SDMMC_DRV, "sdmmc_drv", "sclk_sdmmc", RK3288_SDMMC_CON0, 1), + MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "sclk_sdmmc", RK3288_SDMMC_CON1, 0), + + MMC(SCLK_SDIO0_DRV, "sdio0_drv", "sclk_sdio0", RK3288_SDIO0_CON0, 1), + MMC(SCLK_SDIO0_SAMPLE, "sdio0_sample", "sclk_sdio0", RK3288_SDIO0_CON1, 0), + + MMC(SCLK_SDIO1_DRV, "sdio1_drv", "sclk_sdio1", RK3288_SDIO1_CON0, 1), + MMC(SCLK_SDIO1_SAMPLE, "sdio1_sample", "sclk_sdio1", RK3288_SDIO1_CON1, 0), + + MMC(SCLK_EMMC_DRV, "emmc_drv", "sclk_emmc", RK3288_EMMC_CON0, 1), + MMC(SCLK_EMMC_SAMPLE, "emmc_sample", "sclk_emmc", RK3288_EMMC_CON1, 0), + COMPOSITE(0, "sclk_tspout", mux_tspout_p, 0, RK3288_CLKSEL_CON(35), 14, 2, MFLAGS, 8, 5, DFLAGS, RK3288_CLKGATE_CON(4), 11, GFLAGS), diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index 1e68bff..ae143f8 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -279,6 +279,14 @@ void __init rockchip_clk_register_branches( list->gate_offset, list->gate_shift, list->gate_flags, flags, &clk_lock); break; + case branch_mmc: + clk = rockchip_clk_register_mmc( + list->name, + list->parent_names, list->num_parents, + reg_base + list->muxdiv_offset, + list->div_shift + ); + break; } /* none of the cases above matched */ diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index ca009ab..136e8a9 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -48,6 +48,14 @@ #define RK3288_GLB_SRST_SND 0x1b4 #define RK3288_SOFTRST_CON(x) (x * 0x4 + 0x1b8) #define RK3288_MISC_CON 0x1e8 +#define RK3288_SDMMC_CON0 0x200 +#define RK3288_SDMMC_CON1 0x204 +#define RK3288_SDIO0_CON0 0x208 +#define RK3288_SDIO0_CON1 0x20c +#define RK3288_SDIO1_CON0 0x210 +#define RK3288_SDIO1_CON1 0x214 +#define RK3288_EMMC_CON0 0x218 +#define RK3288_EMMC_CON1 0x21c enum rockchip_pll_type { pll_rk3066, @@ -152,6 +160,10 @@ struct clk *rockchip_clk_register_cpuclk(const char *name, const struct rockchip_cpuclk_rate_table *rates, int nrates, void __iomem *reg_base, spinlock_t *lock); +struct clk *rockchip_clk_register_mmc(const char *name, + const char **parent_names, u8 num_parents, + void __iomem *reg, int shift); + #define PNAME(x) static const char *x[] __initconst enum rockchip_clk_branch_type { @@ -160,6 +172,7 @@ enum rockchip_clk_branch_type { branch_divider, branch_fraction_divider, branch_gate, + branch_mmc, }; struct rockchip_clk_branch { @@ -352,6 +365,16 @@ struct rockchip_clk_branch { .gate_flags = gf, \ } +#define MMC(_id, cname, pname, offset, shift) \ + { \ + .id = _id, \ + .branch_type = branch_mmc, \ + .name = cname, \ + .parent_names = (const char *[]){ pname }, \ + .num_parents = 1, \ + .muxdiv_offset = offset, \ + .div_shift = shift, \ + } void rockchip_clk_init(struct device_node *np, void __iomem *base, unsigned long nr_clks);