From patchwork Wed Aug 2 05:27:27 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Sean_Nyekj=C3=A6r?= X-Patchwork-Id: 9875949 X-Patchwork-Delegate: sboyd@codeaurora.org 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 DC77860360 for ; Wed, 2 Aug 2017 05:37:25 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C498628721 for ; Wed, 2 Aug 2017 05:37:25 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B5F2428778; Wed, 2 Aug 2017 05:37:25 +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.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=ham 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 D215328721 for ; Wed, 2 Aug 2017 05:37:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751811AbdHBFhY (ORCPT ); Wed, 2 Aug 2017 01:37:24 -0400 Received: from mail02.prevas.se ([62.95.78.10]:16402 "EHLO mail02.prevas.se" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751809AbdHBFhX (ORCPT ); Wed, 2 Aug 2017 01:37:23 -0400 X-Greylist: delayed 596 seconds by postgrey-1.27 at vger.kernel.org; Wed, 02 Aug 2017 01:37:22 EDT DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=prevas.dk; i=@prevas.dk; l=7926; q=dns/txt; s=ironport2; t=1501652243; x=1533188243; h=from:to:cc:subject:date:message-id:mime-version; bh=9lguFmKcOkfuiPZG6l43IQmoAIhQ8b5v17IK0opIJQs=; b=Wjby0I4HVVlovzfjiL7ps+In1ZvWjD7Amf6OAmGzQuD5dJdBbjRTNBO2 2hAenARGgf3ZACYA6gA1VGlzpXuRtwUGbiq2d8A0eB7bsHp/TZbn3Rtwl loShq20Kd+4UizAbwLGka8HKxnXc1D2TdO8qrYO7RWqZFguH1P2sIZr+x Y=; X-IronPort-AV: E=Sophos;i="5.41,309,1498514400"; d="scan'208";a="2342351" Received: from vmprevas4.prevas.se (HELO smtp.prevas.se) ([172.16.8.104]) by ironport2.prevas.se with ESMTP/TLS/DHE-RSA-AES256-SHA; 02 Aug 2017 07:27:24 +0200 Received: from skn.prevas.se (172.16.8.31) by smtp.prevas.se (172.16.8.104) with Microsoft SMTP Server (TLS) id 14.2.347.0; Wed, 2 Aug 2017 07:27:23 +0200 From: Sean Nyekjaer To: CC: Sean Nyekjaer , , Subject: [RFCv2] rtc: ds1307: add square wave output from ds1308 Date: Wed, 2 Aug 2017 07:27:27 +0200 Message-ID: <20170802052727.18289-1-sean.nyekjaer@prevas.dk> X-Mailer: git-send-email 2.13.3 MIME-Version: 1.0 X-Originating-IP: [172.16.8.31] Sender: linux-clk-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-clk@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add square wave output to generic clock framework Signed-off-by: Sean Nyekjaer --- This contains some code duplication(of my opinion) I guess it could be done more generic together with the ds3231. Changes from v1: - Removed explicit disabling of square wave output when running from battery. When the clock is disabled using the SQWE bit it's also disabled when running from battery. drivers/rtc/rtc-ds1307.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 165 insertions(+), 2 deletions(-) diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 1cedb21ba792..0c0484260411 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -79,6 +79,7 @@ enum ds_type { # define DS1307_BIT_OUT 0x80 # define DS1338_BIT_OSF 0x20 # define DS1307_BIT_SQWE 0x10 +# define DS1308_BIT_BBCLK 0x04 # define DS1307_BIT_RS1 0x02 # define DS1307_BIT_RS0 0x01 #define DS1337_REG_CONTROL 0x0e @@ -1056,6 +1057,13 @@ enum { #define clk_32khz_to_ds1307(clk) \ container_of(clk, struct ds1307, clks[DS3231_CLK_32KHZ]) +static int ds1308_clk_sqw_rates[] = { + 1, + 4096, + 8192, + 32768, +}; + static int ds3231_clk_sqw_rates[] = { 1, 1024, @@ -1063,6 +1071,19 @@ static int ds3231_clk_sqw_rates[] = { 8192, }; +static int ds1308_write_control(struct ds1307 *ds1307, u8 mask, u8 value) +{ + struct mutex *lock = &ds1307->rtc->ops_lock; + int ret; + + mutex_lock(lock); + ret = regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL, + mask, value); + mutex_unlock(lock); + + return ret; +} + static int ds1337_write_control(struct ds1307 *ds1307, u8 mask, u8 value) { struct mutex *lock = &ds1307->rtc->ops_lock; @@ -1076,6 +1097,24 @@ static int ds1337_write_control(struct ds1307 *ds1307, u8 mask, u8 value) return ret; } +static unsigned long ds1308_clk_sqw_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + int control, ret; + int rate_sel = 0; + + ret = regmap_read(ds1307->regmap, DS1307_REG_CONTROL, &control); + if (ret) + return ret; + if (control & DS1307_BIT_RS0) + rate_sel += 1; + if (control & DS1307_BIT_RS1) + rate_sel += 2; + + return ds1308_clk_sqw_rates[rate_sel]; +} + static unsigned long ds3231_clk_sqw_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -1094,6 +1133,19 @@ static unsigned long ds3231_clk_sqw_recalc_rate(struct clk_hw *hw, return ds3231_clk_sqw_rates[rate_sel]; } +static long ds1308_clk_sqw_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + int i; + + for (i = ARRAY_SIZE(ds1308_clk_sqw_rates) - 1; i >= 0; i--) { + if (ds1308_clk_sqw_rates[i] <= rate) + return ds1308_clk_sqw_rates[i]; + } + + return 0; +} + static long ds3231_clk_sqw_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { @@ -1107,6 +1159,31 @@ static long ds3231_clk_sqw_round_rate(struct clk_hw *hw, unsigned long rate, return 0; } +static int ds1308_clk_sqw_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + int control = 0; + int rate_sel; + + for (rate_sel = 0; rate_sel < ARRAY_SIZE(ds1308_clk_sqw_rates); + rate_sel++) { + if (ds1308_clk_sqw_rates[rate_sel] == rate) + break; + } + + if (rate_sel == ARRAY_SIZE(ds1308_clk_sqw_rates)) + return -EINVAL; + + if (rate_sel & 1) + control |= DS1307_BIT_RS0; + if (rate_sel & 2) + control |= DS1307_BIT_RS1; + + return ds1337_write_control(ds1307, DS1307_BIT_RS0 | DS1307_BIT_RS1, + control); +} + static int ds3231_clk_sqw_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -1132,6 +1209,13 @@ static int ds3231_clk_sqw_set_rate(struct clk_hw *hw, unsigned long rate, control); } +static int ds1308_clk_sqw_prepare(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + + return ds1308_write_control(ds1307, DS1307_BIT_SQWE, 1); +} + static int ds3231_clk_sqw_prepare(struct clk_hw *hw) { struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); @@ -1139,6 +1223,13 @@ static int ds3231_clk_sqw_prepare(struct clk_hw *hw) return ds1337_write_control(ds1307, DS1337_BIT_INTCN, 0); } +static void ds1308_clk_sqw_unprepare(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + + ds1308_write_control(ds1307, DS1307_BIT_SQWE, 0); +} + static void ds3231_clk_sqw_unprepare(struct clk_hw *hw) { struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); @@ -1146,6 +1237,18 @@ static void ds3231_clk_sqw_unprepare(struct clk_hw *hw) ds1337_write_control(ds1307, DS1337_BIT_INTCN, DS1337_BIT_INTCN); } +static int ds1308_clk_sqw_is_prepared(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + int control, ret; + + ret = regmap_read(ds1307->regmap, DS1307_REG_CONTROL, &control); + if (ret) + return ret; + + return control & DS1307_BIT_SQWE; +} + static int ds3231_clk_sqw_is_prepared(struct clk_hw *hw) { struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); @@ -1158,6 +1261,15 @@ static int ds3231_clk_sqw_is_prepared(struct clk_hw *hw) return !(control & DS1337_BIT_INTCN); } +static const struct clk_ops ds1308_clk_sqw_ops = { + .prepare = ds1308_clk_sqw_prepare, + .unprepare = ds1308_clk_sqw_unprepare, + .is_prepared = ds1308_clk_sqw_is_prepared, + .recalc_rate = ds1308_clk_sqw_recalc_rate, + .round_rate = ds1308_clk_sqw_round_rate, + .set_rate = ds1308_clk_sqw_set_rate, +}; + static const struct clk_ops ds3231_clk_sqw_ops = { .prepare = ds3231_clk_sqw_prepare, .unprepare = ds3231_clk_sqw_unprepare, @@ -1220,6 +1332,13 @@ static const struct clk_ops ds3231_clk_32khz_ops = { .recalc_rate = ds3231_clk_32khz_recalc_rate, }; +static struct clk_init_data ds1308_clks_init[] = { + { + .name = "ds1308_clk_sqw", + .ops = &ds1308_clk_sqw_ops, + }, +}; + static struct clk_init_data ds3231_clks_init[] = { [DS3231_CLK_SQW] = { .name = "ds3231_clk_sqw", @@ -1231,6 +1350,44 @@ static struct clk_init_data ds3231_clks_init[] = { }, }; +static int ds1308_clks_register(struct ds1307 *ds1307) +{ + struct device_node *node = ds1307->dev->of_node; + struct clk_onecell_data *onecell; + int i; + + onecell = devm_kzalloc(ds1307->dev, sizeof(*onecell), GFP_KERNEL); + if (!onecell) + return -ENOMEM; + + onecell->clk_num = ARRAY_SIZE(ds1308_clks_init); + onecell->clks = devm_kcalloc(ds1307->dev, onecell->clk_num, + sizeof(onecell->clks[0]), GFP_KERNEL); + if (!onecell->clks) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(ds1308_clks_init); i++) { + struct clk_init_data init = ds1308_clks_init[i]; + + /* optional override of the clockname */ + of_property_read_string_index(node, "clock-output-names", i, + &init.name); + ds1307->clks[i].init = &init; + + onecell->clks[i] = devm_clk_register(ds1307->dev, + &ds1307->clks[i]); + if (IS_ERR(onecell->clks[i])) + return PTR_ERR(onecell->clks[i]); + } + + if (!node) + return 0; + + of_clk_add_provider(node, of_clk_src_onecell_get, onecell); + + return 0; +} + static int ds3231_clks_register(struct ds1307 *ds1307) { struct device_node *node = ds1307->dev->of_node; @@ -1280,10 +1437,16 @@ static void ds1307_clks_register(struct ds1307 *ds1307) { int ret; - if (ds1307->type != ds_3231) + switch (ds1307->type) { + case ds_1308: + ret = ds1308_clks_register(ds1307); + case ds_3231: + ret = ds3231_clks_register(ds1307); + break; + default: return; + } - ret = ds3231_clks_register(ds1307); if (ret) { dev_warn(ds1307->dev, "unable to register clock device %d\n", ret);