From: Christian Marangi <ansuelsmth@gmail.com>
To: Andy Shevchenko <andy.shevchenko@gmail.com>
Cc: "Uwe Kleine-König" <ukleinek@kernel.org>,
"Lukas Wunner" <lukas@wunner.de>,
"AngeloGioacchino Del Regno"
<angelogioacchino.delregno@collabora.com>,
"Herbert Xu" <herbert@gondor.apana.org.au>,
"Andy Shevchenko" <andy@kernel.org>,
"Jonathan Cameron" <Jonathan.Cameron@huawei.com>,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
"Benjamin Larsson" <benjamin.larsson@genexis.eu>,
"Lorenzo Bianconi" <lorenzo@kernel.org>
Subject: Re: [PATCH v15 2/2] pwm: airoha: Add support for EN7581 SoC
Date: Tue, 24 Jun 2025 10:41:54 +0200 [thread overview]
Message-ID: <685a64d5.df0a0220.1f9a42.38b0@mx.google.com> (raw)
In-Reply-To: <CAHp75VcEJ0w5rcyq_DSHHunYanU5S9OgnRz1t8XervXqGQCX4w@mail.gmail.com>
On Tue, Jun 24, 2025 at 09:37:26AM +0300, Andy Shevchenko wrote:
> On Tue, Jun 24, 2025 at 12:11 AM Christian Marangi <ansuelsmth@gmail.com> wrote:
> >
> > Introduce driver for PWM module available on EN7581 SoC.
>
> ...
>
> > Signed-off-by: Benjamin Larsson <benjamin.larsson@genexis.eu>
> > Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
> > Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
> > Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> > Co-developed-by: Christian Marangi <ansuelsmth@gmail.com>
> > Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
> > ---
> > Changes v15:
> > - Fix compilation error for 64bit division on 32bit (patch 01)
> > - Add prefer async probe
>
> Wow, I am impressed!
>
> ...
>
> > +config PWM_AIROHA
> > + tristate "Airoha PWM support"
> > + depends on ARCH_AIROHA || COMPILE_TEST
>
> > + depends on OF
>
> There is nothing dependent on this. If you want to enable run-time,
> why not using this in conjunction with the COMPILE_TEST?
>
> > + select REGMAP_MMIO
>
> ...
>
> > +#include <linux/bitfield.h>
> > +#include <linux/bitops.h>
> > +#include <linux/err.h>
>
> > +#include <linux/gpio.h>
>
> Have you had a chance to read the top of that header file?
> No, just no. This header must not be used in the new code.
>
As you can see by the changelog this is very old code so I wasn't
aware.
> > +#include <linux/io.h>
> > +#include <linux/iopoll.h>
> > +#include <linux/math64.h>
> > +#include <linux/mfd/syscon.h>
> > +#include <linux/module.h>
>
> > +#include <linux/of.h>
>
> Nothing is used from this header. You actually missed mod_devicetable.h.
>
> > +#include <linux/platform_device.h>
> > +#include <linux/pwm.h>
> > +#include <linux/regmap.h>
>
> Missing headers, such as types.h.
> Please, follow the IWYU principle.
>
Aside from types do you have hint of other missing header? Do you have a
tool to identify the missing header?
> ...
>
> > +struct airoha_pwm {
> > + struct regmap *regmap;
>
> > + u64 initialized;
>
> Is it bitmap? This looks really weird, at least a comment is a must to
> explain why 64-bit for the variable that suggests (by naming) only a
> single bit.
>
There could be 33 PWM channel so it doesn't fit a u32. This is why u64.
I feel bitmap might be overkill for the task but if requested, I will
change it.
> > + struct airoha_pwm_bucket buckets[AIROHA_PWM_NUM_BUCKETS];
> > +
> > + /* Cache bucket used by each pwm channel */
> > + u8 channel_bucket[AIROHA_PWM_MAX_CHANNELS];
> > +};
>
> ...
>
> > +static u32 airoha_pwm_get_duty_ticks_from_ns(u64 period_ns, u64 duty_ns)
> > +{
> > + return mul_u64_u64_div_u64(duty_ns, AIROHA_PWM_DUTY_FULL,
> > + period_ns);
>
> For readability this can be one line.
>
Mhhh I try to limit code to 80 column where possible.
> > +}
>
> ...
>
> > + regmap_read(pc->regmap, AIROHA_PWM_REG_GPIO_FLASH_PRD_SET(offset),
> > + &val);
>
> Ditto.
>
> Btw, no error checks for regmap_*() calls?
>
>
> > +static int airoha_pwm_get_generator(struct airoha_pwm *pc, u64 duty_ns,
> > + u64 period_ns)
> > +{
> > + int i, best = -ENOENT, unused = -ENOENT;
>
> Why is 'i' signed?
>
> > + u64 best_period_ns = 0;
> > + u64 best_duty_ns = 0;
> > +
> > + for (i = 0; i < ARRAY_SIZE(pc->buckets); i++) {
> > + struct airoha_pwm_bucket *bucket = &pc->buckets[i];
> > + u64 bucket_period_ns = bucket->period_ns;
> > + u64 bucket_duty_ns = bucket->duty_ns;
> > + u32 duty_ticks, duty_ticks_bucket;
> > +
> > + /* If found, save an unused bucket to return it later */
> > + if (!bucket->used) {
> > + unused = i;
> > + continue;
> > + }
> > +
> > + /* We found a matching bucket, exit early */
> > + if (duty_ns == bucket_duty_ns &&
> > + period_ns == bucket_period_ns)
> > + return i;
> > +
> > + /*
> > + * Unlike duty cycle zero, which can be handled by
> > + * disabling PWM, a generator is needed for full duty
> > + * cycle but it can be reused regardless of period
> > + */
> > + duty_ticks = airoha_pwm_get_duty_ticks_from_ns(period_ns, duty_ns);
> > + duty_ticks_bucket = airoha_pwm_get_duty_ticks_from_ns(bucket_period_ns,
> > + bucket_duty_ns);
> > + if (duty_ticks == AIROHA_PWM_DUTY_FULL &&
> > + duty_ticks_bucket == AIROHA_PWM_DUTY_FULL)
> > + return i;
> > +
> > + /*
> > + * With an unused bucket available, skip searching for
> > + * a bucket to recycle (closer to the requested period/duty)
> > + */
> > + if (unused != -ENOENT)
> > + continue;
> > +
> > + /* Ignore bucket with invalid configs */
> > + if (bucket_period_ns > period_ns ||
> > + bucket_duty_ns > duty_ns)
> > + continue;
> > +
> > + /*
> > + * Search for a bucket closer to the requested period/duty
> > + * that has the maximal possible period that isn't bigger
> > + * than the requested period. For that period pick the maximal
> > + * duty_cycle that isn't bigger than the requested duty_cycle.
> > + */
> > + if (bucket_period_ns > best_period_ns ||
> > + (bucket_period_ns == best_period_ns &&
> > + bucket_duty_ns > best_duty_ns)) {
> > + best_period_ns = bucket_period_ns;
> > + best_duty_ns = bucket_duty_ns;
> > + best = i;
> > + }
> > + }
> > +
> > + /* With no unused bucket, return the best one found (if ever) */
> > + return unused == -ENOENT ? best : unused;
> > +}
>
> This entire function reminds me of something from util_macros.h or
> bsearch.h or similar. Can you double check that you really can't
> utilise one of those?
>
I checked and bsearch can't be used and and for util_macros the closest
can't be used. As explained in previous revision, it's not simply a
matter of finding the closest value but it's about finding a value that
is closest to the period_ns and only with that condition satisfied one
closest to the duty. We can't mix them as search for the closest of
both.
> ...
>
> > + /* Nothing to clear, PWM channel never used */
> > + if (!(pc->initialized & BIT_ULL(hwpwm)))
> > + return;
>
> So, it's a bitmap, why not use bitmap types and APIs?
>
> > + bucket = pc->channel_bucket[hwpwm];
> > + pc->buckets[bucket].used &= ~BIT_ULL(hwpwm);
>
> Oh, why do you need 'used' to be also 64-bit?
>
In the extreme case, a bucket can be used by all 33 PWM channel.
> > +}
>
> ...
>
> > + /*
> > + * Search for a bucket that already satisfy duty and period
>
> satisfies
>
> > + * or an unused one.
> > + * If not found, -ENOENT is returned.
> > + */
>
> ...
>
> > +static int airoha_pwm_sipo_init(struct airoha_pwm *pc)
> > +{
> > + u32 val;
> > +
> > + if (!(pc->initialized >> AIROHA_PWM_NUM_GPIO))
> > + return 0;
>
> It will be clearer if you use bitmap APIs here to show how many bits
> are indeed being used in "initialized" for this check.
> Basically it's something like find_first_set_from() or so (I don't
> remember names by heart). It will show the starting point
> and the limit.
>
> ...
>
> > + regmap_clear_bits(pc->regmap, AIROHA_PWM_REG_SIPO_FLASH_MODE_CFG,
> > + AIROHA_PWM_SERIAL_GPIO_MODE_74HC164);
>
> This is interesting. Can the gpio-74x164 be used as a whole?
>
It's sad but the shift register chip is entirely handled by the SoC. We
can't access to it's registers so the dedicated gpio driver can't be
used.
> ...
>
> > + regmap_write(pc->regmap, AIROHA_PWM_REG_SGPIO_CLK_DLY, 0x0);
>
> '0x' is not needed.
>
> ...
>
> > + if (regmap_read_poll_timeout(pc->regmap, AIROHA_PWM_REG_SGPIO_LED_DATA, val,
> > + !(val & AIROHA_PWM_SGPIO_LED_DATA_SHIFT_FLAG),
> > + 10, 200 * USEC_PER_MSEC))
> > + return -ETIMEDOUT;
>
> Why is the error code shadowed?
> ret = regmap...
> if (ret)
> return ret;
>
> ...
>
> > + if (regmap_read_poll_timeout(pc->regmap, AIROHA_PWM_REG_SGPIO_LED_DATA, val,
> > + !(val & AIROHA_PWM_SGPIO_LED_DATA_SHIFT_FLAG),
> > + 10, 200 * USEC_PER_MSEC))
> > + return -ETIMEDOUT;
>
> Ditto.
>
> ...
>
> > + /* index -1 means disable PWM channel */
>
> Negative index means
>
> > + if (index < 0) {
>
> > + }
>
> ...
>
> > +static int airoha_pwm_config(struct airoha_pwm *pc, struct pwm_device *pwm,
> > + u64 duty_ns, u64 period_ns)
> > +{
> > + unsigned int hwpwm = pwm->hwpwm;
> > + int bucket;
> > +
> > + bucket = airoha_pwm_consume_generator(pc, duty_ns, period_ns,
> > + hwpwm);
> > + if (bucket < 0)
> > + return -EBUSY;
>
> Why is the error code shadowed?
>
> > +
> > + airoha_pwm_config_flash_map(pc, hwpwm, bucket);
> > +
> > + pc->initialized |= BIT_ULL(hwpwm);
> > + pc->channel_bucket[hwpwm] = bucket;
> > +
> > + /*
> > + * SIPO are special GPIO attached to a shift register chip. The handling
> > + * of this chip is internal to the SoC that takes care of applying the
> > + * values based on the flash map. To apply a new flash map, it's needed
> > + * to trigger a refresh on the shift register chip.
> > + * If we are configuring a SIPO, always reinit the shift register chip
> > + * to make sure the correct flash map is applied.
> > + * We skip reconfiguring the shift register if we related hwpwm
>
> s/we/the/ ?
>
> > + * is disabled (as it doesn't need to be mapped).
> > + */
> > + if (!(pc->initialized & BIT_ULL(hwpwm)) && hwpwm >= AIROHA_PWM_NUM_GPIO)
> > + airoha_pwm_sipo_init(pc);
> > +
> > + return 0;
> > +}
>
> ...
>
> > + if (!(pc->initialized >> AIROHA_PWM_NUM_GPIO))
> > + regmap_clear_bits(pc->regmap, AIROHA_PWM_REG_SIPO_FLASH_MODE_CFG,
> > + AIROHA_PWM_SERIAL_GPIO_FLASH_MODE);
>
> If you use regmap cache the "initialized" might be not needed at all.
> It might be possible to read back (from the cache) the current state
> of some registers. Have you checked if this is a feasible approach?
>
The initialized is still needed to understand if a PWM channel has been
provisioned or it's still "dirty" and assigned to a bucket externally to
the kernel (for example by a bootloader)
Also the documentation is not very clear on what is really considered a
volatile register or not so maybe skipping some write might introduce
some unintended regression.
> ...
>
> > + /*
> > + * Duty goes at 255 step, normalize it to check if we can
>
> "in steps of 255 ns" ?
> The original comment is confusing as step in singular form may mislead.
>
I think you are confused duty is divided in 255 segment so I chencged
this to Duty is divided in 255 segment, normalize it t...
> > + * share a generator.
> > + */
>
> --
> With Best Regards,
> Andy Shevchenko
--
Ansuel
next prev parent reply other threads:[~2025-06-24 8:41 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-06-23 21:11 [PATCH v15 1/2] math.h: provide rounddown_ull variant for rounddown MACRO Christian Marangi
2025-06-23 21:11 ` [PATCH v15 2/2] pwm: airoha: Add support for EN7581 SoC Christian Marangi
2025-06-24 6:37 ` Andy Shevchenko
2025-06-24 8:41 ` Christian Marangi [this message]
2025-06-24 13:05 ` Andy Shevchenko
2025-06-24 6:08 ` [PATCH v15 1/2] math.h: provide rounddown_ull variant for rounddown MACRO Andy Shevchenko
2025-06-24 7:45 ` Christian Marangi
2025-06-24 8:40 ` Andy Shevchenko
2025-06-24 8:44 ` Andy Shevchenko
2025-06-24 8:48 ` Christian Marangi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=685a64d5.df0a0220.1f9a42.38b0@mx.google.com \
--to=ansuelsmth@gmail.com \
--cc=Jonathan.Cameron@huawei.com \
--cc=andy.shevchenko@gmail.com \
--cc=andy@kernel.org \
--cc=angelogioacchino.delregno@collabora.com \
--cc=benjamin.larsson@genexis.eu \
--cc=herbert@gondor.apana.org.au \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pwm@vger.kernel.org \
--cc=lorenzo@kernel.org \
--cc=lukas@wunner.de \
--cc=ukleinek@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®