From patchwork Fri Jul 28 09:53:09 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Neil Armstrong X-Patchwork-Id: 9868273 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 282B560382 for ; Fri, 28 Jul 2017 09:54:51 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2E980288A8 for ; Fri, 28 Jul 2017 09:54:51 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 1D4E6288B9; Fri, 28 Jul 2017 09:54:51 +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=-1.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,RCVD_IN_DNSWL_NONE autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 7F7F3288A8 for ; Fri, 28 Jul 2017 09:54:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=ggQqEw78kz5YVB6WZfIJGPGkgF7toIYTamS2pmyCSgg=; b=hlEHKC7ZGb9QC+T/7pmgqgcM1X tv7XQDzAfg6l2BHQlV06Agq0P8esvFe6N71YIVChB8pNUxbqQ4Gek+Sh0z2D5E8lIQ9otK+4BYqML /E2VzxMZBoc+YzJrTIEY3was3j9p8T8aLd7p0TRr7kPJOK3vhLwH1rFndsgCPWJpLjO4OTBIYt509 ojtUlTNZ0L1bS5qH3/ZW/Co1i2JKyTkTjHwUAjNvYWxj4Gfm+cP3diPTxjnu9RnDtHkqP/I+uEiFJ HpM/aW9DbzozNwjxlvm7VoX591Ah4PEnGLeUht5Z57AygX6Ojnrs9m/x7evxxWt+AYF1mSPrVDzgf 2O31rAEw==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1db1yn-0000oj-Rg; Fri, 28 Jul 2017 09:54:45 +0000 Received: from mail-wm0-x230.google.com ([2a00:1450:400c:c09::230]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1db1xk-00081N-3k for linux-arm-kernel@lists.infradead.org; Fri, 28 Jul 2017 09:53:45 +0000 Received: by mail-wm0-x230.google.com with SMTP id m85so17274283wma.0 for ; Fri, 28 Jul 2017 02:53:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=YM6bLaRUJ1nOEKoycGH6jZXd8GHXcJX6byTEPbM7f3M=; b=Bnqvc73xOZGbd1LuVIkoBt/ZEqNcQCm6Z97zhFkWTvaBDV/1ljfcJAiA+gF2UqwOWj 3G+yVPESRfgHD5KTTuu5UAjRGIBgvbqFf8VeuClQtSh6WxqImvIC3J1MYXI6BdSgdwgp cpmrDje/dDlRHZooVnvENonXyWap+HBjR0F4G7ZCf3ncgElBG9e9jDh9BdyRkh9zH0ZP I2HpRgD/qKd3bsjjKnOt5GPXuvcE0iHD5XiIPNp9R2NNcvhugTsYC1rX9eYFm+MsZfds jCBSs7jo5L1fVYyuH9axubc4EVwHQhY7GngZRYarE0PWNGYzQgqE7VrQV5D8XFroGxah Tsgg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=YM6bLaRUJ1nOEKoycGH6jZXd8GHXcJX6byTEPbM7f3M=; b=c5dLtSWlRBif5sRxRiyUXLhI/WGCMEVkI0ICSdsYNrKbecfGYW3fhauYIasFHl9zgP pC3+4LSb6wWVFxbX+j5s6yqjQ2da9JkpGIeuzpN0bQUmN2A/yn7dUQGteUYRrFnAuyRd H0LOy+7gRuJxis1m3yk5rbV3s00jjpWUWdhhOzIqekA2i8CuSwYvO5Ivo33tSOCacwpc 6lFKOHid1J2MmZM1r79aQLxe1HuXM505nfJ2wnOwSWY1+8UnlTQnuluY8vW43Kd8BXv3 OA+CcO0LOfw1dFRHc54XQB/Rj4+tVaV1OXgzWVNv2EUuSd3V7FaCvvDGyMnkMnbEx4hs eFMA== X-Gm-Message-State: AIVw111CNZ4OzFe45K5hCH13714RlIyyKONrN3F/mbW6M4RnHD4B2P+I yVlGn5RtMmyqdfLHodRDsA== X-Received: by 10.28.22.207 with SMTP id 198mr4883425wmw.119.1501235601004; Fri, 28 Jul 2017 02:53:21 -0700 (PDT) Received: from localhost.localdomain ([90.63.244.31]) by smtp.gmail.com with ESMTPSA id o30sm9899694wrb.76.2017.07.28.02.53.20 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 28 Jul 2017 02:53:20 -0700 (PDT) From: Neil Armstrong To: jbrunet@baylibre.com, narmstrong@baylibre.com Subject: [PATCH v2 4/4] clk: meson: gxbb-aoclk: Add CEC 32k clock Date: Fri, 28 Jul 2017 11:53:09 +0200 Message-Id: <1501235589-318-5-git-send-email-narmstrong@baylibre.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1501235589-318-1-git-send-email-narmstrong@baylibre.com> References: <1501235589-318-1-git-send-email-narmstrong@baylibre.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170728_025340_454389_4DE193E0 X-CRM114-Status: GOOD ( 21.43 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-amlogic@lists.infradead.org, linux-clk@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP The CEC 32K AO Clock is a dual divider with dual counter to provide a more precise 32768Hz clock for the CEC subsystem from the external xtal. Signed-off-by: Neil Armstrong --- drivers/clk/meson/Makefile | 2 +- drivers/clk/meson/gxbb-aoclk-32k.c | 180 +++++++++++++++++++++++++++++++++++++ drivers/clk/meson/gxbb-aoclk.c | 21 ++++- drivers/clk/meson/gxbb-aoclk.h | 16 ++++ 4 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 drivers/clk/meson/gxbb-aoclk-32k.c diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile index de65427..b139d41 100644 --- a/drivers/clk/meson/Makefile +++ b/drivers/clk/meson/Makefile @@ -4,4 +4,4 @@ obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-cpu.o clk-mpll.o clk-audio-divider.o obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o -obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-regmap.o +obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-regmap.o gxbb-aoclk-32k.o diff --git a/drivers/clk/meson/gxbb-aoclk-32k.c b/drivers/clk/meson/gxbb-aoclk-32k.c new file mode 100644 index 0000000..497cd09 --- /dev/null +++ b/drivers/clk/meson/gxbb-aoclk-32k.c @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2017 BayLibre, SAS. + * Author: Neil Armstrong + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include "gxbb-aoclk.h" + +/* + * The AO Domain embeds a dual/divider to generate a more precise + * 32,768KHz clock for low-power suspend mode and CEC. + */ + +#define CLK_CNTL0_N1_MASK GENMASK(11, 0) +#define CLK_CNTL0_N2_MASK GENMASK(23, 12) +#define CLK_CNTL0_DUALDIV_EN BIT(28) +#define CLK_CNTL0_OUT_GATE_EN BIT(30) +#define CLK_CNTL0_IN_GATE_EN BIT(31) + +#define CLK_CNTL1_M1_MASK GENMASK(11, 0) +#define CLK_CNTL1_M2_MASK GENMASK(23, 12) +#define CLK_CNTL1_BYPASS_EN BIT(24) +#define CLK_CNTL1_SELECT_OSC BIT(27) + +#define PWR_CNTL_ALT_32K_SEL GENMASK(13, 10) + +struct cec_32k_freq_table { + unsigned long parent_rate; + unsigned long target_rate; + bool dualdiv; + unsigned int n1; + unsigned int n2; + unsigned int m1; + unsigned int m2; +}; + +static const struct cec_32k_freq_table aoclk_cec_32k_table[] = { + [0] = { + .parent_rate = 24000000, + .target_rate = 32768, + .dualdiv = true, + .n1 = 733, + .n2 = 732, + .m1 = 8, + .m2 = 11, + }, +}; + +/* + * If CLK_CNTL0_DUALDIV_EN == 0 + * - will use N1 divider only + * If CLK_CNTL0_DUALDIV_EN == 1 + * - hold M1 cycles of N1 divider then changes to N2 + * - hold M2 cycles of N2 divider then changes to N1 + * Then we can get more accurate division. + */ +static unsigned long aoclk_cec_32k_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw); + unsigned long n1; + u32 reg0, reg1; + + regmap_read(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, ®0); + regmap_read(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL1, ®1); + + if (reg1 & CLK_CNTL1_BYPASS_EN) + return parent_rate; + + if (reg0 & CLK_CNTL0_DUALDIV_EN) { + unsigned long n2, m1, m2, f1, f2, p1, p2; + + n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1; + n2 = FIELD_GET(CLK_CNTL0_N2_MASK, reg0) + 1; + + m1 = FIELD_GET(CLK_CNTL1_M1_MASK, reg1) + 1; + m2 = FIELD_GET(CLK_CNTL1_M2_MASK, reg1) + 1; + + f1 = DIV_ROUND_CLOSEST(parent_rate, n1); + f2 = DIV_ROUND_CLOSEST(parent_rate, n2); + + p1 = DIV_ROUND_CLOSEST(100000000 * m1, f1 * (m1 + m2)); + p2 = DIV_ROUND_CLOSEST(100000000 * m2, f2 * (m1 + m2)); + + return DIV_ROUND_UP(100000000, p1 + p2); + } + + n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1; + + return DIV_ROUND_CLOSEST(parent_rate, n1); +} + +static const struct cec_32k_freq_table *find_cec_32k_freq(unsigned long rate, + unsigned long prate) +{ + int i; + + for (i = 0 ; i < ARRAY_SIZE(aoclk_cec_32k_table) ; ++i) + if (aoclk_cec_32k_table[i].parent_rate == prate && + aoclk_cec_32k_table[i].target_rate == rate) + return &aoclk_cec_32k_table[i]; + + return NULL; +} + +static long aoclk_cec_32k_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate, + *prate); + + /* If invalid return first one */ + if (!freq) + return freq[0].target_rate; + + return freq->target_rate; +} + +static int aoclk_cec_32k_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate, + parent_rate); + struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw); + u32 reg = 0; + + if (!freq) + return -EINVAL; + + /* Disable clock */ + regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, + CLK_CNTL0_IN_GATE_EN | CLK_CNTL0_OUT_GATE_EN, 0); + + if (freq->dualdiv) + reg = CLK_CNTL0_DUALDIV_EN | + FIELD_PREP(CLK_CNTL0_N1_MASK, freq->n1 - 1) | + FIELD_PREP(CLK_CNTL0_N2_MASK, freq->n2 - 1); + else + reg = FIELD_PREP(CLK_CNTL0_N1_MASK, freq->n1 - 1); + + regmap_write(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, reg); + + if (freq->dualdiv) + reg = FIELD_PREP(CLK_CNTL1_M1_MASK, freq->m1 - 1) | + FIELD_PREP(CLK_CNTL1_M2_MASK, freq->m2 - 1); + else + reg = FIELD_PREP(CLK_CNTL1_M1_MASK, freq->m1 - 1); + + regmap_write(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL1, reg); + + /* Enable clock */ + regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, + CLK_CNTL0_IN_GATE_EN, CLK_CNTL0_IN_GATE_EN); + + udelay(200); + + regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, + CLK_CNTL0_OUT_GATE_EN, CLK_CNTL0_OUT_GATE_EN); + + regmap_update_bits(cec_32k->regmap, AO_CRT_CLK_CNTL1, + CLK_CNTL1_SELECT_OSC, CLK_CNTL1_SELECT_OSC); + + /* Select 32k from XTAL */ + regmap_update_bits(cec_32k->regmap, + AO_RTI_PWR_CNTL_REG0, + PWR_CNTL_ALT_32K_SEL, + FIELD_PREP(PWR_CNTL_ALT_32K_SEL, 4)); + + return 0; +} + +const struct clk_ops meson_aoclk_cec_32k_ops = { + .recalc_rate = aoclk_cec_32k_recalc_rate, + .round_rate = aoclk_cec_32k_round_rate, + .set_rate = aoclk_cec_32k_set_rate, +}; diff --git a/drivers/clk/meson/gxbb-aoclk.c b/drivers/clk/meson/gxbb-aoclk.c index f61506c..6c161e0 100644 --- a/drivers/clk/meson/gxbb-aoclk.c +++ b/drivers/clk/meson/gxbb-aoclk.c @@ -59,6 +59,7 @@ #include #include #include +#include #include #include #include "gxbb-aoclk.h" @@ -105,6 +106,17 @@ static int gxbb_aoclk_do_reset(struct reset_controller_dev *rcdev, GXBB_AO_GATE(uart2, 5); GXBB_AO_GATE(ir_blaster, 6); +static struct aoclk_cec_32k cec_32k_ao = { + .lock = &gxbb_aoclk_lock, + .hw.init = &(struct clk_init_data) { + .name = "cec_32k_ao", + .ops = &meson_aoclk_cec_32k_ops, + .parent_names = (const char *[]){ "xtal" }, + .num_parents = 1, + .flags = CLK_IGNORE_UNUSED, + }, +}; + static unsigned int gxbb_aoclk_reset[] = { [RESET_AO_REMOTE] = 16, [RESET_AO_I2C_MASTER] = 18, @@ -131,8 +143,9 @@ static int gxbb_aoclk_do_reset(struct reset_controller_dev *rcdev, [CLKID_AO_UART1] = &uart1_ao.hw, [CLKID_AO_UART2] = &uart2_ao.hw, [CLKID_AO_IR_BLASTER] = &ir_blaster_ao.hw, + [CLKID_AO_CEC_32K] = &cec_32k_ao.hw, }, - .num = ARRAY_SIZE(gxbb_aoclk_gate), + .num = 7, }; static int gxbb_aoclkc_probe(struct platform_device *pdev) @@ -172,6 +185,12 @@ static int gxbb_aoclkc_probe(struct platform_device *pdev) return ret; } + /* Specific clocks */ + cec_32k_ao.regmap = regmap; + ret = devm_clk_hw_register(dev, &cec_32k_ao.hw); + if (ret) + return ret; + return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, &gxbb_aoclk_onecell_data); } diff --git a/drivers/clk/meson/gxbb-aoclk.h b/drivers/clk/meson/gxbb-aoclk.h index 2e26108..e8604c8 100644 --- a/drivers/clk/meson/gxbb-aoclk.h +++ b/drivers/clk/meson/gxbb-aoclk.h @@ -9,7 +9,13 @@ #define __GXBB_AOCLKC_H /* AO Configuration Clock registers offsets */ +#define AO_RTI_PWR_CNTL_REG1 0x0c +#define AO_RTI_PWR_CNTL_REG0 0x10 #define AO_RTI_GEN_CNTL_REG0 0x40 +#define AO_OSCIN_CNTL 0x58 +#define AO_CRT_CLK_CNTL1 0x68 +#define AO_RTC_ALT_CLK_CNTL0 0x94 +#define AO_RTC_ALT_CLK_CNTL1 0x98 struct aoclk_gate_regmap { struct clk_hw hw; @@ -23,4 +29,14 @@ struct aoclk_gate_regmap { extern const struct clk_ops meson_aoclk_gate_regmap_ops; +struct aoclk_cec_32k { + struct clk_hw hw; + struct regmap *regmap; + spinlock_t *lock; +}; + +#define to_aoclk_cec_32k(_hw) container_of(_hw, struct aoclk_cec_32k, hw) + +extern const struct clk_ops meson_aoclk_cec_32k_ops; + #endif /* __GXBB_AOCLKC_H */