* Re: [PATCH 1/8] Add Advantech EIO MFD driver
@ 2025-12-13 23:12 kernel test robot
0 siblings, 0 replies; 4+ messages in thread
From: kernel test robot @ 2025-12-13 23:12 UTC (permalink / raw)
To: oe-kbuild; +Cc: lkp
::::::
:::::: Manual check reason: "low confidence static check warning: drivers/mfd/eio_core.c:39:1: sparse: sparse: bad integer constant expression"
::::::
BCC: lkp@intel.com
CC: oe-kbuild-all@lists.linux.dev
In-Reply-To: <20251212-upstream-v1-v1-1-d50d40ec8d8a@advantech.com>
References: <20251212-upstream-v1-v1-1-d50d40ec8d8a@advantech.com>
TO: Ramiro Oliveira <ramiro.oliveira@advantech.com>
TO: Lee Jones <lee@kernel.org>
TO: Linus Walleij <linusw@kernel.org>
TO: Bartosz Golaszewski <brgl@kernel.org>
TO: Guenter Roeck <linux@roeck-us.net>
TO: Andi Shyti <andi.shyti@kernel.org>
TO: Daniel Thompson <danielt@kernel.org>
TO: Jingoo Han <jingoohan1@gmail.com>
TO: Helge Deller <deller@gmx.de>
TO: Wim Van Sebroeck <wim@linux-watchdog.org>
TO: "Rafael J. Wysocki" <rafael@kernel.org>
TO: Daniel Lezcano <daniel.lezcano@linaro.org>
TO: Zhang Rui <rui.zhang@intel.com>
TO: Lukasz Luba <lukasz.luba@arm.com>
CC: linux-kernel@vger.kernel.org
CC: linux-gpio@vger.kernel.org
CC: linux-hwmon@vger.kernel.org
CC: linux-i2c@vger.kernel.org
CC: dri-devel@lists.freedesktop.org
CC: linux-fbdev@vger.kernel.org
CC: linux-watchdog@vger.kernel.org
CC: linux-pm@vger.kernel.org
CC: Wenkai Chung <wenkai.chung@advantech.com.tw>
CC: "Francisco Aragon-Trivino" <francisco.aragon-trivino@advantech.com>
CC: Hongzhi Wang <hongzhi.wang@advantech.com>
CC: Mikhail Tsukerman <mikhail.tsukerman@advantech.com>
CC: Thomas Kastner <thomas.kastner@advantech.com>
CC: Ramiro Oliveira <ramiro.oliveira@advantech.com>
Hi Ramiro,
kernel test robot noticed the following build warnings:
[auto build test WARNING on d9771d0dbe18dd643760431870a6abf9b0866bb0]
url: https://github.com/intel-lab-lkp/linux/commits/Ramiro-Oliveira/Add-Advantech-EIO-MFD-driver/20251213-004905
base: d9771d0dbe18dd643760431870a6abf9b0866bb0
patch link: https://lore.kernel.org/r/20251212-upstream-v1-v1-1-d50d40ec8d8a%40advantech.com
patch subject: [PATCH 1/8] Add Advantech EIO MFD driver
:::::: branch date: 30 hours ago
:::::: commit date: 30 hours ago
config: powerpc-randconfig-r123-20251213 (https://download.01.org/0day-ci/archive/20251214/202512140653.VFtpkQVM-lkp@intel.com/config)
compiler: powerpc-linux-gcc (GCC) 8.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251214/202512140653.VFtpkQVM-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/r/202512140653.VFtpkQVM-lkp@intel.com/
sparse warnings: (new ones prefixed by >>)
>> drivers/mfd/eio_core.c:39:1: sparse: sparse: bad integer constant expression
drivers/mfd/eio_core.c:39:1: sparse: sparse: static assertion failed: "MODULE_INFO(parmtype, ...) contains embedded NUL byte"
drivers/mfd/eio_core.c:40:1: sparse: sparse: bad integer constant expression
drivers/mfd/eio_core.c:40:1: sparse: sparse: static assertion failed: "MODULE_INFO(parm, ...) contains embedded NUL byte"
drivers/mfd/eio_core.c:618:1: sparse: sparse: bad integer constant expression
drivers/mfd/eio_core.c:618:1: sparse: sparse: static assertion failed: "MODULE_INFO(author, ...) contains embedded NUL byte"
drivers/mfd/eio_core.c:619:1: sparse: sparse: bad integer constant expression
drivers/mfd/eio_core.c:619:1: sparse: sparse: static assertion failed: "MODULE_INFO(author, ...) contains embedded NUL byte"
drivers/mfd/eio_core.c:620:1: sparse: sparse: bad integer constant expression
drivers/mfd/eio_core.c:620:1: sparse: sparse: static assertion failed: "MODULE_INFO(description, ...) contains embedded NUL byte"
drivers/mfd/eio_core.c:621:1: sparse: sparse: bad integer constant expression
drivers/mfd/eio_core.c:621:1: sparse: sparse: static assertion failed: "MODULE_INFO(file, ...) contains embedded NUL byte"
drivers/mfd/eio_core.c:621:1: sparse: sparse: bad integer constant expression
drivers/mfd/eio_core.c:621:1: sparse: sparse: static assertion failed: "MODULE_INFO(license, ...) contains embedded NUL byte"
vim +39 drivers/mfd/eio_core.c
3265ff741af01a Ramiro Oliveira 2025-12-12 29
3265ff741af01a Ramiro Oliveira 2025-12-12 30 /**
3265ff741af01a Ramiro Oliveira 2025-12-12 31 * Timeout: Default timeout in microseconds when a PMC command's
3265ff741af01a Ramiro Oliveira 2025-12-12 32 * timeout is unspecified. PMC command responses typically range
3265ff741af01a Ramiro Oliveira 2025-12-12 33 * from 200us to 2ms. 5ms is quite a safe value for timeout. In
3265ff741af01a Ramiro Oliveira 2025-12-12 34 * In some cases, responses are longer. In such situations, please
3265ff741af01a Ramiro Oliveira 2025-12-12 35 * adding the timeout parameter loading related sub-drivers or
3265ff741af01a Ramiro Oliveira 2025-12-12 36 * this core driver (not recommended).
3265ff741af01a Ramiro Oliveira 2025-12-12 37 */
3265ff741af01a Ramiro Oliveira 2025-12-12 38 static uint timeout = DEFAULT_TIMEOUT;
3265ff741af01a Ramiro Oliveira 2025-12-12 @39 module_param(timeout, uint, 0444);
3265ff741af01a Ramiro Oliveira 2025-12-12 40 MODULE_PARM_DESC(timeout, "Default PMC command timeout in usec.\n");
3265ff741af01a Ramiro Oliveira 2025-12-12 41
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH 0/8] Add support for Advantech EIO MFD series devices
@ 2025-12-12 16:40 Ramiro Oliveira
2025-12-12 16:40 ` [PATCH 1/8] Add Advantech EIO MFD driver Ramiro Oliveira
0 siblings, 1 reply; 4+ messages in thread
From: Ramiro Oliveira @ 2025-12-12 16:40 UTC (permalink / raw)
To: Lee Jones, Linus Walleij, Bartosz Golaszewski, Guenter Roeck,
Andi Shyti, Daniel Thompson, Jingoo Han, Helge Deller,
Wim Van Sebroeck, Rafael J. Wysocki, Daniel Lezcano, Zhang Rui,
Lukasz Luba
Cc: linux-kernel, linux-gpio, linux-hwmon, linux-i2c, dri-devel,
linux-fbdev, linux-watchdog, linux-pm, Wenkai Chung,
Francisco Aragon-Trivino, Hongzhi Wang, Mikhail Tsukerman,
Thomas Kastner, Ramiro Oliveira
This series implements a set of drivers allowing to support the
Advantech EIO-2xx series of devices.
This includes GPIO, hwmon, I2C bus, backlight controller, watchdog,
thermal and a fan driver.
This series of patches targets several different subsystems, but the MFD
subsystem is the main target.
Signed-off-by: Ramiro Oliveira <ramiro.oliveira@advantech.com>
---
Ramiro Oliveira (8):
Add Advantech EIO MFD driver
Add Advantech EIO GPIO driver
Add Advantech EIO Hardware Monitor driver
Add Advantech EIO I2C driver
Add Advantech EIO Backlight driver
Add Advantech EIO Watchdog driver
Add Advantech EIO Thermal driver
Add Advantech EIO Fan driver
MAINTAINERS | 13 +
drivers/gpio/Kconfig | 6 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-eio.c | 273 +++++++++
drivers/hwmon/Kconfig | 10 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/eio-hwmon.c | 344 ++++++++++++
drivers/i2c/busses/Kconfig | 6 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-eio.c | 1142 ++++++++++++++++++++++++++++++++++++++
drivers/mfd/Kconfig | 10 +
drivers/mfd/Makefile | 1 +
drivers/mfd/eio_core.c | 621 +++++++++++++++++++++
drivers/thermal/Kconfig | 17 +
drivers/thermal/Makefile | 2 +
drivers/thermal/eio_fan.c | 490 ++++++++++++++++
drivers/thermal/eio_thermal.c | 352 ++++++++++++
drivers/video/backlight/Kconfig | 6 +
drivers/video/backlight/Makefile | 1 +
drivers/video/backlight/eio_bl.c | 268 +++++++++
drivers/watchdog/Kconfig | 7 +
drivers/watchdog/Makefile | 1 +
drivers/watchdog/eio_wdt.c | 672 ++++++++++++++++++++++
include/linux/mfd/eio.h | 127 +++++
24 files changed, 4372 insertions(+)
---
base-commit: d9771d0dbe18dd643760431870a6abf9b0866bb0
change-id: 20251212-upstream-v1-81338c603f94
Best regards,
--
Ramiro Oliveira <ramiro.oliveira@advantech.com>
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH 1/8] Add Advantech EIO MFD driver 2025-12-12 16:40 [PATCH 0/8] Add support for Advantech EIO MFD series devices Ramiro Oliveira @ 2025-12-12 16:40 ` Ramiro Oliveira 2025-12-13 15:19 ` kernel test robot 2026-01-08 14:45 ` Lee Jones 0 siblings, 2 replies; 4+ messages in thread From: Ramiro Oliveira @ 2025-12-12 16:40 UTC (permalink / raw) To: Lee Jones, Linus Walleij, Bartosz Golaszewski, Guenter Roeck, Andi Shyti, Daniel Thompson, Jingoo Han, Helge Deller, Wim Van Sebroeck, Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba Cc: linux-kernel, linux-gpio, linux-hwmon, linux-i2c, dri-devel, linux-fbdev, linux-watchdog, linux-pm, Wenkai Chung, Francisco Aragon-Trivino, Hongzhi Wang, Mikhail Tsukerman, Thomas Kastner, Ramiro Oliveira Creating the MFD core driver for Advantech EIO, all other drivers (GPIO, I2C, etc) depend on this core driver. Signed-off-by: Ramiro Oliveira <ramiro.oliveira@advantech.com> --- MAINTAINERS | 6 + drivers/mfd/Kconfig | 10 + drivers/mfd/Makefile | 1 + drivers/mfd/eio_core.c | 621 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/eio.h | 127 ++++++++++ 5 files changed, 765 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 663e86eb9ff1..bd9279796c2f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -616,6 +616,12 @@ L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/adv_swbutton.c +ADVANTECH EIO DRIVER +M: Ramiro Oliveira <ramiro.oliveira@advantech.com> +S: Maintained +F: drivers/mfd/eio_core.c +F: include/linux/mfd/eio.h + ADXL313 THREE-AXIS DIGITAL ACCELEROMETER DRIVER M: Lucas Stankus <lucas.p.stankus@gmail.com> S: Supported diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index aace5766b38a..02a0b324eb6a 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -506,6 +506,16 @@ config MFD_DLN2 etc. must be enabled in order to use the functionality of the device. +config MFD_EIO + tristate "Advantech EIO MFD core" + select MFD_CORE + help + This enables support for the Advantech EIO multi-function device. + This core driver provides register access and coordination for the + EIO's subdevices (GPIO, watchdog, hwmon, thermal, backlight, I2C). + This driver supports EIO-IS200, EIO-201, EIO-210 and EIO-211. + + config MFD_ENE_KB3930 tristate "ENE KB3930 Embedded Controller support" depends on I2C diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index e75e8045c28a..f8c53b55b679 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_MFD_CROS_EC_DEV) += cros_ec_dev.o obj-$(CONFIG_MFD_CS42L43) += cs42l43.o obj-$(CONFIG_MFD_CS42L43_I2C) += cs42l43-i2c.o obj-$(CONFIG_MFD_CS42L43_SDW) += cs42l43-sdw.o +obj-$(CONFIG_MFD_EIO) += eio_core.o obj-$(CONFIG_MFD_ENE_KB3930) += ene-kb3930.o obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o obj-$(CONFIG_MFD_GATEWORKS_GSC) += gateworks-gsc.o diff --git a/drivers/mfd/eio_core.c b/drivers/mfd/eio_core.c new file mode 100644 index 000000000000..7a58c62595a5 --- /dev/null +++ b/drivers/mfd/eio_core.c @@ -0,0 +1,621 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Advantech Embedded Controller base Driver + * + * This driver provides an interface to access the EIO Series EC + * firmware via its own Power Management Channel (PMC) for subdrivers: + * + * A system may have one or two independent EIO devices. + * + * Copyright (C) 2025 Advantech Co., Ltd. + */ + +#include <linux/delay.h> +#include <linux/isa.h> +#include <linux/mfd/core.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/sysfs.h> +#include <linux/time.h> +#include <linux/uaccess.h> +#include <linux/version.h> +#include <linux/mfd/eio.h> + +#define TIMEOUT_MAX (10 * USEC_PER_SEC) +#define TIMEOUT_MIN 200 +#define SLEEP_MAX 200 +#define DEFAULT_TIMEOUT 5000 + +/** + * Timeout: Default timeout in microseconds when a PMC command's + * timeout is unspecified. PMC command responses typically range + * from 200us to 2ms. 5ms is quite a safe value for timeout. In + * In some cases, responses are longer. In such situations, please + * adding the timeout parameter loading related sub-drivers or + * this core driver (not recommended). + */ +static uint timeout = DEFAULT_TIMEOUT; +module_param(timeout, uint, 0444); +MODULE_PARM_DESC(timeout, "Default PMC command timeout in usec.\n"); + +struct eio_dev_port { + u16 idx_port; + u16 data_port; +}; + +static struct eio_dev_port pnp_port[] = { + { .idx_port = EIO_PNP_INDEX, .data_port = EIO_PNP_DATA }, + { .idx_port = EIO_SUB_PNP_INDEX, + .data_port = EIO_SUB_PNP_DATA }, +}; + +static struct mfd_cell mfd_devs[] = { + { .name = "eio_wdt" }, + { .name = "gpio_eio" }, + { .name = "eio_hwmon" }, + { .name = "i2c_eio" }, + { .name = "eio_thermal" }, + { .name = "eio_fan" }, + { .name = "eio_bl" }, +}; + +static const struct regmap_range eio_range[] = { + regmap_reg_range(EIO_PNP_INDEX, EIO_PNP_DATA), + regmap_reg_range(EIO_SUB_PNP_INDEX, EIO_SUB_PNP_DATA), + regmap_reg_range(0x200, 0x3FF), +}; + +static const struct regmap_access_table volatile_regs = { + .yes_ranges = eio_range, + .n_yes_ranges = ARRAY_SIZE(eio_range), +}; + +static const struct regmap_config pnp_regmap_config = { + .name = "eio_core", + .reg_bits = 16, + .val_bits = 8, + .volatile_table = &volatile_regs, + .io_port = true, + .cache_type = REGCACHE_NONE, +}; + +static struct { + char name[32]; + int cmd; + int ctrl; + int dev; + int size; + enum { + HEX, + NUMBER, + PNP_ID, + } type; + +} attrs[] = { + { "board_name", 0x53, 0x10, 0, 16 }, + { "board_serial", 0x53, 0x1F, 0, 16 }, + { "board_manufacturer", 0x53, 0x11, 0, 16 }, + { "board_id", 0x53, 0x1E, 0, 4 }, + { "firmware_version", 0x53, 0x21, 0, 4 }, + { "firmware_name", 0x53, 0x22, 0, 16 }, + { "firmware_build", 0x53, 0x23, 0, 26 }, + { "firmware_date", 0x53, 0x24, 0, 16 }, + { "chip_id", 0x53, 0x12, 0, 12 }, + { "chip_detect", 0x53, 0x15, 0, 12 }, + { "platform_type", 0x53, 0x13, 0, 16 }, + { "platform_revision", 0x53, 0x04, 0x44, 4 }, + { "eapi_version", 0x53, 0x04, 0x64, 4 }, + { "eapi_id", 0x53, 0x31, 0, 4 }, + { "boot_count", 0x55, 0x10, 0, 4, NUMBER }, + { "powerup_hour", 0x55, 0x11, 0, 4, NUMBER }, + { "pnp_id", 0x53, 0x04, 0x68, 4, PNP_ID }, +}; + +static ssize_t info_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + uint i; + + for (i = 0; i < ARRAY_SIZE(attrs); i++) { + int ret; + char str[32] = ""; + int val; + + struct pmc_op op = { + .cmd = attrs[i].cmd, + .control = attrs[i].ctrl, + .device_id = attrs[i].dev, + .payload = (u8 *)str, + .size = attrs[i].size, + }; + + if (strcmp(attr->attr.name, attrs[i].name)) + continue; + + ret = eio_core_pmc_operation(dev, &op); + if (ret) + return ret; + + if (attrs[i].size != 4) + return sprintf(buf, "%s\n", str); + + val = *(u32 *)str; + + if (attrs[i].type == HEX) + return sprintf(buf, "0x%08X\n", val); + + if (attrs[i].type == NUMBER) + return sprintf(buf, "%d\n", val); + + /* Should be pnp_id */ + return sprintf(buf, "%c%c%c, %X\n", (val >> 14 & 0x3F) + 0x40, + ((val >> 9 & 0x18) | (val >> 25 & 0x07)) + 0x40, + (val >> 20 & 0x1F) + 0x40, val & 0xFFF); + } + + return -EINVAL; +} + +#define PMC_DEVICE_ATTR_RO(_name) \ + static ssize_t _name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ + { \ + return info_show(dev, attr, buf); \ + } \ + static DEVICE_ATTR_RO(_name) + +PMC_DEVICE_ATTR_RO(board_name); +PMC_DEVICE_ATTR_RO(board_serial); +PMC_DEVICE_ATTR_RO(board_manufacturer); +PMC_DEVICE_ATTR_RO(firmware_name); +PMC_DEVICE_ATTR_RO(firmware_version); +PMC_DEVICE_ATTR_RO(firmware_build); +PMC_DEVICE_ATTR_RO(firmware_date); +PMC_DEVICE_ATTR_RO(chip_id); +PMC_DEVICE_ATTR_RO(chip_detect); +PMC_DEVICE_ATTR_RO(platform_type); +PMC_DEVICE_ATTR_RO(platform_revision); +PMC_DEVICE_ATTR_RO(board_id); +PMC_DEVICE_ATTR_RO(eapi_version); +PMC_DEVICE_ATTR_RO(eapi_id); +PMC_DEVICE_ATTR_RO(boot_count); +PMC_DEVICE_ATTR_RO(powerup_hour); +PMC_DEVICE_ATTR_RO(pnp_id); + +static struct attribute *pmc_attrs[] = { &dev_attr_board_name.attr, + &dev_attr_board_serial.attr, + &dev_attr_board_manufacturer.attr, + &dev_attr_firmware_name.attr, + &dev_attr_firmware_version.attr, + &dev_attr_firmware_build.attr, + &dev_attr_firmware_date.attr, + &dev_attr_chip_id.attr, + &dev_attr_chip_detect.attr, + &dev_attr_platform_type.attr, + &dev_attr_platform_revision.attr, + &dev_attr_board_id.attr, + &dev_attr_eapi_version.attr, + &dev_attr_eapi_id.attr, + &dev_attr_boot_count.attr, + &dev_attr_powerup_hour.attr, + &dev_attr_pnp_id.attr, + NULL }; + +ATTRIBUTE_GROUPS(pmc); + +static unsigned int eio_pnp_read(struct device *dev, + struct eio_dev_port *port, u8 idx) +{ + struct eio_dev *eio = dev_get_drvdata(dev); + unsigned int val; + + if (regmap_write(eio->map, port->idx_port, idx)) + dev_err(dev, "Error port write 0x%X\n", port->idx_port); + + if (regmap_read(eio->map, port->data_port, &val)) + dev_err(dev, "Error port read 0x%X\n", port->data_port); + + return val; +} + +static void eio_pnp_write(struct device *dev, struct eio_dev_port *port, + u8 idx, u8 data) +{ + struct eio_dev *eio = dev_get_drvdata(dev); + + if (regmap_write(eio->map, port->idx_port, idx) || + regmap_write(eio->map, port->data_port, data)) + dev_err(dev, "Error port write 0x%X %X\n", port->idx_port, + port->data_port); +} + +static void eio_pnp_enter(struct device *dev, struct eio_dev_port *port) +{ + struct eio_dev *eio = dev_get_drvdata(dev); + /* Write 0x87 to index port twice to unlock IO port */ + if (regmap_write(eio->map, port->idx_port, + EIO_EXT_MODE_ENTER) || + regmap_write(eio->map, port->idx_port, EIO_EXT_MODE_ENTER)) + dev_err(dev, "Error port write 0x%X\n", port->idx_port); +} + +static void eio_pnp_leave(struct device *dev, struct eio_dev_port *port) +{ + struct eio_dev *eio = dev_get_drvdata(dev); + /* Write 0xAA to index port once to lock IO port */ + if (regmap_write(eio->map, port->idx_port, EIO_EXT_MODE_EXIT)) + dev_err(dev, "Error port write 0x%X\n", port->idx_port); +} + +static int pmc_write_data(struct device *dev, int id, u8 value, u16 timeout) +{ + struct eio_dev *eio = dev_get_drvdata(dev); + int ret; + + if (WAIT_IBF(dev, id, timeout)) + return -ETIME; + + ret = regmap_write(eio->map, eio->pmc[id].data, value); + if (ret) + dev_err(dev, "Error PMC write %X:%X\n", + eio->pmc[id].data, value); + + return ret; +} + +static int pmc_write_cmd(struct device *dev, int id, u8 value, u16 timeout) +{ + struct eio_dev *eio = dev_get_drvdata(dev); + int ret; + + if (WAIT_IBF(dev, id, timeout)) + return -ETIME; + + ret = regmap_write(eio->map, eio->pmc[id].cmd, value); + if (ret) + dev_err(dev, "Error PMC write %X:%X\n", + eio->pmc[id].cmd, value); + + return ret; +} + +static int pmc_read_data(struct device *dev, int id, u8 *value, u16 timeout) +{ + struct eio_dev *eio = dev_get_drvdata(dev); + unsigned int val; + int ret; + + if (WAIT_OBF(dev, id, timeout)) + return -ETIME; + + ret = regmap_read(eio->map, eio->pmc[id].data, &val); + if (ret) + dev_err(dev, "Error PMC read %X\n", eio->pmc[id].data); + else + *value = (u8)(val & 0xFF); + + return ret; +} + +static int pmc_read_status(struct device *dev, int id) +{ + struct eio_dev *eio = dev_get_drvdata(dev); + unsigned int val; + + if (regmap_read(eio->map, eio->pmc[id].status, &val)) { + dev_err(dev, "Error PMC read %X\n", + eio->pmc[id].status); + return 0; + } + + return val; +} + +static void pmc_clear(struct device *dev, int id) +{ + struct eio_dev *eio = dev_get_drvdata(dev); + unsigned int val; + + /* Check if input buffer blocked */ + if ((pmc_read_status(dev, id) & EIO_PMC_STATUS_IBF) == 0) + return; + + /* Read out previous garbage */ + if (regmap_read(eio->map, eio->pmc[id].data, &val)) + dev_err(dev, "Error pmc clear\n"); + + usleep_range(10, 100); +} + +int eio_core_pmc_wait(struct device *dev, int id, + enum eio_pmc_wait wait, uint max_duration) +{ + struct eio_dev *eio = dev_get_drvdata(dev); + uint val; + int new_timeout = max_duration ? max_duration : timeout; + + if (new_timeout < TIMEOUT_MIN || new_timeout > TIMEOUT_MAX) { + dev_err(dev, + "Error timeout value: %dus. Timeout value should between %d and %ld\n", + new_timeout, TIMEOUT_MIN, TIMEOUT_MAX); + return -ETIME; + } + + if (wait == PMC_WAIT_INPUT) + return regmap_read_poll_timeout(eio->map, eio->pmc[id].status, + val, (val & EIO_PMC_STATUS_IBF) == 0, + SLEEP_MAX, new_timeout); + return regmap_read_poll_timeout(eio->map, + eio->pmc[id].status, val, + (val & EIO_PMC_STATUS_OBF) != 0, + SLEEP_MAX, new_timeout); +} +EXPORT_SYMBOL_GPL(eio_core_pmc_wait); + +int eio_core_pmc_operation(struct device *dev, struct pmc_op *op) +{ + struct eio_dev *eio = dev_get_drvdata(dev); + u8 i; + int ret; + bool read_cmd = op->cmd & EIO_FLAG_PMC_READ; + ktime_t t = ktime_get(); + + mutex_lock(&eio->mutex); + + pmc_clear(dev, op->chip); + + ret = pmc_write_cmd(dev, op->chip, op->cmd, op->timeout); + if (ret) + goto err; + + ret = pmc_write_data(dev, op->chip, op->control, op->timeout); + if (ret) + goto err; + + ret = pmc_write_data(dev, op->chip, op->device_id, op->timeout); + if (ret) + goto err; + + ret = pmc_write_data(dev, op->chip, op->size, op->timeout); + if (ret) + goto err; + + for (i = 0; i < op->size; i++) { + if (read_cmd) + ret = pmc_read_data(dev, op->chip, &op->payload[i], + op->timeout); + else + ret = pmc_write_data(dev, op->chip, op->payload[i], + op->timeout); + + if (ret) + goto err; + } + + mutex_unlock(&eio->mutex); + + return 0; + +err: + mutex_unlock(&eio->mutex); + + dev_err(dev, "PMC error duration:%lldus", + ktime_to_us(ktime_sub(ktime_get(), t))); + dev_err(dev, + ".cmd=0x%02X, .ctrl=0x%02X .id=0x%02X, .size=0x%02X .data=0x%02X%02X", + op->cmd, op->control, op->device_id, op->size, op->payload[0], + op->payload[1]); + + return ret; +} +EXPORT_SYMBOL_GPL(eio_core_pmc_operation); + +static int get_pmc_port(struct device *dev, int id, + struct eio_dev_port *port) +{ + struct eio_dev *eio = dev_get_drvdata(dev); + struct _pmc_port *pmc = &eio->pmc[id]; + + eio_pnp_enter(dev, port); + + /* Switch to PMC device page */ + eio_pnp_write(dev, port, EIO_LDN, EIO_LDN_PMC1); + + /* Active this device */ + eio_pnp_write(dev, port, EIO_LDAR, EIO_LDAR_LDACT); + + /* Get PMC cmd and data port */ + pmc->data = eio_pnp_read(dev, port, EIO_IOBA0H) << 8; + pmc->data |= eio_pnp_read(dev, port, EIO_IOBA0L); + pmc->cmd = eio_pnp_read(dev, port, EIO_IOBA1H) << 8; + pmc->cmd |= eio_pnp_read(dev, port, EIO_IOBA1L); + + /* Disable IRQ */ + eio_pnp_write(dev, port, EIO_IRQCTRL, 0); + + eio_pnp_leave(dev, port); + + /* Make sure IO ports are not occupied */ + if (!devm_request_region(dev, pmc->data, 2, KBUILD_MODNAME)) { + dev_err(dev, "Request region %X error\n", pmc->data); + return -EBUSY; + } + + return 0; +} + +static int eio_init(struct device *dev) +{ + struct eio_dev *eio = dev_get_drvdata(dev); + u16 chip_id = 0; + u8 tmp = 0; + int chip = 0; + int ret = -ENOMEM; + + for (chip = 0; chip < ARRAY_SIZE(pnp_port); chip++) { + struct eio_dev_port *port = pnp_port + chip; + + if (!devm_request_region(dev, pnp_port[chip].idx_port, + pnp_port[chip].data_port - + pnp_port[chip].idx_port, + KBUILD_MODNAME)) + continue; + + eio_pnp_enter(dev, port); + + chip_id = eio_pnp_read(dev, port, EIO_CHIPID1) << 8; + chip_id |= eio_pnp_read(dev, port, EIO_CHIPID2); + + if (chip_id != EIO200_CHIPID && chip_id != EIO201_211_CHIPID) + continue; + + /* Turn on the enable flag */ + tmp = eio_pnp_read(dev, port, EIO_SIOCTRL); + tmp |= EIO_SIOCTRL_SIOEN; + + eio_pnp_write(dev, port, EIO_SIOCTRL, tmp); + + eio_pnp_leave(dev, port); + + ret = get_pmc_port(dev, chip, port); + if (ret) + return ret; + + if (chip == 0) + eio->flag |= EIO_F_CHIP_EXIST; + else + eio->flag |= EIO_F_SUB_CHIP_EXIST; + } + + return ret; +} + +static uint8_t acpiram_access(struct device *dev, uint8_t offset) +{ + u8 val; + int ret; + int timeout = 0; + struct eio_dev *eio = dev_get_drvdata(dev); + + /* We only store information on primary EC */ + int chip = 0; + + mutex_lock(&eio->mutex); + + pmc_clear(dev, chip); + + ret = pmc_write_cmd(dev, chip, EIO_PMC_CMD_ACPIRAM_READ, timeout); + if (ret) + goto err; + + ret = pmc_write_data(dev, chip, offset, timeout); + if (ret) + goto err; + + ret = pmc_write_data(dev, chip, sizeof(val), timeout); + if (ret) + goto err; + + ret = pmc_read_data(dev, chip, &val, timeout); + if (ret) + goto err; + +err: + mutex_unlock(&eio->mutex); + return ret ? 0 : val; +} + +static int firmware_code_base(struct device *dev) +{ + struct eio_dev *eio = dev_get_drvdata(dev); + u8 ic_vendor, ic_code, code_base; + + ic_vendor = acpiram_access(dev, EIO_ACPIRAM_ICVENDOR); + ic_code = acpiram_access(dev, EIO_ACPIRAM_ICCODE); + code_base = acpiram_access(dev, EIO_ACPIRAM_CODEBASE); + + if (ic_vendor != 'R') + return -ENODEV; + + if (ic_code != EIO200_ICCODE && ic_code != EIO201_ICCODE && + ic_code != EIO211_ICCODE) + goto err; + + if (code_base == EIO_ACPIRAM_CODEBASE_NEW) { + eio->flag |= EIO_F_NEW_CODE_BASE; + return 0; + } + + if (code_base == 0 && + (ic_code != EIO201_ICCODE && ic_code != EIO211_ICCODE)) { + dev_info(dev, "Old code base not supported, yet."); + return -ENODEV; + } + +err: + /* Codebase error. This should only happen on firmware error. */ + dev_err(dev, + "Codebase check fail: vendor: 0x%X, code: 0x%X, base: 0x%X\n", + ic_vendor, ic_code, code_base); + return -ENODEV; +} + +static int eio_probe(struct device *dev, unsigned int id) +{ + int ret = 0; + struct eio_dev *eio; + + eio = devm_kzalloc(dev, sizeof(*eio), GFP_KERNEL); + if (!eio) + return -ENOMEM; + + eio->dev = dev; + mutex_init(&eio->mutex); + + eio->iomem = devm_ioport_map(dev, 0, EIO_SUB_PNP_DATA + 1); + if (IS_ERR(eio->iomem)) + return PTR_ERR(eio->iomem); + + eio->map = devm_regmap_init_mmio(dev, eio->iomem, &pnp_regmap_config); + if (IS_ERR(eio->map)) + return PTR_ERR(eio->map); + + /* publish instance for subdrivers (dev_get_drvdata(dev->parent)) */ + dev_set_drvdata(dev, eio); + + if (eio_init(dev)) { + dev_dbg(dev, "No device found\n"); + return -ENODEV; + } + + ret = firmware_code_base(dev); + if (ret) { + dev_err(dev, "Chip code base check fail\n"); + return ret; /* keep helper's return (e.g., -EIO) */ + } + + ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, + mfd_devs, ARRAY_SIZE(mfd_devs), + NULL, 0, NULL); + if (ret) + dev_err(dev, "Cannot register child devices (error = %d)\n", ret); + + dev_dbg(dev, "Module insert completed\n"); + return 0; +} + +static struct isa_driver eio_driver = { + .probe = eio_probe, + + .driver = { + .name = "eio_core", + .dev_groups = pmc_groups, + }, +}; +module_isa_driver(eio_driver, 1); + +MODULE_AUTHOR("Wenkai Chung <wenkai.chung@advantech.com.tw>"); +MODULE_AUTHOR("Ramiro Oliveira <ramiro.oliveira@advantech.com>"); +MODULE_DESCRIPTION("Advantech EIO series EC core driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/eio.h b/include/linux/mfd/eio.h new file mode 100644 index 000000000000..b87614274201 --- /dev/null +++ b/include/linux/mfd/eio.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Header for the Advantech EIO core driver and its sub-drivers + * + * Copyright (C) 2025 Advantech Co., Ltd. + */ + +#ifndef _MFD_EIO_H_ +#define _MFD_EIO_H_ +#include <linux/io.h> +#include <linux/regmap.h> + +/* Definition */ +#define EIO_CHIPID1 0x20 +#define EIO_CHIPID2 0x21 +#define EIO_CHIPVER 0x22 +#define EIO_SIOCTRL 0x23 +#define EIO_SIOCTRL_SIOEN BIT(0) +#define EIO_SIOCTRL_SWRST BIT(1) +#define EIO_IRQCTRL 0x70 +#define EIO200_CHIPID 0x9610 +#define EIO201_211_CHIPID 0x9620 +#define EIO200_ICCODE 0x10 +#define EIO201_ICCODE 0x20 +#define EIO211_ICCODE 0x21 + +/* LPC PNP */ +#define EIO_PNP_INDEX 0x299 +#define EIO_PNP_DATA 0x29A +#define EIO_SUB_PNP_INDEX 0x499 +#define EIO_SUB_PNP_DATA 0x49A +#define EIO_EXT_MODE_ENTER 0x87 +#define EIO_EXT_MODE_EXIT 0xAA + +/* LPC LDN */ +#define EIO_LDN 0x07 +#define EIO_LDN_PMC0 0x0C +#define EIO_LDN_PMC1 0x0D + +/* PMC registers */ +#define EIO_PMC_STATUS_IBF BIT(1) +#define EIO_PMC_STATUS_OBF BIT(0) +#define EIO_LDAR 0x30 +#define EIO_LDAR_LDACT BIT(0) +#define EIO_IOBA0H 0x60 +#define EIO_IOBA0L 0x61 +#define EIO_IOBA1H 0x62 +#define EIO_IOBA1L 0x63 +#define EIO_FLAG_PMC_READ BIT(0) + +/* PMC command list */ +#define EIO_PMC_CMD_ACPIRAM_READ 0x31 +#define EIO_PMC_CMD_CFG_SAVE 0x56 + +/* OLD PMC */ +#define EIO_PMC_NO_INDEX 0xFF + +/* ACPI RAM Address Table */ +#define EIO_ACPIRAM_VERSIONSECTION (0xFA) +#define EIO_ACPIRAM_ICVENDOR (EIO_ACPIRAM_VERSIONSECTION + 0x00) +#define EIO_ACPIRAM_ICCODE (EIO_ACPIRAM_VERSIONSECTION + 0x01) +#define EIO_ACPIRAM_CODEBASE (EIO_ACPIRAM_VERSIONSECTION + 0x02) + +#define EIO_ACPIRAM_CODEBASE_NEW BIT(7) + +/* Firmware */ +#define EIO_F_SUB_NEW_CODE_BASE BIT(6) +#define EIO_F_SUB_CHANGED BIT(7) +#define EIO_F_NEW_CODE_BASE BIT(8) +#define EIO_F_CHANGED BIT(9) +#define EIO_F_SUB_CHIP_EXIST BIT(30) +#define EIO_F_CHIP_EXIST BIT(31) + +/* Others */ +#define EIO_EC_NUM 2 + +struct _pmc_port { + union { + u16 cmd; + u16 status; + }; + u16 data; +}; + +struct pmc_op { + u8 cmd; + u8 control; + u8 device_id; + u8 size; + u8 *payload; + u8 chip; + u16 timeout; +}; + +enum eio_rw_operation { + OPERATION_READ, + OPERATION_WRITE, +}; + +struct eio_dev { + struct device *dev; + struct regmap *map; + void __iomem *iomem; + struct mutex mutex; /* Protects PMC command access */ + struct _pmc_port pmc[EIO_EC_NUM]; + u32 flag; +}; + +int eio_core_pmc_operation(struct device *dev, struct pmc_op *operation); + +enum eio_pmc_wait { + PMC_WAIT_INPUT, + PMC_WAIT_OUTPUT, +}; + +int eio_core_pmc_wait(struct device *dev, int id, enum eio_pmc_wait wait, + uint timeout); + +#define WAIT_IBF(dev, id, timeout) eio_core_pmc_wait(dev, id, PMC_WAIT_INPUT, timeout) +#define WAIT_OBF(dev, id, timeout) eio_core_pmc_wait(dev, id, PMC_WAIT_OUTPUT, timeout) + +#ifdef pr_fmt +#undef pr_fmt +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#endif + +#endif -- 2.43.0 ^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH 1/8] Add Advantech EIO MFD driver 2025-12-12 16:40 ` [PATCH 1/8] Add Advantech EIO MFD driver Ramiro Oliveira @ 2025-12-13 15:19 ` kernel test robot 2026-01-08 14:45 ` Lee Jones 1 sibling, 0 replies; 4+ messages in thread From: kernel test robot @ 2025-12-13 15:19 UTC (permalink / raw) To: Ramiro Oliveira, Lee Jones, Linus Walleij, Bartosz Golaszewski, Guenter Roeck, Andi Shyti, Daniel Thompson, Jingoo Han, Helge Deller, Wim Van Sebroeck, Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba Cc: oe-kbuild-all, linux-kernel, linux-gpio, linux-hwmon, linux-i2c, dri-devel, linux-fbdev, linux-watchdog, linux-pm, Wenkai Chung, Francisco Aragon-Trivino, Hongzhi Wang, Mikhail Tsukerman, Thomas Kastner, Ramiro Oliveira Hi Ramiro, kernel test robot noticed the following build warnings: [auto build test WARNING on d9771d0dbe18dd643760431870a6abf9b0866bb0] url: https://github.com/intel-lab-lkp/linux/commits/Ramiro-Oliveira/Add-Advantech-EIO-MFD-driver/20251213-004905 base: d9771d0dbe18dd643760431870a6abf9b0866bb0 patch link: https://lore.kernel.org/r/20251212-upstream-v1-v1-1-d50d40ec8d8a%40advantech.com patch subject: [PATCH 1/8] Add Advantech EIO MFD driver config: nios2-allmodconfig (https://download.01.org/0day-ci/archive/20251213/202512132239.HrAPSw6z-lkp@intel.com/config) compiler: nios2-linux-gcc (GCC) 11.5.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251213/202512132239.HrAPSw6z-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202512132239.HrAPSw6z-lkp@intel.com/ All warnings (new ones prefixed by >>): >> Warning: drivers/mfd/eio_core.c:37 cannot understand function prototype: 'uint timeout = DEFAULT_TIMEOUT;' -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH 1/8] Add Advantech EIO MFD driver 2025-12-12 16:40 ` [PATCH 1/8] Add Advantech EIO MFD driver Ramiro Oliveira 2025-12-13 15:19 ` kernel test robot @ 2026-01-08 14:45 ` Lee Jones 1 sibling, 0 replies; 4+ messages in thread From: Lee Jones @ 2026-01-08 14:45 UTC (permalink / raw) To: Ramiro Oliveira Cc: Linus Walleij, Bartosz Golaszewski, Guenter Roeck, Andi Shyti, Daniel Thompson, Jingoo Han, Helge Deller, Wim Van Sebroeck, Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba, linux-kernel, linux-gpio, linux-hwmon, linux-i2c, dri-devel, linux-fbdev, linux-watchdog, linux-pm, Wenkai Chung, Francisco Aragon-Trivino, Hongzhi Wang, Mikhail Tsukerman, Thomas Kastner On Fri, 12 Dec 2025, Ramiro Oliveira wrote: > Creating the MFD core driver for Advantech EIO, all other drivers (GPIO, > I2C, etc) depend on this core driver. You're going to have to come up with a MUCH better commit message than that for 800 line driver! > Signed-off-by: Ramiro Oliveira <ramiro.oliveira@advantech.com> > --- > MAINTAINERS | 6 + > drivers/mfd/Kconfig | 10 + > drivers/mfd/Makefile | 1 + > drivers/mfd/eio_core.c | 621 ++++++++++++++++++++++++++++++++++++++++++++++++ > include/linux/mfd/eio.h | 127 ++++++++++ > 5 files changed, 765 insertions(+) > > diff --git a/MAINTAINERS b/MAINTAINERS > index 663e86eb9ff1..bd9279796c2f 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -616,6 +616,12 @@ L: platform-driver-x86@vger.kernel.org > S: Maintained > F: drivers/platform/x86/adv_swbutton.c > > +ADVANTECH EIO DRIVER > +M: Ramiro Oliveira <ramiro.oliveira@advantech.com> > +S: Maintained > +F: drivers/mfd/eio_core.c > +F: include/linux/mfd/eio.h > + > ADXL313 THREE-AXIS DIGITAL ACCELEROMETER DRIVER > M: Lucas Stankus <lucas.p.stankus@gmail.com> > S: Supported > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig > index aace5766b38a..02a0b324eb6a 100644 > --- a/drivers/mfd/Kconfig > +++ b/drivers/mfd/Kconfig > @@ -506,6 +506,16 @@ config MFD_DLN2 > etc. must be enabled in order to use the functionality of > the device. > > +config MFD_EIO > + tristate "Advantech EIO MFD core" Drop the term MFD, it doesn't mean anything. We made it up. What is this device? > + select MFD_CORE > + help > + This enables support for the Advantech EIO multi-function device. Remove all mentions of MFD. > + This core driver provides register access and coordination for the > + EIO's subdevices (GPIO, watchdog, hwmon, thermal, backlight, I2C). > + This driver supports EIO-IS200, EIO-201, EIO-210 and EIO-211. Which are? > + > + > config MFD_ENE_KB3930 > tristate "ENE KB3930 Embedded Controller support" > depends on I2C > diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile > index e75e8045c28a..f8c53b55b679 100644 > --- a/drivers/mfd/Makefile > +++ b/drivers/mfd/Makefile > @@ -19,6 +19,7 @@ obj-$(CONFIG_MFD_CROS_EC_DEV) += cros_ec_dev.o > obj-$(CONFIG_MFD_CS42L43) += cs42l43.o > obj-$(CONFIG_MFD_CS42L43_I2C) += cs42l43-i2c.o > obj-$(CONFIG_MFD_CS42L43_SDW) += cs42l43-sdw.o > +obj-$(CONFIG_MFD_EIO) += eio_core.o > obj-$(CONFIG_MFD_ENE_KB3930) += ene-kb3930.o > obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o > obj-$(CONFIG_MFD_GATEWORKS_GSC) += gateworks-gsc.o > diff --git a/drivers/mfd/eio_core.c b/drivers/mfd/eio_core.c > new file mode 100644 > index 000000000000..7a58c62595a5 > --- /dev/null > +++ b/drivers/mfd/eio_core.c > @@ -0,0 +1,621 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Advantech Embedded Controller base Driver > + * > + * This driver provides an interface to access the EIO Series EC > + * firmware via its own Power Management Channel (PMC) for subdrivers: ':' without follow-up looks odd. > + * A system may have one or two independent EIO devices. > + * > + * Copyright (C) 2025 Advantech Co., Ltd. This needs updating on the next iteration. > + */ > + > +#include <linux/delay.h> > +#include <linux/isa.h> > +#include <linux/mfd/core.h> > +#include <linux/module.h> > +#include <linux/mutex.h> > +#include <linux/regmap.h> > +#include <linux/sysfs.h> > +#include <linux/time.h> > +#include <linux/uaccess.h> > +#include <linux/version.h> > +#include <linux/mfd/eio.h> Alphabetical. Can you make sure that _all_ of these are in use. > +#define TIMEOUT_MAX (10 * USEC_PER_SEC) > +#define TIMEOUT_MIN 200 > +#define SLEEP_MAX 200 > +#define DEFAULT_TIMEOUT 5000 Tab these out. Are these values arbitrary or do they come from some spec? > + > +/** Why are you using kernel-doc comments here? Did you compile with W=1? > + * Timeout: Default timeout in microseconds when a PMC command's > + * timeout is unspecified. PMC command responses typically range > + * from 200us to 2ms. 5ms is quite a safe value for timeout. In Superfluous "In". > + * In some cases, responses are longer. In such situations, please In what cases? > + * adding the timeout parameter loading related sub-drivers or > + * this core driver (not recommended). I can't read this. > + */ > +static uint timeout = DEFAULT_TIMEOUT; > +module_param(timeout, uint, 0444); > +MODULE_PARM_DESC(timeout, "Default PMC command timeout in usec.\n"); You want the user to override the timeout? Are you sure? > +struct eio_dev_port { > + u16 idx_port; > + u16 data_port; > +}; > + > +static struct eio_dev_port pnp_port[] = { > + { .idx_port = EIO_PNP_INDEX, .data_port = EIO_PNP_DATA }, > + { .idx_port = EIO_SUB_PNP_INDEX, > + .data_port = EIO_SUB_PNP_DATA }, Either place this on the line above or use proper multi-line format. > +}; > + > +static struct mfd_cell mfd_devs[] = { eio_devs > + { .name = "eio_wdt" }, > + { .name = "gpio_eio" }, > + { .name = "eio_hwmon" }, > + { .name = "i2c_eio" }, > + { .name = "eio_thermal" }, > + { .name = "eio_fan" }, > + { .name = "eio_bl" }, MFD_CELL_NAME() > +}; > + > +static const struct regmap_range eio_range[] = { > + regmap_reg_range(EIO_PNP_INDEX, EIO_PNP_DATA), > + regmap_reg_range(EIO_SUB_PNP_INDEX, EIO_SUB_PNP_DATA), > + regmap_reg_range(0x200, 0x3FF), > +}; > + > +static const struct regmap_access_table volatile_regs = { > + .yes_ranges = eio_range, > + .n_yes_ranges = ARRAY_SIZE(eio_range), > +}; > + > +static const struct regmap_config pnp_regmap_config = { > + .name = "eio_core", > + .reg_bits = 16, > + .val_bits = 8, > + .volatile_table = &volatile_regs, > + .io_port = true, > + .cache_type = REGCACHE_NONE, > +}; > + > +static struct { > + char name[32]; > + int cmd; > + int ctrl; > + int dev; > + int size; > + enum { > + HEX, > + NUMBER, > + PNP_ID, > + } type; > + Remove this line. > +} attrs[] = { > + { "board_name", 0x53, 0x10, 0, 16 }, > + { "board_serial", 0x53, 0x1F, 0, 16 }, > + { "board_manufacturer", 0x53, 0x11, 0, 16 }, > + { "board_id", 0x53, 0x1E, 0, 4 }, > + { "firmware_version", 0x53, 0x21, 0, 4 }, > + { "firmware_name", 0x53, 0x22, 0, 16 }, > + { "firmware_build", 0x53, 0x23, 0, 26 }, > + { "firmware_date", 0x53, 0x24, 0, 16 }, > + { "chip_id", 0x53, 0x12, 0, 12 }, > + { "chip_detect", 0x53, 0x15, 0, 12 }, > + { "platform_type", 0x53, 0x13, 0, 16 }, > + { "platform_revision", 0x53, 0x04, 0x44, 4 }, > + { "eapi_version", 0x53, 0x04, 0x64, 4 }, > + { "eapi_id", 0x53, 0x31, 0, 4 }, > + { "boot_count", 0x55, 0x10, 0, 4, NUMBER }, > + { "powerup_hour", 0x55, 0x11, 0, 4, NUMBER }, > + { "pnp_id", 0x53, 0x04, 0x68, 4, PNP_ID }, > +}; As "fun" as all of these sysfs entries are, how useful are they to you? Can you say in good conscience that they are all in active use? > +static ssize_t info_show(struct device *dev, struct device_attribute *attr, > + char *buf) Use 100-chars to avoid these line-wraps. > +{ > + uint i; > + > + for (i = 0; i < ARRAY_SIZE(attrs); i++) { > + int ret; > + char str[32] = ""; > + int val; > + > + struct pmc_op op = { > + .cmd = attrs[i].cmd, > + .control = attrs[i].ctrl, > + .device_id = attrs[i].dev, > + .payload = (u8 *)str, > + .size = attrs[i].size, > + }; > + > + if (strcmp(attr->attr.name, attrs[i].name)) > + continue; > + > + ret = eio_core_pmc_operation(dev, &op); > + if (ret) > + return ret; > + > + if (attrs[i].size != 4) Is this dictated by the user? > + return sprintf(buf, "%s\n", str); sprintf() is unsafe. Use sysfs_emit() instead. Throughout. > + val = *(u32 *)str; > + > + if (attrs[i].type == HEX) > + return sprintf(buf, "0x%08X\n", val); > + > + if (attrs[i].type == NUMBER) > + return sprintf(buf, "%d\n", val); > + > + /* Should be pnp_id */ "Should"? Not good enough. Why not check for PNP_ID instead? > + return sprintf(buf, "%c%c%c, %X\n", (val >> 14 & 0x3F) + 0x40, > + ((val >> 9 & 0x18) | (val >> 25 & 0x07)) + 0x40, > + (val >> 20 & 0x1F) + 0x40, val & 0xFFF); > + } > + > + return -EINVAL; > +} > + > +#define PMC_DEVICE_ATTR_RO(_name) \ > + static ssize_t _name##_show(struct device *dev, \ > + struct device_attribute *attr, char *buf) \ > + { \ > + return info_show(dev, attr, buf); \ > + } \ > + static DEVICE_ATTR_RO(_name) Place this out the way, in a header file. > +PMC_DEVICE_ATTR_RO(board_name); > +PMC_DEVICE_ATTR_RO(board_serial); > +PMC_DEVICE_ATTR_RO(board_manufacturer); > +PMC_DEVICE_ATTR_RO(firmware_name); > +PMC_DEVICE_ATTR_RO(firmware_version); > +PMC_DEVICE_ATTR_RO(firmware_build); > +PMC_DEVICE_ATTR_RO(firmware_date); > +PMC_DEVICE_ATTR_RO(chip_id); > +PMC_DEVICE_ATTR_RO(chip_detect); > +PMC_DEVICE_ATTR_RO(platform_type); > +PMC_DEVICE_ATTR_RO(platform_revision); > +PMC_DEVICE_ATTR_RO(board_id); > +PMC_DEVICE_ATTR_RO(eapi_version); > +PMC_DEVICE_ATTR_RO(eapi_id); > +PMC_DEVICE_ATTR_RO(boot_count); > +PMC_DEVICE_ATTR_RO(powerup_hour); > +PMC_DEVICE_ATTR_RO(pnp_id); > + > +static struct attribute *pmc_attrs[] = { &dev_attr_board_name.attr, The attribute goes on a new line, then you can reign in all of the crazy tabbing that follows. > + &dev_attr_board_serial.attr, > + &dev_attr_board_manufacturer.attr, > + &dev_attr_firmware_name.attr, > + &dev_attr_firmware_version.attr, > + &dev_attr_firmware_build.attr, > + &dev_attr_firmware_date.attr, > + &dev_attr_chip_id.attr, > + &dev_attr_chip_detect.attr, > + &dev_attr_platform_type.attr, > + &dev_attr_platform_revision.attr, > + &dev_attr_board_id.attr, > + &dev_attr_eapi_version.attr, > + &dev_attr_eapi_id.attr, > + &dev_attr_boot_count.attr, > + &dev_attr_powerup_hour.attr, > + &dev_attr_pnp_id.attr, > + NULL }; > + > +ATTRIBUTE_GROUPS(pmc); > + > +static unsigned int eio_pnp_read(struct device *dev, > + struct eio_dev_port *port, u8 idx) 100-chars throughout. > +{ > + struct eio_dev *eio = dev_get_drvdata(dev); > + unsigned int val; > + > + if (regmap_write(eio->map, port->idx_port, idx)) > + dev_err(dev, "Error port write 0x%X\n", port->idx_port); > + > + if (regmap_read(eio->map, port->data_port, &val)) > + dev_err(dev, "Error port read 0x%X\n", port->data_port); > + > + return val; > +} > + > +static void eio_pnp_write(struct device *dev, struct eio_dev_port *port, Why are these functions not propagating errors? > + u8 idx, u8 data) > +{ > + struct eio_dev *eio = dev_get_drvdata(dev); > + > + if (regmap_write(eio->map, port->idx_port, idx) || > + regmap_write(eio->map, port->data_port, data)) > + dev_err(dev, "Error port write 0x%X %X\n", port->idx_port, > + port->data_port); You cannot print and error, then return like everything is okay. > +} > + > +static void eio_pnp_enter(struct device *dev, struct eio_dev_port *port) _unlock_port() > +{ > + struct eio_dev *eio = dev_get_drvdata(dev); '\n' > + /* Write 0x87 to index port twice to unlock IO port */ > + if (regmap_write(eio->map, port->idx_port, > + EIO_EXT_MODE_ENTER) || This does on the line above. > + regmap_write(eio->map, port->idx_port, EIO_EXT_MODE_ENTER)) > + dev_err(dev, "Error port write 0x%X\n", port->idx_port); > +} > + > +static void eio_pnp_leave(struct device *dev, struct eio_dev_port *port) _lock_port() > +{ > + struct eio_dev *eio = dev_get_drvdata(dev); '\n' > + /* Write 0xAA to index port once to lock IO port */ > + if (regmap_write(eio->map, port->idx_port, EIO_EXT_MODE_EXIT)) > + dev_err(dev, "Error port write 0x%X\n", port->idx_port); > +} What's the difference between these eio_ calls and the pmc_ ones below? Please find a way to make that clear - a header comment? > +static int pmc_write_data(struct device *dev, int id, u8 value, u16 timeout) > +{ > + struct eio_dev *eio = dev_get_drvdata(dev); > + int ret; > + > + if (WAIT_IBF(dev, id, timeout)) This is not a macro. Just use eio_core_pmc_wait() and have done. > + return -ETIME; I think you mean -ETIMEDOUT, throughout. Also, eio_core_pmc_wait() returns its own error value which you are now overwriting. Why not simply propagate the original error? > + > + ret = regmap_write(eio->map, eio->pmc[id].data, value); > + if (ret) > + dev_err(dev, "Error PMC write %X:%X\n", > + eio->pmc[id].data, value); > + > + return ret; > +} > + > +static int pmc_write_cmd(struct device *dev, int id, u8 value, u16 timeout) > +{ > + struct eio_dev *eio = dev_get_drvdata(dev); > + int ret; > + > + if (WAIT_IBF(dev, id, timeout)) > + return -ETIME; > + > + ret = regmap_write(eio->map, eio->pmc[id].cmd, value); > + if (ret) > + dev_err(dev, "Error PMC write %X:%X\n", > + eio->pmc[id].cmd, value); > + > + return ret; > +} > + > +static int pmc_read_data(struct device *dev, int id, u8 *value, u16 timeout) > +{ > + struct eio_dev *eio = dev_get_drvdata(dev); > + unsigned int val; > + int ret; > + > + if (WAIT_OBF(dev, id, timeout)) > + return -ETIME; > + > + ret = regmap_read(eio->map, eio->pmc[id].data, &val); > + if (ret) > + dev_err(dev, "Error PMC read %X\n", eio->pmc[id].data); > + else > + *value = (u8)(val & 0xFF); > + > + return ret; > +} > + > +static int pmc_read_status(struct device *dev, int id) > +{ > + struct eio_dev *eio = dev_get_drvdata(dev); > + unsigned int val; > + > + if (regmap_read(eio->map, eio->pmc[id].status, &val)) { > + dev_err(dev, "Error PMC read %X\n", > + eio->pmc[id].status); > + return 0; > + } > + > + return val; > +} > + > +static void pmc_clear(struct device *dev, int id) > +{ > + struct eio_dev *eio = dev_get_drvdata(dev); > + unsigned int val; > + > + /* Check if input buffer blocked */ > + if ((pmc_read_status(dev, id) & EIO_PMC_STATUS_IBF) == 0) > + return; > + > + /* Read out previous garbage */ > + if (regmap_read(eio->map, eio->pmc[id].data, &val)) > + dev_err(dev, "Error pmc clear\n"); What do you expect the user to do about this? Why is it an issue that there is nothing to read? > + > + usleep_range(10, 100); > +} > + > +int eio_core_pmc_wait(struct device *dev, int id, > + enum eio_pmc_wait wait, uint max_duration) > +{ > + struct eio_dev *eio = dev_get_drvdata(dev); > + uint val; > + int new_timeout = max_duration ? max_duration : timeout; max_duration?: timeout; > + > + if (new_timeout < TIMEOUT_MIN || new_timeout > TIMEOUT_MAX) { > + dev_err(dev, > + "Error timeout value: %dus. Timeout value should between %d and %ld\n", Suggest that the user should not specify a timeout, then all of this can go. > + new_timeout, TIMEOUT_MIN, TIMEOUT_MAX); > + return -ETIME; > + } > + > + if (wait == PMC_WAIT_INPUT) > + return regmap_read_poll_timeout(eio->map, eio->pmc[id].status, > + val, (val & EIO_PMC_STATUS_IBF) == 0, > + SLEEP_MAX, new_timeout); > + return regmap_read_poll_timeout(eio->map, > + eio->pmc[id].status, val, > + (val & EIO_PMC_STATUS_OBF) != 0, > + SLEEP_MAX, new_timeout); These stacked timeouts are going to need some explanation. > +} > +EXPORT_SYMBOL_GPL(eio_core_pmc_wait); > + > +int eio_core_pmc_operation(struct device *dev, struct pmc_op *op) > +{ > + struct eio_dev *eio = dev_get_drvdata(dev); > + u8 i; > + int ret; > + bool read_cmd = op->cmd & EIO_FLAG_PMC_READ; Suggest a rename. read_cmd sounds more like a function call or command value rather than a bool to match on. What about "reading". > + ktime_t t = ktime_get(); Nit: Reverse Christmas tree is kinder on the reader. > + mutex_lock(&eio->mutex); > + > + pmc_clear(dev, op->chip); > + > + ret = pmc_write_cmd(dev, op->chip, op->cmd, op->timeout); Why not just provide "op" and let the callee extract what it needs? > + if (ret) > + goto err; > + > + ret = pmc_write_data(dev, op->chip, op->control, op->timeout); > + if (ret) > + goto err; > + > + ret = pmc_write_data(dev, op->chip, op->device_id, op->timeout); > + if (ret) > + goto err; > + > + ret = pmc_write_data(dev, op->chip, op->size, op->timeout); > + if (ret) > + goto err; > + > + for (i = 0; i < op->size; i++) { > + if (read_cmd) > + ret = pmc_read_data(dev, op->chip, &op->payload[i], > + op->timeout); > + else > + ret = pmc_write_data(dev, op->chip, op->payload[i], > + op->timeout); > + > + if (ret) > + goto err; Why not break, unlock, then return 0 if (!ret). > + } > + > + mutex_unlock(&eio->mutex); > + > + return 0; > + > +err: > + mutex_unlock(&eio->mutex); > + > + dev_err(dev, "PMC error duration:%lldus", > + ktime_to_us(ktime_sub(ktime_get(), t))); Who is this helpful to? > + dev_err(dev, > + ".cmd=0x%02X, .ctrl=0x%02X .id=0x%02X, .size=0x%02X .data=0x%02X%02X", > + op->cmd, op->control, op->device_id, op->size, op->payload[0], > + op->payload[1]); This looks like debug crud that can be removed when the driver is published. > + > + return ret; > +} > +EXPORT_SYMBOL_GPL(eio_core_pmc_operation); > + > +static int get_pmc_port(struct device *dev, int id, This does not tell me what the function does. Please improve the nomenclature. > + struct eio_dev_port *port) > +{ > + struct eio_dev *eio = dev_get_drvdata(dev); > + struct _pmc_port *pmc = &eio->pmc[id]; > + > + eio_pnp_enter(dev, port); > + > + /* Switch to PMC device page */ > + eio_pnp_write(dev, port, EIO_LDN, EIO_LDN_PMC1); > + > + /* Active this device */ > + eio_pnp_write(dev, port, EIO_LDAR, EIO_LDAR_LDACT); > + > + /* Get PMC cmd and data port */ > + pmc->data = eio_pnp_read(dev, port, EIO_IOBA0H) << 8; > + pmc->data |= eio_pnp_read(dev, port, EIO_IOBA0L); > + pmc->cmd = eio_pnp_read(dev, port, EIO_IOBA1H) << 8; > + pmc->cmd |= eio_pnp_read(dev, port, EIO_IOBA1L); > + > + /* Disable IRQ */ > + eio_pnp_write(dev, port, EIO_IRQCTRL, 0); > + > + eio_pnp_leave(dev, port); > + > + /* Make sure IO ports are not occupied */ > + if (!devm_request_region(dev, pmc->data, 2, KBUILD_MODNAME)) { Break the call out of the if please. > + dev_err(dev, "Request region %X error\n", pmc->data); > + return -EBUSY; > + } > + > + return 0; > +} > + > +static int eio_init(struct device *dev) > +{ > + struct eio_dev *eio = dev_get_drvdata(dev); > + u16 chip_id = 0; > + u8 tmp = 0; control > + int chip = 0; Why are all of these being pre-initialised? > + int ret = -ENOMEM; > + > + for (chip = 0; chip < ARRAY_SIZE(pnp_port); chip++) { for (int chip_id ...) Hold on, you have chip_id below. What's the difference between chip and chip_id? > + struct eio_dev_port *port = pnp_port + chip; > + > + if (!devm_request_region(dev, pnp_port[chip].idx_port, Break this out. > + pnp_port[chip].data_port - > + pnp_port[chip].idx_port, > + KBUILD_MODNAME)) > + continue; > + > + eio_pnp_enter(dev, port); > + > + chip_id = eio_pnp_read(dev, port, EIO_CHIPID1) << 8; > + chip_id |= eio_pnp_read(dev, port, EIO_CHIPID2); > + > + if (chip_id != EIO200_CHIPID && chip_id != EIO201_211_CHIPID) > + continue; > + > + /* Turn on the enable flag */ > + tmp = eio_pnp_read(dev, port, EIO_SIOCTRL); > + tmp |= EIO_SIOCTRL_SIOEN; > + > + eio_pnp_write(dev, port, EIO_SIOCTRL, tmp); > + > + eio_pnp_leave(dev, port); > + > + ret = get_pmc_port(dev, chip, port); > + if (ret) > + return ret; > + > + if (chip == 0) > + eio->flag |= EIO_F_CHIP_EXIST; > + else > + eio->flag |= EIO_F_SUB_CHIP_EXIST; > + } > + > + return ret; > +} > + > +static uint8_t acpiram_access(struct device *dev, uint8_t offset) What's actually happening here? Should that be "acpi_ram" > +{ > + u8 val; > + int ret; > + int timeout = 0; > + struct eio_dev *eio = dev_get_drvdata(dev); > + > + /* We only store information on primary EC */ > + int chip = 0; > + > + mutex_lock(&eio->mutex); > + > + pmc_clear(dev, chip); > + > + ret = pmc_write_cmd(dev, chip, EIO_PMC_CMD_ACPIRAM_READ, timeout); Isn't timeout always 0? > + if (ret) > + goto err; > + > + ret = pmc_write_data(dev, chip, offset, timeout); > + if (ret) > + goto err; > + > + ret = pmc_write_data(dev, chip, sizeof(val), timeout); > + if (ret) > + goto err; > + > + ret = pmc_read_data(dev, chip, &val, timeout); > + if (ret) > + goto err; > + > +err: > + mutex_unlock(&eio->mutex); > + return ret ? 0 : val; What? No. Return the error. > +} > + > +static int firmware_code_base(struct device *dev) > +{ > + struct eio_dev *eio = dev_get_drvdata(dev); > + u8 ic_vendor, ic_code, code_base; > + > + ic_vendor = acpiram_access(dev, EIO_ACPIRAM_ICVENDOR); > + ic_code = acpiram_access(dev, EIO_ACPIRAM_ICCODE); > + code_base = acpiram_access(dev, EIO_ACPIRAM_CODEBASE); > + > + if (ic_vendor != 'R') > + return -ENODEV; > + > + if (ic_code != EIO200_ICCODE && ic_code != EIO201_ICCODE && > + ic_code != EIO211_ICCODE) > + goto err; > + > + if (code_base == EIO_ACPIRAM_CODEBASE_NEW) { > + eio->flag |= EIO_F_NEW_CODE_BASE; > + return 0; > + } > + > + if (code_base == 0 && > + (ic_code != EIO201_ICCODE && ic_code != EIO211_ICCODE)) { > + dev_info(dev, "Old code base not supported, yet."); Drop the yet. If it becomes supported later, so be it. > + return -ENODEV; > + } > + > +err: > + /* Codebase error. This should only happen on firmware error. */ > + dev_err(dev, > + "Codebase check fail: vendor: 0x%X, code: 0x%X, base: 0x%X\n", > + ic_vendor, ic_code, code_base); > + return -ENODEV; > +} > + > +static int eio_probe(struct device *dev, unsigned int id) > +{ > + int ret = 0; > + struct eio_dev *eio; > + > + eio = devm_kzalloc(dev, sizeof(*eio), GFP_KERNEL); > + if (!eio) > + return -ENOMEM; > + > + eio->dev = dev; > + mutex_init(&eio->mutex); > + > + eio->iomem = devm_ioport_map(dev, 0, EIO_SUB_PNP_DATA + 1); > + if (IS_ERR(eio->iomem)) > + return PTR_ERR(eio->iomem); > + > + eio->map = devm_regmap_init_mmio(dev, eio->iomem, &pnp_regmap_config); > + if (IS_ERR(eio->map)) > + return PTR_ERR(eio->map); > + > + /* publish instance for subdrivers (dev_get_drvdata(dev->parent)) */ "Publish" Actually drop this - it's not required. > + dev_set_drvdata(dev, eio); > + > + if (eio_init(dev)) { > + dev_dbg(dev, "No device found\n"); Drop all debug cruft. > + return -ENODEV; > + } > + > + ret = firmware_code_base(dev); > + if (ret) { > + dev_err(dev, "Chip code base check fail\n"); > + return ret; /* keep helper's return (e.g., -EIO) */ Always do this. > + } > + > + ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, > + mfd_devs, ARRAY_SIZE(mfd_devs), > + NULL, 0, NULL); > + if (ret) > + dev_err(dev, "Cannot register child devices (error = %d)\n", ret); > + > + dev_dbg(dev, "Module insert completed\n"); Drop. > + return 0; > +} > + > +static struct isa_driver eio_driver = { > + .probe = eio_probe, > + Remove this line. Nothing to remove? > + .driver = { > + .name = "eio_core", > + .dev_groups = pmc_groups, > + }, > +}; > +module_isa_driver(eio_driver, 1); What's 1? Please define. > +MODULE_AUTHOR("Wenkai Chung <wenkai.chung@advantech.com.tw>"); > +MODULE_AUTHOR("Ramiro Oliveira <ramiro.oliveira@advantech.com>"); > +MODULE_DESCRIPTION("Advantech EIO series EC core driver"); > +MODULE_LICENSE("GPL"); > diff --git a/include/linux/mfd/eio.h b/include/linux/mfd/eio.h > new file mode 100644 > index 000000000000..b87614274201 > --- /dev/null > +++ b/include/linux/mfd/eio.h > @@ -0,0 +1,127 @@ > +/* SPDX-License-Identifier: GPL-2.0-only */ > +/* > + * Header for the Advantech EIO core driver and its sub-drivers No need for the header part. We can see that this is a header file. > + * > + * Copyright (C) 2025 Advantech Co., Ltd. > + */ > + > +#ifndef _MFD_EIO_H_ > +#define _MFD_EIO_H_ '\n' > +#include <linux/io.h> > +#include <linux/regmap.h> > + > +/* Definition */ ??? > +#define EIO_CHIPID1 0x20 > +#define EIO_CHIPID2 0x21 > +#define EIO_CHIPVER 0x22 > +#define EIO_SIOCTRL 0x23 > +#define EIO_SIOCTRL_SIOEN BIT(0) > +#define EIO_SIOCTRL_SWRST BIT(1) > +#define EIO_IRQCTRL 0x70 > +#define EIO200_CHIPID 0x9610 > +#define EIO201_211_CHIPID 0x9620 > +#define EIO200_ICCODE 0x10 > +#define EIO201_ICCODE 0x20 > +#define EIO211_ICCODE 0x21 > + > +/* LPC PNP */ > +#define EIO_PNP_INDEX 0x299 > +#define EIO_PNP_DATA 0x29A > +#define EIO_SUB_PNP_INDEX 0x499 > +#define EIO_SUB_PNP_DATA 0x49A > +#define EIO_EXT_MODE_ENTER 0x87 > +#define EIO_EXT_MODE_EXIT 0xAA > + > +/* LPC LDN */ > +#define EIO_LDN 0x07 > +#define EIO_LDN_PMC0 0x0C > +#define EIO_LDN_PMC1 0x0D > + > +/* PMC registers */ > +#define EIO_PMC_STATUS_IBF BIT(1) > +#define EIO_PMC_STATUS_OBF BIT(0) > +#define EIO_LDAR 0x30 > +#define EIO_LDAR_LDACT BIT(0) > +#define EIO_IOBA0H 0x60 > +#define EIO_IOBA0L 0x61 > +#define EIO_IOBA1H 0x62 > +#define EIO_IOBA1L 0x63 > +#define EIO_FLAG_PMC_READ BIT(0) > + > +/* PMC command list */ > +#define EIO_PMC_CMD_ACPIRAM_READ 0x31 > +#define EIO_PMC_CMD_CFG_SAVE 0x56 > + > +/* OLD PMC */ > +#define EIO_PMC_NO_INDEX 0xFF > + > +/* ACPI RAM Address Table */ > +#define EIO_ACPIRAM_VERSIONSECTION (0xFA) > +#define EIO_ACPIRAM_ICVENDOR (EIO_ACPIRAM_VERSIONSECTION + 0x00) > +#define EIO_ACPIRAM_ICCODE (EIO_ACPIRAM_VERSIONSECTION + 0x01) > +#define EIO_ACPIRAM_CODEBASE (EIO_ACPIRAM_VERSIONSECTION + 0x02) > + > +#define EIO_ACPIRAM_CODEBASE_NEW BIT(7) > + > +/* Firmware */ > +#define EIO_F_SUB_NEW_CODE_BASE BIT(6) > +#define EIO_F_SUB_CHANGED BIT(7) > +#define EIO_F_NEW_CODE_BASE BIT(8) > +#define EIO_F_CHANGED BIT(9) > +#define EIO_F_SUB_CHIP_EXIST BIT(30) > +#define EIO_F_CHIP_EXIST BIT(31) > + > +/* Others */ > +#define EIO_EC_NUM 2 > + > +struct _pmc_port { > + union { > + u16 cmd; > + u16 status; > + }; > + u16 data; > +}; > + > +struct pmc_op { > + u8 cmd; > + u8 control; > + u8 device_id; > + u8 size; > + u8 *payload; > + u8 chip; > + u16 timeout; > +}; > + > +enum eio_rw_operation { > + OPERATION_READ, > + OPERATION_WRITE, > +}; > + > +struct eio_dev { > + struct device *dev; > + struct regmap *map; > + void __iomem *iomem; > + struct mutex mutex; /* Protects PMC command access */ > + struct _pmc_port pmc[EIO_EC_NUM]; > + u32 flag; > +}; > + > +int eio_core_pmc_operation(struct device *dev, struct pmc_op *operation); > + > +enum eio_pmc_wait { > + PMC_WAIT_INPUT, > + PMC_WAIT_OUTPUT, > +}; > + > +int eio_core_pmc_wait(struct device *dev, int id, enum eio_pmc_wait wait, > + uint timeout); > + > +#define WAIT_IBF(dev, id, timeout) eio_core_pmc_wait(dev, id, PMC_WAIT_INPUT, timeout) > +#define WAIT_OBF(dev, id, timeout) eio_core_pmc_wait(dev, id, PMC_WAIT_OUTPUT, timeout) > + > +#ifdef pr_fmt > +#undef pr_fmt > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > +#endif Does this really do anything valuable? > + > +#endif > > -- > 2.43.0 > -- Lee Jones [李琼斯] ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-01-08 14:45 UTC | newest] Thread overview: 4+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-12-13 23:12 [PATCH 1/8] Add Advantech EIO MFD driver kernel test robot -- strict thread matches above, loose matches on Subject: below -- 2025-12-12 16:40 [PATCH 0/8] Add support for Advantech EIO MFD series devices Ramiro Oliveira 2025-12-12 16:40 ` [PATCH 1/8] Add Advantech EIO MFD driver Ramiro Oliveira 2025-12-13 15:19 ` kernel test robot 2026-01-08 14:45 ` Lee Jones
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.