Re: [PATCH] platform/x86: Add Silicom Platform Driver

From: Ilpo Järvinen
Date: Wed Oct 04 2023 - 04:38:39 EST


On Tue, 3 Oct 2023, Henry Shi wrote:

> platform/x86: Add Silicom Platform Driver
>
> Add Silicom platform (silicom-platform) Linux driver for Swisscom
> Business Box (Swisscom BB) as well as Cordoba family products.
>
> This platform driver provides support for various functions via
> the Linux LED framework, GPIO framework, Hardware Monitoring (HWMON)
> and device attributes.
>
> Signed-off-by: Henry Shi <henryshi2018@xxxxxxxxx>
> ---
>
> Changes from v1 to v2:
> ===========================
>
> Suggested by Hans de Goede <hdegoede@xxxxxxxxxx>
> .Use git send-email to submit patch.
> .patch contents should be in message body.
> .Kconfig bit for the driver should be in drivers/platform/x86/Kconfig.
>
> changes from patch v2 to v3
> ===========================
>
> changes suggested by Guenter Roeck <groeck7@xxxxxxxxx>
> .Removed unnecessary include file linux/thermal.h.
> .Removed EXPORT_SYMBOL for mutex lock/unlock function.
>
> Changes suggested by Ilpo Järvinen <ilpo.jarvinen@xxxxxxxxxxxxxxx>
> .Remove extra new line in code on multiple position.
> .Use table instead of space in code.
> .Uss Linux defined bit operation MACRO define.
> .Removed local variable in rpm_get().
> .Corrected typo in comments.
> .Corrected incorrect indentation.
> .Removed unnecessary comments in silicom_mc_leds_register().
> .Rewrite efuse_status_show() to used defined variable and removed
> uncessary local variables.
> .Rewrite uc_version_show() to used defined variable and removed
> uncessary local variables.
> .Removed unused MACRO define: #define CHANNEL_TO_BIT(chan) ((chan) & 0x07).
> .Rewrite powercycle_uc() to used defined variable and removed uncessary
> local variables.
> .use GENMASK() and use FIELD_GET() instead of bit shift.
> .Added define for constant 0x28 used in efuse_status_show().
> .Added define for constant 0x0 used in uc_version_show().
> .Added define for constant 0x0 used in powercycle_uc().
> .Rearrange functions to avoid uncessary pre-define.
> .Rewrite rpm_get() to used defined variable and removed uncessary
> local variables.
> .Rewrite temp_get() to used defined variable and removed uncessary
> local variables.
> .Use FIELD_GET instead of bit shift in temp_get().
> .Used #define for constant variable 0/1.
>
> Changes suggested by Christophe JAILLET <christophe.jaillet@xxxxxxxxxx>:
> .use "if (!led->subled_info)" instead of
> "if (IS_ERR_OR_NULL(led->subled_info))
> "in silicom_mc_leds_register
>
> changes from patch v3 to v4
> ===========================
>
> changes suggested by Guenter Roeck <groeck7@xxxxxxxxx>
>
> Changes suggested by Ilpo Järvinen <ilpo.jarvinen@xxxxxxxxxxxxxxx>:
> .Rewrite silicom_mec_led/gpip_set/get() functions to use two newly created
> silicom_mec_port_get()/silicom_mec_port_set() which have common code.
> .Remove duplicate code in silicom_mec_port_get()
> .Rewrite uc_version_show() to use Linux bit operation MACRO, and add
> logic to check un-supported register value.
> .Added "#define MEC_EFUSE_LSB_ADDR 0x28" and "#define
> MEC_POWER_CYCLE_ADDR 0x24"
> .Added "#define MEC_VERSION_MAJOR GENMASK(15, 14)" and "#define
> MEC_VERSION_MINOR GENMASK(13, 8)".
>
> Changes suggested by Christophe JAILLET <christophe.jaillet@xxxxxxxxxx>:
> .Used a local variable to store "sizeof(struct mc_subled)" in function
> silicom_mc_leds_register().
>
> change from patch v4 to v5
> ===========================================
>
> changes suggested by Guenter Roeck <groeck7@xxxxxxxxx>:
> .Corrected return value in temp_get() to return 1/10000th degree.
> .Removed local variable struct silicom_fan_control_data *ctl in
> silicom_fan_control_read_fan(),
> removed storing rpm value to ctl variable.
> .Removed local variable struct silicom_fan_control_data *ctl in
> silicom_fan_control_read_temp(),
> .removed storing rpm value to ctl variable.
> .Changed return string in silicom_fan_control_read_labels() to
> specific string for Silicom platform driver.
> .Removed silicom_fan_control_data structure.
> .Removed static variable mec_io_base and mec_io_len, and added
> "#define MEC_IO_BASE 0x0800 and #define MEC_IO_LEN 0x8".
> .Removed ".write = NULL" in silicom_fan_control_hwmon_ops
> structure defination.
> .Removed unnecessary function silicom_fan_control_write().
> .Removed unnecessary check for silicom_led_info in function
> silicom_platform_probe.
> .Removed unnecessary local variable "silicom_fan_control_data *ctl"
> in silicom_platform_probe().
> .Clean out driver initialization error handling in
> silicom_platform_init();
> .Add patch version and changelog for patch submission.
>
> Changes suggested by Ilpo Järvinen <ilpo.jarvinen@xxxxxxxxxxxxxxx>:
> .Rename "#define MEC_DATA(offset) to "#define MEC_DATA_OFFSET(offset).
> .Use constant defined in include/linux/units.h instead of a literal.
> .return directly instead of go to err condition when
> platform_device_register_simple() failed.
> .Remove unnecessary check for silicom_led_info and silicom_gpiochip.
> .Use a local variable to how multiple use of array size.
> .Align the arguments to the same column in
> silicom_mec_led_mc_brightness_set.
> .Add patch version and changelog that shows version to version changes
> for patch submission.
>
> Changes suggested by Christophe JAILLET <christophe.jaillet@xxxxxxxxxx>:
> .Use "sizeof(*led)" instead of "sizeof(struct led_classdev_mc)"
> .Use "if (!led)" instead of "if (IS_ERR_OR_NULL(led))"
> .Removed unnecessary error message:
> "dev_err(dev, "Failed to alloc led_classdev_mc[%d]:
> %ld\n", i, PTR_ERR(led)).
>
> change from patch vv5 to v6
> ===========================================
>
> changes suggested by Guenter Roeck <groeck7@xxxxxxxxx>:
> .Removed checkpath warnings.
> .Resoved dependencies between CONFIG_HWMON and CONFIG_SILICOM_PLATFORM.
>
> change from patch v6 to v7
> ===========================================
>
> changes suggested by Hans de Goede <hdegoede@xxxxxxxxxx>:
> .Usa a proper subsystem prefix for this patch subject:
> Subject: platform/x86: Add Silicom Platform Driver.
>
> change from patch v7 to v8 (current patch)
> ===========================================
>
> changes suggested by Hans de Goede <hdegoede@xxxxxxxxxx>:
> .Chnage commit message of this driver.
> .Adjust location of change log and signed-off-by.
> .Change "config SILICOM_PLATFORM" and help contents location,
> and put it to source "drivers/platform/x86/siemens/Kconfig".
> .Set editor tab to 8 and align the start of extra function
> parameters to directly after (. This should be applied for
> all function.
> .Do not manually create a sysfs dir and register sysfs attribute,
> instead define a platform_driver structure.
> .Move MODULE_DEVICE_TABLE(dmi, silicom_dmi_ids) directly after
> table declaration.
> .Using pr_info() instead of dev_info() in function
> silicom_platform_info_init().
> .Made dmi_check_system() check the first thing to do in
> silicom_platform_init().
> .Instead of separate platform_device creation + driver registration,
> switched to using platform_create_bundle().
> .Removed mutex_destroy(&mec_io_mutex).
>
>
> Changes suggested by Ilpo Järvinen <ilpo.jarvinen@xxxxxxxxxxxxxxx>:
> .Too many GENMASK() within to code itself, need put them to
> #define. Removed all GENMASK() in c functions.
>
> drivers/platform/x86/Kconfig | 14 +
> drivers/platform/x86/Makefile | 1 +
> drivers/platform/x86/silicom-platform.c | 1001 +++++++++++++++++++++++
> 3 files changed, 1016 insertions(+)
> create mode 100644 drivers/platform/x86/silicom-platform.c
>
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index 2a1070543391..f38fbd97f33d 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -1076,6 +1076,20 @@ config INTEL_SCU_IPC_UTIL
>
> source "drivers/platform/x86/siemens/Kconfig"
>
> +config SILICOM_PLATFORM
> + tristate "Silicom Edge Networking device support"
> + depends on HWMON
> + select LEDS_CLASS_MULTICOLOR
> + help
> + This option enables support for the LEDs/GPIO/etc downstream of the
> + embedded controller on Silicom "Cordoba" hardware and derivatives.
> +
> + This platform driver provides support for various functions via
> + the Linux LED framework, GPIO framework, Hardware Monitoring (HWMON)
> + and device attributes.
> +
> + If you have a Silicom network appliance, say Y or M here.
> +
> config WINMATE_FM07_KEYS
> tristate "Winmate FM07/FM07P front-panel keys driver"
> depends on INPUT
> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> index b457de5abf7d..07efff8b24f7 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -113,6 +113,7 @@ obj-$(CONFIG_SERIAL_MULTI_INSTANTIATE) += serial-multi-instantiate.o
> obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o
> obj-$(CONFIG_TOUCHSCREEN_DMI) += touchscreen_dmi.o
> obj-$(CONFIG_WIRELESS_HOTKEY) += wireless-hotkey.o
> +obj-$(CONFIG_SILICOM_PLATFORM) += silicom-platform.o
> obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets/
>
> # Intel uncore drivers
> diff --git a/drivers/platform/x86/silicom-platform.c b/drivers/platform/x86/silicom-platform.c
> new file mode 100644
> index 000000000000..8786d09476f2
> --- /dev/null
> +++ b/drivers/platform/x86/silicom-platform.c
> @@ -0,0 +1,1001 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +//
> +// silicom-platform.c - Silicom MEC170x platform driver
> +//
> +// Copyright (C) 2023 Henry Shi <henrys@xxxxxxxxxxxxxxx>
> +#include <linux/dmi.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/init.h>
> +#include <linux/ioport.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/led-class-multicolor.h>
> +#include <linux/module.h>
> +#include <linux/hwmon.h>
> +#include <linux/mutex.h>
> +#include <linux/platform_device.h>
> +#include <linux/string.h>
> +#include <linux/kobject.h>
> +#include <linux/sysfs.h>
> +#include <linux/bits.h>
> +#include <linux/bitfield.h>
> +#include <linux/units.h>
> +
> +#define MEC_EFUSE_LSB_ADDR 0x28
> +#define MEC_POWER_CYCLE_ADDR 0x24
> +#define MEC_PORT_OFFSET_MASK 0xfc
> +#define MEC_PORT_CHANNEL_MASK 0x7
> +#define MEC_DATA_OFFSET_MASK 0x03
> +#define MEC_GPIO_IN_POS 0x08
> +#define MEC_IO_BASE 0x0800
> +#define MEC_IO_LEN 0x8
> +#define MEC_ADDR ((MEC_IO_BASE) + 0x02)
> +#define MEC_DATA_OFFSET(offset) ((MEC_IO_BASE) + 0x04 + (offset))
> +#define EC_ADDR_LSB MEC_ADDR
> +#define EC_ADDR_MSB ((MEC_IO_BASE) + MEC_DATA_OFFSET_MASK)
> +#define SILICOM_MEC_MAGIC 0x5a
> +#define OFFSET_BIT_TO_CHANNEL(off, bit) ((((off) + 0x014) << 3) | (bit))
> +#define CHANNEL_TO_OFFSET(chan) (((chan) >> 3) - 0x14)
> +#define MEC_VERSION_MAJOR GENMASK(15, 14)
> +#define MEC_VERSION_MINOR GENMASK(13, 8)
> +#define MEC_GET_BITS(high, low, arg) FIELD_GET(GENMASK(high, low), arg)
> +#define IO_REG_BANK 0x0
> +#define DEFAULT_CHAN_LO 0
> +#define DEFAULT_CHAN_HI 0
> +#define DEFAULT_CHAN_LO_T 0xc
> +
> +static DEFINE_MUTEX(mec_io_mutex);
> +static int efuse_status;
> +static int mec_uc_version;
> +static int power_cycle;
> +
> +static const struct hwmon_channel_info *silicom_fan_control_info[] = {
> + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL),
> + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL),
> + NULL
> +};

> +static void silicom_mec_port_set(int channel, int on)
> +{
> + u8 reg;
> +
> + mutex_lock(&mec_io_mutex);
> + /* Get the dword offset from the channel */
> + outb(MEC_GET_BITS(31, 3, channel) & MEC_PORT_OFFSET_MASK, MEC_ADDR);
> +
> + /* Get the current port settings */
> + reg = inb(MEC_DATA_OFFSET(MEC_GET_BITS(31, 3, channel) & MEC_DATA_OFFSET_MASK));

Unfortunately this is not how should be done. This ended up just replaced
one generic name in the code with another.

What I'm after the name shown in the code should tell what the field is
(GOODPREFIX_GOODNAME where the intent becomes obvious because the name
describes what field is extracted).

You should do this in the define blocks:
#define GOODPREFIX_GOODNAME GENMASK(literalhigh, literallow)

Then here in the code, you do:
reg = inb(MEC_DATA_OFFSET(FIELD_GET(GOODPREFIX_GOODNAME, channel)));

You have done what is expected with e.g. MEC_VERSION_MAJOR, just do the
same with all places in the where you previously had GENMASK() in the
code (currently MEC_GET_BITS() is there, obviously, but it should go
away and be replaced with FIELD_GET(GOODPREFIX_GOODNAME, ...))).

> +static ssize_t uc_version_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + u32 reg;
> + int uc_version;
> +
> + mutex_lock(&mec_io_mutex);
> + outb(IO_REG_BANK, EC_ADDR_MSB);
> + outb(DEFAULT_CHAN_LO, EC_ADDR_LSB);
> +
> + reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
> + mutex_unlock(&mec_io_mutex);
> + uc_version = MEC_GET_BITS(15, 8, reg);
> + if (uc_version >= 192)
> + pr_err("uc version not supported\n");

This is sysfs so it's odd to print pr_err() like that here. If the driver
does not support those versions at all, the probe should fail. If driver
is fine but just doesn't know how to interpret such a version, you should
return -Esomething here.

> +
> + uc_version = FIELD_GET(MEC_VERSION_MAJOR, reg) * CENTI +

I'd just use 100 literal here as it's not really normal to do SI like
prefixes with versions but it's up to you what you want to do here.

> + FIELD_GET(MEC_VERSION_MINOR, reg);

Align FIELD_GET()s to the same column.

In anycase, the calculation code itself looks better now!

> +
> + mec_uc_version = uc_version;
> + return sprintf(buf, "%d\n", mec_uc_version);

You could make mac_uc_version unsigned btw and use %u here.


And please keep the version the subject too so that I don't lose track of
which version you're posting.

--
i.