Received: by 2002:a25:8b12:0:0:0:0:0 with SMTP id i18csp562077ybl; Wed, 14 Aug 2019 02:24:40 -0700 (PDT) X-Google-Smtp-Source: APXvYqxXRPR96oPwBiHD9gdvpnd4xlNIayU2vzqHgaGUm0YxonR+NUfscs4BPQn889w9LtjBFoEX X-Received: by 2002:aa7:9a52:: with SMTP id x18mr16941813pfj.8.1565774679959; Wed, 14 Aug 2019 02:24:39 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1565774679; cv=none; d=google.com; s=arc-20160816; b=BYrhi0AyyPuRw/fZBvtIXpWFhSHFnjUc0ugzuu9MvWhDpgDQ8OU2n+n/WuPN2GAyJr Ykl8R0B5AMxDKUT1SnY7TImdMbi/oOrDw4zxui8CEvfmv4GITKqjfjuURpEsL3AAuxgj dX8ZIqSKj6YAlsmfd+BN2aZHOjaodrOSBwekdlGDJVcpKu6/m8YIisYpA4HVX65ioOYt CgXpQ/3pdAgIbdSqeAVr7fIIYf9CDWCYOpnyeqnwEHTRLwA8rk9VilkyQ+R9c1f71Ym5 XLKszpLqbMpwJuL1B45q/8wJxBscHnSqVmZ7I2BckV6qt/M6+YKp/9sy5oqoGhvc6tUb I75w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:user-agent:in-reply-to :content-transfer-encoding:content-disposition:mime-version :references:message-id:subject:cc:to:from:date; bh=rSzJoTzFmPVN9Wo/7cirViIz82Ut6kywkNnpDZ8yTdE=; b=PRp3bZOouHGHQuL2b88o6kvom+91beLP/9DRxWrUK7F1BCqkyZl8oRkDEiQGnJUib3 +Puq9tejhLbGc0U91pUR4Z/Hh3ewP3Ys60EmcxvFKrB+3Pz2IDNnfUym+wFPpFSLxoi2 rC1PgsUiVXwIDzsFuVIHfivTXMZ9G/+lBqwB/ItK7jHo31CFhdzlMrJQE5V70EPBozbw yK8L2/oOpO/nq7qA1Kulv2puK2Extf0VDoqdszneO1ziAZAzk0SLN1SsCCWuAsNWqtby FsuQGYUYEHzHjHVG73JHMwwewxvLa3db659X2ck9yyIOBFiXktdM+oke8WGoZlrN5Z+1 z7Tw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id j5si2572336pjn.27.2019.08.14.02.24.23; Wed, 14 Aug 2019 02:24:39 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726465AbfHNJXq (ORCPT + 99 others); Wed, 14 Aug 2019 05:23:46 -0400 Received: from metis.ext.pengutronix.de ([85.220.165.71]:53145 "EHLO metis.ext.pengutronix.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725800AbfHNJXq (ORCPT ); Wed, 14 Aug 2019 05:23:46 -0400 Received: from pty.hi.pengutronix.de ([2001:67c:670:100:1d::c5]) by metis.ext.pengutronix.de with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1hxpVM-0000pP-7X; Wed, 14 Aug 2019 11:23:40 +0200 Received: from ukl by pty.hi.pengutronix.de with local (Exim 4.89) (envelope-from ) id 1hxpVL-0004Xl-K7; Wed, 14 Aug 2019 11:23:39 +0200 Date: Wed, 14 Aug 2019 11:23:39 +0200 From: Uwe =?iso-8859-1?Q?Kleine-K=F6nig?= To: Baolin Wang Cc: Thierry Reding , Rob Herring , Mark Rutland , Orson Zhai , Chunyan Zhang , Vincent Guittot , linux-pwm@vger.kernel.org, DTML , LKML , kernel@pengutronix.de Subject: Re: [PATCH v2 2/2] pwm: sprd: Add Spreadtrum PWM support Message-ID: <20190814092339.73ybj5mycklvpnrq@pengutronix.de> References: <4f6e3110b4d7e0a2f7ab317bba98a933de12e5da.1565703607.git.baolin.wang@linaro.org> <20190813151612.v6x6e6kzxflkpu7b@pengutronix.de> MIME-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: User-Agent: NeoMutt/20170113 (1.7.2) X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::c5 X-SA-Exim-Mail-From: ukl@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-kernel@vger.kernel.org Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Hello Baolin, On Wed, Aug 14, 2019 at 04:42:28PM +0800, Baolin Wang wrote: > On Tue, 13 Aug 2019 at 23:16, Uwe Kleine-K?nig > wrote: > > On Tue, Aug 13, 2019 at 09:46:41PM +0800, Baolin Wang wrote: > > [...] > Not really, our hardware's method is, when you changed a new > configuration (MOD or duty is changed) , the hardware will wait for a > while to complete current period, then change to the new period. Can you describe that in more detail? This doesn't explain why MOD must be configured before DUTY. Is there another reason for that? > > > +static int sprd_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, > > > + struct pwm_state *state) > > > +{ > > > + struct sprd_pwm_chip *spc = > > > + container_of(chip, struct sprd_pwm_chip, chip); > > > + struct sprd_pwm_chn *chn = &spc->chn[pwm->hwpwm]; > > > + struct pwm_state cstate; > > > + int ret; > > > + > > > + pwm_get_state(pwm, &cstate); > > > > I don't like it when pwm drivers call pwm_get_state(). If ever > > pwm_get_state would take a lock, this would deadlock as the lock is > > probably already taken when your .apply() callback is running. Moreover > > the (expensive) calculations are not used appropriately. See below. > > I do not think so, see: > > static inline void pwm_get_state(const struct pwm_device *pwm, struct > pwm_state *state) > { > *state = pwm->state; > } OK, the PWM framework currently caches this for you. Still I would prefer if you didn't call PWM API functions in your lowlevel driver. There is (up to now) nothing bad that will happen if you do it anyhow, but when the PWM framework evolves it might (and I want to work on such an evolution). You must not call clk_get_rate() in a .set_rate() callback of a clock either. > > > + if (state->enabled) { > > > + if (!cstate.enabled) { > > > > To just know the value of cstate.enabled you only need to read the > > register with the ENABLE flag. That is cheaper than calling get_state. > > > > > + /* > > > + * The clocks to PWM channel has to be enabled first > > > + * before writing to the registers. > > > + */ > > > + ret = clk_bulk_prepare_enable(SPRD_PWM_NUM_CLKS, > > > + chn->clks); > > > + if (ret) { > > > + dev_err(spc->dev, > > > + "failed to enable pwm%u clocks\n", > > > + pwm->hwpwm); > > > + return ret; > > > + } > > > + } > > > + > > > + if (state->period != cstate.period || > > > + state->duty_cycle != cstate.duty_cycle) { > > > > This is a coarse check. If state->period and cstate.period only differ > > by one calling sprd_pwm_config(spc, pwm, state->duty_cycle, > > state->period) probably results in a noop. So you're doing an expensive > > division to get an unreliable check. It would be better to calculate the > > register values from the requested state and compare the register > > values. The costs are more or less the same than calling .get_state and > > the check is reliable. And you don't need to spend another division to > > calculate the new register values. > > Same as above comment. When taking the caching into account (which I wouldn't) the check is still incomplete. OK, you could argue avoiding the recalculation in 90% (to just say some number) of the cases where it is unnecessary is good. > > > > > + ret = sprd_pwm_config(spc, pwm, state->duty_cycle, > > > + state->period); > > > + if (ret) > > > + return ret; > > > + } > > > + > > > + sprd_pwm_write(spc, pwm->hwpwm, SPRD_PWM_ENABLE, 1); > > > + } else if (cstate.enabled) { > > > + sprd_pwm_write(spc, pwm->hwpwm, SPRD_PWM_ENABLE, 0); > > > + > > > + clk_bulk_disable_unprepare(SPRD_PWM_NUM_CLKS, chn->clks); > > > > Assuming writing SPRD_PWM_ENABLE = 0 to the hardware completes the > > currently running period and the write doesn't block that long: Does > > disabling the clocks interfere with completing the period? > > Writing SPRD_PWM_ENABLE = 0 will stop the PWM immediately, will not > waiting for completing the currently period. The currently active period is supposed to be completed. If you cannot implement this please point this out as limitation at the top of the driver. Honestly I think most pwm users won't mind and they should get the possibility to tell they prefer pwm_apply_state to return immediately even if this could result in a spike. But we're not there yet. (Actually there are three things a PWM consumer might want: a) stop immediately; b) complete the currently running period, then stop and return only when the period is completed; or c) complete the currently running period and then stop, return immediately if possible. Currently the expected semantic is b). > > > +static int sprd_pwm_remove(struct platform_device *pdev) > > > +{ > > > + struct sprd_pwm_chip *spc = platform_get_drvdata(pdev); > > > + int ret, i; > > > + > > > + ret = pwmchip_remove(&spc->chip); > > > + > > > + for (i = 0; i < spc->num_pwms; i++) { > > > + struct sprd_pwm_chn *chn = &spc->chn[i]; > > > + > > > + clk_bulk_disable_unprepare(SPRD_PWM_NUM_CLKS, chn->clks); > > > > If a PWM was still running you're effectively stopping it here, right? > > Are you sure you don't disable once more than you enabled? > > Yes, you are right. I should check current enable status of the PWM channel. > Thanks for your comments. I didn't recheck, but I think the right approach is to not fiddle with the clocks at all and rely on the PWM framework to not let someone call sprd_pwm_remove when a PWM is still in use. Best regards Uwe -- Pengutronix e.K. | Uwe Kleine-K?nig | Industrial Linux Solutions | http://www.pengutronix.de/ |