Received: by 2002:a05:6359:c8b:b0:c7:702f:21d4 with SMTP id go11csp2620079rwb; Mon, 19 Sep 2022 07:38:31 -0700 (PDT) X-Google-Smtp-Source: AMsMyM5LBNvd/DG4UbHFLqqBcv2hMSQyJ34XOqVJIKMRJmwXZF1uiYP/P3xT10JPru7hd/CZGXN7 X-Received: by 2002:a17:907:7f1c:b0:77d:248:c1c3 with SMTP id qf28-20020a1709077f1c00b0077d0248c1c3mr12276176ejc.416.1663598310761; Mon, 19 Sep 2022 07:38:30 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1663598310; cv=none; d=google.com; s=arc-20160816; b=lCW9cM+jPnu6mfgjF5FcyGSTuGur6JLeitHnMrqzfOT1Fhu0Ew0UmX746Uzq4aSyzs dNHPhIq2cdtt3uqcj05pGG2gyaNGojM6Sr4klDkBtJD4X0EOrngVt0s9+ubFtURr6utl lsxNOaCy2rTU0KarFumx8LuuP+ibY5nI43AhG1inDI6KRVr6NB26h6O9ueKZJCOvNpzj d3PnneXQj1QKKwk2MPfQVZv7SvSgYsV4LgfCgnsAeYfTDBxN0Fxyv5Hh9XaBZurGLINr RSISUp/02Fz4nRExSVjXGd++t2gPIhMEV1i0GVuI8wPGx/UHngGU/bUIsVRS3sFqRSqE syCQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:in-reply-to:content-transfer-encoding :content-disposition:mime-version:references:message-id:subject:cc :to:from:date:dkim-signature; bh=zyqU+Akx9ms9uSXPhSpkd1tBxGQ81FLJP4mc+m3Nc6s=; b=x8TgyP1G5CQXfQb9CUaO2R5MLeAI8fE3iVhQpWfmHNlGI+cC6Ztp8BAuMK6KKoMeQu kp+HzxKPae+qNAgj0gQBt8AAjROw/w6Njzn5v+Nkxc2+drqESsH4J4Po9JVkdHDvugBT b1jJXvXv8m44xhTisrycKL3kFe5u65Hi0oMOsk8ocITdCm0kkPyR6g7knxag4Bg1XpSP Zyu7FGhiv7uDIfXgLAD+dj68reZ3k5mE3hM8kYVrE02JFvmpHZwDkFAZUGYGTnPrDhm8 8D1IOPZWw134fCTWD0+fGCKWZn0DZpu+6fLb03Zc1xqMcS+OFDGbRInYo9LMOGBEgmd9 NUdA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@microchip.com header.s=mchp header.b=hSYN5OON; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=microchip.com Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id qw10-20020a1709066a0a00b00718d0985aa0si30630345ejc.247.2022.09.19.07.38.04; Mon, 19 Sep 2022 07:38:30 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@microchip.com header.s=mchp header.b=hSYN5OON; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=microchip.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229985AbiISO35 (ORCPT + 99 others); Mon, 19 Sep 2022 10:29:57 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55124 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229888AbiISO3o (ORCPT ); Mon, 19 Sep 2022 10:29:44 -0400 Received: from esa.microchip.iphmx.com (esa.microchip.iphmx.com [68.232.153.233]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 91761A44C; Mon, 19 Sep 2022 07:29:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=microchip.com; i=@microchip.com; q=dns/txt; s=mchp; t=1663597782; x=1695133782; h=date:from:to:cc:subject:message-id:references: mime-version:content-transfer-encoding:in-reply-to; bh=IsiSELyQZHXzhmHlKNVp/4AhfXD4NvYslEVAsWis7tk=; b=hSYN5OONx1NwFdMUuG1qZ5FbsNsWASEzmfBmfjW3WD7IZ0N0OiXmTcis /mwHd+dBTr4vc87jYiebps15WimfFBqIiXm+9TETlqoDuJR3kxdYm095Z YNeMs/F9uolO7U7xNUF89/cRP7w4icZbJGzavP1CqMrvZECmGDPjp1cny Nk5vhufAnE5zKZKeXegurla6Mue6x0rT2ss9dXZD1gIvnWgYn6LCxUwDV GQOjsV9DjXp7kdqOjuxd15PjHTfTlC8m1g5kKx1Q7yC72Ppwe+Ir1jHdu p5wb/qgiyJGuG22kABj6ljPEh1nAQKnYKP0Xydhnq0sW9mfHriglP9uUG A==; X-IronPort-AV: E=Sophos;i="5.93,328,1654585200"; d="scan'208";a="181099035" Received: from unknown (HELO email.microchip.com) ([170.129.1.10]) by esa3.microchip.iphmx.com with ESMTP/TLS/AES256-SHA256; 19 Sep 2022 07:29:41 -0700 Received: from chn-vm-ex03.mchp-main.com (10.10.85.151) by chn-vm-ex01.mchp-main.com (10.10.85.143) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.12; Mon, 19 Sep 2022 07:29:41 -0700 Received: from wendy (10.10.115.15) by chn-vm-ex03.mchp-main.com (10.10.85.151) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.12 via Frontend Transport; Mon, 19 Sep 2022 07:29:39 -0700 Date: Mon, 19 Sep 2022 15:29:19 +0100 From: Conor Dooley To: Uwe =?iso-8859-1?Q?Kleine-K=F6nig?= CC: Thierry Reding , Rob Herring , Krzysztof Kozlowski , Daire McNamara , , , , Subject: Re: [PATCH v10 3/4] pwm: add microchip soft ip corePWM driver Message-ID: References: <20220824091215.141577-1-conor.dooley@microchip.com> <20220824091215.141577-4-conor.dooley@microchip.com> <20220915072152.y346csakn7wetpz5@pengutronix.de> <20220919135008.sahwmwbfwvgplji4@pengutronix.de> MIME-Version: 1.0 Content-Type: text/plain; charset="iso-8859-1" Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <20220919135008.sahwmwbfwvgplji4@pengutronix.de> X-Spam-Status: No, score=-4.4 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_MED, SPF_HELO_PASS,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Hey Uwe, On Mon, Sep 19, 2022 at 03:50:08PM +0200, Uwe Kleine-K?nig wrote: > On Mon, Sep 19, 2022 at 01:53:56PM +0100, Conor Dooley wrote: > > Hey Uwe, > > Thanks (as always). I've switched up my email setup a bit so I hope > > that I've not mangled anything here. > > > > On Thu, Sep 15, 2022 at 09:21:52AM +0200, Uwe Kleine-K?nig wrote: > > > Hello, > > > > > > On Wed, Aug 24, 2022 at 10:12:14AM +0100, Conor Dooley wrote: > > > > Add a driver that supports the Microchip FPGA "soft" PWM IP core. > > > > > > > > Signed-off-by: Conor Dooley > > > > --- > > > > drivers/pwm/Kconfig | 10 + > > > > drivers/pwm/Makefile | 1 + > > > > drivers/pwm/pwm-microchip-core.c | 402 +++++++++++++++++++++++++++++++ > > > > 3 files changed, 413 insertions(+) > > > > create mode 100644 drivers/pwm/pwm-microchip-core.c > > > > > > > > > > +static int mchp_core_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, > > > > + const struct pwm_state *state) > > > > +{ > > > > + struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip); > > > > + struct pwm_state current_state = pwm->state; > > > > + bool period_locked; > > > > + u64 duty_steps; > > > > + u16 prescale; > > > > + u8 period_steps; > > > > + int ret; > > > > + > > > > + mutex_lock(&mchp_core_pwm->lock); > > > > + > > > > + if (!state->enabled) { > > > > + mchp_core_pwm_enable(chip, pwm, false, current_state.period); > > > > + mutex_unlock(&mchp_core_pwm->lock); > > > > + return 0; > > > > + } > > > > + > > > > + /* > > > > + * If the only thing that has changed is the duty cycle or the polarity, > > > > + * we can shortcut the calculations and just compute/apply the new duty > > > > + * cycle pos & neg edges > > > > + * As all the channels share the same period, do not allow it to be > > > > + * changed if any other channels are enabled. > > > > + * If the period is locked, it may not be possible to use a period > > > > + * less than that requested. In that case, we just abort. > > > > + */ > > > > + period_locked = mchp_core_pwm->channel_enabled & ~(1 << pwm->hwpwm); > > > > + > > > > + if (period_locked) { > > > > + u16 hw_prescale; > > > > + u8 hw_period_steps; > > > > + > > > > + mchp_core_pwm_calc_period(chip, state, (u8 *)&prescale, &period_steps); > > > > > > Huh, if (u8 *)&prescale works depends on endianness. > > > > Big endian? What's that? ;) > > I think the cast can just be dropped and the u16 used directly instead. > > > > > > > > > + hw_prescale = readb_relaxed(mchp_core_pwm->base + MCHPCOREPWM_PRESCALE); > > > > + hw_period_steps = readb_relaxed(mchp_core_pwm->base + MCHPCOREPWM_PERIOD); > > > > + > > > > + if ((period_steps + 1) * (prescale + 1) < > > > > + (hw_period_steps + 1) * (hw_prescale + 1)) { > > > > + mutex_unlock(&mchp_core_pwm->lock); > > > > + return -EINVAL; > > > > + } > > > > + > > > > + /* > > > > + * It is possible that something could have set the period_steps > > > > > > My German feel for the English language says s/could have/has/ > > > > What I wrote is _fine_ but the could is redudant given the possible. > > I'll change it over. > > > > > > + * register to 0xff, which would prevent us from setting a 100% > > > > > > For my understanding: It would also prevent a 0% relative duty, right? > > > > Yeah, I guess the comment could reflect that. > > > > > > > > > + * duty cycle, as explained in the mchp_core_pwm_calc_period() > > > > > > s/duty/relative duty/; s/the // > > > > > > > + * above. > > > > + * The period is locked and we cannot change this, so we abort. > > > > + */ > > > > + if (period_steps == MCHPCOREPWM_PERIOD_STEPS_MAX) { > > > > > > Don't you need to check hw_period_steps == MCHPCOREPWM_PERIOD_STEPS_MAX > > > here? > > > > D'oh. > > > > > > > > > + mutex_unlock(&mchp_core_pwm->lock); > > > > + return -EINVAL; > > > > + } > > > > + > > > > + prescale = hw_prescale; > > > > + period_steps = hw_period_steps; > > > > + } else if (!current_state.enabled || current_state.period != state->period) { > > > > + ret = mchp_core_pwm_calc_period(chip, state, (u8 *)&prescale, &period_steps); > > > > > > ret is only used in this block, so the declaration can go into here, > > > too. > > > > > > > + if (ret) { > > > > + mutex_unlock(&mchp_core_pwm->lock); > > > > + return ret; > > > > + } > > > > + mchp_core_pwm_apply_period(mchp_core_pwm, prescale, period_steps); > > > > + } else { > > > > + prescale = readb_relaxed(mchp_core_pwm->base + MCHPCOREPWM_PRESCALE); > > > > + period_steps = readb_relaxed(mchp_core_pwm->base + MCHPCOREPWM_PERIOD); > > > > + /* > > > > + * As above, it is possible that something could have set the > > > > + * period_steps register to 0xff, which would prevent us from > > > > + * setting a 100% duty cycle, as explained above. > > > > + * As the period is not locked, we are free to fix this. > > > > + */ > > > > > > Are you sure this is safe? I think it isn't. Consider: > > > > > > pwm_apply_state(mypwm, { .duty = 0, .period = A, .enabled = true, }); > > > pwm_apply_state(mypwm, { .duty = 0, .period = B, .enabled = false, }); > > > pwm_apply_state(mypwm, { .duty = 0, .period = B, .enabled = true, }); > > > > > > Then you have in the third call prescale and period_steps still > > > corresponding to A because you didn't update these registers in the 2nd > > > call as you exited early. > > > > Riiight. I think I am a little confused here - this comment does not > > refer to my comment but rather to the whole logic I have? > > > > As in, what you're concerned about is the early exit if the state is > > disabled & that I take the values in the hardware as accurate? > > No, the thing I'm concerned about is assuming MCHPCOREPWM_PRESCALE and > MCHPCOREPWM_PERIOD correspond to state->period. So I'd drop the last > block use the 2nd last instead without further condition. So, if the period isn't locked always re-configure it. Makes life easier for me. > > > What makes sense to me to do here (assuming I understood correctly) > > is to compare state->period against what is in the hardare rather than > > against what the pwm core thinks? > > Or else I could stop exiting early if the pwm is to be disabled & > > instead allow the period and duty to be set so that the state of the > > hardware is as close to the pwm core's representation of it as possible. > > exiting early is fine. > > > > > [...] > > > > + period_steps = PREG_TO_VAL(readb_relaxed(mchp_core_pwm->base + MCHPCOREPWM_PERIOD)); > > > > + state->period = period_steps * prescale * NSEC_PER_SEC; > > > > > > This is broken on 32 bit archs (here: arm): > > > > > > $ cat test.c > > > #include > > > #include > > > #include > > > > > > int main(int argc, char *argv[]) > > > { > > > uint8_t period_steps = atoi(argv[1]); > > > uint16_t prescale = atoi(argv[2]); > > > uint64_t period; > > > > > > period = period_steps * prescale * 1000000000L; > > > > > > printf("period_steps = %" PRIu8 "\n", period_steps); > > > printf("prescale = %" PRIu16 "\n", prescale); > > > printf("period = %" PRIu64 "\n", period); > > > > > > return 0; > > > } > > > > > > $ make test > > > cc test.c -o test > > > > > > $ ./test 255 65535 > > > period_steps = 255 > > > prescale = 65535 > > > period = 18446744073018591744 > > > > > > The problem is that the result of 16711425 * 1000000000L isn't affected > > > by the type of period and so it's promoted to L which isn't big enough > > > to hold 16711425000000000 where longs are only 32 bit wide. > > > > I don't think this is ever going to be hit in the wild, since prescale > > comes from the hardware where it is limited to 255 - but preventing the > > issue seems trivially done by splitting the multiplication so no reason > > not to. Thanks for providing the test program btw :) > > Even 255 * 255 * 1000000000 overflows. With a maintainer's hat on, it is > very valuable to prevent such issues because your driver might be used > as a template for the next driver. > > > > > + state->period = DIV64_U64_ROUND_UP(state->period, clk_get_rate(mchp_core_pwm->clk)); > > > > + > > > > + posedge = readb_relaxed(mchp_core_pwm->base + MCHPCOREPWM_POSEDGE(pwm->hwpwm)); > > > > + negedge = readb_relaxed(mchp_core_pwm->base + MCHPCOREPWM_NEGEDGE(pwm->hwpwm)); > > > > + > > > > + if ((negedge == posedge) && state->enabled) { > > > > > > Why do you need that state->enabled? > > > > Because I was running into conflicts between the reporting here and some > > of the checks that I have added to prevent the PWM being put into an > > invalid state. On boot both negedge and posedge will be zero & this was > > preventing me from setting the period at all. > > I don't understood that. On startup, (negedge == posedge) is true as both are zero, but the reset values for prescale and period are actually 0x8. If on reset I try to set a small period, say "echo 1000 > period" apply() returns -EINVAL because of a check in the pwm core in pwm_apply_state() as I am attempting to set the period to lower than the out-of-reset duty cycle. I considered zeroing the registers, but if something below Linux had been using the PWM I felt that may not be the right thing to do. Can I continue to check for the enablement here or would you rather I did something different? Thanks, Conor.