From: Andy Shevchenko <andy@kernel.org>
To: "Marek Behún" <kabel@kernel.org>
Cc: Gregory CLEMENT <gregory.clement@bootlin.com>,
Arnd Bergmann <arnd@arndb.de>,
soc@kernel.org, arm@kernel.org,
Linus Walleij <linus.walleij@linaro.org>,
Bartosz Golaszewski <brgl@bgdev.pl>,
linux-gpio@vger.kernel.org
Subject: Re: [PATCH v2 3/7] platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs
Date: Tue, 19 Sep 2023 16:00:39 +0300 [thread overview]
Message-ID: <ZQmbd211FzPjA97r@smile.fi.intel.com> (raw)
In-Reply-To: <20230919103815.16818-4-kabel@kernel.org>
On Tue, Sep 19, 2023 at 12:38:11PM +0200, Marek Behún wrote:
> Add support for GPIOs connected to the MCU on the Turris Omnia board.
>
> This include:
includes
> - front button pin
> - enable pins for USB regulators
> - MiniPCIe / mSATA card presence pins in MiniPCIe port 0
> - LED output pins from WAN ethernet PHY, LAN switch and MiniPCIe ports
> - on board revisions 32+ also various peripheral resets and another
> voltage regulator enable pin
Can the main driver provide a regmap and all other use it?
...
> +Date: August 2023
> +KernelVersion: 6.6
Same as per previous patch review.
...
> + ret = omnia_mcu_register_gpiochip(mcu);
> + if (ret)
> + return ret;
> +
> return 0;
return ..._gpiochip(...);
?
...
> + switch (cmd) {
> + case CMD_GENERAL_CONTROL:
> + buf[1] = val;
> + buf[2] = mask;
> + len = 3;
> + break;
> +
> + case CMD_EXT_CONTROL:
> + put_unaligned_le16(val, &buf[1]);
> + put_unaligned_le16(mask, &buf[3]);
> + len = 5;
> + break;
> +
> + default:
> + unreachable();
You meant BUG_ON()?
> + }
...
> +static int omnia_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
> +{
> + struct omnia_mcu *mcu = gpiochip_get_data(gc);
> +
> + if (offset == OMNIA_GPIO_PHY_SFP_OFFSET) {
> + int val;
> +
> + mutex_lock(&mcu->lock);
> + val = omnia_cmd_read_bit(mcu->client,
> + CMD_GET_EXT_CONTROL_STATUS,
> + EXT_CTL_PHY_SFP_AUTO);
> + mutex_unlock(&mcu->lock);
> +
> + if (val < 0)
> + return val;
> +
> + if (val)
> + return GPIO_LINE_DIRECTION_IN;
> + else
Redundant 'else'.
> + return GPIO_LINE_DIRECTION_OUT;
> + }
> +
> + if (omnia_gpios[offset].ctl_cmd)
> + return GPIO_LINE_DIRECTION_OUT;
> + else
Ditto.
> + return GPIO_LINE_DIRECTION_IN;
> +}
...
> + if (offset == OMNIA_GPIO_PHY_SFP_OFFSET)
> + return omnia_ctl_cmd(mcu, CMD_EXT_CONTROL, EXT_CTL_PHY_SFP_AUTO,
> + EXT_CTL_PHY_SFP_AUTO);
> +
> + if (gpio->ctl_cmd)
> + return -EOPNOTSUPP;
I believe internally we use ENOTSUPP.
Ditto for all cases like this.
...
> + uint16_t val, mask;
So, why uintXX_t out of a sudden?
...
> + if (gpio->ctl_cmd)
> + return omnia_ctl_cmd(mcu, gpio->ctl_cmd, val, mask);
> + else
Redundant 'else'.
> + return -EOPNOTSUPP;
> +}
...
> +static void omnia_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
> +{
> + const struct omnia_gpio *gpio = &omnia_gpios[offset];
> + struct omnia_mcu *mcu = gpiochip_get_data(gc);
> + u16 val, mask;
> + int err = 0;
Redundant assignment.
> + if (!gpio->ctl_cmd)
> + return;
> +
> + mask = gpio->ctl_bit;
> + val = value ? mask : 0;
> +
> + err = omnia_ctl_cmd(mcu, gpio->ctl_cmd, val, mask);
> + if (err)
> + dev_err(&mcu->client->dev, "Cannot set GPIO %u: %d\n",
> + offset, err);
> +}
...
> + mutex_lock(&mcu->lock);
> +
> + if (ctl_mask)
> + err = omnia_ctl_cmd_unlocked(mcu, CMD_GENERAL_CONTROL, ctl,
> + ctl_mask);
> + if (!err && ext_ctl_mask)
> + err = omnia_ctl_cmd_unlocked(mcu, CMD_EXT_CONTROL, ext_ctl,
> + ext_ctl_mask);
Can it be
if (err)
goto out_unlock;
if (_mask)
...
?
> + mutex_unlock(&mcu->lock);
> + if (err)
> + dev_err(&mcu->client->dev, "Cannot set GPIOs: %d\n", err);
How is useful this one?
...
> +static bool omnia_gpio_available(struct omnia_mcu *mcu,
> + const struct omnia_gpio *gpio)
> +{
> + if (gpio->feat_mask)
> + return (mcu->features & gpio->feat_mask) == gpio->feat;
> + else if (gpio->feat)
> + return mcu->features & gpio->feat;
> + else
Redundant 'else':s.
> + return true;
> +}
...
> +static int omnia_gpio_init_valid_mask(struct gpio_chip *gc,
> + unsigned long *valid_mask,
> + unsigned int ngpios)
> +{
> + struct omnia_mcu *mcu = gpiochip_get_data(gc);
> + bitmap_zero(valid_mask, ngpios);
No need.
Also do you have bitops.h included?
> + for (int i = 0; i < ngpios; ++i) {
> + const struct omnia_gpio *gpio = &omnia_gpios[i];
> +
> + if (!gpio->cmd && !gpio->int_bit)
> + continue;
Use clear_bit() here...
> + if (omnia_gpio_available(mcu, gpio))
> + set_bit(i, valid_mask);
...and assign_bit() here.
> + }
> +
> + return 0;
> +}
...
> + dev_dbg(&mcu->client->dev,
> + "set int mask %02x %02x %02x %02x %02x %02x %02x %02x\n",
> + cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7], cmd[8]);
%8ph
...
> + /*
> + * Remember which GPIOs have both rising and falling interrupts enabled.
> + * For those we will cache their value so that .get() method is faster.
> + * We also need to forget cached values of GPIOs that aren't cached
> + * anymore.
> + */
> + if (!err) {
if (err)
goto out_unlock;
> + mcu->both = rising & falling;
> + mcu->is_cached &= mcu->both;
> + }
> +
> + mutex_unlock(&mcu->lock);
...
> +static void omnia_irq_init_valid_mask(struct gpio_chip *gc,
> + unsigned long *valid_mask,
> + unsigned int ngpios)
> +{
> + struct omnia_mcu *mcu = gpiochip_get_data(gc);
> + bitmap_zero(valid_mask, ngpios);
No need (see above how).
> + for (int i = 0; i < ngpios; ++i) {
> + const struct omnia_gpio *gpio = &omnia_gpios[i];
> +
> + if (!gpio->int_bit)
> + continue;
> +
> + if (omnia_gpio_available(mcu, gpio))
> + set_bit(i, valid_mask);
> + }
> +}
...
> + u8 cmd[9] = {};
Magic number in a few places. Please, use self-explanatory pre-defined constant
instead.
...
> + dev_err(&mcu->client->dev, "Cannot read pending IRQs: %d\n",
> + err);
In all functions with help of
struct device *dev = &mcu->client->dev;
you can make code shorter.
...
> + rising = reply[0] | (reply[2] << 8) | (reply[4] << 16) |
> + (reply[6] << 24);
> + falling = reply[1] | (reply[3] << 8) | (reply[5] << 16) |
> + (reply[7] << 24);
With a help of two masks, you can access to the both edges as to 64-bit value
and simplify the code.
...
> + int ret = omnia_cmd_read_u16(mcu->client, CMD_GET_STATUS_WORD);
Please, split this for better maintenance.
> + if (ret < 0)
> + return ret;
...
> +static irqreturn_t omnia_irq_thread_handler(int irq, void *dev_id)
> +{
> + struct omnia_mcu *mcu = dev_id;
> + irqreturn_t res = IRQ_NONE;
> + unsigned long pending;
> + int i;
> +
> + if (!omnia_irq_read_pending(mcu, &pending))
> + return IRQ_NONE;
> +
> + for_each_set_bit(i, &pending, 32) {
> + unsigned int nested_irq =
> + irq_find_mapping(mcu->gc.irq.domain,
> + omnia_int_to_gpio_idx[i]);
It's much better to read in a form
unsigned int nested_irq;
domain = ...
nested_irq = irq_find_mapping(domain, omnia_int_to_gpio_idx[i]);
(exactly 80 characters, btw).
> + handle_nested_irq(nested_irq);
> + res = IRQ_HANDLED;
No need.
> + }
> + return res;
return IRQ_RETVAL(pending);
> +}
...
> +static ssize_t front_button_mode_show(struct device *dev,
> + struct device_attribute *a, char *buf)
> +{
> + struct omnia_mcu *mcu = i2c_get_clientdata(to_i2c_client(dev));
> + int val;
> +
> + if (mcu->features & FEAT_NEW_INT_API)
> + val = omnia_cmd_read_bit(mcu->client, CMD_GET_STATUS_WORD,
> + STS_BUTTON_MODE);
> + else
> + val = !!(mcu->last_status & STS_BUTTON_MODE);
> + if (val < 0)
Can be true only in one case, why to check for the second oner?/
> + return val;
> + return sysfs_emit(buf, "%s\n", val ? "cpu" : "mcu");
With a static const array of string literals...
> +}
...
> + if (sysfs_streq(buf, "mcu"))
> + val = 0;
> + else if (sysfs_streq(buf, "cpu"))
> + val = mask;
> + else
> + return -EINVAL;
...and help of sysfs_match_string() you can simplify the code.
...
> +static struct attribute *omnia_mcu_gpio_attrs[] = {
> + &dev_attr_front_button_mode.attr,
> + NULL,
> +};
> +
> +static const struct attribute_group omnia_mcu_gpio_group = {
> + .attrs = omnia_mcu_gpio_attrs,
> +};
ATTRIBUTE_GROUPS() ?
...
> + err = devm_gpiochip_add_data(dev, &mcu->gc, mcu);
> + if (err) {
> + dev_err(dev, "Cannot add GPIO chip: %d\n", err);
> + return err;
return dev_err_probe(...);
Ditto for other places like this in the probe stage.
> + }
...
> + err = devm_device_add_group(dev, &omnia_mcu_gpio_group);
No way, no-one should use the API scheduled for removal.
What's wrong with .dev_groups ?
...
> +void omnia_mcu_unregister_gpiochip(struct omnia_mcu *mcu)
> +{
> + if (!(mcu->features & FEAT_NEW_INT_API))
> + cancel_delayed_work_sync(&mcu->button_release_emul_work);
> +
> + mutex_destroy(&mcu->lock);
Wrong order?
> +}
...
> struct omnia_mcu {
> struct i2c_client *client;
> const char *type;
> u16 features;
> +
> + /* GPIO chip */
> + struct gpio_chip gc;
Making this a first member may lead to the better code. Check with bloat-o-meter.
> + struct mutex lock;
> + u32 mask, rising, falling, both, cached, is_cached;
> + /* Old MCU firmware handling needs the following */
> + u16 last_status;
> + struct delayed_work button_release_emul_work;
Swapping these two may free a few bytes. Check with pahole tool.
> + bool button_pressed_emul;
> };
...
> + if (!bits) {
> + *reply = 0;
> + return 0;
> + }
> +
> + ret = omnia_cmd_read(client, cmd, reply, (ilog2(bits) >> 3) + 1);
> +
Redundant blank line.
> + if (ret >= 0)
> + *reply = le32_to_cpu(*reply) & bits;
Huh?! Please, check your code with sparse like
make W=1 C=1 CF=-D__CHECK_ENDIAN__ ...
> + return ret < 0 ? ret : 0;
--
With Best Regards,
Andy Shevchenko
next prev parent reply other threads:[~2023-09-19 13:00 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-09-19 10:38 [PATCH v2 0/7] Turris Omnia MCU driver Marek Behún
2023-09-19 10:38 ` [PATCH v2 1/7] dt-bindings: arm: add cznic,turris-omnia-mcu binding Marek Behún
2023-09-20 12:37 ` Krzysztof Kozlowski
2023-09-19 10:38 ` [PATCH v2 2/7] platform: cznic: Add preliminary support for Turris Omnia MCU Marek Behún
2023-09-19 12:29 ` Andy Shevchenko
2023-09-19 15:16 ` Marek Behún
2023-09-19 18:27 ` Andy Shevchenko
2023-09-20 14:19 ` Marek Behún
2023-09-20 14:47 ` Andy Shevchenko
2023-09-19 10:38 ` [PATCH v2 3/7] platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs Marek Behún
2023-09-19 13:00 ` Andy Shevchenko [this message]
2023-09-20 17:08 ` Marek Behún
2023-09-21 9:55 ` Andy Shevchenko
2023-09-21 20:25 ` Marek Behún
2023-09-22 14:18 ` Andy Shevchenko
2023-09-25 10:03 ` Marek Behún
2023-09-25 10:29 ` Andy Shevchenko
2023-09-21 18:42 ` Marek Behún
2023-09-22 14:14 ` Andy Shevchenko
2023-09-20 11:58 ` Linus Walleij
2023-09-20 13:36 ` Andy Shevchenko
2023-09-21 19:45 ` Marek Behún
2023-09-21 20:14 ` Marek Behún
2023-09-22 14:21 ` Andy Shevchenko
2023-09-19 10:38 ` [PATCH v2 4/7] platform: cznic: turris-omnia-mcu: Add support for poweroff and wakeup Marek Behún
2023-09-22 16:28 ` Marek Behún
2023-09-19 10:38 ` [PATCH v2 5/7] platform: cznic: turris-omnia-mcu: Add support for MCU watchdog Marek Behún
2023-09-19 13:50 ` Guenter Roeck
2023-09-22 11:46 ` Marek Behún
2023-09-19 10:38 ` [PATCH v2 6/7] ARM: dts: turris-omnia: Add MCU system-controller node Marek Behún
2023-09-19 10:38 ` [PATCH v2 7/7] ARM: dts: turris-omnia: Add GPIO key node for front button Marek Behún
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=ZQmbd211FzPjA97r@smile.fi.intel.com \
--to=andy@kernel.org \
--cc=arm@kernel.org \
--cc=arnd@arndb.de \
--cc=brgl@bgdev.pl \
--cc=gregory.clement@bootlin.com \
--cc=kabel@kernel.org \
--cc=linus.walleij@linaro.org \
--cc=linux-gpio@vger.kernel.org \
--cc=soc@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.