Received: by 2002:a05:6a10:f347:0:0:0:0 with SMTP id d7csp935687pxu; Wed, 16 Dec 2020 20:06:14 -0800 (PST) X-Google-Smtp-Source: ABdhPJwwjuSkGAlU2x7fURZWP3wfWj7xlxYKpfyJp/L6f0osxhaqcdqpQ4kaQsO9zExh3GHf8L17 X-Received: by 2002:a05:6402:2207:: with SMTP id cq7mr2002102edb.310.1608177974034; Wed, 16 Dec 2020 20:06:14 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1608177974; cv=none; d=google.com; s=arc-20160816; b=NfKG6Z+v63yG7w0hpqIPec4VdHMSVUyflky2Wmar76g2HcRzgnDV5mcIVrFw8n8En4 IBiPNfaYUuPusC4mdSAuL5Uuv9wRtcle8N26dxpQymPDlMTQpnMcW6ehlyOLki6W0DzK CUB4ngQ22FO7ROeAZUTZlwa85DcO3+XFNhoN+Edq3SD5SuhvnbiyW1Sd1nY8e9xdmub/ eraOrThQjkvfGqiKMUWcX8RcRSPQ+6F3c/x6+9WAllSsRjC49OE0D+WOoTKyFRsQcdFX qoeBVfMYdE0I4Co8zMtDEf/RTJEcJquv8fUQyHcxLYBd8Xb0WeWnLKEgCz0J8EvDM9Tq TRTg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:subject:message-id:date:from:in-reply-to :references:mime-version:dkim-signature; bh=wpvV4G1320CTcs8x7p4CfEdvxJWtV5tnsFEXn9LRPH0=; b=QPrqgmvoaS3tog+kijYVilOF911gWMhcCYaK7/QEplO+oBazz5HlgLCZURoc5nnaqg 0GCTc5iepak6SXNfwSD+Xaqb2RGoT8MMmf737OLyFetkJrO5PPlCj0+Jqfsn92w+4e21 /XvTdJyQ9xxtY2j8U7y7QTdkpzVJ64Ks3VbxEXZss+m5gyeD4TcxG4VZ5Jl8ZxRCdSTm iC3XVjN/DiTYOpRQJH1Qa5T7UPUB0T3jw5JUKc0wECcYoR7wVcXk7elnmDFUH6Pk9C8Q shzQN8n/pqpOv2ar4P7lbCiiiuGarppRfSeUX5swIHP9rf2Fp9TUK9WURfYoCNzgHmB4 mogg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=TKMEytSW; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id z11si1966126ejw.19.2020.12.16.20.05.51; Wed, 16 Dec 2020 20:06:14 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=TKMEytSW; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727457AbgLQEEd (ORCPT + 99 others); Wed, 16 Dec 2020 23:04:33 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53232 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727788AbgLQEEc (ORCPT ); Wed, 16 Dec 2020 23:04:32 -0500 Received: from mail-vk1-xa2d.google.com (mail-vk1-xa2d.google.com [IPv6:2607:f8b0:4864:20::a2d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A2CF9C061794; Wed, 16 Dec 2020 20:03:51 -0800 (PST) Received: by mail-vk1-xa2d.google.com with SMTP id m67so3820591vkg.7; Wed, 16 Dec 2020 20:03:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=wpvV4G1320CTcs8x7p4CfEdvxJWtV5tnsFEXn9LRPH0=; b=TKMEytSWe/cqxWm6g5cTDMI+7fdNKB///tfnib7Q4WpFIS+i51uonxTKysS/ImG9R5 J1PR4KxmjnXVJx7TAaOmtHSkAGpIlYp1oAa7PZI7OwLq6tJ84NY4jNEGzd2J1dUpPZqg MFixL+YJkvYH7d07ZPMhwV2DjIrYf6wMkYm6erAczWSFNbP4jQ73NJw85q3Fv+3p+DBX it295E2eoliUyzkNHZ0ySglUXEJVuWHd78kc7MoRUp05p/QPG04FvY4Vwis+CCszJtxz K1HEWdvRvYS9t/4uYBKXc+quUcaXW8JSIiWbNGyCONTxivEyVlhd9DvU4594+NXLZXJg wBqQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=wpvV4G1320CTcs8x7p4CfEdvxJWtV5tnsFEXn9LRPH0=; b=Qn2nwN/juq6FULSTE8WgBY38dMJ2xGIOJZ9zXljyYxY9oaX+aEPgiRBtDYRjJaAUrK VCYmJ2DWNpM70/+QE0ryBiTw+nVok+4GU6rOz8rIJl3xGqZOfS+rz1RcqR4vBNM+UcKJ 8Tn9Q8KPJdBo8k3lBB5xFoveYJUj5TPhGAv/PTxv2iYPTPoYJHgWBJzS0p+3E63Npx+L KIoBPGCFWKqXiHocDr3ANXXMeJ4g5yIoIivec2mkkBYkiaAWYzwwdLycSXfuK364G2yM PgtDsRt+cO8LPdFPAflKPc4bem3Ms96gH8PvQ7RwyR1+2meSJGrAEITeaKdJzvVRDXwp Sudg== X-Gm-Message-State: AOAM533KnNNmbb9o0GyKGW5Zl4G5JRufqJ8AD2b76DHb828s2a9MgKY9 je+Q66XK+Ne9x4JxkC4Q/UkCYT9XlUVV1A6XtPA= X-Received: by 2002:a1f:fc84:: with SMTP id a126mr25223346vki.23.1608177830721; Wed, 16 Dec 2020 20:03:50 -0800 (PST) MIME-Version: 1.0 References: <20201215212228.185517-1-clemens.gruber@pqgruber.com> <20201215212228.185517-7-clemens.gruber@pqgruber.com> In-Reply-To: <20201215212228.185517-7-clemens.gruber@pqgruber.com> From: Sven Van Asbroeck Date: Wed, 16 Dec 2020 23:03:39 -0500 Message-ID: Subject: Re: [PATCH v5 7/7] pwm: pca9685: Restrict period change for prescaler users To: Clemens Gruber Cc: linux-pwm@vger.kernel.org, Thierry Reding , =?UTF-8?Q?Uwe_Kleine=2DK=C3=B6nig?= , Lee Jones , Linux Kernel Mailing List , Mika Westerberg , David Jander Content-Type: text/plain; charset="UTF-8" Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Hi Clemens, see below. On Wed, Dec 16, 2020 at 7:53 AM Clemens Gruber wrote: > > Previously, the last used PWM channel could change the global prescale > setting, even if other channels were already in use. > > Fix it by only allowing the first user of the prescaler to change the > global chip-wide prescale setting. If there is more than one channel in > use, the prescale settings resulting from the chosen periods must match. > > PWMs that are disabled or have a duty cycle of 0% or 100% are not > considered to be using the prescaler as they have the full OFF or full > ON bits set. This also applies to channels used as GPIOs. > > Signed-off-by: Clemens Gruber > --- > drivers/pwm/pwm-pca9685.c | 51 +++++++++++++++++++++++++++++++++------ > 1 file changed, 44 insertions(+), 7 deletions(-) > > diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c > index ff916980de49..438492d4aed4 100644 > --- a/drivers/pwm/pwm-pca9685.c > +++ b/drivers/pwm/pwm-pca9685.c > @@ -23,11 +23,11 @@ > #include > > /* > - * Because the PCA9685 has only one prescaler per chip, changing the period of > - * one channel affects the period of all 16 PWM outputs! > - * However, the ratio between each configured duty cycle and the chip-wide > - * period remains constant, because the OFF time is set in proportion to the > - * counter range. > + * Because the PCA9685 has only one prescaler per chip, only the first channel > + * that uses the prescaler is allowed to change the prescale register. > + * PWM channels requested afterwards must use a period that results in the same > + * prescale setting as the one set by the first requested channel, unless they > + * use duty cycles of 0% or 100% (prescaler not used for full OFF/ON). > */ > > #define PCA9685_MODE1 0x00 > @@ -80,6 +80,8 @@ struct pca9685 { > struct pwm_chip chip; > struct regmap *regmap; > bool staggered_outputs; > + struct mutex prescaler_users_lock; Keep things simple by re-using the "struct mutex lock" below? This code isn't performance-intensive, so having a single lock for pwm/gpio requests + pwm_apply() is probably ok. > + DECLARE_BITMAP(prescaler_users, PCA9685_MAXCHAN + 1); Rename to pwms_use_prescale ? > #if IS_ENABLED(CONFIG_GPIOLIB) > struct mutex lock; > struct gpio_chip gpio; > @@ -92,6 +94,18 @@ static inline struct pca9685 *to_pca(struct pwm_chip *chip) > return container_of(chip, struct pca9685, chip); > } > > +/* This function is supposed to be called with the prescaler_users_lock held */ > +static inline bool pca9685_may_change_prescaler(struct pca9685 *pca, int channel) Drop the inline? Only the compiler knows if inlining this function makes sense on a platform (armv7, x86, etc). Compilers are usually better at this then humans... Rename to pca9685_prescaler_can_change() ? > +{ > + /* > + * A PWM channel may only change the prescaler if there are no users of > + * the prescaler yet or that same channel is the only one in use. > + */ > + return bitmap_empty(pca->prescaler_users, PCA9685_MAXCHAN + 1) || > + (bitmap_weight(pca->prescaler_users, PCA9685_MAXCHAN + 1) == 1 && > + test_bit(channel, pca->prescaler_users)); > +} I found this logic expression quite complex to read. Perhaps simplify by using a few steps? For example: /* if prescaler not in use, we can always change it */ if (empty) return true; /* if more than one pwm is using the prescaler, we can never change it */ if (weight > 1) return false; /* one pwm is using the prescaler, we can only change it if it's us */ return test_bit(us); > + > static void pca9685_pwm_set_duty(struct pca9685 *pca, int channel, unsigned int duty) > { > unsigned int on, off; > @@ -337,16 +351,25 @@ static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, > duty = PCA9685_COUNTER_RANGE * state->duty_cycle; > duty = DIV_ROUND_CLOSEST_ULL(duty, state->period); > > + mutex_lock(&pca->prescaler_users_lock); > + > if (!state->enabled || duty < 1) { > pca9685_pwm_set_duty(pca, pwm->hwpwm, 0); > - return 0; > + goto prescaler_unused; > } else if (duty == PCA9685_COUNTER_RANGE) { > pca9685_pwm_set_duty(pca, pwm->hwpwm, duty); > - return 0; > + goto prescaler_unused; > } > > regmap_read(pca->regmap, PCA9685_PRESCALE, &val); > if (prescale != val) { > + if (!pca9685_may_change_prescaler(pca, pwm->hwpwm)) { > + mutex_unlock(&pca->prescaler_users_lock); > + dev_err(chip->dev, > + "prescaler not set: already in use with different setting!\n"); > + return -EBUSY; > + } > + > /* > * Putting the chip briefly into SLEEP mode > * at this point won't interfere with the > @@ -364,6 +387,14 @@ static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, > } > > pca9685_pwm_set_duty(pca, pwm->hwpwm, duty); > + > + set_bit(pwm->hwpwm, pca->prescaler_users); > + mutex_unlock(&pca->prescaler_users_lock); > + return 0; > + > +prescaler_unused: > + clear_bit(pwm->hwpwm, pca->prescaler_users); > + mutex_unlock(&pca->prescaler_users_lock); > return 0; > } The need for the mutex makes this function quite "messy": we have to guard all the exits, and that's easy to forget. Maybe simplify the function by moving the mutex to a helper? Example: static int __pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { ... just do stuff and don't worry about the mutex } static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { /* document why we serialize pwm_apply */ mutex_lock(); __pca9685_pwm_apply(chip, pwm, state); mutex_unlock(); } > > @@ -422,7 +453,11 @@ static void pca9685_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) > { > struct pca9685 *pca = to_pca(chip); > > + mutex_lock(&pca->prescaler_users_lock); > + clear_bit(pwm->hwpwm, pca->prescaler_users); > pca9685_pwm_set_duty(pca, pwm->hwpwm, 0); > + mutex_unlock(&pca->prescaler_users_lock); > + > pm_runtime_put(chip->dev); > pca9685_pwm_clear_inuse(pca, pwm->hwpwm); > } > @@ -463,6 +498,8 @@ static int pca9685_pwm_probe(struct i2c_client *client, > > i2c_set_clientdata(client, pca); > > + mutex_init(&pca->prescaler_users_lock); > + > regmap_read(pca->regmap, PCA9685_MODE2, ®); > > if (device_property_read_bool(&client->dev, "invert")) > -- > 2.29.2 >