From patchwork Fri Mar 1 15:01:27 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Mack X-Patchwork-Id: 2201921 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork1.kernel.org (Postfix) with ESMTP id 5CDEB3FCF6 for ; Fri, 1 Mar 2013 15:05:23 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1UBRSp-00049p-2G; Fri, 01 Mar 2013 15:01:35 +0000 Received: from mail-bk0-f48.google.com ([209.85.214.48]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1UBRSl-00048y-0f for linux-arm-kernel@lists.infradead.org; Fri, 01 Mar 2013 15:01:32 +0000 Received: by mail-bk0-f48.google.com with SMTP id jf20so1389460bkc.21 for ; Fri, 01 Mar 2013 07:01:28 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=x-received:message-id:date:from:user-agent:mime-version:to:cc :subject:references:in-reply-to:x-enigmail-version:content-type; bh=XKKvbAR+nUUIszpvktL87iSCmHjdBgY4pajmWX2zS4k=; b=xQLFkZ+gCzsoB/Oap9WK2TpHpv/gCM2/nRPdI4KOosWWdYiIXya8B0MdIV0fZuoLJD EDrquXrBiZnJF9z0D21WFPG4bjX+AW8oEsUAqSUMfUGHg0LJT9wcSL4zJ4b468t2WKhk 0eKDXR4x5REZNJezZKVePB7oVDHRvmSsXq5qxiIzo39Oh2VD4JbJtBfALDftPXFtcS6q N3CK5LREU/63fb4GukkzkemO9nZigr2u080P45L7kg38oOGai4AoTdtDDHFGej5R7vvf qFuV89WHztakH+tdxAtdx0Jzc0og/Tf1vX2mblxvswfioF0F8wdB+IVUWU0hcBDcU75u N+5Q== X-Received: by 10.204.149.87 with SMTP id s23mr4122675bkv.33.1362150088402; Fri, 01 Mar 2013 07:01:28 -0800 (PST) Received: from [192.168.44.145] (p5099deeb.dip0.t-ipconnect.de. [80.153.222.235]) by mx.google.com with ESMTPS id io13sm2975680bkc.15.2013.03.01.07.01.26 (version=TLSv1 cipher=RC4-SHA bits=128/128); Fri, 01 Mar 2013 07:01:27 -0800 (PST) Message-ID: <5130C2C7.30400@gmail.com> Date: Fri, 01 Mar 2013 16:01:27 +0100 From: Daniel Mack User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20130219 Thunderbird/17.0.3 MIME-Version: 1.0 To: Sebastian Hesselbarth Subject: Re: [PATCH] clk: add si5351 i2c common clock driver References: <1360414772-12232-1-git-send-email-sebastian.hesselbarth@gmail.com> <5123CF5B.9060804@gmail.com> In-Reply-To: X-Enigmail-Version: 1.5 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130301_100131_280122_118455C9 X-CRM114-Status: GOOD ( 41.88 ) X-Spam-Score: -2.7 (--) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-2.7 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [209.85.214.48 listed in list.dnswl.org] 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider (zonque[at]gmail.com) -0.0 SPF_PASS SPF: sender matches SPF record -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature Cc: Mike Turquette , Russell King - ARM Linux , linux-doc@vger.kernel.org, devicetree-discuss@lists.ozlabs.org, linux-kernel@vger.kernel.org, Rob Herring , Rabeeh Khoury , Dom Cobley , Stephen Warren , Andrew Morton , linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org Hi Sebastian, On 27.02.2013 11:01, Sebastian Hesselbarth wrote: > first of all sorry for the late answer but thanks for testing the driver. > > On 2/19/13, Daniel Mack wrote: >> Hi Sebastian, >> >> I did some more tests today and it took me a while to dig for the root >> cause why things were not working for me in the first place - see below. >> >> >> On 09.02.2013 13:59, Sebastian Hesselbarth wrote: >> >>> +==Example== >>> + >>> +/* 25MHz reference crystal */ >>> +ref25: ref25M { >>> + compatible = "fixed-clock"; >>> + #clock-cells = <0>; >>> + clock-frequency = <25000000>; >>> +}; >>> + >>> +i2c-master-node { >>> + >>> + /* Si5351a msop10 i2c clock generator */ >>> + si5351a: clock-generator@60 { >>> + compatible = "silabs,si5351a-msop"; >>> + reg = <0x60>; >>> + #address-cells = <1>; >>> + #size-cells = <0>; >>> + #clock-cells = <1>; >>> + >>> + /* connect xtal input to 25MHz reference */ >>> + clocks = <&ref25>; >> >> As referred to in another thread, registering the ref25M clock that way >> didn't suffice for me on my platform - but that's a different story. > > I guess "fixed-clock" isn't registered by OMAP's clock init code? I had > to do this on dove, too. Actually, I will come back to clock initialization for > dove later and was hoping that there will be some global way of registering > core common clock drivers (or at least fixed-clock) until then. > >>> +static void si5351_read_parameters(struct si5351_driver_data *drvdata, >>> + unsigned char reg, struct si5351_parameters *params) >>> +{ >>> + unsigned char buf[SI5351_PARAMETERS_LENGTH]; >> >> On a general note, I think you can use u8 instead of unsigned char all >> over the place here, which will save you some indentation trouble. > > Ok, I guess I was deriving "unsigned char" usage from other clock drivers > and never went to u8. But I ll reconsider using u8 when all issues are > worked out. > >>> +static inline int _si5351_clkout_reparent(struct si5351_driver_data >>> *drvdata, >>> + unsigned char num, unsigned char parent) >>> +{ >>> + struct clk *pclk; >>> + >>> + if (num > 8 || >>> + (drvdata->variant == SI5351_VARIANT_A3 && num > 3)) >>> + return -EINVAL; >>> + >>> + switch (parent) { >>> + case 0: >>> + pclk = drvdata->msynth[num].hw.clk; >>> + break; >>> + case 1: >>> + pclk = drvdata->msynth[0].hw.clk; >>> + if (num >= 4) >>> + pclk = drvdata->msynth[4].hw.clk; >>> + break; >>> + case 2: >>> + pclk = drvdata->xtal.clk; >>> + break; >>> + case 3: >>> + if (drvdata->variant != SI5351_VARIANT_C) >>> + return -EINVAL; >>> + pclk = drvdata->clkin.clk; >>> + break; >>> + default: >>> + return -EINVAL; >>> + } >>> + return clk_set_parent(drvdata->clkout[num].hw.clk, pclk); >>> +} >> >> [...] >> >>> +static int si5351_clkout_set_parent(struct clk_hw *hw, u8 index) >>> +{ >>> + struct si5351_hw_data *hwdata = >>> + container_of(hw, struct si5351_hw_data, hw); >>> + unsigned val; >>> + >>> + val = 0; >>> + hw->clk->flags &= ~CLK_SET_RATE_PARENT; >>> + switch (index) { >>> + case 0: >>> + hw->clk->flags |= CLK_SET_RATE_PARENT; >>> + val = SI5351_CLK_INPUT_MULTISYNTH_N; >>> + break; >> >> I fugured that _si5351_clkout_reparent() wouldn't actually call >> ->set_parent() on the clock, which leads to the fact that >> CLK_SET_RATE_PARENT is not set in the flags. That way, only the clkout >> end leaf is actually supplied with a new rate, which leads to incorrect >> effective clocks, depending on the current multisynth/pll configuration. > > Yeah, true. Unfortunately, _clkout_reparent() is more like a dirty hack to > allow to reparent the clock output. At registration internal configuration of > si5351 is not known and when I parse the DT for clock configuration I might > have been already assigned to the same parent clk that you later explicitly > configure. Hmm, but then we need to either set the configuration on the chip to match the internal state of the driver, or tell the clock framework that the current state is not currently known (don't know if that's possible). > What I basically want for si5351 (or any other eeprom based programmable > clock gen) is that stored configuration is not touched. But it can be changed > after eeprom contents have been copied into device's sram - and this _is_ > mandatory for the si5351 that I use on CuBox where there is no useful config > stored at all. Yes, same here. We set up the si5351 from the bootloader with some binary blob via i2c to bring some components to live before the kernel boots. > Anyway, there still seem to be some more issues with doing it right on current > common clk framwork - thanks for pointing it out. I'd be happy to test more versions if you have any. FWIW, attached is a patch for caching the 'prepared' states of the clocks. This fixes the 'scheduling while atomic' bug I described earlier in this thread. You can squash it into your patch if you want. Thanks, Daniel From 3e7a1a8e1f3e472e473e5ab47329bb44a9ec24ba Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sun, 17 Feb 2013 18:01:08 +0100 Subject: [PATCH] si5351: cache 'prepared' bits of clocks --- drivers/clk/clk-si5351.c | 47 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index b526742..528f88a 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -54,6 +54,7 @@ struct si5351_hw_data { struct si5351_driver_data *drvdata; struct si5351_parameters params; unsigned char num; + bool prepared; }; struct si5351_driver_data { @@ -70,6 +71,9 @@ struct si5351_driver_data { struct si5351_hw_data pll[2]; struct si5351_hw_data *msynth; struct si5351_hw_data *clkout; + + bool xtal_prepared; + bool clkin_prepared; }; static const char const *si5351_input_names[] = { @@ -223,6 +227,8 @@ static int si5351_xtal_prepare(struct clk_hw *hw) container_of(hw, struct si5351_driver_data, xtal); si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE, SI5351_XTAL_ENABLE, SI5351_XTAL_ENABLE); + drvdata->xtal_prepared = true; + return 0; } @@ -232,15 +238,14 @@ static void si5351_xtal_unprepare(struct clk_hw *hw) container_of(hw, struct si5351_driver_data, xtal); si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE, SI5351_XTAL_ENABLE, 0); + drvdata->xtal_prepared = false; } static int si5351_xtal_is_enabled(struct clk_hw *hw) { struct si5351_driver_data *drvdata = container_of(hw, struct si5351_driver_data, xtal); - unsigned char reg; - reg = si5351_reg_read(drvdata, SI5351_FANOUT_ENABLE); - return (reg & SI5351_XTAL_ENABLE) ? 1 : 0; + return drvdata->xtal_prepared; } static const struct clk_ops si5351_xtal_ops = { @@ -258,7 +263,8 @@ static int si5351_clkin_prepare(struct clk_hw *hw) container_of(hw, struct si5351_driver_data, clkin); si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE, SI5351_CLKIN_ENABLE, SI5351_CLKIN_ENABLE); - return 0; + drvdata->clkin_prepared = true; + return 1; } static void si5351_clkin_unprepare(struct clk_hw *hw) @@ -267,15 +273,14 @@ static void si5351_clkin_unprepare(struct clk_hw *hw) container_of(hw, struct si5351_driver_data, clkin); si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE, SI5351_CLKIN_ENABLE, 0); + drvdata->clkin_prepared = false; } static int si5351_clkin_is_enabled(struct clk_hw *hw) { struct si5351_driver_data *drvdata = container_of(hw, struct si5351_driver_data, clkin); - unsigned char reg; - reg = si5351_reg_read(drvdata, SI5351_FANOUT_ENABLE); - return (reg & SI5351_CLKIN_ENABLE) ? 1 : 0; + return drvdata->clkin_prepared; } /* @@ -660,6 +665,7 @@ static unsigned long si5351_msynth_recalc_rate(struct clk_hw *hw, m += hwdata->params.p2; m += 512 * hwdata->params.p3; } +printk(" XXXX m %d (%d)\n", m, hwdata->num); do_div(rate, m); dev_dbg(&hwdata->drvdata->client->dev, @@ -887,6 +893,8 @@ static int si5351_clkout_prepare(struct clk_hw *hw) SI5351_CLK_POWERDOWN, 0); si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL, (1 << hwdata->num), 0); + hwdata->prepared = true; + return 0; } @@ -899,21 +907,28 @@ static void si5351_clkout_unprepare(struct clk_hw *hw) SI5351_CLK_POWERDOWN, SI5351_CLK_POWERDOWN); si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL, (1 << hwdata->num), (1 << hwdata->num)); + hwdata->prepared = false; } -static int si5351_clkout_is_enabled(struct clk_hw *hw) +static void __si5351_read_clkout_prepared(struct si5351_hw_data *hwdata) { - struct si5351_hw_data *hwdata = - container_of(hw, struct si5351_hw_data, hw); unsigned char val; val = si5351_reg_read(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num); if (val & SI5351_CLK_POWERDOWN) - return 0; + hwdata->prepared = false; val = si5351_reg_read(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL); if (val & (1 << hwdata->num)) - return 0; - return 1; + hwdata->prepared = false; + hwdata->prepared = true; +} + +static int si5351_clkout_is_enabled(struct clk_hw *hw) +{ + struct si5351_hw_data *hwdata = + container_of(hw, struct si5351_hw_data, hw); + + return hwdata->prepared; } static u8 si5351_clkout_get_parent(struct clk_hw *hw) @@ -1306,6 +1321,11 @@ static int si5351_i2c_probe( return -EINVAL; } + /* read current clock enable bits */ + ret = si5351_reg_read(drvdata, SI5351_FANOUT_ENABLE); + drvdata->xtal_prepared = !!(ret & SI5351_XTAL_ENABLE); + drvdata->clkin_prepared = !!(ret & SI5351_CLKIN_ENABLE); + /* register PLLB or VXCO (Si5351B) */ drvdata->pll[1].num = 1; drvdata->pll[1].drvdata = drvdata; @@ -1394,6 +1414,7 @@ static int si5351_i2c_probe( return -EINVAL; } drvdata->onecell.clks[n] = clk; + __si5351_read_clkout_prepared(&drvdata->clkout[n]); } /* setup clock setup from DT */ -- 1.8.1.2