Turris Omnia MCU driver
Subsystems (6)
Marek Behún
maintainerv2 cover Turris Omnia MCU driver
Hello Arnd, Gregorgy and others,
this is v2 of series adding support for Turris Omnia MCU. See the cover letter from v1:
https://patchwork.kernel.org/project/linux-soc/cover/[email protected]/
I am also sending this to specific subsystem maintainers as requested by Krzysztof.
Changes since v1:
- incorporated small changes requested by Krzysztof Kozlowski
- in order to make reviewing simpler, I split the second patch, which implements the MCU driver, into four incremental patches:
- patch 2/7 adds basic driver skeleton
- patch 3/7 adds GPIO support
- patch 4/7 adds board poweroff and wakeup support
- patch 5/7 adds MCU watchdog support
Marek
Marek Behún (7):
dt-bindings: arm: add cznic,turris-omnia-mcu binding
platform: cznic: Add preliminary support for Turris Omnia MCU
platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs
platform: cznic: turris-omnia-mcu: Add support for poweroff and wakeup
platform: cznic: turris-omnia-mcu: Add support for MCU watchdog
ARM: dts: turris-omnia: Add MCU system-controller node
ARM: dts: turris-omnia: Add GPIO key node for front button
.../sysfs-bus-i2c-devices-turris-omnia-mcu | 77 ++ .../bindings/arm/cznic,turris-omnia-mcu.yaml | 67 ++ MAINTAINERS | 4 + .../dts/marvell/armada-385-turris-omnia.dts | 35 +- drivers/platform/Kconfig | 2 + drivers/platform/Makefile | 1 + drivers/platform/cznic/Kconfig | 47 + drivers/platform/cznic/Makefile | 7 + .../platform/cznic/turris-omnia-mcu-base.c | 273 +++++ .../platform/cznic/turris-omnia-mcu-gpio.c | 971 ++++++++++++++++++ .../cznic/turris-omnia-mcu-sys-off-wakeup.c | 245 +++++ .../cznic/turris-omnia-mcu-watchdog.c | 126 +++ drivers/platform/cznic/turris-omnia-mcu.h | 164 +++ include/linux/turris-omnia-mcu-interface.h | 194 ++++ 14 files changed, 2212 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu create mode 100644 Documentation/devicetree/bindings/arm/cznic,turris-omnia-mcu.yaml create mode 100644 drivers/platform/cznic/Kconfig create mode 100644 drivers/platform/cznic/Makefile create mode 100644 drivers/platform/cznic/turris-omnia-mcu-base.c create mode 100644 drivers/platform/cznic/turris-omnia-mcu-gpio.c create mode 100644 drivers/platform/cznic/turris-omnia-mcu-sys-off-wakeup.c create mode 100644 drivers/platform/cznic/turris-omnia-mcu-watchdog.c create mode 100644 drivers/platform/cznic/turris-omnia-mcu.h create mode 100644 include/linux/turris-omnia-mcu-interface.h
Signature · 1 line
2.41.0
Marek Behún
maintainerPATCH v2 1/7 dt-bindings: arm: add cznic,turris-omnia-mcu binding
Add binding for cznic,turris-omnia-mcu, the device-tree node representing the system-controller features provided by the MCU on the Turris Omnia router.
--- .../bindings/arm/cznic,turris-omnia-mcu.yaml | 67 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 68 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/cznic,turris-omnia-mcu.yaml
Documentation/devicetree/bindings/arm/cznic,turris-omnia-mcu.yaml new file +67 -0
@@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/cznic,turris-omnia-mcu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: CZ.NIC's Turris Omnia MCU + +maintainers: + - Marek Behún <[email protected]> + +description: + The MCU on Turris Omnia acts as a system controller providing additional + GPIOs, interrupts, watchdog, system power off and wakeup configuration. + +properties: + compatible: + const: cznic,turris-omnia-mcu + + reg: + description: MCU I2C slave address + maxItems: 1 + + interrupts: + maxItems: 1 + + interrupt-controller: true + + '#interrupt-cells': + const: 2 + + gpio-controller: true + + '#gpio-cells': + const: 2 + +required: + - compatible + - reg + - interrupts + - interrupt-controller + - gpio-controller + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + system-controller@2a { + compatible = "cznic,turris-omnia-mcu"; + reg = <0x2a>; + + interrupt-parent = <&gpio1>; + interrupts = <11 IRQ_TYPE_NONE>; + + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + };
MAINTAINERS +1 -0
@@ -2091,6 +2091,7 @@ W: https://www.turris.cz/ F: Documentation/ABI/testing/debugfs-moxtet F: Documentation/ABI/testing/sysfs-bus-moxtet-devices F: Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm +F: Documentation/devicetree/bindings/arm/cznic,turris-omnia-mcu.yaml F: Documentation/devicetree/bindings/bus/moxtet.txt F: Documentation/devicetree/bindings/firmware/cznic,turris-mox-rwtm.txt F: Documentation/devicetree/bindings/gpio/gpio-moxtet.txt
Signature · 1 line
2.41.0
Re: [PATCH v2 1/7] dt-bindings: arm: add cznic,turris-omnia-mcu binding
Quoted 6 lines · Marek Behún
Add binding for cznic,turris-omnia-mcu, the device-tree node representing the system-controller features provided by the MCU on the Turris Omnia router.
Signed-off-by Marek Behún <[email protected]>---
Best regards,
Krzysztof
Marek Behún
maintainerPATCH v2 2/7 platform: cznic: Add preliminary support for Turris Omnia MCU
In reply to Marek BehúnAdd a new platform driver that exposes the features provided by the microcontroller on the Turris Omnia board.
This commit adds the basic skeleton for the driver.
--- .../sysfs-bus-i2c-devices-turris-omnia-mcu | 45 ++++ MAINTAINERS | 3 + drivers/platform/Kconfig | 2 + drivers/platform/Makefile | 1 + drivers/platform/cznic/Kconfig | 26 ++ drivers/platform/cznic/Makefile | 4 + .../platform/cznic/turris-omnia-mcu-base.c | 253 ++++++++++++++++++ drivers/platform/cznic/turris-omnia-mcu.h | 65 +++++ include/linux/turris-omnia-mcu-interface.h | 194 ++++++++++++++ 9 files changed, 593 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu create mode 100644 drivers/platform/cznic/Kconfig create mode 100644 drivers/platform/cznic/Makefile create mode 100644 drivers/platform/cznic/turris-omnia-mcu-base.c create mode 100644 drivers/platform/cznic/turris-omnia-mcu.h create mode 100644 include/linux/turris-omnia-mcu-interface.h
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu new file +45 -0
@@ -0,0 +1,45 @@ +What: /sys/bus/i2c/devices/<mcu_device>/fw_features +Date: August 2023 +KernelVersion: 6.6 +Contact: Marek Behún <[email protected]> +Description: (RO) Newer versions of the microcontroller firmware report the + features they support. These can be read from this file. If the + MCU firmware is too old, this file reads 0x0. + + Format: 0x%x. + +What: /sys/bus/i2c/devices/<mcu_device>/fw_version_hash_application +Date: August 2023 +KernelVersion: 6.6 +Contact: Marek Behún <[email protected]> +Description: (RO) Contains the version hash (commit hash) of the application + part of the microcontroller firmware. + + Format: %s. + +What: /sys/bus/i2c/devices/<mcu_device>/fw_version_hash_bootloader +Date: August 2023 +KernelVersion: 6.6 +Contact: Marek Behún <[email protected]> +Description: (RO) Contains the version hash (commit hash) of the bootloader + part of the microcontroller firmware. + + Format: %s. + +What: /sys/bus/i2c/devices/<mcu_device>/mcu_type +Date: August 2023 +KernelVersion: 6.6 +Contact: Marek Behún <[email protected]> +Description: (RO) Contains the microcontroller type (STM32, GD32, MKL). + + Format: %s. + +What: /sys/bus/i2c/devices/<mcu_device>/reset_selector +Date: August 2023 +KernelVersion: 6.6 +Contact: Marek Behún <[email protected]> +Description: (RO) Contains the selected factory reset level, determined by + how long the rear reset button was held by the user during board + reset. + + Format: %i.
MAINTAINERS +3 -0
@@ -2089,6 +2089,7 @@ M: Marek Behún <[email protected]> S: Maintained W: https://www.turris.cz/ F: Documentation/ABI/testing/debugfs-moxtet +F: Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu F: Documentation/ABI/testing/sysfs-bus-moxtet-devices F: Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm F: Documentation/devicetree/bindings/arm/cznic,turris-omnia-mcu.yaml
@@ -2102,10 +2103,12 @@ F: drivers/firmware/turris-mox-rwtm.c F: drivers/gpio/gpio-moxtet.c F: drivers/leds/leds-turris-omnia.c F: drivers/mailbox/armada-37xx-rwtm-mailbox.c +F: drivers/platform/cznic/ F: drivers/watchdog/armada_37xx_wdt.c F: include/dt-bindings/bus/moxtet.h F: include/linux/armada-37xx-rwtm-mailbox.h F: include/linux/moxtet.h +F: include/linux/turris-omnia-mcu-interface.h ARM/FARADAY FA526 PORT M: Hans Ulli Kroll <[email protected]>
drivers/platform/Kconfig +2 -0
@@ -7,6 +7,8 @@ source "drivers/platform/goldfish/Kconfig" source "drivers/platform/chrome/Kconfig" +source "drivers/platform/cznic/Kconfig" + source "drivers/platform/mellanox/Kconfig" source "drivers/platform/olpc/Kconfig"
drivers/platform/Makefile +1 -0
@@ -11,3 +11,4 @@ obj-$(CONFIG_OLPC_EC) += olpc/ obj-$(CONFIG_GOLDFISH) += goldfish/ obj-$(CONFIG_CHROME_PLATFORMS) += chrome/ obj-$(CONFIG_SURFACE_PLATFORMS) += surface/ +obj-$(CONFIG_CZNIC_PLATFORMS) += cznic/
drivers/platform/cznic/Kconfig new file +26 -0
@@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# For a description of the syntax of this configuration file, +# see Documentation/kbuild/kconfig-language.rst. +# + +menuconfig CZNIC_PLATFORMS + bool "Platform support for CZ.NIC's Turris hardware" + depends on MACH_ARMADA_38X || COMPILE_TEST + help + Say Y here to be able to choose driver support for CZ.NIC's Turris + devices. This option alone does not add any kernel code. + +if CZNIC_PLATFORMS + +config TURRIS_OMNIA_MCU + tristate "Turris Omnia MCU driver" + depends on MACH_ARMADA_38X || COMPILE_TEST + depends on I2C + help + Say Y here to add support for the features implemented by the + microcontroller on the CZ.NIC's Turris Omnia SOHO router. + To compile this driver as a module, choose M here; the module will be + called turris-omnia-mcu. + +endif # CZNIC_PLATFORMS
drivers/platform/cznic/Makefile new file +4 -0
@@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o +turris-omnia-mcu-objs := turris-omnia-mcu-base.o
drivers/platform/cznic/turris-omnia-mcu-base.c new file +253 -0
253 lines not on this page · raw message
drivers/platform/cznic/turris-omnia-mcu.h new file +65 -0
65 lines not on this page · raw message
include/linux/turris-omnia-mcu-interface.h new file +194 -0
194 lines not on this page · raw message
Signature · 1 line
2.41.0
Re: [PATCH v2 2/7] platform: cznic: Add preliminary support for Turris Omnia MCU
Quoted 4 lines · Marek Behún
Add a new platform driver that exposes the features provided by the microcontroller on the Turris Omnia board.
This commit adds the basic skeleton for the driver.
Read "Submitting Patches" documentation (find "This patch" in it) and amend your commit message accordingly.
...
Review
Quoted 2 lines
(quoted diff) +2
-0+Date: August 2023 +KernelVersion: 6.6
Wrong and outdated. Use phb-crystall-ball to find the closest possible values for both parameters here.
Ditto for all others with the same issue.
Note, it likely might be part of v6.7, not earlier.
...
drivers/platform/Makefile obj-$(CONFIG_OLPC_EC) += olpc/ · line 14
Quoted 5 lines
drivers/platform/Makefile +1
-0@@ -11,3 +11,4 @@ obj-$(CONFIG_OLPC_EC) += olpc/ obj-$(CONFIG_GOLDFISH) += goldfish/ obj-$(CONFIG_CHROME_PLATFORMS) += chrome/ obj-$(CONFIG_SURFACE_PLATFORMS) += surface/ +obj-$(CONFIG_CZNIC_PLATFORMS) += cznic/
Why not ordered (to some extent) here (as you did in the other file)?
...
Quoted 1 line
+turris-omnia-mcu-objs := turris-omnia-mcu-base.o
objs is for user space tools. Kernel code should use m,y.
...
drivers/platform/cznic/turris-omnia-mcu-base.c line 9
Quoted 2 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +2
-0+#include <linux/hex.h> +#include <linux/module.h>
Missing types.h, sysfs.h, mod_devicetable.h, i2c.h, ...
...
drivers/platform/cznic/turris-omnia-mcu-base.c line 22
Quoted 11 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +10
-0+static int omnia_get_version_hash (struct omnia_mcu *mcu,bool bootloader, +u8 *version) +{ +u8 reply[20 ]; +int ret; + + ret =omnia_cmd_read (mcu->client, bootloader ?CMD_GET_FW_VERSION_BOOT : +CMD_GET_FW_VERSION_APP , + reply,sizeof (reply)); +if (ret <0 )
Can it return positive value? What would be the meaning of it?
drivers/platform/cznic/turris-omnia-mcu-base.c line 25
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +2
-0+return ret; + version[40 ] ='\0' ;
How do you know the version has enough space?
drivers/platform/cznic/turris-omnia-mcu-base.c line 36
Quoted 12 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +11
-0+bin2hex (version, reply,20 ); + +return 0 ; +} + +static ssize_t fw_version_hash_show (struct device *dev,char *buf, +bool bootloader) +{ +struct omnia_mcu *mcu =i2c_get_clientdata (to_i2c_client (dev)); +u8 version[41 ]; +int err;
In two near functions you already inconsistent in the naming of the return code variable. Be consistent across all your code, i.e. choose one name and use it everywhere.
Quoted 6 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +6
-0+ err =omnia_get_version_hash (mcu, bootloader, version); +if (err) +return err; + +return sysfs_emit (buf,"%s\n" , version); +}
...
drivers/platform/cznic/turris-omnia-mcu-base.c line 66
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu-base.c new file +1
-0+return sysfs_emit (buf,"%#x\n" , mcu->features);
"0" will be printed differently, is this on purpose?
...
drivers/platform/cznic/turris-omnia-mcu-base.c line 85
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +4
-0+int ret =omnia_cmd_read_u8 (to_i2c_client (dev),CMD_GET_RESET ); + +if (ret <0 ) +return ret;
Better from maintenance perspective to have
int ret; ret = omnia_cmd_read_u8(to_i2c_client(dev), CMD_GET_RESET); if (ret < 0) return ret;
...
drivers/platform/cznic/turris-omnia-mcu-base.c line 97
Quoted 8 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +7
-0+static struct attribute *omnia_mcu_attrs[] = { + &dev_attr_fw_version_hash_application.attr, + &dev_attr_fw_version_hash_bootloader.attr, + &dev_attr_fw_features.attr, + &dev_attr_mcu_type.attr, + &dev_attr_reset_selector.attr, +NULL ,
No comma for the terminator line. It will make your code robust at compile time against some misplaced entries.
drivers/platform/cznic/turris-omnia-mcu-base.c line 99
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +2
-0+}; +
Unneeded blank line.
Quoted 1 line
+ATTRIBUTE_GROUPS(omnia_mcu);
...
Review
Quoted 1 line
(quoted diff) +1
-0+ u8 version[41];
This magic sizes should go away. Use predefined constant, or allocate on the heap, depending on the case(s) you have.
...
drivers/platform/cznic/turris-omnia-mcu-base.c line 154
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu-base.c new file +1
-0+int status;
My gosh, it's a _third_ name for the same!
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +3
-0+ status =omnia_cmd_read_u16 (mcu->client,CMD_GET_STATUS_WORD ); +if (status <0 ) +return status;
...
drivers/platform/cznic/turris-omnia-mcu-base.c line 186
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu-base.c new file +1
-0+for (int i =0 ; i <ARRAY_SIZE (features); ++i) {
Why signed?
Why pre-increment?
drivers/platform/cznic/turris-omnia-mcu-base.c line 187
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu-base.c new file +1
-0+if (!(mcu->features & features[i].mask)) {
Wouldn't be better
if (mcu->features & features[i].mask) continue;
?
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +4
-0+omnia_info_missing_feature (dev, features[i].name); + suggest_fw_upgrade = true; + } + }
...
Review
Quoted 2 lines
(quoted diff) +2
-0+ dev_info(dev, + "Consider upgrading MCU firmware with the omnia-mcutool utility.\n");
You have so-o many dev_info() calls, are you sure you not abusing use of that?
...
drivers/platform/cznic/turris-omnia-mcu-base.c line 217
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +4
-0+if (ret) { +dev_err (dev,"Cannot determine MCU supported features: %d\n" , + ret); +return ret;
return dev_err_probe(...);
Quoted 1 line
(quoted diff) +1
-0+ }
...
drivers/platform/cznic/turris-omnia-mcu-base.c line 223
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +4
-0+if (!client->irq) { +dev_err (dev,"IRQ resource not found\n" ); +return -ENODATA ; + }
Why you need to allocate memory, go through the initial checks, etc and then fail? What's wrong with checking this first?
Why -ENODATA?!
...
drivers/platform/cznic/turris-omnia-mcu-base.c line 229
Quoted 2 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +2
-0+static const struct of_device_id of_omnia_mcu_match[] = { + { .compatible ="cznic,turris-omnia-mcu" , },
Inner comma is not needed.
drivers/platform/cznic/turris-omnia-mcu-base.c line 230
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu-base.c new file +1
-0+ {},
No comma for the terminator entry.
Quoted 1 line
+};
...
drivers/platform/cznic/turris-omnia-mcu-base.c line 234
Quoted 2 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +2
-0+static const struct i2c_device_id omnia_mcu_id[] = { + {"turris-omnia-mcu" ,0 },
', 0' part is redundant.
Quoted 2 lines
(quoted diff) +2
-0+ { } +};
...
drivers/platform/cznic/turris-omnia-mcu-base.c line 248
Quoted 11 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +10
-0+static struct i2c_driver omnia_mcu_driver = { + .probe = omnia_mcu_probe, + .id_table = omnia_mcu_id, + .driver = { + .name ="turris-omnia-mcu" , + .of_match_table = of_omnia_mcu_match, + .dev_groups = omnia_mcu_groups, + }, +}; +
Redundant blank line.
Quoted 1 line
+module_i2c_driver(omnia_mcu_driver);
...
drivers/platform/cznic/turris-omnia-mcu.h line 9
Quoted 2 lines
drivers/platform/cznic/turris-omnia-mcu.h new file +2
-0+#ifndef __DRIVERS_PLATFORM_CZNIC_TURRIS_OMNIA_MCU_H +#define __DRIVERS_PLATFORM_CZNIC_TURRIS_OMNIA_MCU_H
WE_LIKE_VERY_LONG_AND_OVERFLOWED_DEFINITIONS_H!
...
Quoted 1 line
+#include <
asm/byteorder.h>
This usually goes after linux/*.h.
Quoted 1 line
+#include <
linux/i2c.h>
Missing types.h, errno.h, ...
+ blank line?
Quoted 1 line
+#include <
linux/turris-omnia-mcu-interface.h>
...
drivers/platform/cznic/turris-omnia-mcu.h line 37
Quoted 2 lines
drivers/platform/cznic/turris-omnia-mcu.h new file +2
-0+ ret =i2c_transfer (client->adapter, msgs,ARRAY_SIZE (msgs)); +if (likely (ret ==ARRAY_SIZE (msgs)))
Why likely()? Please, justify.
drivers/platform/cznic/turris-omnia-mcu.h line 42
Quoted 6 lines
drivers/platform/cznic/turris-omnia-mcu.h new file +5
-0+return len; + else if (ret < 0) + return ret; + else + return -EIO;
In both cases the 'else' is redundant. Moreover, the usual pattern is to check for the errors first.
Quoted 1 line
+}
...
include/linux/turris-omnia-mcu-interface.h line 9
Quoted 2 lines
include/linux/turris-omnia-mcu-interface.h new file +2
-0+#ifndef __INCLUDE_LINUX_TURRIS_OMNIA_MCU_INTERFACE_H +#define __INCLUDE_LINUX_TURRIS_OMNIA_MCU_INTERFACE_H
My gosh!
...
include/linux/turris-omnia-mcu-interface.h line 13
Quoted 3 lines
include/linux/turris-omnia-mcu-interface.h new file +2
-0+#include <linux/bits.h> +enum commands_e {
Are you sure the name is unique enough / properly namespaced? Same Q to all enums.
...
include/linux/turris-omnia-mcu-interface.h line 96
Quoted 1 line
include/linux/turris-omnia-mcu-interface.h new file +1
-0+/*CTL_RESERVED = BIT(2),*/
Missing spaces?
Also, needs a comment why this is commented out.
...
include/linux/turris-omnia-mcu-interface.h line 101
Quoted 1 line
include/linux/turris-omnia-mcu-interface.h new file +1
-0+CTL_BOOTLOADER =BIT (7 )
Add trailing comma.
Signature · 2 lines
With Best Regards, Andy Shevchenko
Marek Behún
maintainerRe: [PATCH v2 2/7] platform: cznic: Add preliminary support for Turris Omnia MCU
Hello Andy,
thank you for your review. I have some questions regarding your comments.
Quoted 8 lines · On Tue, 19 Sep 2023 15:29:08 +0300 Andy Shevchenko <[email protected]> wrote:
Quoted 4 lines · Marek Behún
Add a new platform driver that exposes the features provided by the microcontroller on the Turris Omnia board.
This commit adds the basic skeleton for the driver.
Read "Submitting Patches" documentation (find "This patch" in it) and amend your commit message accordingly.
OK.
Quoted 9 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+Date: August 2023 +KernelVersion: 6.6Wrong and outdated. Use phb-crystall-ball to find the closest possible values for both parameters here.
Ditto for all others with the same issue.
Note, it likely might be part of v6.7, not earlier.
OK
Quoted 6 lines
Quoted 4 lines
obj-$(CONFIG_GOLDFISH) += goldfish/
obj-$(CONFIG_CHROME_PLATFORMS) += chrome/
obj-$(CONFIG_SURFACE_PLATFORMS) += surface/
+obj-$(CONFIG_CZNIC_PLATFORMS) += cznic/Why not ordered (to some extent) here (as you did in the other file)?
Where should I put it? The other entries are not ordered, see
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/platform/Makefile?h=v6.6-rc2
Quoted 3 lines
Quoted 1 line
+turris-omnia-mcu-objs := turris-omnia-mcu-base.o
objs is for user space tools. Kernel code should use m,y.
I don't understand. There are plenty driver using -objs. The driver consists of multiple C source files. I thought this should be done as
obj-$(CONFIG_XXX) += xxx.o
xxx-objs := xxx-a.o xxx-b.o xxx-c.o
If you choose CONFIG_XXX = y, then the driver will be compiled into kernel, if you choose CONFIG_XXX = m, then the driver will be a module xxx.ko.
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-base.c line 9
Quoted 2 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +2
-0+#include <linux/hex.h> +#include <linux/module.h> Missing
types.h,sysfs.h,mod_devicetable.h,i2c.h, ...
i2c.h comes from turris-omnia-mcu.h. The others I will fix. Does the the .c file also include all those headers if it includes a local header already?
Quoted 13 lines
Quoted 12 lines
+static int omnia_get_version_hash(struct
omnia_mcu*mcu, bool bootloader,drivers/platform/cznic/turris-omnia-mcu-base.c new file +6
-0+ u8 *version) +{ +u8 reply[20 ]; +int ret; + + ret = omnia_cmd_read(mcu->client, bootloader ?
CMD_GET_FW_VERSION_BOOT:
+n average development tiCMD_GET_FW_VERSION_APP,drivers/platform/cznic/turris-omnia-mcu-base.c new file +1
-0+ reply, sizeof(reply));
Quoted 3 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ if (ret < 0)Can it return positive value? What would be the meaning of it?
Originally I had it return 0 on success, but recently I've had a discussion about this same thing with LED subsystem maintainer about some LED patches, Lee Jones, who requested the _read function to return number of bytes read on success. See at
https://lore.kernel.org/linux-leds/[email protected]/
where he says:
Read and write APIs, even abstracted ones such as these generally return the number of bytes successfully read and written respectively. If you are going to normalise, then please do so against this standard.
I do not agree with him, since this is a local command accessor which only succeeds if all the bytes are read/written as requested. But I adjusted my code as per his request since he is the maintainer.
I will change the code back so that it returns 0 on success instead of the number of bytes written.
Quoted 5 lines
drivers/platform/cznic/turris-omnia-mcu-base.c line 25
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +2
-0+return ret; + version[40 ] ='\0' ;How do you know the version has enough space?
The function is only use within the file, and both users know how much space it needs. But ok, I shall enforce it with the static keyword as
static int
omnia_get_version_hash(...,
u8 version[static 2*OMNIA_FW_VERSION_LEN + 1]);
Would that be okay?
Quoted 12 lines
Quoted 11 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +9
-0+bin2hex (version, reply,20 ); + +return 0 ; +} + +static ssize_t fw_version_hash_show (struct device *dev,char *buf, +bool bootloader) +{ + struct omnia_mcu *mcu =i2c_get_clientdata(to_i2c_client(dev));
(quoted diff) +1
-0+ u8 version[41];
Quoted 5 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ int err;In two near functions you already inconsistent in the naming of the return code variable. Be consistent across all your code, i.e. choose one name and use it everywhere.
I use
ret - when the return value can also be positive and mean something err - when the return value is either 0 (no error) or negative errno
Is this inconsistent?
Quoted 9 lines
Quoted 6 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +6
-0+ err =omnia_get_version_hash (mcu, bootloader, version); +if (err) +return err; + +return sysfs_emit (buf,"%s\n" , version); +}...
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu-base.c line 66
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu-base.c new file +1
-0+return sysfs_emit (buf,"%#x\n" , mcu->features);"0" will be printed differently, is this on purpose?
No, I wanted it to print 0x0, didn't know '#' would print just 0. Thanks.
Quoted 7 lines
Review
Quoted 4 lines
(quoted diff) +1
-0+ int ret = omnia_cmd_read_u8(to_i2c_client(dev),
CMD_GET_RESET); +(quoted diff) +2
-0+ if (ret < 0) + return ret;Better from maintenance perspective to have
Quoted 5 lines
int ret; ret = omnia_cmd_read_u8(to_i2c_client(dev), CMD_GET_RESET); if (ret < 0) return ret;
OK
Quoted 11 lines
drivers/platform/cznic/turris-omnia-mcu-base.c line 97
Quoted 8 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +7
-0+static struct attribute *omnia_mcu_attrs[] = { + &dev_attr_fw_version_hash_application.attr, + &dev_attr_fw_version_hash_bootloader.attr, + &dev_attr_fw_features.attr, + &dev_attr_mcu_type.attr, + &dev_attr_reset_selector.attr, +NULL ,No comma for the terminator line. It will make your code robust at compile time against some misplaced entries.
OK
Quoted 5 lines
drivers/platform/cznic/turris-omnia-mcu-base.c line 99
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +2
-0+}; +Unneeded blank line.
OK
Quoted 8 lines
Quoted 1 line
+ATTRIBUTE_GROUPS(omnia_mcu);
...
Review
Quoted 1 line
(quoted diff) +1
-0+ u8 version[41];This magic sizes should go away. Use predefined constant, or allocate on the heap, depending on the case(s) you have.
OK
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu-base.c line 154
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu-base.c new file +1
-0+int status;My gosh, it's a _third_ name for the same!
The variable holds value of the status register, which is needed at another point in this function. I thought naming it just "ret" would cause confusions... Do you really propose to rename it such?
Quoted 10 lines
Quoted 4 lines
(quoted diff) +1
-0+ status = omnia_cmd_read_u16(mcu->client,
CMD_GET_STATUS_WORD);drivers/platform/cznic/turris-omnia-mcu-base.c new file +2
-0+if (status <0 ) +return status;...
drivers/platform/cznic/turris-omnia-mcu-base.c line 186
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu-base.c new file +1
-0+for (int i =0 ; i <ARRAY_SIZE (features); ++i) {Why signed?
Because it is shorter and makes no difference and there are tons of
for (int i = 0; i < ARRAY_SIZE(x); i++)
in the kernel already. But if you really want, I can change it. Please let me know.
Quoted 1 line
Why pre-increment?
Again, it makes no difference, is widely used in the kernel and I personally prefer it. But I will change it if you want.
Quoted 9 lines
drivers/platform/cznic/turris-omnia-mcu-base.c line 187
Quoted 2 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +1
-0+if (!(mcu->features & features[i].mask)) {Wouldn't be better
if (mcu->features & features[i].mask) continue;?
OK
Quoted 8 lines
Quoted 5 lines
(quoted diff) +1
-0+ omnia_info_missing_feature(dev,features[i].name);
drivers/platform/cznic/turris-omnia-mcu-base.c new file +3
-0+ suggest_fw_upgrade = true; + } + }...
Quoted 6 lines
Quoted 3 lines
(quoted diff) +2
-0+ dev_info(dev, + "Consider upgrading MCU firmware with theomnia-mcutool utility.\n");
You have so-o many
dev_info()calls, are you sure you not abusing use of that?
I want the users to upgrade the MCU firmware. These infos are only printed during probe time, and won't be printed at all if the firmware is upgraded. Is this a problem?
Quoted 10 lines
...
drivers/platform/cznic/turris-omnia-mcu-base.c line 217
Quoted 5 lines
(quoted diff) +2
-0+ if (ret) { + dev_err(dev, "Cannot determine MCU supportedfeatures: %d\n",
drivers/platform/cznic/turris-omnia-mcu-base.c new file +2
-0+ ret); +return ret;return dev_err_probe(...);
Thx. Didn't know this one.
Quoted 12 lines
Quoted 1 line
(quoted diff) +1
-0+ }...
drivers/platform/cznic/turris-omnia-mcu-base.c line 223
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +4
-0+if (!client->irq) { +dev_err (dev,"IRQ resource not found\n" ); +return -ENODATA ; + }Why you need to allocate memory, go through the initial checks, etc and then fail? What's wrong with checking this first?
Quoted 1 line
Why
-ENODATA?!
OK
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-base.c line 229
Quoted 2 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +2
-0+static const struct of_device_id of_omnia_mcu_match[] = { + { .compatible ="cznic,turris-omnia-mcu" , },Inner comma is not needed.
OK
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu-base.c line 230
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu-base.c new file +1
-0+ {},No comma for the terminator entry.
OK
Quoted 8 lines
Quoted 1 line
+};
...
drivers/platform/cznic/turris-omnia-mcu-base.c line 234
Quoted 2 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +2
-0+static const struct i2c_device_id omnia_mcu_id[] = { + {"turris-omnia-mcu" ,0 },', 0' part is redundant.
OK
Quoted 15 lines
Quoted 2 lines
(quoted diff) +2
-0+ { } +};...
Quoted 9 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +9
-0+static struct i2c_driver omnia_mcu_driver = { + .probe = omnia_mcu_probe, + .id_table = omnia_mcu_id, + .driver = { + .name ="turris-omnia-mcu" , + .of_match_table = of_omnia_mcu_match, + .dev_groups = omnia_mcu_groups, + }, +};
Quoted 3 lines
Quoted 1 line
+
Redundant blank line.
OK
Quoted 8 lines
Quoted 1 line
+module_i2c_driver(omnia_mcu_driver);
...
drivers/platform/cznic/turris-omnia-mcu.h line 9
Quoted 2 lines
drivers/platform/cznic/turris-omnia-mcu.h new file +2
-0+#ifndef __DRIVERS_PLATFORM_CZNIC_TURRIS_OMNIA_MCU_H +#define __DRIVERS_PLATFORM_CZNIC_TURRIS_OMNIA_MCU_H
WE_LIKE_VERY_LONG_AND_OVERFLOWED_DEFINITIONS_H!
Sigh... OK, I will change it :)
Quoted 3 lines
Quoted 1 line
+#include <
asm/byteorder.h>This usually goes after linux/*.h.
OK
Quoted 6 lines
Quoted 2 lines
+#include <
linux/i2c.h>Missing
types.h,errno.h, ...+ blank line?
Will change.
Quoted 9 lines
Quoted 1 line
+#include <
linux/turris-omnia-mcu-interface.h>...
drivers/platform/cznic/turris-omnia-mcu.h line 37
Quoted 3 lines
(quoted diff) +1
-0+ ret = i2c_transfer(client->adapter, msgs,
ARRAY_SIZE(msgs));drivers/platform/cznic/turris-omnia-mcu.h new file +1
-0+if (likely (ret ==ARRAY_SIZE (msgs)))Why
likely()? Please, justify.
Becuase it is unlikely the I2C transaction will fail. In most cases, it does not.
Quoted 10 lines
drivers/platform/cznic/turris-omnia-mcu.h line 42
Quoted 6 lines
drivers/platform/cznic/turris-omnia-mcu.h new file +5
-0+return len; + else if (ret < 0) + return ret; + else + return -EIO;In both cases the 'else' is redundant. Moreover, the usual pattern is to check for the errors first.
Quoted 8 lines
Quoted 1 line
+}
...
include/linux/turris-omnia-mcu-interface.h line 9
Quoted 2 lines
include/linux/turris-omnia-mcu-interface.h new file +2
-0+#ifndef __INCLUDE_LINUX_TURRIS_OMNIA_MCU_INTERFACE_H +#define __INCLUDE_LINUX_TURRIS_OMNIA_MCU_INTERFACE_HMy gosh!
:)
Quoted 6 lines
include/linux/turris-omnia-mcu-interface.h line 13
Quoted 3 lines
include/linux/turris-omnia-mcu-interface.h new file +2
-0+#include <linux/bits.h> +enum commands_e {Are you sure the name is unique enough / properly namespaced? Same Q to all enums.
I can change it. I wanted it to be compatible with the header in the firmware repository.
Quoted 7 lines
...
include/linux/turris-omnia-mcu-interface.h line 96
Quoted 1 line
include/linux/turris-omnia-mcu-interface.h new file +1
-0+/*CTL_RESERVED = BIT(2),*/ Missing spaces?
Also, needs a comment why this is commented out.
OK
Quoted 3 lines
include/linux/turris-omnia-mcu-interface.h line 101
Quoted 1 line
include/linux/turris-omnia-mcu-interface.h new file +1
-0+CTL_BOOTLOADER =BIT (7 )Add trailing comma.
OK
Marek
Re: [PATCH v2 2/7] platform: cznic: Add preliminary support for Turris Omnia MCU
Quoted 3 lines · Marek Behún
Quoted 1 line · On Tue, 19 Sep 2023 15:29:08 +0300 Andy Shevchenko <[email protected]> wrote:
On Tue, Sep 19, 2023 at 12:38:10PM +0200, Marek Behún wrote:
...
Quoted 9 lines
Quoted 6 lines
Quoted 4 lines
obj-$(CONFIG_GOLDFISH) += goldfish/
obj-$(CONFIG_CHROME_PLATFORMS) += chrome/
obj-$(CONFIG_SURFACE_PLATFORMS) += surface/
+obj-$(CONFIG_CZNIC_PLATFORMS) += cznic/Why not ordered (to some extent) here (as you did in the other file)?
Where should I put it? The other entries are not ordered, see
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/platform/Makefile?h=v6.6-rc2
With the visible context above, at least after chrome already much better, no?
...
Quoted 4 lines
Quoted 3 lines
Quoted 1 line
+turris-omnia-mcu-objs := turris-omnia-mcu-base.o
objs is for user space tools. Kernel code should use m,y.
Quoted 8 lines
I don't understand. There are plenty driver using -objs. The driver consists of multiple C source files. I thought this should be done as
obj-$(CONFIG_XXX) += xxx.o
xxx-objs := xxx-a.o xxx-b.o xxx-c.oIf you choose
CONFIG_XXX= y, then the driver will be compiled into kernel, if you chooseCONFIG_XXX= m, then the driver will be a module xxx.ko.
Hmm... Maybe I mixed something... Let me check.
For now than it's okay.
...
Quoted 8 lines
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-base.c line 9
Quoted 2 lines
drivers/platform/cznic/turris-omnia-mcu-base.c new file +2
-0+#include <linux/hex.h> +#include <linux/module.h> Missing
types.h,sysfs.h,mod_devicetable.h,i2c.h, ...
i2c.hcomes fromturris-omnia-mcu.h. The others I will fix. Does the the .c file also include all those headers if it includes a local header already?
For generic ones, yes.
...
Quoted 4 lines
Quoted 3 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ if (ret < 0)Can it return positive value? What would be the meaning of it?
Quoted 13 lines
Originally I had it return 0 on success, but recently I've had a discussion about this same thing with LED subsystem maintainer about some LED patches, Lee Jones, who requested the
_readfunction to return number of bytes read on success. See at
https://lore.kernel.org/linux-leds/[email protected]/
where he says:
Read and write APIs, even abstracted ones such as these generally return the number of bytes successfully read and written respectively. If you are going to normalise, then please do so against this standard.
I do not agree with him, since this is a local command accessor which only succeeds if all the bytes are read/written as requested. But I adjusted my code as per his request since he is the maintainer.
This is strange. For example, regmap APIs never returns amount of data written or read. I think it's solely depends on the API. It might be useful for i²c APIs, in case you can do something about it. but if you have wrappers on top of that already (meaning not using directly the i2c_*() calls, I dunno the positive return is anyhow useful.
Quoted 4 lines
I will change the code back so that it returns 0 on success instead of the number of bytes written.
Quoted 1 line
Quoted 1 line
(quoted diff) +1
-0+ return ret;
...
Quoted 10 lines
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu-base.c line 25
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu-base.c new file +1
-0+ version[40 ] ='\0' ;How do you know the version has enough space?
The function is only use within the file, and both users know how much space it needs. But ok, I shall enforce it with the static keyword as
static int
omnia_get_version_hash(...,u8 version[static 2*OMNIA_FW_VERSION_LEN + 1]);Would that be okay?
Perhaps, it's a thing in a category "TIL", and it seems we have so few callers of that.
...
Quoted 10 lines
Quoted 5 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ int err;In two near functions you already inconsistent in the naming of the return code variable. Be consistent across all your code, i.e. choose one name and use it everywhere.
I use
ret - when the return value can also be positive and mean something err - when the return value is either 0 (no error) or negative errno
Is this inconsistent?
TBH sounds good, but I close to never had heard about such a distinction in a Linux kernel source file(s).
...
Quoted 7 lines
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu-base.c line 154
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu-base.c new file +1
-0+int status;My gosh, it's a _third_ name for the same!
The variable holds value of the status register, which is needed at another point in this function. I thought naming it just "ret" would cause confusions... Do you really propose to rename it such?
It's not bad per se, just put yourself on the place of somebody who would like to understand the semantics of all these variables. Maybe you can add a comment on top explaining that (in short)?
...
Quoted 8 lines
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu-base.c line 186
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu-base.c new file +1
-0+for (int i =0 ; i <ARRAY_SIZE (features); ++i) {Why signed?
Because it is shorter and makes no difference and there are tons of
for (int i = 0; i <ARRAY_SIZE(x); i++)
in the kernel already. But if you really want, I can change it. Please let me know.
It just makes harder to get the code, because of possible unneeded questions like "if it's a signed type, maybe it's on purpose?"
Quoted 4 lines
Quoted 1 line
Why pre-increment?
Again, it makes no difference, is widely used in the kernel and I personally prefer it. But I will change it if you want.
Pre-increment usually also a sign of something special about the iterator variable. In non-special cases we use post-increment. And if you count the use of each you will see the difference.
...
Quoted 10 lines
Quoted 6 lines
Quoted 3 lines
(quoted diff) +2
-0+ dev_info(dev, + "Consider upgrading MCU firmware with theomnia-mcutool utility.\n");
You have so-o many
dev_info()calls, are you sure you not abusing use of that?I want the users to upgrade the MCU firmware. These infos are only printed during probe time, and won't be printed at all if the firmware is upgraded. Is this a problem?
Depends how really useful they are. If you think it's way to go, go for it.
...
Quoted 6 lines
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu.h line 37
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu.h new file +1
-0+if (likely (ret ==ARRAY_SIZE (msgs)))Why
likely()? Please, justify.Becuase it is unlikely the I2C transaction will fail. In most cases, it does not.
Yes, but why likely() is needed? So, i.o.w. what's the benefit in _this_ case?
Quoted 1 line
Quoted 1 line
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu.h new file +1
-0+return len;
...
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
+enum
commands_e{Are you sure the name is unique enough / properly namespaced? Same Q to all enums.
I can change it. I wanted it to be compatible with the header in the firmware repository.
Are these being generated, or also statically written in that repo? If the latter, I think better to use more unique naming schema in the kernel.
Signature · 2 lines
With Best Regards, Andy Shevchenko
Marek Behún
maintainerRe: [PATCH v2 2/7] platform: cznic: Add preliminary support for Turris Omnia MCU
Hello Andy,
Quoted 12 lines · On Tue, 19 Sep 2023 21:27:04 +0300 Andy Shevchenko <[email protected]> wrote:
Quoted 3 lines · Marek Behún
Quoted 1 line · On Tue, 19 Sep 2023 15:29:08 +0300 Andy Shevchenko <[email protected]> wrote:
On Tue, Sep 19, 2023 at 12:38:10PM +0200, Marek Behún wrote:
...
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
obj-$(CONFIG_GOLDFISH) += goldfish/
obj-$(CONFIG_CHROME_PLATFORMS) += chrome/
obj-$(CONFIG_SURFACE_PLATFORMS) += surface/
+obj-$(CONFIG_CZNIC_PLATFORMS) += cznic/
Quoted 6 lines
Quoted 4 lines
Quoted 1 line
Why not ordered (to some extent) here (as you did in the other file)?
Where should I put it? The other entries are not ordered, see
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/platform/Makefile?h=v6.6-rc2With the visible context above, at least after chrome already much better, no?
OK
...
Quoted 7 lines
Quoted 5 lines
Quoted 1 line
Missing
types.h,sysfs.h,mod_devicetable.h,i2c.h, ...
i2c.hcomes fromturris-omnia-mcu.h. The others I will fix. Does the the .c file also include all those headers if it includes a local header already?For generic ones, yes.
OK
Quoted 18 lines
Quoted 4 lines
Quoted 3 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ if (ret < 0)Can it return positive value? What would be the meaning of it?
Quoted 13 lines
Originally I had it return 0 on success, but recently I've had a discussion about this same thing with LED subsystem maintainer about some LED patches, Lee Jones, who requested the
_readfunction to return number of bytes read on success. See at
https://lore.kernel.org/linux-leds/[email protected]/
where he says:
Read and write APIs, even abstracted ones such as these generally return the number of bytes successfully read and written respectively. If you are going to normalise, then please do so against this standard.
I do not agree with him, since this is a local command accessor which only succeeds if all the bytes are read/written as requested. But I adjusted my code as per his request since he is the maintainer.
Quoted 5 lines
This is strange. For example, regmap APIs never returns amount of data written or read. I think it's solely depends on the API. It might be useful for i²c APIs, in case you can do something about it. but if you have wrappers on top of that already (meaning not using directly the i2c_*() calls, I dunno the positive return is anyhow useful.
I shall change it back then.
...
Quoted 11 lines
Quoted 7 lines
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu-base.c line 154
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu-base.c new file +1
-0+int status;My gosh, it's a _third_ name for the same!
The variable holds value of the status register, which is needed at another point in this function. I thought naming it just "ret" would cause confusions... Do you really propose to rename it such?
It's not bad per se, just put yourself on the place of somebody who would like to understand the semantics of all these variables. Maybe you can add a comment on top explaining that (in short)?
OK
Quoted 11 lines
Quoted 8 lines
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu-base.c line 186
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu-base.c new file +1
-0+for (int i =0 ; i <ARRAY_SIZE (features); ++i) {Why signed?
Because it is shorter and makes no difference and there are tons of
for (int i = 0; i <ARRAY_SIZE(x); i++)
in the kernel already. But if you really want, I can change it. Please let me know.It just makes harder to get the code, because of possible unneeded questions like "if it's a signed type, maybe it's on purpose?"
OK
Quoted 8 lines
Quoted 4 lines
Quoted 1 line
Why pre-increment?
Again, it makes no difference, is widely used in the kernel and I personally prefer it. But I will change it if you want.
Pre-increment usually also a sign of something special about the iterator variable. In non-special cases we use post-increment. And if you count the use of each you will see the difference.
OK
...
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu.h line 37
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu.h new file +1
-0+if (likely (ret ==ARRAY_SIZE (msgs)))Why
likely()? Please, justify.Becuase it is unlikely the I2C transaction will fail. In most cases, it does not.
Yes, but why
likely()is needed? So, i.o.w. what's the benefit in _this_ case?
Compiler optimization (one branch avoided). But I guess this isn't a hot path, since I2C is insanely slow anyway. OK, I shall remove the likely() usage.
Quoted 7 lines
...
Quoted 5 lines
Quoted 4 lines
Quoted 1 line
+enum
commands_e{Are you sure the name is unique enough / properly namespaced? Same Q to all enums.
Quoted 6 lines
Quoted 2 lines
I can change it. I wanted it to be compatible with the header in the firmware repository.
Are these being generated, or also statically written in that repo? If the latter, I think better to use more unique naming schema in the kernel.
OK, I shall add some prefix.
Marek
Re: [PATCH v2 2/7] platform: cznic: Add preliminary support for Turris Omnia MCU
Quoted 6 lines · Marek Behún
Quoted 4 lines · On Tue, 19 Sep 2023 21:27:04 +0300 Andy Shevchenko <[email protected]> wrote:
Quoted 3 lines · Marek Behún
Quoted 1 line · On Tue, 19 Sep 2023 15:29:08 +0300 Andy Shevchenko <[email protected]> wrote:
On Tue, Sep 19, 2023 at 12:38:10PM +0200, Marek Behún wrote:
...
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu.h line 37
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu.h new file +1
-0+if (likely (ret ==ARRAY_SIZE (msgs)))Why
likely()? Please, justify.
Quoted 8 lines
Quoted 4 lines
Quoted 2 lines
Becuase it is unlikely the I2C transaction will fail. In most cases, it does not.
Yes, but why
likely()is needed? So, i.o.w. what's the benefit in _this_ case?Compiler optimization (one branch avoided). But I guess this isn't a hot path, since I2C is insanely slow anyway. OK, I shall remove the
likely()usage.
Have you seen the difference in the generated code, btw?
I don't think it will get you one independently on the hot/slow path.
Signature · 2 lines
With Best Regards, Andy Shevchenko
Marek Behún
maintainerPATCH v2 3/7 platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs
In reply to Marek BehúnAdd support for GPIOs connected to the MCU on the Turris Omnia board.
This include:
- 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
--- .../sysfs-bus-i2c-devices-turris-omnia-mcu | 16 + drivers/platform/cznic/Kconfig | 15 + drivers/platform/cznic/Makefile | 1 + .../platform/cznic/turris-omnia-mcu-base.c | 12 + .../platform/cznic/turris-omnia-mcu-gpio.c | 973 ++++++++++++++++++ drivers/platform/cznic/turris-omnia-mcu.h | 56 + 6 files changed, 1073 insertions(+) create mode 100644 drivers/platform/cznic/turris-omnia-mcu-gpio.c
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu +16 -0
@@ -1,3 +1,19 @@ +What: /sys/bus/i2c/devices/<mcu_device>/front_button_mode +Date: August 2023 +KernelVersion: 6.6 +Contact: Marek Behún <[email protected]> +Description: (RW) The front button on the Turris Omnia router can be + configured either to change the intensity of all the LEDs on the + front panel, or to send the press event to the CPU as an + interrupt. + + This file switches between these two modes: + - "mcu" makes the button press event be handled by the MCU to + change the LEDs panel intensity. + - "cpu" makes the button press event be handled by the CPU. + + Format: %s. + What: /sys/bus/i2c/devices/<mcu_device>/fw_features Date: August 2023 KernelVersion: 6.6
drivers/platform/cznic/Kconfig +15 -0
@@ -17,9 +17,24 @@ config TURRIS_OMNIA_MCU tristate "Turris Omnia MCU driver" depends on MACH_ARMADA_38X || COMPILE_TEST depends on I2C + select GPIOLIB + select GPIOLIB_IRQCHIP help Say Y here to add support for the features implemented by the microcontroller on the CZ.NIC's Turris Omnia SOHO router. + The features include: + - GPIO pins + - to get front button press events (the front button can be + configured either to generate press events to the CPU or to change + front LEDs panel brightness) + - to enable / disable USB port voltage regulators and to detect + USB overcurrent + - to detect MiniPCIe / mSATA card presence in MiniPCIe port 0 + - to configure resets of various peripherals on board revisions 32+ + - to enable / disable the VHV voltage regulator to the SOC in order + to be able to program SOC's OTP on board revisions 32+ + - to get input from the LED output pins of the WAN ethernet PHY, LAN + switch and MiniPCIe ports To compile this driver as a module, choose M here; the module will be called turris-omnia-mcu.
drivers/platform/cznic/Makefile +1 -0
@@ -2,3 +2,4 @@ obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o turris-omnia-mcu-objs := turris-omnia-mcu-base.o +turris-omnia-mcu-objs += turris-omnia-mcu-gpio.o
drivers/platform/cznic/turris-omnia-mcu-base.c +12 -0
@@ -222,9 +222,20 @@ static int omnia_mcu_probe(struct i2c_client *client)return -ENODATA ; } + ret =omnia_mcu_register_gpiochip (mcu); +if (ret) +return ret; +return 0 ; } +static void omnia_mcu_remove (struct i2c_client *client) +{ +struct omnia_mcu *mcu =i2c_get_clientdata (client); + +omnia_mcu_unregister_gpiochip (mcu); +} +static const struct of_device_id of_omnia_mcu_match[] = { { .compatible ="cznic,turris-omnia-mcu" , }, {},
@@ -238,6 +249,7 @@ MODULE_DEVICE_TABLE(i2c, omnia_mcu_id);static struct i2c_driver omnia_mcu_driver = { .probe = omnia_mcu_probe, + .remove = omnia_mcu_remove, .id_table = omnia_mcu_id, .driver = { .name ="turris-omnia-mcu" ,
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +973 -0
973 lines not on this page · raw message
drivers/platform/cznic/turris-omnia-mcu.h +56 -0
81 lines not on this page · raw message
Signature · 1 line
2.41.0
Re: [PATCH v2 3/7] platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs
Quoted 3 lines · Marek Behún
Add support for GPIOs connected to the MCU on the Turris Omnia board.
This include:
includes
Quoted 6 lines
- 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?
...
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu line 3
Quoted 2 lines
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu +2
-0+Date: August 2023 +KernelVersion: 6.6
Same as per previous patch review.
...
drivers/platform/cznic/turris-omnia-mcu-base.c static int omnia_mcu_probe(struct i2c_client *client) · line 229
Quoted 5 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +4
-0+ ret =omnia_mcu_register_gpiochip (mcu); +if (ret) +return ret; +return 0 ;
return ..._gpiochip(...);
?
...
Quoted 5 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +5
-0+switch (cmd) { +case CMD_GENERAL_CONTROL : + buf[1 ] = val; + buf[2 ] = mask; + len =3 ;
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 199
Quoted 10 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +10
-0+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()?
Quoted 1 line
(quoted diff) +1
-0+ }
...
Quoted 10 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +10
-0+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,
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 242
Quoted 10 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +9
-0+ EXT_CTL_PHY_SFP_AUTO); +mutex_unlock (&mcu->lock); + +if (val <0 ) +return val; + +if (val) +return GPIO_LINE_DIRECTION_IN ; +else
Redundant 'else'.
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 248
Quoted 7 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +6
-0+return GPIO_LINE_DIRECTION_OUT ; + } + +if (omnia_gpios[offset].ctl_cmd) +return GPIO_LINE_DIRECTION_OUT ; +else
Ditto.
Quoted 2 lines
(quoted diff) +2
-0+ return GPIO_LINE_DIRECTION_IN; +}
...
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 262
Quoted 6 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +6
-0+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.
...
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 272
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +1
-0+uint16_t val, mask;
So, why uintXX_t out of a sudden?
...
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 282
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +3
-0+if (gpio->ctl_cmd) +return omnia_ctl_cmd (mcu, gpio->ctl_cmd, val, mask); +else
Redundant 'else'.
Quoted 2 lines
(quoted diff) +2
-0+ return -EOPNOTSUPP; +}
...
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 392
Quoted 7 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +6
-0+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.
Quoted 11 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +11
-0+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); +}
...
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 439
Quoted 9 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +8
-0+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) ...
?
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 444
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +3
-0+mutex_unlock (&mcu->lock); +if (err) +dev_err (&mcu->client->dev,"Cannot set GPIOs: %d\n" , err);
How is useful this one?
...
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 454
Quoted 8 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +8
-0+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.
Quoted 2 lines
(quoted diff) +2
-0+ return true; +}
...
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 464
Quoted 7 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +6
-0+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?
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 470
Quoted 5 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +5
-0+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...
Review
Quoted 2 lines
(quoted diff) +2
-0+ if (omnia_gpio_available(mcu, gpio)) + set_bit(i, valid_mask);
...and assign_bit() here.
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +4
-0+ } + +return 0 ; +}
...
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 582
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +3
-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
...
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 594
Quoted 7 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +7
-0+/* +* 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;
Quoted 5 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +5
-0+ mcu->both = rising & falling; + mcu->is_cached &= mcu->both; + } + +mutex_unlock (&mcu->lock);
...
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 620
Quoted 7 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +6
-0+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).
Quoted 10 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +10
-0+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); + } +}
...
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 636
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +1
-0+u8 cmd[9 ] = {};
Magic number in a few places. Please, use self-explanatory pre-defined constant instead.
...
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 666
Quoted 2 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +2
-0+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.
...
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 673
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +4
-0+ 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.
...
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 691
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +1
-0+int ret =omnia_cmd_read_u16 (mcu->client,CMD_GET_STATUS_WORD );
Please, split this for better maintenance.
Quoted 2 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +2
-0+if (ret <0 ) +return ret;
...
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +4
-0+static irqreturn_t omnia_irq_thread_handler (int irq,void *dev_id) +{ +struct omnia_mcu *mcu = dev_id; +irqreturn_t res =IRQ_NONE ;
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 822
Quoted 10 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +10
-0+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).
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 826
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +2
-0+handle_nested_irq (nested_irq); + res =IRQ_HANDLED ;
No need.
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 829
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +2
-0+ } +return res;
return IRQ_RETVAL(pending);
Quoted 1 line
+}
...
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 844
Quoted 13 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +12
-0+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?/
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 847
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +2
-0+return val; +return sysfs_emit (buf,"%s\n" , val ?"cpu" :"mcu" );
With a static const array of string literals...
Quoted 1 line
+}
...
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 865
Quoted 6 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +6
-0+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.
...
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 882
Quoted 8 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +8
-0+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() ?
...
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 925
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +4
-0+ 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.
Quoted 1 line
(quoted diff) +1
-0+ }
...
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 959
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +1
-0+ 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 ?
...
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 972
Quoted 6 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +6
-0+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?
Quoted 1 line
+}
...
drivers/platform/cznic/turris-omnia-mcu.h line 25
Quoted 7 lines
struct
omnia_mcu{drivers/platform/cznic/turris-omnia-mcu.h +3
-0struct 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.
drivers/platform/cznic/turris-omnia-mcu.h line 30
Quoted 6 lines
drivers/platform/cznic/turris-omnia-mcu.h +5
-0+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.
Quoted 2 lines
drivers/platform/cznic/turris-omnia-mcu.h +1
-0+bool button_pressed_emul; };
...
drivers/platform/cznic/turris-omnia-mcu.h static inline int omnia_cmd_read(const struct i2c_client *client, u8 cmd, void * · line 78
Quoted 8 lines
drivers/platform/cznic/turris-omnia-mcu.h +7
-0+if (!bits) { + *reply =0 ; +return 0 ; + } + + ret =omnia_cmd_read (client, cmd, reply, (ilog2 (bits) >>3 ) +1 ); +
Redundant blank line.
drivers/platform/cznic/turris-omnia-mcu.h static inline int omnia_cmd_read(const struct i2c_client *client, u8 cmd, void * · line 80
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu.h +2
-0+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__ ...
Quoted 1 line
(quoted diff) +1
-0+ return ret < 0 ? ret : 0;
Signature · 2 lines
With Best Regards, Andy Shevchenko
Marek Behún
maintainerRe: [PATCH v2 3/7] platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs
Hi Andy,
I'm sending reply to some of your comments. For those to which I do not reply I will simply incorporate your suggestions.
Quoted 9 lines · On Tue, 19 Sep 2023 16:00:39 +0300 Andy Shevchenko <[email protected]> wrote:
Quoted 6 lines · Marek Behún
- 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 pinCan the main driver provide a regmap and all other use it?
It can't. These are not registers accessible via I2C at specific addresses, but commands with different-length arguments. This was already discussed 4 years ago with Jacek, see
https://www.spinics.net/lists/linux-leds/msg11587.html
There, Jacek suggested writing regmap API for the leds-turris-omnia driver (the MCU is also a LED controller and has same command interface, only at different I2C address).
Quoted 5 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ if (gpio->ctl_cmd) + return -EOPNOTSUPP;I believe internally we use ENOTSUPP.
Ditto for all cases like this.
checkpatch warns:
ENOTSUPP is not a SUSV4 error code, prefer EOPNOTSUPP
Quoted 14 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 439
Quoted 11 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +8
-0+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
Quoted 5 lines
if (err) goto out_unlock; if (_mask) ...
Linus suggested using guards, so I will refactor this away.
Quoted 7 lines
Review
Quoted 5 lines
(quoted diff) +3
-0+ mutex_unlock(&mcu->lock); + if (err) + dev_err(&mcu->client->dev, "Cannot set GPIOs: %d\n", err);How is useful this one?
The function does not return, this way we will know something has gone wrong. I am used to do this from the networking subsystem, where people at least from mv88e6xxx wanted such behavior. Do you want me to drop this?
Quoted 11 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 464
Quoted 7 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +6
-0+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.hincluded?
bitmap.h actually for this
Quoted 7 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 673
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +4
-0+ 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.
Huh? As in
rising = reply & 0x00ff00ff00ff00ff;
falling = reply & 0xff00ff00ff00ff00;
?
But then I can't or the rising bit with the corresponding falling bit to get pending...
Or I guess i can with:
pending = rising & (pending >> 8);
Am I understanding you correctly?
But then I would need to store the mask in driver data as a 64-bit value with half the data not used. Also the CPU is 32-bit.
Quoted 10 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 882
Quoted 8 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +8
-0+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()?
I only want to create ane attribute_group. (I am using devm_device_add_group(), not devm_device_add_groups()).
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 959
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +1
-0+ 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 ?
Can I add them conditionally? GPIO chip is always created, but the patch 4/7 only adds the attributes conditionally. Is it possible via .dev_groups?
Quoted 11 lines
...
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 972
Quoted 6 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +5
-0+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?
No, the mutex may be used in the work. Can't destroy it first. Or am I misunderstanding something?
Quoted 10 lines
drivers/platform/cznic/turris-omnia-mcu.h line 25
Quoted 7 lines
struct
omnia_mcu{drivers/platform/cznic/turris-omnia-mcu.h +3
-0struct 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.
kabel@dellmb ~/linux $ ./scripts/bloat-o-meter \
turris-omnia-mcu.o turris-omnia-mcu-gpiochip-first.o
add/remove: 0/0 grow/shrink: 9/0 up/down: 52/0 (52)
Function old new delta omnia_mcu_register_gpiochip 840 852 +12 omnia_mcu_probe 696 708 +12 omnia_mcu_unregister_gpiochip 20 24 +4 omnia_irq_bus_sync_unlock 256 260 +4 omnia_irq_bus_lock 36 40 +4 omnia_gpio_get_multiple 872 876 +4 omnia_gpio_get 372 376 +4 fw_features_show 28 32 +4 front_button_mode_show 260 264 +4
Total: Before=10468, After=10520, chg +0.50%
Seems the code grew when I swapped it.
Marek
Re: [PATCH v2 3/7] platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs
Quoted 3 lines · Marek Behún
Quoted 1 line · On Tue, 19 Sep 2023 16:00:39 +0300 Andy Shevchenko <[email protected]> wrote:
On Tue, Sep 19, 2023 at 12:38:11PM +0200, Marek Behún wrote:
...
Quoted 4 lines
Quoted 1 line
Ditto for all cases like this.
checkpatch warns:
ENOTSUPP is not a SUSV4 error code, prefer EOPNOTSUPP
So, fix checkpatch then. It has false positives, because it doesn't know if the actual error code will sneak outside the kernel or not. But in the subsystem we internally use ENOTSUPP.
...
Quoted 1 line
Linus suggested using guards, so I will refactor this away.
Even better.
...
Quoted 9 lines
Quoted 4 lines
Review
Quoted 2 lines
(quoted diff) +1
-0+ dev_err(&mcu->client->dev, "Cannot set GPIOs: %d\n", err);How is useful this one?
The function does not return, this way we will know something has gone wrong. I am used to do this from the networking subsystem, where people at least from mv88e6xxx wanted such behavior. Do you want me to drop this?
You are the developer and owner of the specific hardware, if you have a good justification for this message _in production_, then leave it, otherwise drop.
...
Quoted 7 lines
Quoted 5 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ bitmap_zero(valid_mask, ngpios);No need.
Also do you have
bitops.hincluded?
bitmap.hactually for this
Right, but I already thought a step forward, when you drop bitmap APIs, the bitops are still in place.
...
Quoted 17 lines
Quoted 7 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 673
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +4
-0+ 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.Huh? As in
rising = reply &0x00ff00ff00ff00ff;
falling = reply &0xff00ff00ff00ff00;
?
But then I can't or the rising bit with the corresponding falling bit to get pending...
Or I guess i can with:
pending = rising & (pending >> 8);
Quoted 4 lines
Am I understanding you correctly?
But then I would need to store the mask in driver data as a 64-bit value with half the data not used. Also the CPU is 32-bit.
If you use proper bitmaps, perhaps this will be easier. You can use one for each and merge them whenever you want (with bitmap_or() call) or split (with bitmap_and() respectively):
bitmap_or(full, raising, failing); // merge bitmap_and(raising, full, rasing_mask); // split
...
Quoted 4 lines
Quoted 1 line
ATTRIBUTE_GROUPS()?I only want to create ane
attribute_group. (I am usingdevm_device_add_group(), notdevm_device_add_groups()).
...and you should not use that APIs.
Quoted 8 lines
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 959
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +1
-0+ 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 ?
Can I add them conditionally? GPIO chip is always created, but the patch 4/7 only adds the attributes conditionally. Is it possible via .dev_groups?
Yes. You use __ATTRIBUTE_GROUPS() and .is_visible callback.
...
Quoted 11 lines
Quoted 8 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 972
Quoted 6 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +5
-0+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?
No, the mutex may be used in the work. Can't destroy it first. Or am I misunderstanding something?
I mean you are using a lot of devm(), can mutex be used in IRQ or whatever that can be triggered after this call?
...
Quoted 23 lines
Quoted 9 lines
drivers/platform/cznic/turris-omnia-mcu.h line 25
Quoted 6 lines
drivers/platform/cznic/turris-omnia-mcu.h +3
-0struct 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.kabel@dellmb ~/linux $
./scripts/bloat-o-meter\
turris-omnia-mcu.o turris-omnia-mcu-gpiochip-first.o
add/remove: 0/0 grow/shrink: 9/0 up/down: 52/0 (52)Function old new delta omnia_mcu_register_gpiochip 840 852 +12 omnia_mcu_probe 696 708 +12 omnia_mcu_unregister_gpiochip 20 24 +4 omnia_irq_bus_sync_unlock 256 260 +4 omnia_irq_bus_lock 36 40 +4 omnia_gpio_get_multiple 872 876 +4 omnia_gpio_get 372 376 +4 fw_features_show 28 32 +4 front_button_mode_show 260 264 +4
Quoted 3 lines
Total: Before=10468, After=10520, chg +0.50%
Seems the code grew when I swapped it.
Thanks for checking! It means that access to client pointer is needed more often.
Signature · 2 lines
With Best Regards, Andy Shevchenko
Marek Behún
maintainerRe: [PATCH v2 3/7] platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs
Hello Andy,
Quoted 6 lines · On Thu, 21 Sep 2023 12:55:08 +0300 Andy Shevchenko <[email protected]> wrote:
Quoted 2 lines
checkpatch warns:
ENOTSUPP is not a SUSV4 error code, prefer EOPNOTSUPPSo, fix checkpatch then. It has false positives, because it doesn't know if the actual error code will sneak outside the kernel or not. But in the subsystem we internally use ENOTSUPP.
I will change it, sorry, checkpatch warning confused me.
Quoted 22 lines
Quoted 17 lines
Quoted 7 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 673
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +4
-0+ 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.Huh? As in
rising = reply &0x00ff00ff00ff00ff;
falling = reply &0xff00ff00ff00ff00;
?
But then I can't or the rising bit with the corresponding falling bit to get pending...
Or I guess i can with:
pending = rising & (pending >> 8);Quoted 4 lines
Am I understanding you correctly?
But then I would need to store the mask in driver data as a 64-bit value with half the data not used. Also the CPU is 32-bit.
Quoted 6 lines
If you use proper bitmaps, perhaps this will be easier. You can use one for each and merge them whenever you want (with
bitmap_or()call) or split (withbitmap_and()respectively):
bitmap_or(full, raising, failing); // mergebitmap_and(raising, full, rasing_mask); // split
Hmm. But then what? I or the result and use it as pending interrupt bitmap, to be iterated over. The indexes of the bits correspond to the constants in the MCU API.
So after your suggestion I have rising and falling containgin
rising = 00rr00rr00rr00rr; /* r means rising bits */ falling = 00ff00ff00ff00ff; /* f means falling bits */ pending = rising | falling;
which means:
pending = pp00pp00pp00pp; /* p means pending bits */
But these bit positions do not correspond to the interrupt number anymore.
I still think the de-interleaving of the buffer from
rr ff rr ff rr ff rr ff
into two words:
rising = rrrrrrrr;
falling = ffffffff;
is simpler...
Or am I wrong?
Quoted 9 lines
Quoted 9 lines
Quoted 8 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 972
Quoted 6 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +5
-0+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?
Quoted 5 lines
Quoted 2 lines
No, the mutex may be used in the work. Can't destroy it first. Or am I misunderstanding something?
I mean you are using a lot of
devm(), can mutex be used in IRQ or whatever that can be triggered after this call?
OK, I think I need to free the irq before canceling the work. Thank you!
Marek
Re: [PATCH v2 3/7] platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs
Quoted 2 lines · Marek Behún
On Thu, 21 Sep 2023 12:55:08 +0300
Andy Shevchenko <[email protected]> wrote:
...
Quoted 42 lines
Quoted 22 lines
Quoted 17 lines
Quoted 7 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 673
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +4
-0+ 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.Huh? As in
rising = reply &0x00ff00ff00ff00ff;
falling = reply &0xff00ff00ff00ff00;
?
But then I can't or the rising bit with the corresponding falling bit to get pending...
Or I guess i can with:
pending = rising & (pending >> 8);Quoted 4 lines
Am I understanding you correctly?
But then I would need to store the mask in driver data as a 64-bit value with half the data not used. Also the CPU is 32-bit.
Quoted 6 lines
If you use proper bitmaps, perhaps this will be easier. You can use one for each and merge them whenever you want (with
bitmap_or()call) or split (withbitmap_and()respectively):
bitmap_or(full, raising, failing); // mergebitmap_and(raising, full, rasing_mask); // splitHmm. But then what? I or the result and use it as pending interrupt bitmap, to be iterated over. The indexes of the bits correspond to the constants in the MCU API.
So after your suggestion I have rising and falling containgin
rising = 00rr00rr00rr00rr; /* r means rising bits */ falling =00ff00ff00ff00ff; /* f means falling bits */ pending = rising | falling;
which means:
pending = pp00pp00pp00pp; /* p means pending bits */
But these bit positions do not correspond to the interrupt number anymore.
Quoted 6 lines
I still think the de-interleaving of the buffer from
rr ff rr ff rr ff rr ff
into two words:
rising = rrrrrrrr;
falling = ffffffff;
is simpler...
There are two sides of this: OS and hardware. See Xilinx GPIO driver how it's made there. But before going that way, check on
https://lore.kernel.org/all/[email protected]/
That APIs you would need I am pretty sure.
...
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 6 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 972
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +3
-0+if (!(mcu->features &FEAT_NEW_INT_API )) +cancel_delayed_work_sync (&mcu->button_release_emul_work); + +mutex_destroy (&mcu->lock);Wrong order?
Quoted 7 lines
Quoted 5 lines
Quoted 2 lines
No, the mutex may be used in the work. Can't destroy it first. Or am I misunderstanding something?
I mean you are using a lot of
devm(), can mutex be used in IRQ or whatever that can be triggered after this call?OK, I think I need to free the irq before canceling the work. Thank you!
Can you rather switch everything to be devm managed?
Signature · 2 lines
With Best Regards, Andy Shevchenko
Marek Behún
maintainerRe: [PATCH v2 3/7] platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs
Quoted 55 lines · On Fri, 22 Sep 2023 17:18:56 +0300 Andy Shevchenko <[email protected]> wrote:
Quoted 2 lines · Marek Behún
On Thu, 21 Sep 2023 12:55:08 +0300
Andy Shevchenko <[email protected]> wrote:...
Quoted 42 lines
Quoted 22 lines
Quoted 17 lines
Quoted 7 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 673
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +4
-0+ 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.Huh? As in
rising = reply &0x00ff00ff00ff00ff;
falling = reply &0xff00ff00ff00ff00;
?
But then I can't or the rising bit with the corresponding falling bit to get pending...
Or I guess i can with:
pending = rising & (pending >> 8);Quoted 4 lines
Am I understanding you correctly?
But then I would need to store the mask in driver data as a 64-bit value with half the data not used. Also the CPU is 32-bit.
Quoted 6 lines
If you use proper bitmaps, perhaps this will be easier. You can use one for each and merge them whenever you want (with
bitmap_or()call) or split (withbitmap_and()respectively):
bitmap_or(full, raising, failing); // mergebitmap_and(raising, full, rasing_mask); // splitHmm. But then what? I or the result and use it as pending interrupt bitmap, to be iterated over. The indexes of the bits correspond to the constants in the MCU API.
So after your suggestion I have rising and falling containgin
rising = 00rr00rr00rr00rr; /* r means rising bits */ falling =00ff00ff00ff00ff; /* f means falling bits */ pending = rising | falling;
which means:
pending = pp00pp00pp00pp; /* p means pending bits */
But these bit positions do not correspond to the interrupt number anymore.Quoted 6 lines
I still think the de-interleaving of the buffer from
rr ff rr ff rr ff rr ff
into two words:
rising = rrrrrrrr;
falling = ffffffff;
is simpler...
Quoted 4 lines
There are two sides of this: OS and hardware. See Xilinx GPIO driver how it's made there. But before going that way, check on
https://lore.kernel.org/all/[email protected]/
That APIs you would need I am pretty sure.
Andy, thank you for patience in reviewing this.
Hmm. I like the names, scatter and gather. In the firmware, I used interleave and deinterleave, see
https://gitlab.nic.cz/turris/hw/omnia_hw_ctrl/-/blob/master/src/drivers/i2c_iface.c#L360
But those functions work bit-wise. I realize that the I2C transfers in the driver are so slow that such bit-wise cycling over a bitmap won't matter much, but I still find my original proposal more simple and straight-forward. But I will cave if you insist. Please let me know (and can I then send your local patch in the series?)
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
Quoted 6 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 972
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +3
-0+if (!(mcu->features &FEAT_NEW_INT_API )) +cancel_delayed_work_sync (&mcu->button_release_emul_work); + +mutex_destroy (&mcu->lock);Wrong order?
No, the mutex may be used in the work. Can't destroy it first. Or am I misunderstanding something?
Quoted 6 lines
Quoted 4 lines
Quoted 2 lines
I mean you are using a lot of
devm(), can mutex be used in IRQ or whatever that can be triggered after this call?OK, I think I need to free the irq before canceling the work. Thank you!
Can you rather switch everything to be devm managed?
There are no devm_ calls for mutex and work initialization. Are you suggesting that I should write a release function for the gpio sub-driver? Something like
static void omnia_gpiochip_release(dev, res)
{
cancel_work();
mutex_destroy();
}
int omnia_mcu_register_gpiochip(mcu)
{
...
x = devres_alloc(omnia_gpiochip_release);
devres_add(dev, x);
...
}
Marek
Re: [PATCH v2 3/7] platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs
Quoted 5 lines · Marek Behún
Quoted 3 lines · On Fri, 22 Sep 2023 17:18:56 +0300 Andy Shevchenko <[email protected]> wrote:
Quoted 2 lines · Marek Behún
On Thu, 21 Sep 2023 12:55:08 +0300
Andy Shevchenko <[email protected]> wrote:
...
Quoted 60 lines
Quoted 49 lines
Quoted 42 lines
Quoted 22 lines
Quoted 17 lines
Quoted 7 lines
Review
Quoted 4 lines
(quoted diff) +4
-0+ 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.Huh? As in
rising = reply &0x00ff00ff00ff00ff;
falling = reply &0xff00ff00ff00ff00;
?
But then I can't or the rising bit with the corresponding falling bit to get pending...
Or I guess i can with:
pending = rising & (pending >> 8);Quoted 4 lines
Am I understanding you correctly?
But then I would need to store the mask in driver data as a 64-bit value with half the data not used. Also the CPU is 32-bit.
Quoted 6 lines
If you use proper bitmaps, perhaps this will be easier. You can use one for each and merge them whenever you want (with
bitmap_or()call) or split (withbitmap_and()respectively):
bitmap_or(full, raising, failing); // mergebitmap_and(raising, full, rasing_mask); // splitHmm. But then what? I or the result and use it as pending interrupt bitmap, to be iterated over. The indexes of the bits correspond to the constants in the MCU API.
So after your suggestion I have rising and falling containgin
rising = 00rr00rr00rr00rr; /* r means rising bits */ falling =00ff00ff00ff00ff; /* f means falling bits */ pending = rising | falling;
which means:
pending = pp00pp00pp00pp; /* p means pending bits */
But these bit positions do not correspond to the interrupt number anymore.Quoted 6 lines
I still think the de-interleaving of the buffer from
rr ff rr ff rr ff rr ff
into two words:
rising = rrrrrrrr;
falling = ffffffff;
is simpler...Quoted 4 lines
There are two sides of this: OS and hardware. See Xilinx GPIO driver how it's made there. But before going that way, check on
https://lore.kernel.org/all/[email protected]/
That APIs you would need I am pretty sure.Andy, thank you for patience in reviewing this.
Hmm. I like the names, scatter and gather. In the firmware, I used interleave and deinterleave, see
https://gitlab.nic.cz/turris/hw/omnia_hw_ctrl/-/blob/master/src/drivers/i2c_iface.c#L360
Quoted 5 lines
But those functions work bit-wise. I realize that the I2C transfers in the driver are so slow that such bit-wise cycling over a bitmap won't matter much, but I still find my original proposal more simple and straight-forward. But I will cave if you insist. Please let me know (and can I then send your local patch in the series?)
You can. but I need to add test cases there.
Yes, I think the best is to have hardware values and Linux cached ones to be separated. Let me try my best and send it out this week.
...
Quoted 21 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
Quoted 6 lines
Review
Quoted 4 lines
(quoted diff) +3
-0+ if (!(mcu->features & FEAT_NEW_INT_API)) + cancel_delayed_work_sync(&mcu->button_release_emul_work); + + mutex_destroy(&mcu->lock);Wrong order?
No, the mutex may be used in the work. Can't destroy it first. Or am I misunderstanding something?
Quoted 6 lines
Quoted 4 lines
Quoted 2 lines
I mean you are using a lot of
devm(), can mutex be used in IRQ or whatever that can be triggered after this call?OK, I think I need to free the irq before canceling the work. Thank you!
Can you rather switch everything to be devm managed?
There are no
devm_calls for mutex and work initialization. Are you suggesting that I should write a release function for the gpio sub-driver? Something like
Quoted 5 lines
static void
omnia_gpiochip_release(dev, res)
{
cancel_work();
mutex_destroy();
}
Not together, but
- for mutex use devm_add_action_or_reset() as done in many other drivers for the same reason;
- for the work we have devm_work_autocancel()
(you need to include devm-helpers.h)
Quoted 7 lines
int
omnia_mcu_register_gpiochip(mcu)
{
...
x =devres_alloc(omnia_gpiochip_release);
devres_add(dev, x);
...
}
Signature · 2 lines
With Best Regards, Andy Shevchenko
Marek Behún
maintainerRe: [PATCH v2 3/7] platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs
In reply to Andy ShevchenkoQuoted 12 lines · On Tue, 19 Sep 2023 16:00:39 +0300 Andy Shevchenko <[email protected]> wrote:
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 439
Quoted 9 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +8
-0+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
Quoted 7 lines
if (err) goto out_unlock; if (_mask) ...?
Hi Andy,
so I am refactoring this to use guard(mutex), but now I have this:
guard(mutex, &mcu->lock);
if (ctl_mask) {
err = ...;
if (err)
goto out_err;
}
if (ext_ctl_mask) {
err = ...;
if (err)
goto out_err;
}
return;
out_err:
dev_err(dev, "Cannot set GPIOs: %d\n", err);which clearly is not any better... or at least the original
if (!err && ext_ctl_mask)
is better IMO.
Compare with:
guard(mutex, &mcu->lock); if (ctl_mask) err = ...; if (!err && ext_ctl_mask) err = ...; if (err) dev_err(dev, "Cannot set GPIOs: %d\n", err);
Do you have a better suggestion?
Re: [PATCH v2 3/7] platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs
Quoted 2 lines · Marek Behún
On Tue, 19 Sep 2023 16:00:39 +0300
Andy Shevchenko <[email protected]> wrote:
...
Quoted 42 lines
Quoted 12 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 439
Quoted 9 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +8
-0+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
Quoted 7 lines
if (err) goto out_unlock; if (_mask) ...?
Hi Andy,
so I am refactoring this to use guard(mutex), but now I have this:
guard(mutex, &mcu->lock); if (ctl_mask) { err = ...; if (err) goto out_err; } if (ext_ctl_mask) { err = ...; if (err) goto out_err; } return; out_err: dev_err(dev, "Cannot set GPIOs: %d\n", err);
Quoted 1 line
which clearly is not any better... or at least the original
...which rather means that the design of above is not so good, i.e. why do you need the same message in the different situations?
Quoted 2 lines
if (!err &&
ext_ctl_mask)
is better IMO.
I disagree.
Quoted 14 lines
Compare with:
guard(mutex, &mcu->lock); if (ctl_mask) err = ...; if (!err && ext_ctl_mask) err = ...; if (err) dev_err(dev, "Cannot set GPIOs: %d\n", err);
Quoted 1 line
Do you have a better suggestion?
Use different messages (if even needed) for different situations.
With cleanup.h in place you shouldn't supposed to have goto:s (in simple cases like yours).
Signature · 2 lines
With Best Regards, Andy Shevchenko
Re: [PATCH v2 3/7] platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs
In reply to Marek BehúnHi Marek,
thanks for your patch! This is a very interesting hardware.
Quoted 11 lines · Marek Behún
Add support for GPIOs connected to the MCU on the Turris Omnia board.
This include:
- 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 pinSigned-off-by Marek Behún <[email protected]>
OK all pretty straight-forward devices built on top of GPIO in something like device tree or ACPI etc.
Quoted 8 lines
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu +5
-0@@ -1,3 +1,19 @@ +What: /sys/bus/i2c/devices/<mcu_device>/front_button_mode +Date: August 2023 +KernelVersion: 6.6 +Contact: Marek Behún <[email protected]> +Description: (RW) The front button on the Turris Omnia router can be
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu line 15
Quoted 10 lines
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu +10
-0+ configured either to change the intensity of all the LEDs on the + front panel, or to send the press event to the CPU as an + interrupt. + + This file switches between these two modes: + - "mcu" makes the button press event be handled by the MCU to + change the LEDs panel intensity. + - "cpu" makes the button press event be handled by the CPU. + + Format: %s.
I understand what this does, but why does this have to be configured at runtime from userspace sysfs? Why can't this just be a property in device tree or ACPI (etc)? I don't imagine a user is going to change this back and forth are they? They likely want one or another.
drivers/platform/cznic/turris-omnia-mcu-base.c static int omnia_mcu_probe(struct i2c_client *client) · line 227
Quoted 9 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +3
-0@@ -222,9 +222,20 @@ static int omnia_mcu_probe(struct i2c_client *client)return -ENODATA ; } + ret =omnia_mcu_register_gpiochip (mcu); +if (ret) +return ret;
I guess it's OK to have a GPIOchip with the rest of the stuff, there are a few precedents, such as drivers/bcma.
drivers/platform/cznic/turris-omnia-mcu-base.c static int omnia_mcu_probe(struct i2c_client *client) · line 228
Quoted 1 line
drivers/platform/cznic/turris-omnia-mcu-base.c +1
-0+static const char *const omnia_mcu_gpio_names[64 ] = {
I would name these _templates since it is not the final names that will be used. Very nice with all debug names though!
Quoted 23 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +23
-0+#define _DEF_GPIO (_cmd, _ctl_cmd, _bit, _ctl_bit, _int_bit, _feat, _feat_mask) \ + { \ + .cmd = _cmd, \ + .ctl_cmd = _ctl_cmd, \ + .bit = _bit, \ + .ctl_bit = _ctl_bit, \ + .int_bit = _int_bit, \ + .feat = _feat, \ + .feat_mask = _feat_mask, \ + } +#define _DEF_GPIO_STS (_name) \ + _DEF_GPIO(CMD_GET_STATUS_WORD, 0, STS_ ## _name, 0, INT_ ## _name, 0, 0) +#define _DEF_GPIO_CTL (_name) \ + _DEF_GPIO(CMD_GET_STATUS_WORD, CMD_GENERAL_CONTROL, STS_ ## _name, \ + CTL_ ## _name, 0, 0, 0) +#define _DEF_GPIO_EXT_STS (_name, _feat) \ + _DEF_GPIO(CMD_GET_EXT_STATUS_DWORD, 0, EXT_STS_ ## _name, 0, \ + INT_ ## _name, FEAT_ ## _feat | FEAT_EXT_CMDS, \ + FEAT_ ## _feat | FEAT_EXT_CMDS) +#define _DEF_GPIO_EXT_STS_LED (_name, _ledext) \ + _DEF_GPIO(CMD_GET_EXT_STATUS_DWORD, 0, EXT_STS_ ## _name, 0, \ + INT_ ## _name, FEAT_LED_STATE_ ## _ledext, \ + FEAT_LED_STATE_EXT_MASK)
drivers/platform/cznic/turris-omnia-mcu-base.c static int omnia_mcu_probe(struct i2c_client *client)
Quoted 10 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +10
-0+#define _DEF_GPIO_EXT_STS_LEDALL (_name) \ + _DEF_GPIO(CMD_GET_EXT_STATUS_DWORD, 0, EXT_STS_ ## _name, 0, \ + INT_ ## _name, FEAT_LED_STATE_EXT_MASK, 0) +#define _DEF_GPIO_EXT_CTL (_name, _feat) \ + _DEF_GPIO(CMD_GET_EXT_CONTROL_STATUS, CMD_EXT_CONTROL, \ + EXT_CTL_ ## _name, EXT_CTL_ ## _name, 0, \ + FEAT_ ## _feat | FEAT_EXT_CMDS, \ + FEAT_ ## _feat | FEAT_EXT_CMDS) +#define _DEF_INT (_name) \ + _DEF_GPIO(0, 0, 0, 0, INT_ ## _name, 0, 0)
Ugh that's really hard to read and understand, but I can't think of anything better
so I guess we live with it.
drivers/platform/cznic/turris-omnia-mcu-base.c
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +4
-0+static const struct omnia_gpio { + u8 cmd, ctl_cmd; + u32 bit, ctl_bit; + u32 int_bit;
If they are really just ONE bit I would actually use an int, encode the bit number rather than the mask, and then use BIT( ..->int_bit) from <linux/bits.h> everywhere I need a mask. No strong preference though.
drivers/platform/cznic/turris-omnia-mcu-base.c
Quoted 5 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +5
-0+/* mapping from interrupts to indexes of GPIOs in the omnia_gpios array */ +static const unsigned int omnia_int_to_gpio_idx[32 ] = { + [ilog2 (INT_CARD_DET )] =4 , + [ilog2 (INT_MSATA_IND )] =5 , + [ilog2 (INT_USB30_OVC )] =6 ,
ilog2 is a bit unituitive for a programmer, are you a schooled mathematician Marek? ;)
It is also a consequence of using a bitmask rather than a bit number in the struct, all ilog2 does is fls-1. So what about just putting the bit number in the struct and then all of these assignments will look natural as well.
drivers/platform/cznic/turris-omnia-mcu-base.c
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +4
-0+/* index of PHY_SFP GPIO in the omnia_gpios array */ +enum { +OMNIA_GPIO_PHY_SFP_OFFSET =54 , +};
I would just use a define for this, the enum isn't very useful for singular values.
drivers/platform/cznic/turris-omnia-mcu-base.c
Quoted 6 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +6
-0+static int omnia_ctl_cmd (struct omnia_mcu *mcu,u8 cmd,u16 val,u16 mask) +{ +int err; + +mutex_lock (&mcu->lock); + err =omnia_ctl_cmd_unlocked (mcu, cmd, val, mask);
We just discussed this on an unrelated topic: *_unlocked means it should be called without need for the appropriate mutex to be locked, since it clearly requires &mcu->lock to be taken, this should be omnia_ctl_cmd_locked() as in "call this with the lock held".
I did a quick grep _locked to convince myself about this. Obvious exampled include wait_event_interruptible_locked_irq() or wake_up_locked() in the core kernel infrastructure that clearly states (include/linux/wait.h for wait_event_interruptible_locked():
"It must be called with wq.lock being held."
Quoted 2 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +2
-0+static int omnia_gpio_get (struct gpio_chip *gc,unsigned int offset) +{
(...)
drivers/platform/cznic/turris-omnia-mcu-base.c
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +3
-0+if (gpio->cmd ==CMD_GET_STATUS_WORD && + !(mcu->features &FEAT_NEW_INT_API )) +return !!(mcu->last_status & gpio->bit);
So I expect something like return !!(mcu->last_status & BIT(gpio->bit));
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +4
-0+static int omnia_gpio_get_multiple (struct gpio_chip *gc,unsigned long *mask, +unsigned long *bits) +{ +mutex_lock (&mcu->lock);
(...)
Quoted 2 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +2
-0+if (err) + goto err_unlock;
(...)
Quoted 2 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +2
-0+if (err) + goto err_unlock;
(...)
drivers/platform/cznic/turris-omnia-mcu-base.c
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +4
-0+if (err) + goto err_unlock; + +mutex_unlock (&mcu->lock);
This function is kind of a good example of where using scoped guards from <linux/cleanup.h> can simplify the code. After the initial lock you can just return on error and let the cleanup handler unlock the mutex, just guard(mutex)(&mcu->lock);
In the beginning and then it's done, the lock will be held until the function is exited.
drivers/platform/cznic/turris-omnia-mcu-base.c
Quoted 3 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +3
-0+static void omnia_gpio_set_multiple (struct gpio_chip *gc,unsigned long *mask, +unsigned long *bits) +{
(...)
same here.
Quoted 6 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +6
-0+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); +
drivers/platform/cznic/turris-omnia-mcu-base.c
Quoted 10 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +10
-0+bitmap_zero (valid_mask, ngpios); + +for (int i =0 ; i < ngpios; ++i) { +const struct omnia_gpio *gpio = &omnia_gpios[i]; + +if (!gpio->cmd && !gpio->int_bit) +continue ; + +if (omnia_gpio_available (mcu, gpio)) +set_bit (i, valid_mask);
Speaking of locks, isn't this where you should use __set_bit() rather than set_bit()?
__set_bit() is incomprehensible shorthand for "set_bit_nonatomic".
drivers/platform/cznic/turris-omnia-mcu-base.c
Quoted 9 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +9
-0+static void omnia_irq_shutdown (struct irq_data *d) +{ +struct gpio_chip *gc =irq_data_get_irq_chip_data (d); +struct omnia_mcu *mcu =gpiochip_get_data (gc); +irq_hw_number_t hwirq =irqd_to_hwirq (d); +u32 bit = omnia_gpios[hwirq].int_bit; + + mcu->rising &= ~bit; + mcu->falling &= ~bit;
Yeah so here it would be BIT(bit) etc.
drivers/platform/cznic/turris-omnia-mcu-base.c
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +4
-0+if (type &IRQ_TYPE_EDGE_RISING ) + mcu->rising |= bit; +else + mcu->rising &= ~bit;
And with bits in place of bitmasks these would be
if ()
__set_bit(bit, mcu->rising);
else
__clear_bit(bit, mcu->rising);
etc
Quoted 6 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +6
-0+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); +
drivers/platform/cznic/turris-omnia-mcu-base.c
Quoted 10 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +10
-0+bitmap_zero (valid_mask, ngpios); + +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);
__set_bit()
Quoted 5 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +5
-0+static bool omnia_irq_read_pending_new (struct omnia_mcu *mcu, +unsigned long *pending) +{ +u32 rising, falling; +u8 reply[8 ] = {};
drivers/platform/cznic/turris-omnia-mcu-base.c
Quoted 10 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +10
-0+size_t len; +int err; + +/* +* Determine how many bytes we need to read from the reply to the +* CMD_GET_INT_AND_CLEAR command in order to retrieve all unmasked +* interrupts. +*/ + len =max (((ilog2 (mcu->rising & mcu->mask) >>3 ) <<1 ) +1 , + ((ilog2 (mcu->falling & mcu->mask) >>3 ) <<1 ) +2 );
Really hard to read, and again easier if we store the bit number rather than the bitmask.
Quoted 1 line
+
mutex_lock(&mcu->lock);
Scoped guard?
Quoted 9 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +9
-0+static void button_release_emul_fn (struct work_struct *work) +{ +struct omnia_mcu *mcu =container_of (to_delayed_work (work), + struct omnia_mcu, + button_release_emul_work); + + mcu->button_pressed_emul = false; +generic_handle_irq_safe (mcu->client->irq); +}
(...)
Quoted 20 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +20
-0+/* +* The old firmware triggers an interrupt whenever status word changes, +* but does not inform about which bits rose or fell. We need to compute +* this here by comparing with the last status word value. +* +* The STS_BUTTON_PRESSED bit needs special handling, because the old +* firmware clears the STS_BUTTON_PRESSED bit on successful completion +* of the CMD_GET_STATUS_WORD command, resulting in another interrupt: +* - first we get an interrupt, we read the status word where +* STS_BUTTON_PRESSED is present, +* - MCU clears the STS_BUTTON_PRESSED bit because we read the status +* word, +* - we get another interrupt because the status word changed again +* (STS_BUTTON_PRESSED was cleared). +* +* The gpiolib-cdev, gpiolib-sysfs and gpio-keys input driver all call +* the gpiochip's .get() method after an edge event on a requested GPIO +* occurs. +* +* We ensure that the .get() method reads 1 for the button GPIO for some
drivers/platform/cznic/turris-omnia-mcu-base.c
Quoted 10 lines
drivers/platform/cznic/turris-omnia-mcu-base.c +10
-0+* time. +*/ + +if (status &STS_BUTTON_PRESSED ) { + mcu->button_pressed_emul = true; +mod_delayed_work (system_wq, &mcu->button_release_emul_work, +msecs_to_jiffies (50 )); + }else if (mcu->button_pressed_emul) { + status |=STS_BUTTON_PRESSED ; + }
I feel a bit icky about this, would be best if Dmitry Torokhov review such input subsystem-related things.
It looks like a reimplementation of drivers/input/keyboard/gpio_keys.c so why not just connect a gpio-keys device using either device tree or a software node? (Dmitry knows all about how to create proper software nodes for stuff like this, DT is self-evident.)
Quoted 1 line
+
mcu->gc.names=omnia_mcu_gpio_names;
Do we really support format strings in these names? I don't think so, or I'm wrong about my own code... (Which wouldn't be the first time.)
drivers/platform/cznic/turris-omnia-mcu-base.c
Quoted 4 lines
#include <
asm/byteorder.h>drivers/platform/cznic/turris-omnia-mcu-base.c +2
-0+#include <linux/gpio/driver.h> #include <linux/i2c.h> +#include <linux/log2.h>
I think we want to get rid of log2.h from this driver.
Yours,
Linus Walleij
Re: [PATCH v2 3/7] platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs
Quoted 1 line · Linus Walleij
On Tue, Sep 19, 2023 at 12:38 PM Marek Behún <[email protected]> wrote:
...
Quoted 11 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c line 532
Quoted 4 lines
drivers/platform/cznic/turris-omnia-mcu-gpio.c new file +4
-0+if (type &IRQ_TYPE_EDGE_RISING ) + mcu->rising |= bit; +else + mcu->rising &= ~bit;And with bits in place of bitmasks these would be
if ()
__set_bit(bit, mcu->rising);
else
__clear_bit(bit, mcu->rising);
More precisely __assign_bit() in this case.
Signature · 2 lines
With Best Regards, Andy Shevchenko
Marek Behún
maintainerRe: [PATCH v2 3/7] platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs
In reply to Linus WalleijHello Linus,
I am sending some comments/questions to your review. To those of your comments that I do not reply, I will incorporate your suggestion.
Quoted 15 lines · On Wed, 20 Sep 2023 13:58:37 +0200 Linus Walleij <[email protected]>…
Quoted 15 lines
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu +12
-0@@ -1,3 +1,19 @@ +What: /sys/bus/i2c/devices/<mcu_device>/front_button_mode +Date: August 2023 +KernelVersion: 6.6 +Contact: Marek Behún <[email protected]> +Description: (RW) The front button on the Turris Omnia router can be + configured either to change the intensity of all the LEDs on the + front panel, or to send the press event to the CPU as an + interrupt. + + This file switches between these two modes: + - "mcu" makes the button press event be handled by the MCU to + change the LEDs panel intensity.
Quoted 8 lines
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu line 15
Quoted 3 lines
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu +3
-0+ - "cpu" makes the button press event be handled by the CPU. + + Format: %s.I understand what this does, but why does this have to be configured at runtime from userspace sysfs? Why can't this just be a property in device tree or ACPI (etc)? I don't imagine a user is going to change this back and forth are they? They likely want one or another.
Yes, we really do expect this, we even want to have some applications on the router to do this.
Quoted 9 lines
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu line 19
Quoted 4 lines
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu +4
-0+static const struct omnia_gpio { + u8 cmd, ctl_cmd; + u32 bit, ctl_bit; + u32 int_bit;If they are really just ONE bit I would actually use an int, encode the bit number rather than the mask, and then use
BIT( ..->int_bit)from <linux/bits.h> everywhere I need a mask. No strong preference though.
See below.
Quoted 9 lines
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu
Quoted 5 lines
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu +5
-0+/* mapping from interrupts to indexes of GPIOs in the omnia_gpios array */ +static const unsigned int omnia_int_to_gpio_idx[32] = { + [ilog2(INT_CARD_DET)] = 4, + [ilog2(INT_MSATA_IND)] = 5, + [ilog2(INT_USB30_OVC)] = 6,ilog2 is a bit unituitive for a programmer, are you a schooled mathematician Marek? ;)
Quoted 3 lines
It is also a consequence of using a bitmask rather than a bit number in the struct, all ilog2 does is fls-1. So what about just putting the bit number in the struct and then all of these assignments will look natural as well.
Not really, altough I studied advanced analysis :) I guess ilog2 might confuse some people. Would bf_shf() (as in bitfield shift) from linux/bitfield.h be better?
So, the reasoning behind why the bits are defined as such in linux/turris-omnia-mcu-interface.h (from patch 2/7):
- if you look for example at enum sts_word_e, the entries correspond to fields withing the word. And not all fields are 1-bit wide. For example the STS_MCU_TYPE field is 2 bits wide. Similarly the FEAT_LED_STATE_EXT_MASK field in enum features_e
- I have seen similar code within many parts of the kernel and have adopted it. I think many developers are accustomed to it. Look for example into how register fields are defined in the mv88e6xxx driver header files
In order to make maintaining smoother, I would like to have the turris-omnia-mcu-interface.h file be one-to-one copy of the corresponding file within the MCU firmware repository
(https://gitlab.nic.cz/turris/hw/omnia_hw_ctrl/-/blob/master/src/drivers/i2c_iface.h)
wherein the constants are defined this way and used throughout the codebase.
Since I am the author and maintainer of the (current versions of) MCU firmware, I could take your suggestion and change the constants, and rewrite the firmware.
But taking into consideration the above two points, I am reluctant to do so.
So... do you insist on this change? I would rather keep it as it is, but I can change the ilog2() to bf_shf() to make it easier for non-mathematicians.
Quoted 10 lines
Quoted 10 lines
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu +10
-0+static bool omnia_irq_read_pending_new(struct omnia_mcu *mcu, + unsigned long *pending) +{ + u32 rising, falling; + u8 reply[8] = {}; + size_t len; + int err; + + /* + * Determine how many bytes we need to read from the reply to the
Quoted 8 lines
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu
Quoted 5 lines
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu +5
-0+ * CMD_GET_INT_AND_CLEAR command in order to retrieve all unmasked + * interrupts. + */ + len = max(((ilog2(mcu->rising & mcu->mask) >> 3) << 1) + 1, + ((ilog2(mcu->falling & mcu->mask) >> 3) << 1) + 2);Really hard to read, and again easier if we store the bit number rather than the bitmask.
No. Even if I take your suggestion and rewrite this to store bit offsets instead of bit masks, here I really need to compute this, since there might be multiple bits set. For example there might be bits 4, 5, and 14 set in mcu->rising (mcu->rising = 0x4030).
I need to determine how many bytes of the I2C reply I will need, and for that I need to know the position of the first set bit. (I could keep a track of the maximum set bit, but honestly this seems to me much easier).
I could use ffs(x) instead of ilog2(x) + 1.
Quoted 41 lines
Quoted 9 lines
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu +9
-0+static void button_release_emul_fn(struct work_struct *work) +{ + struct omnia_mcu *mcu = container_of(to_delayed_work(work), + struct omnia_mcu, + button_release_emul_work); + + mcu->button_pressed_emul = false; + generic_handle_irq_safe(mcu->client->irq); +}(...)
Quoted 20 lines
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu +20
-0+ /* + * The old firmware triggers an interrupt whenever status word changes, + * but does not inform about which bits rose or fell. We need to compute + * this here by comparing with the last status word value. + * + * The STS_BUTTON_PRESSED bit needs special handling, because the old + * firmware clears the STS_BUTTON_PRESSED bit on successful completion + * of the CMD_GET_STATUS_WORD command, resulting in another interrupt: + * - first we get an interrupt, we read the status word where + * STS_BUTTON_PRESSED is present, + * - MCU clears the STS_BUTTON_PRESSED bit because we read the status + * word, + * - we get another interrupt because the status word changed again + * (STS_BUTTON_PRESSED was cleared). + * + * The gpiolib-cdev, gpiolib-sysfs and gpio-keys input driver all call + * the gpiochip's .get() method after an edge event on a requested GPIO + * occurs. + * + * We ensure that the .get() method reads 1 for the button GPIO for someDocumentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu
Quoted 10 lines
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu +10
-0+ * time. + */ + + if (status & STS_BUTTON_PRESSED) { + mcu->button_pressed_emul = true; + mod_delayed_work(system_wq, &mcu->button_release_emul_work, + msecs_to_jiffies(50)); + } else if (mcu->button_pressed_emul) { + status |= STS_BUTTON_PRESSED; + }
Quoted 7 lines
I feel a bit icky about this, would be best if Dmitry Torokhov review such input subsystem-related things.
It looks like a reimplementation of
drivers/input/keyboard/gpio_keys.cso why not just connect a gpio-keys device using either device tree or a software node? (Dmitry knows all about how to create proper software nodes for stuff like this, DT is self-evident.)
You have it the other way around :-) This is done in order so that the gpio-keys driver can use it. Let me explain:
The new versions of the MCU firmware support rising and falling edges for GPIO events. So when button is pressed, we get an event, and when it is released, we get an event. gpio-keys works perfectly.
The problem with old MCU firmware is that the interrupt API was tragically designed, which resulted in that on button press event, we get an interrupt, and when we read the status word via I2C, the bit gets cleared immediately (and we get another interrupt because the bit is cleared). There is no interrupt when the button is truly released. This fake "button release interrupt" comes so fast that the gpio_keys driver ignores it even if we set debounce_interval to smallest possible value of 1ms (if I understand the gpio_keys driver correctly).
So this workaround is there so that we generate a GPIO release event for the button only after 50ms, if the MCU is running old firmware.
A similar thing is implemented in the gpio-keys driver when there is no release event: if the event source is an interrupt descriptor instead of a gpio descriptor, the gpio-keys will emulate button release. I could use this, but then we would need to change the device-tree based on whether the MCU is running old firmware vs new, which I want to avoid. I also want to avoid software nodes, since I want the button to be properly described in device-tree.
Honestly, because of this whole debacle I was also considering writing my own small input driver within this turris-omnia-mcu driver...
Quoted 5 lines
Quoted 1 line
+
mcu->gc.names=omnia_mcu_gpio_names;Do we really support format strings in these names? I don't think so, or I'm wrong about my own code... (Which wouldn't be the first time.)
Yes we do, but only one :) The number of the GPIO.
Quoted 6 lines
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu
Quoted 4 lines
#include <
asm/byteorder.h>Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu +2
-0+#include <linux/gpio/driver.h> #include <linux/i2c.h> +#include <linux/log2.h>I think we want to get rid of
log2.hfrom this driver.
See above, even if I take your suggestion, I will still need the position of the first set bit within word in omnia_irq_read_pending_new().
Marek
Marek Behún
maintainerRe: [PATCH v2 3/7] platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs
Quoted 1 line · On Thu, 21 Sep 2023 21:45:57 +0200 Marek Behún <[email protected]> wrote:
I could use ffs(x) instead of ilog2(x) + 1.
Pardon me, I meant fls(). Or maybe get_bitmask_order() from linux/bitops.h.
Marek
Re: [PATCH v2 3/7] platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs
Quoted 7 lines · Marek Behún
Quoted 1 line · On Thu, 21 Sep 2023 21:45:57 +0200 Marek Behún <[email protected]> wrote:
I could use ffs(x) instead of ilog2(x) + 1.
Pardon me, I meant
fls(). Or maybeget_bitmask_order()fromlinux/bitops.h.
In any case it's bitops.h APIs that you will need and I think it's fine and Linus will approve that.
Signature · 2 lines
With Best Regards, Andy Shevchenko
Marek Behún
maintainerPATCH v2 4/7 platform: cznic: turris-omnia-mcu: Add support for poweroff and wakeup
In reply to Marek BehúnAdd support for true board poweroff (MCU can disable all unnecessary voltage regulators) and wakeup at a specified time, implemented via a RTC driver so that the rtcwake utility can be used to configure it.
--- .../sysfs-bus-i2c-devices-turris-omnia-mcu | 16 ++ drivers/platform/cznic/Kconfig | 4 + drivers/platform/cznic/Makefile | 1 + .../platform/cznic/turris-omnia-mcu-base.c | 4 + .../platform/cznic/turris-omnia-mcu-gpio.c | 2 - .../cznic/turris-omnia-mcu-sys-off-wakeup.c | 245 ++++++++++++++++++ drivers/platform/cznic/turris-omnia-mcu.h | 19 ++ 7 files changed, 289 insertions(+), 2 deletions(-) create mode 100644 drivers/platform/cznic/turris-omnia-mcu-sys-off-wakeup.c
Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu +16 -0
@@ -14,6 +14,22 @@ Description: (RW) The front button on the Turris Omnia router can be Format: %s. +What: /sys/bus/i2c/devices/<mcu_device>/front_button_poweron +Date: August 2023 +KernelVersion: 6.6 +Contact: Marek Behún <[email protected]> +Description: (RW) Newer versions of the microcontroller firmware of the + Turris Omnia router support powering off the router into true + low power mode. The router can be powered on by pressing the + front button. + + This file configures whether front button power on is enabled. + + This file is present only if the power off feature is supported + by the firmware. + + Format: %i. + What: /sys/bus/i2c/devices/<mcu_device>/fw_features Date: August 2023 KernelVersion: 6.6
drivers/platform/cznic/Kconfig +4 -0
@@ -19,10 +19,14 @@ config TURRIS_OMNIA_MCU depends on I2C select GPIOLIB select GPIOLIB_IRQCHIP + select RTC_CLASS help Say Y here to add support for the features implemented by the microcontroller on the CZ.NIC's Turris Omnia SOHO router. The features include: + - board poweroff into true low power mode (with voltage regulators + disabled) and the ability to configure wake up from this mode (via + rtcwake) - GPIO pins - to get front button press events (the front button can be configured either to generate press events to the CPU or to change
drivers/platform/cznic/Makefile +1 -0
@@ -3,3 +3,4 @@ obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o turris-omnia-mcu-objs := turris-omnia-mcu-base.o turris-omnia-mcu-objs += turris-omnia-mcu-gpio.o +turris-omnia-mcu-objs += turris-omnia-mcu-sys-off-wakeup.o
drivers/platform/cznic/turris-omnia-mcu-base.c +4 -0
@@ -226,6 +226,10 @@ static int omnia_mcu_probe(struct i2c_client *client)if (ret)return ret; + ret =omnia_mcu_register_sys_off_and_wakeup (mcu); +if (ret) +return ret; +return 0 ; }
drivers/platform/cznic/turris-omnia-mcu-gpio.c +0 -2
@@ -5,8 +5,6 @@* 2023 by Marek Behún <[email protected]> */ -#include <asm/unaligned.h> -#include "turris-omnia-mcu.h" static const char *const omnia_mcu_gpio_names[64 ] = {
drivers/platform/cznic/turris-omnia-mcu-sys-off-wakeup.c new file +245 -0
245 lines not on this page · raw message
drivers/platform/cznic/turris-omnia-mcu.h +19 -0
@@ -9,10 +9,12 @@#define __DRIVERS_PLATFORM_CZNIC_TURRIS_OMNIA_MCU_H#include <asm/byteorder.h> +#include <asm/unaligned.h> #include <linux/gpio/driver.h> #include <linux/i2c.h> #include <linux/log2.h> #include <linux/mutex.h> +#include <linux/rtc.h> #include <linux/turris-omnia-mcu-interface.h> #include <linux/workqueue.h>
@@ -29,6 +31,11 @@ struct omnia_mcu {u16 last_status;struct delayed_work button_release_emul_work;bool button_pressed_emul; + +/* RTC device for configuring wake-up */ +struct rtc_device *rtcdev; +u32 rtc_alarm; +bool front_button_poweron; }; static inline int omnia_cmd_write(const struct i2c_client *client, void *cmd,
@@ -39,6 +46,17 @@ static inline int omnia_cmd_write(const struct i2c_client *client, void *cmd,return ret <0 ? ret :0 ; } +static inline int omnia_cmd_write_u32 (const struct i2c_client *client,u8 cmd, +u32 val) +{ +u8 buf[5 ]; + + buf[0 ] = cmd; +put_unaligned_le32 (val, &buf[1 ]); + +return omnia_cmd_write (client, buf,sizeof (buf)); +} +static inline int omnia_cmd_read (const struct i2c_client *client,u8 cmd,void *reply,unsigned int len) {
@@ -117,5 +135,6 @@ static inline int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd)int omnia_mcu_register_gpiochip (struct omnia_mcu *mcu);void omnia_mcu_unregister_gpiochip (struct omnia_mcu *mcu); +int omnia_mcu_register_sys_off_and_wakeup (struct omnia_mcu *mcu);#endif /* __DRIVERS_PLATFORM_CZNIC_TURRIS_OMNIA_MCU_H */
Signature · 1 line
2.41.0
Marek Behún
maintainerRe: [PATCH v2 4/7] platform: cznic: turris-omnia-mcu: Add support for poweroff and wakeup
Allesandro, Alexandre, would you be able to find some time to review this patch?
Marek
Quoted 404 lines · On Tue, 19 Sep 2023 12:38:12 +0200 Marek Behún <[email protected]> wrote:
Add support for true board poweroff (MCU can disable all unnecessary voltage regulators) and wakeup at a specified time, implemented via a RTC driver so that the rtcwake utility can be used to configure it.
Signed-off-by Marek Behún <[email protected]>--- .../sysfs-bus-i2c-devices-turris-omnia-mcu | 16 ++ drivers/platform/cznic/Kconfig | 4 + drivers/platform/cznic/Makefile | 1 + .../platform/cznic/turris-omnia-mcu-base.c | 4 + .../platform/cznic/turris-omnia-mcu-gpio.c | 2 - .../cznic/turris-omnia-mcu-sys-off-wakeup.c | 245 ++++++++++++++++++ drivers/platform/cznic/turris-omnia-mcu.h | 19 ++ 7 files changed, 289 insertions(+), 2 deletions(-) create mode 100644 drivers/platform/cznic/turris-omnia-mcu-sys-off-wakeup.cDocumentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu +16
-0@@ -14,6 +14,22 @@ Description: (RW) The front button on the Turris Omnia router can be Format: %s. +What: /sys/bus/i2c/devices/<mcu_device>/front_button_poweron +Date: August 2023 +KernelVersion: 6.6 +Contact: Marek Behún <[email protected]> +Description: (RW) Newer versions of the microcontroller firmware of the + Turris Omnia router support powering off the router into true + low power mode. The router can be powered on by pressing the + front button. + + This file configures whether front button power on is enabled. + + This file is present only if the power off feature is supported + by the firmware. + + Format: %i. + What: /sys/bus/i2c/devices/<mcu_device>/fw_features Date: August 2023 KernelVersion: 6.6drivers/platform/cznic/Kconfig +4
-0@@ -19,10 +19,14 @@ config TURRIS_OMNIA_MCU depends on I2C select GPIOLIB select GPIOLIB_IRQCHIP + select RTC_CLASS help Say Y here to add support for the features implemented by the microcontroller on the CZ.NIC's Turris Omnia SOHO router. The features include: + - board poweroff into true low power mode (with voltage regulators + disabled) and the ability to configure wake up from this mode (via + rtcwake) - GPIO pins - to get front button press events (the front button can be configured either to generate press events to the CPU or to changedrivers/platform/cznic/Makefile +1
-0@@ -3,3 +3,4 @@ obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o turris-omnia-mcu-objs := turris-omnia-mcu-base.o turris-omnia-mcu-objs += turris-omnia-mcu-gpio.o +turris-omnia-mcu-objs += turris-omnia-mcu-sys-off-wakeup.odrivers/platform/cznic/turris-omnia-mcu-base.c +4
-0@@ -226,6 +226,10 @@ static int omnia_mcu_probe(struct i2c_client *client)if (ret)return ret; + ret =omnia_mcu_register_sys_off_and_wakeup (mcu); +if (ret) +return ret; +return 0 ; }drivers/platform/cznic/turris-omnia-mcu-gpio.c +0
-2@@ -5,8 +5,6 @@* 2023 by Marek Behún <[email protected]> */ -#include <asm/unaligned.h> -#include "turris-omnia-mcu.h" static const char *const omnia_mcu_gpio_names[64 ] = {drivers/platform/cznic/turris-omnia-mcu-sys-off-wakeup.c new file +245
-0@@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* CZ.NIC's Turris Omnia MCU system off and RTC wakeup driver +* +* This is not a true RTC driver (in the sense that it does not provide a +* real-time clock), rather the MCU implements a wakeup from powered off state +* at a specified time relative to MCU boot, and we expose this feature via RTC +* alarm, so that it can be used via the rtcwake command, which is the standard +* Linux command for this. +* +* 2023 by Marek Behún <[email protected]> +*/ + +#include <linux/crc32.h> +#include <linux/delay.h> +#include <linux/reboot.h> + +#include "turris-omnia-mcu.h" + +static int omnia_get_uptime_wakeup (const struct i2c_client *client,u32 *uptime, +u32 *wakeup) +{ +u32 reply[2 ]; +int err; + + err =omnia_cmd_read (client,CMD_GET_UPTIME_AND_WAKEUP , reply, +sizeof (reply)); +if (err) +return err; + +if (uptime) + *uptime =le32_to_cpu (reply[0 ]); + +if (wakeup) + *wakeup =le32_to_cpu (reply[1 ]); + +return 0 ; +} + +static int omnia_read_time (struct device *dev,struct rtc_time *tm) +{ +u32 uptime; +int err; + + err =omnia_get_uptime_wakeup (to_i2c_client (dev), &uptime,NULL ); +if (err) +return err; + +rtc_time64_to_tm (uptime, tm); + +return 0 ; +} + +static int omnia_read_alarm (struct device *dev,struct rtc_wkalrm *alrm) +{ +struct i2c_client *client =to_i2c_client (dev); +struct omnia_mcu *mcu =i2c_get_clientdata (client); +u32 wakeup; +int err; + + err =omnia_get_uptime_wakeup (client,NULL , &wakeup); +if (err) +return err; + + alrm->enabled = !!wakeup; +rtc_time64_to_tm (wakeup ?: mcu->rtc_alarm, &alrm->time); + +return 0 ; +} + +static int omnia_set_alarm (struct device *dev,struct rtc_wkalrm *alrm) +{ +struct i2c_client *client =to_i2c_client (dev); +struct omnia_mcu *mcu =i2c_get_clientdata (client); + + mcu->rtc_alarm =rtc_tm_to_time64 (&alrm->time); + +if (alrm->enabled) +return omnia_cmd_write_u32 (client,CMD_SET_WAKEUP , + mcu->rtc_alarm); +else +return 0 ; +} + +static int omnia_alarm_irq_enable (struct device *dev,unsigned int enabled) +{ +struct i2c_client *client =to_i2c_client (dev); +struct omnia_mcu *mcu =i2c_get_clientdata (client); + +return omnia_cmd_write_u32 (client,CMD_SET_WAKEUP , + enabled ? mcu->rtc_alarm :0 ); +} + +static const struct rtc_class_ops omnia_rtc_ops = { + .read_time = omnia_read_time, + .read_alarm = omnia_read_alarm, + .set_alarm = omnia_set_alarm, + .alarm_irq_enable = omnia_alarm_irq_enable, +}; + +static int omnia_power_off (struct sys_off_data *data) +{ +struct omnia_mcu *mcu = data->cb_data; +u8 cmd[9 ]; +u32 tmp; +u16 arg; +int err; + +if (mcu->front_button_poweron) + arg =CMD_POWER_OFF_POWERON_BUTTON ; +else + arg =0 ; + + cmd[0 ] =CMD_POWER_OFF ; +put_unaligned_le16 (CMD_POWER_OFF_MAGIC , &cmd[1 ]); +put_unaligned_le16 (arg, &cmd[3 ]); + + tmp =cpu_to_be32 (get_unaligned_le32 (&cmd[1 ])); +put_unaligned_le32 (crc32_be (0xffffffff , (void *)&tmp,sizeof (tmp)), + &cmd[5 ]); + + err =omnia_cmd_write (mcu->client, cmd,sizeof (cmd)); +if (err) +dev_err (&mcu->client->dev, +"Unable to send the poweroff command: %d\n" , err); + +return NOTIFY_DONE ; +} + +static int omnia_restart (struct sys_off_data *data) +{ +struct omnia_mcu *mcu = data->cb_data; +u8 cmd[3 ]; +int err; + + cmd[0 ] =CMD_GENERAL_CONTROL ; + +if (reboot_mode ==REBOOT_HARD ) + cmd[1 ] = cmd[2 ] =CTL_HARD_RST ; +else + cmd[1 ] = cmd[2 ] =CTL_LIGHT_RST ; + + err =omnia_cmd_write (mcu->client, cmd,sizeof (cmd)); +if (err) +dev_err (&mcu->client->dev, +"Unable to send the restart command: %d\n" , err); + +/* +* MCU needs a little bit to process the I2C command, otherwise it will +* do a light reset based on SOC SYSRES_OUT pin. +*/ +mdelay (1 ); + +return NOTIFY_DONE ; +} + +static ssize_t front_button_poweron_show (struct device *dev, +struct device_attribute *a,char *buf) +{ +struct omnia_mcu *mcu =i2c_get_clientdata (to_i2c_client (dev)); + +return sysfs_emit (buf,"%d\n" , mcu->front_button_poweron); +} + +static ssize_t front_button_poweron_store (struct device *dev, +struct device_attribute *a, +const char *buf,size_t count) +{ +struct omnia_mcu *mcu =i2c_get_clientdata (to_i2c_client (dev)); +bool val; + +if (kstrtobool (buf, &val) <0 ) +return -EINVAL ; + + mcu->front_button_poweron = val; + +return count; +} +static DEVICE_ATTR_RW (front_button_poweron); + +static struct attribute *omnia_mcu_poweroff_attrs[] = { + &dev_attr_front_button_poweron.attr, +NULL , +}; + +static const struct attribute_group omnia_mcu_poweroff_group = { + .attrs = omnia_mcu_poweroff_attrs, +}; + +int omnia_mcu_register_sys_off_and_wakeup (struct omnia_mcu *mcu) +{ +struct device *dev = &mcu->client->dev; +int err; + +/* MCU restart is always available */ + err =devm_register_sys_off_handler (dev,SYS_OFF_MODE_RESTART , +SYS_OFF_PRIO_FIRMWARE , + omnia_restart, mcu); +if (err) { +dev_err (dev,"Cannot register system restart handler: %d\n" , + err); +return err; + } + +/* +* poweroff and wakeup are available only if POWEROFF_WAKEUP feature is +* present +*/ +if (!(mcu->features &FEAT_POWEROFF_WAKEUP )) +return 0 ; + + err =devm_register_sys_off_handler (dev,SYS_OFF_MODE_POWER_OFF , +SYS_OFF_PRIO_FIRMWARE , + omnia_power_off, mcu); +if (err) { +dev_err (dev,"Cannot register system power off handler: %d\n" , + err); +return err; + } + + mcu->rtcdev =devm_rtc_allocate_device (dev); +if (IS_ERR (mcu->rtcdev)) { + err =PTR_ERR (mcu->rtcdev); +dev_err (dev,"Cannot allocate RTC device: %d\n" , err); +return err; + } + + mcu->rtcdev->ops = &omnia_rtc_ops; + mcu->rtcdev->range_max =U32_MAX ; + + err =devm_rtc_register_device (mcu->rtcdev); +if (err) { +dev_err (dev,"Cannot register RTC device: %d\n" , err); +return err; + } + + mcu->front_button_poweron = true; + + err =devm_device_add_group (dev, &omnia_mcu_poweroff_group); +if (err) +dev_err (dev,"Cannot add poweroff sysfs attribute group: %d\n" , + err); + +return err; +}drivers/platform/cznic/turris-omnia-mcu.h +19
-0@@ -9,10 +9,12 @@#define __DRIVERS_PLATFORM_CZNIC_TURRIS_OMNIA_MCU_H#include <asm/byteorder.h> +#include <asm/unaligned.h> #include <linux/gpio/driver.h> #include <linux/i2c.h> #include <linux/log2.h> #include <linux/mutex.h> +#include <linux/rtc.h> #include <linux/turris-omnia-mcu-interface.h> #include <linux/workqueue.h> @@ -29,6 +31,11 @@ struct omnia_mcu {u16 last_status;struct delayed_work button_release_emul_work;bool button_pressed_emul; + +/* RTC device for configuring wake-up */ +struct rtc_device *rtcdev; +u32 rtc_alarm; +bool front_button_poweron; }; static inline int omnia_cmd_write(const struct i2c_client *client, void *cmd,@@ -39,6 +46,17 @@ static inline int omnia_cmd_write(const struct i2c_client *client, void *cmd,return ret <0 ? ret :0 ; } +static inline int omnia_cmd_write_u32 (const struct i2c_client *client,u8 cmd, +u32 val) +{ +u8 buf[5 ]; + + buf[0 ] = cmd; +put_unaligned_le32 (val, &buf[1 ]); + +return omnia_cmd_write (client, buf,sizeof (buf)); +} +static inline int omnia_cmd_read (const struct i2c_client *client,u8 cmd,void *reply,unsigned int len) {@@ -117,5 +135,6 @@ static inline int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd)int omnia_mcu_register_gpiochip (struct omnia_mcu *mcu);void omnia_mcu_unregister_gpiochip (struct omnia_mcu *mcu); +int omnia_mcu_register_sys_off_and_wakeup (struct omnia_mcu *mcu);#endif /* __DRIVERS_PLATFORM_CZNIC_TURRIS_OMNIA_MCU_H */
Marek Behún
maintainerPATCH v2 5/7 platform: cznic: turris-omnia-mcu: Add support for MCU watchdog
In reply to Marek BehúnAdd support for the watchdog mechanism provided by the MCU.
--- drivers/platform/cznic/Kconfig | 2 + drivers/platform/cznic/Makefile | 1 + .../platform/cznic/turris-omnia-mcu-base.c | 4 + .../cznic/turris-omnia-mcu-watchdog.c | 126 ++++++++++++++++++ drivers/platform/cznic/turris-omnia-mcu.h | 24 ++++ 5 files changed, 157 insertions(+) create mode 100644 drivers/platform/cznic/turris-omnia-mcu-watchdog.c
drivers/platform/cznic/Kconfig +2 -0
@@ -20,6 +20,7 @@ config TURRIS_OMNIA_MCU select GPIOLIB select GPIOLIB_IRQCHIP select RTC_CLASS + select WATCHDOG_CORE help Say Y here to add support for the features implemented by the microcontroller on the CZ.NIC's Turris Omnia SOHO router.
@@ -27,6 +28,7 @@ config TURRIS_OMNIA_MCU - board poweroff into true low power mode (with voltage regulators disabled) and the ability to configure wake up from this mode (via rtcwake) + - MCU watchdog - GPIO pins - to get front button press events (the front button can be configured either to generate press events to the CPU or to change
drivers/platform/cznic/Makefile +1 -0
@@ -4,3 +4,4 @@ obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o turris-omnia-mcu-objs := turris-omnia-mcu-base.o turris-omnia-mcu-objs += turris-omnia-mcu-gpio.o turris-omnia-mcu-objs += turris-omnia-mcu-sys-off-wakeup.o +turris-omnia-mcu-objs += turris-omnia-mcu-watchdog.o
drivers/platform/cznic/turris-omnia-mcu-base.c +4 -0
@@ -230,6 +230,10 @@ static int omnia_mcu_probe(struct i2c_client *client)if (ret)return ret; + ret =omnia_mcu_register_watchdog (mcu); +if (ret) +return ret; +return 0 ; }
drivers/platform/cznic/turris-omnia-mcu-watchdog.c new file +126 -0
126 lines not on this page · raw message
drivers/platform/cznic/turris-omnia-mcu.h +24 -0
@@ -16,6 +16,7 @@#include <linux/mutex.h> #include <linux/rtc.h> #include <linux/turris-omnia-mcu-interface.h> +#include <linux/watchdog.h> #include <linux/workqueue.h> struct omnia_mcu {
@@ -36,6 +37,9 @@ struct omnia_mcu {struct rtc_device *rtcdev;u32 rtc_alarm;bool front_button_poweron; + +/* MCU watchdog */ +struct watchdog_device wdt; }; static inline int omnia_cmd_write(const struct i2c_client *client, void *cmd,
@@ -46,6 +50,25 @@ static inline int omnia_cmd_write(const struct i2c_client *client, void *cmd,return ret <0 ? ret :0 ; } +static inline int omnia_cmd_write_u8 (const struct i2c_client *client,u8 cmd, +u8 val) +{ +u8 buf[2 ] = { cmd, val }; + +return omnia_cmd_write (client, buf,sizeof (buf)); +} + +static inline int omnia_cmd_write_u16 (const struct i2c_client *client,u8 cmd, +u16 val) +{ +u8 buf[3 ]; + + buf[0 ] = cmd; +put_unaligned_le16 (val, &buf[1 ]); + +return omnia_cmd_write (client, buf,sizeof (buf)); +} +static inline int omnia_cmd_write_u32 (const struct i2c_client *client,u8 cmd,u32 val) {
@@ -136,5 +159,6 @@ static inline int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd)int omnia_mcu_register_gpiochip (struct omnia_mcu *mcu);void omnia_mcu_unregister_gpiochip (struct omnia_mcu *mcu);int omnia_mcu_register_sys_off_and_wakeup (struct omnia_mcu *mcu); +int omnia_mcu_register_watchdog (struct omnia_mcu *mcu);#endif /* __DRIVERS_PLATFORM_CZNIC_TURRIS_OMNIA_MCU_H */
Signature · 1 line
2.41.0
Guenter Roeck
maintainerRe: [PATCH v2 5/7] platform: cznic: turris-omnia-mcu: Add support for MCU watchdog
Quoted 84 lines · Marek Behún
Add support for the watchdog mechanism provided by the MCU.
Signed-off-by Marek Behún <[email protected]>--- drivers/platform/cznic/Kconfig | 2 + drivers/platform/cznic/Makefile | 1 + .../platform/cznic/turris-omnia-mcu-base.c | 4 + .../cznic/turris-omnia-mcu-watchdog.c | 126 ++++++++++++++++++ drivers/platform/cznic/turris-omnia-mcu.h | 24 ++++ 5 files changed, 157 insertions(+)create mode 100644
drivers/platform/cznic/turris-omnia-mcu-watchdog.cdrivers/platform/cznic/Kconfig +2
-0@@ -20,6 +20,7 @@ config TURRIS_OMNIA_MCU select GPIOLIB select GPIOLIB_IRQCHIP select RTC_CLASS + select WATCHDOG_CORE help Say Y here to add support for the features implemented by the microcontroller on the CZ.NIC's Turris Omnia SOHO router.@@ -27,6 +28,7 @@ config TURRIS_OMNIA_MCU - board poweroff into true low power mode (with voltage regulators disabled) and the ability to configure wake up from this mode (via rtcwake) + - MCU watchdog - GPIO pins - to get front button press events (the front button can be configured either to generate press events to the CPU or to changedrivers/platform/cznic/Makefile +1
-0@@ -4,3 +4,4 @@ obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o turris-omnia-mcu-objs := turris-omnia-mcu-base.o turris-omnia-mcu-objs += turris-omnia-mcu-gpio.o turris-omnia-mcu-objs += turris-omnia-mcu-sys-off-wakeup.o +turris-omnia-mcu-objs += turris-omnia-mcu-watchdog.odrivers/platform/cznic/turris-omnia-mcu-base.c +4
-0@@ -230,6 +230,10 @@ static int omnia_mcu_probe(struct i2c_client *client)if (ret)return ret; + ret =omnia_mcu_register_watchdog (mcu); +if (ret) +return ret; +return 0 ; }drivers/platform/cznic/turris-omnia-mcu-watchdog.c new file +22
-0@@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* CZ.NIC's Turris Omnia MCU watchdog driver +* +* 2023 by Marek Behún <[email protected]> +*/ + +#include "turris-omnia-mcu.h" + +#define WATCHDOG_TIMEOUT 120 + +static unsigned int timeout; +module_param (timeout, int,0 ); +MODULE_PARM_DESC (timeout,"Watchdog timeout in seconds" ); + +static bool nowayout =WATCHDOG_NOWAYOUT ; +module_param (nowayout, bool,0 ); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static int omnia_wdt_start (struct watchdog_device *wdt) +{
drivers/platform/cznic/turris-omnia-mcu-watchdog.c line 32
Quoted 10 lines
drivers/platform/cznic/turris-omnia-mcu-watchdog.c new file +10
-0+struct omnia_mcu *mcu =watchdog_get_drvdata (wdt); + +/* +* The MCU also implements LED controller, and the LED driver may in +* some cases require more I2C bandwidth. In order to prevent stealing +* that bandwidth from the LED driver, configure minimum time between +* pings to 10% of the configured timeout (for 120 second timeout we +* will be pinging at most every 12 seconds). +*/ + mcu->wdt.min_hw_heartbeat_ms =100 * mcu->wdt.timeout;
This is not what min_hw_heartbeat_ms is supposed to be used for, and it is an abuse of the API which is not guaranteed to work. The value is exopected to be set at registration time, and it is not supposed to change dynamically. It is also competely unnecessary since no sane watchdog daemon will ping at such a high rate. Please drop.
Guenter
Quoted 149 lines
drivers/platform/cznic/turris-omnia-mcu-watchdog.c new file +94
-0+ +return omnia_cmd_write_u8 (mcu->client,CMD_SET_WATCHDOG_STATE ,1 ); +} + +static int omnia_wdt_stop (struct watchdog_device *wdt) +{ +struct omnia_mcu *mcu =watchdog_get_drvdata (wdt); + +return omnia_cmd_write_u8 (mcu->client,CMD_SET_WATCHDOG_STATE ,0 ); +} + +static int omnia_wdt_ping (struct watchdog_device *wdt) +{ +struct omnia_mcu *mcu =watchdog_get_drvdata (wdt); + +return omnia_cmd_write_u8 (mcu->client,CMD_SET_WATCHDOG_STATE ,1 ); +} + +static int omnia_wdt_set_timeout (struct watchdog_device *wdt, +unsigned int timeout) +{ +struct omnia_mcu *mcu =watchdog_get_drvdata (wdt); + +return omnia_cmd_write_u16 (mcu->client,CMD_SET_WDT_TIMEOUT , + timeout *10 ); +} + +static unsigned int omnia_wdt_get_timeleft (struct watchdog_device *wdt) +{ +struct omnia_mcu *mcu =watchdog_get_drvdata (wdt); +int ret; + + ret =omnia_cmd_read_u16 (mcu->client,CMD_GET_WDT_TIMELEFT ); +if (ret <0 ) { +dev_err (&mcu->client->dev,"Cannot get watchdog timeleft: %d\n" , + ret); +return 0 ; + } + +return ret /10 ; +} + +static const struct watchdog_info omnia_wdt_info = { + .options =WDIOF_SETTIMEOUT |WDIOF_KEEPALIVEPING |WDIOF_MAGICCLOSE , + .identity ="Turris Omnia MCU Watchdog" , +}; + +static const struct watchdog_ops omnia_wdt_ops = { + .owner =THIS_MODULE , + .start = omnia_wdt_start, + .stop = omnia_wdt_stop, + .ping = omnia_wdt_ping, + .set_timeout = omnia_wdt_set_timeout, + .get_timeleft = omnia_wdt_get_timeleft, +}; + +int omnia_mcu_register_watchdog (struct omnia_mcu *mcu) +{ +struct device *dev = &mcu->client->dev; +int ret; + +if (!(mcu->features &FEAT_WDT_PING )) +return 0 ; + + mcu->wdt.info = &omnia_wdt_info; + mcu->wdt.ops = &omnia_wdt_ops; + mcu->wdt.parent = dev; + mcu->wdt.min_timeout =1 ; + mcu->wdt.max_timeout =6553 ;/* 65535 deciseconds */ + + mcu->wdt.timeout =WATCHDOG_TIMEOUT ; +watchdog_init_timeout (&mcu->wdt, timeout, dev); + +watchdog_set_drvdata (&mcu->wdt, mcu); + +omnia_wdt_set_timeout (&mcu->wdt, mcu->wdt.timeout); + + ret =omnia_cmd_read_u8 (mcu->client,CMD_GET_WATCHDOG_STATE ); +if (ret <0 ) { +dev_err (dev,"Cannot get MCU watchdog state: %d\n" , ret); +return ret; + } + +if (ret) +set_bit (WDOG_HW_RUNNING , &mcu->wdt.status); + +watchdog_set_nowayout (&mcu->wdt, nowayout); +watchdog_stop_on_reboot (&mcu->wdt); + ret =devm_watchdog_register_device (dev, &mcu->wdt); +if (ret) +dev_err (dev,"Cannot register MCU watchdog: %d\n" , ret); + +return ret; +}drivers/platform/cznic/turris-omnia-mcu.h +24
-0@@ -16,6 +16,7 @@#include <linux/mutex.h> #include <linux/rtc.h> #include <linux/turris-omnia-mcu-interface.h> +#include <linux/watchdog.h> #include <linux/workqueue.h> struct omnia_mcu {@@ -36,6 +37,9 @@ struct omnia_mcu {struct rtc_device *rtcdev;u32 rtc_alarm;bool front_button_poweron; + +/* MCU watchdog */ +struct watchdog_device wdt; }; static inline int omnia_cmd_write(const struct i2c_client *client, void *cmd,@@ -46,6 +50,25 @@ static inline int omnia_cmd_write(const struct i2c_client *client, void *cmd,return ret <0 ? ret :0 ; } +static inline int omnia_cmd_write_u8 (const struct i2c_client *client,u8 cmd, +u8 val) +{ +u8 buf[2 ] = { cmd, val }; + +return omnia_cmd_write (client, buf,sizeof (buf)); +} + +static inline int omnia_cmd_write_u16 (const struct i2c_client *client,u8 cmd, +u16 val) +{ +u8 buf[3 ]; + + buf[0 ] = cmd; +put_unaligned_le16 (val, &buf[1 ]); + +return omnia_cmd_write (client, buf,sizeof (buf)); +} +static inline int omnia_cmd_write_u32 (const struct i2c_client *client,u8 cmd,u32 val) {@@ -136,5 +159,6 @@ static inline int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd)int omnia_mcu_register_gpiochip (struct omnia_mcu *mcu);void omnia_mcu_unregister_gpiochip (struct omnia_mcu *mcu);int omnia_mcu_register_sys_off_and_wakeup (struct omnia_mcu *mcu); +int omnia_mcu_register_watchdog (struct omnia_mcu *mcu);#endif /* __DRIVERS_PLATFORM_CZNIC_TURRIS_OMNIA_MCU_H */
Marek Behún
maintainerRe: [PATCH v2 5/7] platform: cznic: turris-omnia-mcu: Add support for MCU watchdog
Quoted 10 lines
Quoted 10 lines
drivers/platform/cznic/turris-omnia-mcu-watchdog.c new file +10
-0+static int omnia_wdt_start (struct watchdog_device *wdt) +{ +struct omnia_mcu *mcu =watchdog_get_drvdata (wdt); + +/* +* The MCU also implements LED controller, and the LED driver may in +* some cases require more I2C bandwidth. In order to prevent stealing +* that bandwidth from the LED driver, configure minimum time between +* pings to 10% of the configured timeout (for 120 second timeout we +* will be pinging at most every 12 seconds).
Quoted 8 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ */ + mcu->wdt.min_hw_heartbeat_ms = 100 * mcu->wdt.timeout;This is not what
min_hw_heartbeat_msis supposed to be used for, and it is an abuse of the API which is not guaranteed to work. The value is exopected to be set at registration time, and it is not supposed to change dynamically. It is also competely unnecessary since no sane watchdog daemon will ping at such a high rate. Please drop.
Hello Guenter, thanks for the review.
Sorry about this, I thought this was correct cause rti_wdt driver and feared that watchdog daemons would actually ping at such rate.
I will drop it in the next version of the series.
Marek
Marek Behún
maintainerPATCH v2 6/7 ARM: dts: turris-omnia: Add MCU system-controller node
In reply to Marek BehúnTurris Omnia's MCU provides various features that can be configured over I2C at address 0x2a. Add device-tree node.
--- .../dts/marvell/armada-385-turris-omnia.dts | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-)
arch/arm/boot/dts/marvell/armada-385-turris-omnia.dts +21 -1
@@ -218,7 +218,22 @@ i2c@0 { #size-cells = <0>; reg = <0>; - /* STM32F0 command interface at address 0x2a */ + mcu: system-controller@2a { + compatible = "cznic,turris-omnia-mcu"; + reg = <0x2a>; + + pinctrl-names = "default"; + pinctrl-0 = <&mcu_pins>; + + interrupt-parent = <&gpio1>; + interrupts = <11 IRQ_TYPE_NONE>; + + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; led-controller@2b { compatible = "cznic,turris-omnia-leds";
@@ -503,6 +518,11 @@ fixed-link { }; &pinctrl { + mcu_pins: mcu-pins { + marvell,pins = "mpp43"; + marvell,function = "gpio"; + }; + pcawan_pins: pcawan-pins { marvell,pins = "mpp46"; marvell,function = "gpio";
Signature · 1 line
2.41.0
Marek Behún
maintainerPATCH v2 7/7 ARM: dts: turris-omnia: Add GPIO key node for front button
In reply to Marek BehúnNow that we have the MCU device-tree node, which acts as a GPIO controller, add GPIO key node for the front button.
--- .../boot/dts/marvell/armada-385-turris-omnia.dts | 13 +++++++++++++ 1 file changed, 13 insertions(+)
arch/arm/boot/dts/marvell/armada-385-turris-omnia.dts +13 -0
@@ -112,6 +112,19 @@ sfp: sfp { status = "disabled"; }; + gpio-keys { + compatible = "gpio-keys"; + + front-button { + label = "Front Button"; + linux,code = <KEY_VENDOR>; + linux,can-disable; + gpios = <&mcu 12 GPIO_ACTIVE_HIGH>; + /* debouncing is done by the microcontroller */ + debounce-interval = <0>; + }; + }; + sound { compatible = "simple-audio-card"; simple-audio-card,name = "SPDIF";
Signature · 1 line
2.41.0