From nobody Thu Jun 11 00:03:34 2026 Received: from mailgw.kylinos.cn (mailgw.kylinos.cn [124.126.103.232]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8DB9B231C91; Wed, 10 Jun 2026 01:49:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=124.126.103.232 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781056173; cv=none; b=K+7RI26ddI0tHi2nTfsw+dk20GDAvN2J7sqd0B5UX5MULKrMqFHf8lkkwbIp7esQxYnlI3KsduHzoiUXuAkl+m/av1DC9spy4NdR0cYOCrEIC00uZt2ixNewKe9OiX06b5B6S4yQU9XF97O90/aN5nmskK0RnnurfFU7FRJ5cRo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781056173; c=relaxed/simple; bh=NgjjmwzsCQGAuSsq4L55P1NxIIrg8He2voY7GAdNGmE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=j8nqRekxBguNw1/vb1JoMMgveC+djaoroWY3MakyYqiAcKF1GXH4oR6/5hcuBO5xzXNZn23LDBTs0fNvK/cqPOroisv/cFDRwrwFtLX2tK8HC5p2k4I5gkSbdGGszCNfursVVhcuVNAtzc8NCDBLFtXjxmFcMR0aAW2Wu0qDS8c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kylinos.cn; spf=pass smtp.mailfrom=kylinos.cn; arc=none smtp.client-ip=124.126.103.232 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kylinos.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=kylinos.cn X-UUID: 9764a336646e11f1aa26b74ffac11d73-20260610 X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.3.12,REQID:66aaae5b-da7e-4763-b8ed-7b7e5ac30363,IP:0,U RL:0,TC:0,Content:0,EDM:25,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTION :release,TS:25 X-CID-META: VersionHash:e7bac3a,CLOUDID:c164803dc07a01bdb5a40daf6a10008c,BulkI D:nil,BulkQuantity:0,Recheck:0,SF:81|82|102|850|865|898,TC:nil,Content:0|1 5|50,EDM:5,IP:nil,URL:0,File:nil,RT:nil,Bulk:nil,QS:nil,BEC:nil,COL:0,OSI: 0,OSA:0,AV:0,LES:1,SPR:NO,DKR:0,DKP:0,BRR:0,BRE:0,ARC:0 X-CID-BVR: 2,SSN|SDN X-CID-BAS: 2,SSN|SDN,0,_ X-CID-FACTOR: TF_CID_SPAM_SNR X-CID-RHF: D41D8CD98F00B204E9800998ECF8427E X-UUID: 9764a336646e11f1aa26b74ffac11d73-20260610 X-User: xiaopei01@kylinos.cn Received: from localhost.localdomain [(10.44.16.150)] by mailgw.kylinos.cn (envelope-from ) (Generic MTA with TLSv1.3 TLS_AES_256_GCM_SHA384 256/256) with ESMTP id 1389895520; Wed, 10 Jun 2026 09:49:17 +0800 From: Pei Xiao To: linux@roeck-us.net, linux-hwmon@vger.kernel.org, linux-kernel@vger.kernel.org Cc: cryolitia@uniontech.com, Pei Xiao Subject: [PATCH v4 1/4] hwmon: (gpd-fan): drop global driver data and use per-device allocation Date: Wed, 10 Jun 2026 09:49:09 +0800 Message-Id: <1cd3e13033fdd3d0f9b59322f7c86e350d113b92.1781055639.git.xiaopei01@kylinos.cn> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" replace the global state gpd_driver_priv with per-device private data (struct gpd_fan_data) allocated in probe. This allows the driver to support multiple instances in the future and aligns with kernel best practices. No functional change intended. Signed-off-by: Pei Xiao --- changes in v4: 1.Add a missing sob. changes in v3: 1.Add v3 tag changes in v2: 1.Platform_create_bundle pass a driver_data pointer 2.gpd_init_ec is needed before hwmon registration, submit as separate bug fix. --- drivers/hwmon/gpd-fan.c | 209 ++++++++++++++++++++++------------------ 1 file changed, 115 insertions(+), 94 deletions(-) diff --git a/drivers/hwmon/gpd-fan.c b/drivers/hwmon/gpd-fan.c index 80de5f20781e..7284babd4f5c 100644 --- a/drivers/hwmon/gpd-fan.c +++ b/drivers/hwmon/gpd-fan.c @@ -40,12 +40,11 @@ enum FAN_PWM_ENABLE { AUTOMATIC =3D 2, }; =20 -static struct { +struct gpd_fan_data { enum FAN_PWM_ENABLE pwm_enable; u8 pwm_value; - const struct gpd_fan_drvdata *drvdata; -} gpd_driver_priv; +}; =20 struct gpd_fan_drvdata { const char *board_name; // Board name for module param comparison @@ -249,10 +248,10 @@ static const struct gpd_fan_drvdata *gpd_module_drvda= ta[] =3D { }; =20 // Helper functions to handle EC read/write -static void gpd_ecram_read(u16 offset, u8 *val) +static void gpd_ecram_read(const struct gpd_fan_drvdata *drvdata, u16 offs= et, u8 *val) { - u16 addr_port =3D gpd_driver_priv.drvdata->addr_port; - u16 data_port =3D gpd_driver_priv.drvdata->data_port; + u16 addr_port =3D drvdata->addr_port; + u16 data_port =3D drvdata->data_port; =20 outb(0x2E, addr_port); outb(0x11, data_port); @@ -270,10 +269,10 @@ static void gpd_ecram_read(u16 offset, u8 *val) *val =3D inb(data_port); } =20 -static void gpd_ecram_write(u16 offset, u8 value) +static void gpd_ecram_write(const struct gpd_fan_drvdata *drvdata, u16 off= set, u8 value) { - u16 addr_port =3D gpd_driver_priv.drvdata->addr_port; - u16 data_port =3D gpd_driver_priv.drvdata->data_port; + u16 addr_port =3D drvdata->addr_port; + u16 data_port =3D drvdata->data_port; =20 outb(0x2E, addr_port); outb(0x11, data_port); @@ -291,198 +290,198 @@ static void gpd_ecram_write(u16 offset, u8 value) outb(value, data_port); } =20 -static int gpd_generic_read_rpm(void) +static int gpd_generic_read_rpm(struct gpd_fan_data *data) { - const struct gpd_fan_drvdata *const drvdata =3D gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata =3D data->drvdata; u8 high, low; =20 - gpd_ecram_read(drvdata->rpm_read, &high); - gpd_ecram_read(drvdata->rpm_read + 1, &low); + gpd_ecram_read(drvdata, drvdata->rpm_read, &high); + gpd_ecram_read(drvdata, drvdata->rpm_read + 1, &low); =20 return (u16)high << 8 | low; } =20 -static int gpd_wm2_read_rpm(void) +static int gpd_wm2_read_rpm(struct gpd_fan_data *data) { + const struct gpd_fan_drvdata *drvdata =3D data->drvdata; + for (u16 pwm_ctr_offset =3D GPD_PWM_CTR_OFFSET; pwm_ctr_offset <=3D GPD_PWM_CTR_OFFSET + 2; pwm_ctr_offset++) { u8 PWMCTR; =20 - gpd_ecram_read(pwm_ctr_offset, &PWMCTR); + gpd_ecram_read(drvdata, pwm_ctr_offset, &PWMCTR); =20 if (PWMCTR !=3D 0xB8) - gpd_ecram_write(pwm_ctr_offset, 0xB8); + gpd_ecram_write(drvdata, pwm_ctr_offset, 0xB8); } =20 - return gpd_generic_read_rpm(); + return gpd_generic_read_rpm(data); } =20 // Read value for fan1_input -static int gpd_read_rpm(void) +static int gpd_read_rpm(struct gpd_fan_data *data) { - switch (gpd_driver_priv.drvdata->board) { + switch (data->drvdata->board) { case win4_6800u: case win_mini: case duo: case mpc2: - return gpd_generic_read_rpm(); + return gpd_generic_read_rpm(data); case win_max_2: - return gpd_wm2_read_rpm(); + return gpd_wm2_read_rpm(data); } =20 return 0; } =20 -static int gpd_wm2_read_pwm(void) +static int gpd_wm2_read_pwm(struct gpd_fan_data *data) { - const struct gpd_fan_drvdata *const drvdata =3D gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata =3D data->drvdata; u8 var; =20 - gpd_ecram_read(drvdata->pwm_write, &var); + gpd_ecram_read(drvdata, drvdata->pwm_write, &var); =20 // Match gpd_generic_write_pwm(u8) below return DIV_ROUND_CLOSEST((var - 1) * 255, (drvdata->pwm_max - 1)); } =20 // Read value for pwm1 -static int gpd_read_pwm(void) +static int gpd_read_pwm(struct gpd_fan_data *data) { - switch (gpd_driver_priv.drvdata->board) { + switch (data->drvdata->board) { case win_mini: case duo: case win4_6800u: case mpc2: - switch (gpd_driver_priv.pwm_enable) { + switch (data->pwm_enable) { case DISABLE: return 255; case MANUAL: - return gpd_driver_priv.pwm_value; + return data->pwm_value; case AUTOMATIC: return -EOPNOTSUPP; } break; case win_max_2: - return gpd_wm2_read_pwm(); + return gpd_wm2_read_pwm(data); } return 0; } =20 // PWM value's range in EC is 1 - pwm_max, cast 0 - 255 to it. -static inline u8 gpd_cast_pwm_range(u8 val) +static inline u8 gpd_cast_pwm_range(const struct gpd_fan_drvdata *drvdata,= u8 val) { - const struct gpd_fan_drvdata *const drvdata =3D gpd_driver_priv.drvdata; - return DIV_ROUND_CLOSEST(val * (drvdata->pwm_max - 1), 255) + 1; } =20 -static void gpd_generic_write_pwm(u8 val) +static void gpd_generic_write_pwm(struct gpd_fan_data *data, u8 val) { - const struct gpd_fan_drvdata *const drvdata =3D gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata =3D data->drvdata; u8 pwm_reg; =20 - pwm_reg =3D gpd_cast_pwm_range(val); - gpd_ecram_write(drvdata->pwm_write, pwm_reg); + pwm_reg =3D gpd_cast_pwm_range(drvdata, val); + gpd_ecram_write(drvdata, drvdata->pwm_write, pwm_reg); } =20 -static void gpd_duo_write_pwm(u8 val) +static void gpd_duo_write_pwm(struct gpd_fan_data *data, u8 val) { - const struct gpd_fan_drvdata *const drvdata =3D gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata =3D data->drvdata; u8 pwm_reg; =20 - pwm_reg =3D gpd_cast_pwm_range(val); - gpd_ecram_write(drvdata->pwm_write, pwm_reg); - gpd_ecram_write(drvdata->pwm_write + 1, pwm_reg); + pwm_reg =3D gpd_cast_pwm_range(drvdata, val); + gpd_ecram_write(drvdata, drvdata->pwm_write, pwm_reg); + gpd_ecram_write(drvdata, drvdata->pwm_write + 1, pwm_reg); } =20 // Write value for pwm1 -static int gpd_write_pwm(u8 val) +static int gpd_write_pwm(struct gpd_fan_data *data, u8 val) { - if (gpd_driver_priv.pwm_enable !=3D MANUAL) + if (data->pwm_enable !=3D MANUAL) return -EPERM; =20 - switch (gpd_driver_priv.drvdata->board) { + switch (data->drvdata->board) { case duo: - gpd_duo_write_pwm(val); + gpd_duo_write_pwm(data, val); break; case win_mini: case win4_6800u: case win_max_2: case mpc2: - gpd_generic_write_pwm(val); + gpd_generic_write_pwm(data, val); break; } =20 return 0; } =20 -static void gpd_win_mini_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable) +static void gpd_win_mini_set_pwm_enable(struct gpd_fan_data *data, enum FA= N_PWM_ENABLE pwm_enable) { switch (pwm_enable) { case DISABLE: - gpd_generic_write_pwm(255); + gpd_generic_write_pwm(data, 255); break; case MANUAL: - gpd_generic_write_pwm(gpd_driver_priv.pwm_value); + gpd_generic_write_pwm(data, data->pwm_value); break; case AUTOMATIC: - gpd_ecram_write(gpd_driver_priv.drvdata->pwm_write, 0); + gpd_ecram_write(data->drvdata, data->drvdata->pwm_write, 0); break; } } =20 -static void gpd_duo_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable) +static void gpd_duo_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM= _ENABLE pwm_enable) { switch (pwm_enable) { case DISABLE: - gpd_duo_write_pwm(255); + gpd_duo_write_pwm(data, 255); break; case MANUAL: - gpd_duo_write_pwm(gpd_driver_priv.pwm_value); + gpd_duo_write_pwm(data, data->pwm_value); break; case AUTOMATIC: - gpd_ecram_write(gpd_driver_priv.drvdata->pwm_write, 0); + gpd_ecram_write(data->drvdata, data->drvdata->pwm_write, 0); break; } } =20 -static void gpd_wm2_set_pwm_enable(enum FAN_PWM_ENABLE enable) +static void gpd_wm2_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM= _ENABLE enable) { - const struct gpd_fan_drvdata *const drvdata =3D gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata =3D data->drvdata; =20 switch (enable) { case DISABLE: - gpd_generic_write_pwm(255); - gpd_ecram_write(drvdata->manual_control_enable, 1); + gpd_generic_write_pwm(data, 255); + gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1); break; case MANUAL: - gpd_generic_write_pwm(gpd_driver_priv.pwm_value); - gpd_ecram_write(drvdata->manual_control_enable, 1); + gpd_generic_write_pwm(data, data->pwm_value); + gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1); break; case AUTOMATIC: - gpd_ecram_write(drvdata->manual_control_enable, 0); + gpd_ecram_write(drvdata, drvdata->manual_control_enable, 0); break; } } =20 // Write value for pwm1_enable -static void gpd_set_pwm_enable(enum FAN_PWM_ENABLE enable) +static void gpd_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENA= BLE enable) { if (enable =3D=3D MANUAL) // Set pwm_value to max firstly when switching to manual mode, in // consideration of device safety. - gpd_driver_priv.pwm_value =3D 255; + data->pwm_value =3D 255; =20 - switch (gpd_driver_priv.drvdata->board) { + switch (data->drvdata->board) { case win_mini: case win4_6800u: case mpc2: - gpd_win_mini_set_pwm_enable(enable); + gpd_win_mini_set_pwm_enable(data, enable); break; case duo: - gpd_duo_set_pwm_enable(enable); + gpd_duo_set_pwm_enable(data, enable); break; case win_max_2: - gpd_wm2_set_pwm_enable(enable); + gpd_wm2_set_pwm_enable(data, enable); break; } } @@ -505,15 +504,16 @@ static umode_t gpd_fan_hwmon_is_visible(__always_unus= ed const void *drvdata, return 0; } =20 -static int gpd_fan_hwmon_read(__always_unused struct device *dev, +static int gpd_fan_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, __always_unused int channel, long *val) { + struct gpd_fan_data *data =3D dev_get_drvdata(dev); int ret; =20 if (type =3D=3D hwmon_fan) { if (attr =3D=3D hwmon_fan_input) { - ret =3D gpd_read_rpm(); + ret =3D gpd_read_rpm(data); =20 if (ret < 0) return ret; @@ -524,10 +524,10 @@ static int gpd_fan_hwmon_read(__always_unused struct = device *dev, } else if (type =3D=3D hwmon_pwm) { switch (attr) { case hwmon_pwm_enable: - *val =3D gpd_driver_priv.pwm_enable; + *val =3D data->pwm_enable; return 0; case hwmon_pwm_input: - ret =3D gpd_read_pwm(); + ret =3D gpd_read_pwm(data); =20 if (ret < 0) return ret; @@ -540,27 +540,29 @@ static int gpd_fan_hwmon_read(__always_unused struct = device *dev, return -EOPNOTSUPP; } =20 -static int gpd_fan_hwmon_write(__always_unused struct device *dev, +static int gpd_fan_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, __always_unused int channel, long val) { + struct gpd_fan_data *data =3D dev_get_drvdata(dev); + if (type =3D=3D hwmon_pwm) { switch (attr) { case hwmon_pwm_enable: if (!in_range(val, 0, 3)) return -EINVAL; =20 - gpd_driver_priv.pwm_enable =3D val; + data->pwm_enable =3D val; =20 - gpd_set_pwm_enable(gpd_driver_priv.pwm_enable); + gpd_set_pwm_enable(data, data->pwm_enable); return 0; case hwmon_pwm_input: if (!in_range(val, 0, 256)) return -EINVAL; =20 - gpd_driver_priv.pwm_value =3D val; + data->pwm_value =3D val; =20 - return gpd_write_pwm(val); + return gpd_write_pwm(data, val); } } =20 @@ -584,26 +586,27 @@ static struct hwmon_chip_info gpd_fan_chip_info =3D { .info =3D gpd_fan_hwmon_channel_info }; =20 -static void gpd_win4_init_ec(void) +static void gpd_win4_init_ec(struct gpd_fan_data *data) { + const struct gpd_fan_drvdata *drvdata =3D data->drvdata; u8 chip_id, chip_ver; =20 - gpd_ecram_read(0x2000, &chip_id); + gpd_ecram_read(drvdata, 0x2000, &chip_id); =20 if (chip_id =3D=3D 0x55) { - gpd_ecram_read(0x1060, &chip_ver); - gpd_ecram_write(0x1060, chip_ver | 0x80); + gpd_ecram_read(drvdata, 0x1060, &chip_ver); + gpd_ecram_write(drvdata, 0x1060, chip_ver | 0x80); } } =20 -static void gpd_init_ec(void) +static void gpd_init_ec(struct gpd_fan_data *data) { // The buggy firmware won't initialize EC properly on boot. // Before its initialization, reading RPM will always return 0, // and writing PWM will have no effect. // Initialize it manually on driver load. - if (gpd_driver_priv.drvdata->board =3D=3D win4_6800u) - gpd_win4_init_ec(); + if (data->drvdata->board =3D=3D win4_6800u) + gpd_win4_init_ec(data); } =20 static int gpd_fan_probe(struct platform_device *pdev) @@ -611,7 +614,9 @@ static int gpd_fan_probe(struct platform_device *pdev) struct device *dev =3D &pdev->dev; const struct resource *region; const struct resource *res; - const struct device *hwdev; + struct device *hwdev; + struct gpd_fan_data *data; + const struct gpd_fan_drvdata *match; =20 res =3D platform_get_resource(pdev, IORESOURCE_IO, 0); if (!res) @@ -624,24 +629,42 @@ static int gpd_fan_probe(struct platform_device *pdev) return dev_err_probe(dev, -EBUSY, "Failed to request region\n"); =20 + data =3D devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + match =3D dev_get_platdata(dev); + if (!match) + return -EINVAL; + + data->drvdata =3D match; + data->pwm_enable =3D AUTOMATIC; + data->pwm_value =3D 255; + + dev_set_drvdata(dev, data); + hwdev =3D devm_hwmon_device_register_with_info(dev, DRIVER_NAME, - NULL, + data, &gpd_fan_chip_info, NULL); if (IS_ERR(hwdev)) return dev_err_probe(dev, PTR_ERR(hwdev), "Failed to register hwmon device\n"); =20 - gpd_init_ec(); + gpd_init_ec(data); =20 return 0; } =20 -static void gpd_fan_remove(__always_unused struct platform_device *pdev) +static void gpd_fan_remove(struct platform_device *pdev) { - gpd_driver_priv.pwm_enable =3D AUTOMATIC; - gpd_set_pwm_enable(AUTOMATIC); + struct gpd_fan_data *data =3D dev_get_drvdata(&pdev->dev); + + if (data) { + data->pwm_enable =3D AUTOMATIC; + gpd_set_pwm_enable(data, AUTOMATIC); + } } =20 static struct platform_driver gpd_fan_driver =3D { @@ -668,6 +691,7 @@ static int __init gpd_fan_init(void) if (!match) { const struct dmi_system_id *dmi_match =3D dmi_first_match(dmi_table); + if (dmi_match) match =3D dmi_match->driver_data; } @@ -675,10 +699,6 @@ static int __init gpd_fan_init(void) if (!match) return -ENODEV; =20 - gpd_driver_priv.pwm_enable =3D AUTOMATIC; - gpd_driver_priv.pwm_value =3D 255; - gpd_driver_priv.drvdata =3D match; - struct resource gpd_fan_resources[] =3D { { .start =3D match->addr_port, @@ -690,7 +710,8 @@ static int __init gpd_fan_init(void) gpd_fan_platform_device =3D platform_create_bundle(&gpd_fan_driver, gpd_fan_probe, gpd_fan_resources, - 1, NULL, 0); + 1, + match, sizeof(*match)); =20 if (IS_ERR(gpd_fan_platform_device)) { pr_warn("Failed to create platform device\n"); --=20 2.25.1 From nobody Thu Jun 11 00:03:34 2026 Received: from mailgw.kylinos.cn (mailgw.kylinos.cn [124.126.103.232]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9E91B17745; Wed, 10 Jun 2026 01:49:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=124.126.103.232 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781056168; cv=none; b=dINPA9LzMjnWZAWiTccXQojeKolOgdZ2AXKy45IfYRanViVoTpPRJI8fX8D2wvD2sRdsTvwH9mODZQAcd6Al0iojccarKk5KraYBexCzqm3dFXl8sxQq3TOCXgmsn4b7bEc4rAkKceDnNz0JvXr011/mvToCH7lHx3ueCtbCsCQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781056168; c=relaxed/simple; bh=GeNqjlpqTxfWI5MxXPIr8CsB4xa4Ci6lPJIHL6QSk3g=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=tnxu/q+ETQlDLDtkhXXGUetoGcbIu+bs7n5eZWgLAfda/cIXY3+zZkRQW+JlExvL/ZNmw5Iqd5hkKA46Uvd+E4eaAd7M/246jWbAKY1wfiLExiav+eMMGW/wArW+wJbAMJEyjlS6lxH5tWRJEWVP5OF/jlkL2QQk0TJZZ079W1U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kylinos.cn; spf=pass smtp.mailfrom=kylinos.cn; arc=none smtp.client-ip=124.126.103.232 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kylinos.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=kylinos.cn X-UUID: 9789a460646e11f1aa26b74ffac11d73-20260610 X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.3.12,REQID:be117e03-ea2f-4271-984e-0c5801c1fd9c,IP:0,U RL:0,TC:0,Content:0,EDM:25,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTION :release,TS:25 X-CID-META: VersionHash:e7bac3a,CLOUDID:cc09ebfcad3fc90b3b6e1ce8a4c9cfa3,BulkI D:nil,BulkQuantity:0,Recheck:0,SF:81|82|102|850|865|898,TC:nil,Content:0|1 5|50,EDM:5,IP:nil,URL:0,File:nil,RT:nil,Bulk:nil,QS:nil,BEC:nil,COL:0,OSI: 0,OSA:0,AV:0,LES:1,SPR:NO,DKR:0,DKP:0,BRR:0,BRE:0,ARC:0 X-CID-BVR: 2,SSN|SDN X-CID-BAS: 2,SSN|SDN,0,_ X-CID-FACTOR: TF_CID_SPAM_SNR X-CID-RHF: D41D8CD98F00B204E9800998ECF8427E X-UUID: 9789a460646e11f1aa26b74ffac11d73-20260610 X-User: xiaopei01@kylinos.cn Received: from localhost.localdomain [(10.44.16.150)] by mailgw.kylinos.cn (envelope-from ) (Generic MTA with TLSv1.3 TLS_AES_256_GCM_SHA384 256/256) with ESMTP id 1053356942; Wed, 10 Jun 2026 09:49:17 +0800 From: Pei Xiao To: linux@roeck-us.net, linux-hwmon@vger.kernel.org, linux-kernel@vger.kernel.org Cc: cryolitia@uniontech.com, Pei Xiao Subject: [PATCH v4 2/4] hwmon: (gpd-fan): Initialize EC before registering hwmon device Date: Wed, 10 Jun 2026 09:49:10 +0800 Message-Id: <4be3734b135c8013157979ab5e80c7ee51243ddd.1781055639.git.xiaopei01@kylinos.cn> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Move the gpd_init_ec() call to before devm_hwmon_device_register_with_info in the probe function. With the previous ordering the hwmon device was registered and exposed to userspace before the EC initialization completes, creating a window where sysfs reads could return invalid values. Some buggy firmware won't initialize EC properly on boot. Before its initialization, reading RPM will always return 0, and writing PWM will have no effect. So move gpd_init_ec to before hwmon device register. Fixes: 0ab88e239439 ("hwmon: add GPD devices sensor driver") Signed-off-by: Pei Xiao --- changes in v4: No changes changes in v3: 1.Add v3 tag changes in v2: 1.gpd_init_ec needed before hwmon registration, as separate bug fix --- drivers/hwmon/gpd-fan.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/hwmon/gpd-fan.c b/drivers/hwmon/gpd-fan.c index 7284babd4f5c..745b3fb9e3a4 100644 --- a/drivers/hwmon/gpd-fan.c +++ b/drivers/hwmon/gpd-fan.c @@ -643,6 +643,7 @@ static int gpd_fan_probe(struct platform_device *pdev) =20 dev_set_drvdata(dev, data); =20 + gpd_init_ec(data); hwdev =3D devm_hwmon_device_register_with_info(dev, DRIVER_NAME, data, @@ -651,9 +652,6 @@ static int gpd_fan_probe(struct platform_device *pdev) if (IS_ERR(hwdev)) return dev_err_probe(dev, PTR_ERR(hwdev), "Failed to register hwmon device\n"); - - gpd_init_ec(data); - return 0; } =20 --=20 2.25.1 From nobody Thu Jun 11 00:03:34 2026 Received: from mailgw.kylinos.cn (mailgw.kylinos.cn [124.126.103.232]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AEA6730498E; Wed, 10 Jun 2026 01:49:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=124.126.103.232 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781056166; cv=none; b=I1G+WdC64OnaGrvHKCeg+jHfeIhaM6fORhbuZt8b/mmx/73ekCwUp2igqWT/rizZGyQQXcEC3WoUXb68Nj4CWkwEBBuK0AsyQRgYOXXn6AgQjQUT6+QoayznKBcrDyL762mhB6833dljQdlPjJR17BLwL+vK1PIY/FlMcaVMw4Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781056166; c=relaxed/simple; bh=y1MWajtQHSqQDF7SSEa84yc+lLM53hC6IdiAXzXolyg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=I4KuqH14CsmSvjU2K2yr/3DM1+5fSuvtUSx8RaJvB86dQAqLo/1VX/GlBMG8T2HN/EMGOI7iMesmSLKy4plm4DdXVPCs0gP6xySYXb0sqHKknyYeQkl5LjTjbcB+6J8gu6GJO3jfDm3Qil9s3kdz2tJVgEzM21DULSgCRy6bl48= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kylinos.cn; spf=pass smtp.mailfrom=kylinos.cn; arc=none smtp.client-ip=124.126.103.232 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kylinos.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=kylinos.cn X-UUID: 97ae461c646e11f1aa26b74ffac11d73-20260610 X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.3.12,REQID:f60f99ca-9c51-4aab-8234-8d9e321e0a92,IP:0,U RL:0,TC:0,Content:0,EDM:25,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTION :release,TS:25 X-CID-META: VersionHash:e7bac3a,CLOUDID:589b620ea6047ad3368b3eb7941c3aa0,BulkI D:nil,BulkQuantity:0,Recheck:0,SF:81|82|102|136|850|865|898,TC:nil,Content :0|15|50,EDM:5,IP:nil,URL:0,File:nil,RT:nil,Bulk:nil,QS:nil,BEC:nil,COL:0, OSI:0,OSA:0,AV:0,LES:1,SPR:NO,DKR:0,DKP:0,BRR:0,BRE:0,ARC:0 X-CID-BVR: 2,SSN|SDN X-CID-BAS: 2,SSN|SDN,0,_ X-CID-FACTOR: TF_CID_SPAM_SNR X-CID-RHF: D41D8CD98F00B204E9800998ECF8427E X-UUID: 97ae461c646e11f1aa26b74ffac11d73-20260610 X-User: xiaopei01@kylinos.cn Received: from localhost.localdomain [(10.44.16.150)] by mailgw.kylinos.cn (envelope-from ) (Generic MTA with TLSv1.3 TLS_AES_256_GCM_SHA384 256/256) with ESMTP id 1127309952; Wed, 10 Jun 2026 09:49:17 +0800 From: Pei Xiao To: linux@roeck-us.net, linux-hwmon@vger.kernel.org, linux-kernel@vger.kernel.org Cc: cryolitia@uniontech.com, Pei Xiao Subject: [PATCH v4 3/4] hwmon: (gpd-fan): upgrade log level from warn to err for platform device creation failure Date: Wed, 10 Jun 2026 09:49:11 +0800 Message-Id: X-Mailer: git-send-email 2.25.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" When platform_create_bundle() fails, the error is fatal and prevents the driver from loading. Use pr_err() instead of pr_warn() to clearly indicate a critical failure. Signed-off-by: Pei Xiao --- changes in v4: No changes. changes in v3: 1.Add v3 tag. changes in v2: 1.No changes. --- drivers/hwmon/gpd-fan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/gpd-fan.c b/drivers/hwmon/gpd-fan.c index 745b3fb9e3a4..1b57a5add934 100644 --- a/drivers/hwmon/gpd-fan.c +++ b/drivers/hwmon/gpd-fan.c @@ -712,7 +712,7 @@ static int __init gpd_fan_init(void) match, sizeof(*match)); =20 if (IS_ERR(gpd_fan_platform_device)) { - pr_warn("Failed to create platform device\n"); + pr_err("Failed to create platform device\n"); return PTR_ERR(gpd_fan_platform_device); } =20 --=20 2.25.1 From nobody Thu Jun 11 00:03:34 2026 Received: from mailgw.kylinos.cn (mailgw.kylinos.cn [124.126.103.232]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8C4472EC54C; Wed, 10 Jun 2026 01:49:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=124.126.103.232 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781056169; cv=none; b=DqMXkXz3/gFyfqkfAkNixh6IL23WMCxFABqrh0MVrE+BlNlwILI8ZZDlaLvbVwzev2uPpmd5+NIYH1d7QUvInj6Ip8Iv40EFDBxSBgH3gRB8kZuSQTu83ayGOL/7msk0v8G0COOGjc/fkqFFbOOBguzwXDdsuuRumyTNXByesfg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781056169; c=relaxed/simple; bh=cVXQlRjygCDxAOwydV9uY5a/QLQC+hzXzqe89qZudig=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=jil3ebI9jLx4KCSgFgcNOn2lPv6OERA7s6QQsiFpZShMeSt30PIJb+o/BAPNYxf7PQbIUVh5T/AnaGP9X9+4Xwnc1RKbE6CLZuH58t0MXZcy+dmVIOO9WjR3XAdFT+TtmT3DZT1S0/n/MPTixZymTN+CN9adBI4ouqP8N4xHQI8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kylinos.cn; spf=pass smtp.mailfrom=kylinos.cn; arc=none smtp.client-ip=124.126.103.232 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kylinos.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=kylinos.cn X-UUID: 97d333aa646e11f1aa26b74ffac11d73-20260610 X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.3.12,REQID:49705e76-6389-4b32-bf89-d084d9a00e80,IP:0,U RL:0,TC:0,Content:0,EDM:25,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTION :release,TS:25 X-CID-META: VersionHash:e7bac3a,CLOUDID:c834a6ad2cd66ce09ef2683dbc640129,BulkI D:nil,BulkQuantity:0,Recheck:0,SF:81|82|102|850|865|898,TC:nil,Content:0|1 5|50,EDM:5,IP:nil,URL:0,File:nil,RT:nil,Bulk:nil,QS:nil,BEC:nil,COL:0,OSI: 0,OSA:0,AV:0,LES:1,SPR:NO,DKR:0,DKP:0,BRR:0,BRE:0,ARC:0 X-CID-BVR: 2,SSN|SDN X-CID-BAS: 2,SSN|SDN,0,_ X-CID-FACTOR: TF_CID_SPAM_SNR X-CID-RHF: D41D8CD98F00B204E9800998ECF8427E X-UUID: 97d333aa646e11f1aa26b74ffac11d73-20260610 X-User: xiaopei01@kylinos.cn Received: from localhost.localdomain [(10.44.16.150)] by mailgw.kylinos.cn (envelope-from ) (Generic MTA with TLSv1.3 TLS_AES_256_GCM_SHA384 256/256) with ESMTP id 2027105824; Wed, 10 Jun 2026 09:49:18 +0800 From: Pei Xiao To: linux@roeck-us.net, linux-hwmon@vger.kernel.org, linux-kernel@vger.kernel.org Cc: cryolitia@uniontech.com, Pei Xiao Subject: [PATCH v4 4/4] hwmon: (gpd-fan): fix race condition between device removal and sysfs access Date: Wed, 10 Jun 2026 09:49:12 +0800 Message-Id: <4400828422cf3a88adad4db224d9efccdb1049d2.1781055639.git.xiaopei01@kylinos.cn> X-Mailer: git-send-email 2.25.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Replace the manual gpd_fan_remove() callback with a devres-managed action using devm_add_action_or_reset(). The original remove hook resets the fan to AUTOMATIC mode, but the hwmon sysfs interface (registered with devm_hwmon_device_register_with_info()) remains active until after the remove callback completes. This creates a race window where a concurrent userspace sysfs access can interleave with the EC I/O sequence, potentially corrupting EC registers. Using devm_add_action_or_reset() registers the reset function as a devres action. Due to the LIFO release order of devres, the hwmon device is unregistered (sysfs removed) before the reset action executes, eliminating the race condition. Fixes: 0ab88e239439 ("hwmon: add GPD devices sensor driver") Signed-off-by: Pei Xiao --- changes in v4: No changes. changes in v3: 1.Add v3 tag changes in v2: 1.Fix a race condition between device removal and sysfs access report by sashiko-bot --- drivers/hwmon/gpd-fan.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/hwmon/gpd-fan.c b/drivers/hwmon/gpd-fan.c index 1b57a5add934..d1993cd645cb 100644 --- a/drivers/hwmon/gpd-fan.c +++ b/drivers/hwmon/gpd-fan.c @@ -609,6 +609,16 @@ static void gpd_init_ec(struct gpd_fan_data *data) gpd_win4_init_ec(data); } =20 +static void gpd_fan_reset_hardware(void *pdata) +{ + struct gpd_fan_data *data =3D pdata; + + if (data) { + data->pwm_enable =3D AUTOMATIC; + gpd_set_pwm_enable(data, AUTOMATIC); + } +} + static int gpd_fan_probe(struct platform_device *pdev) { struct device *dev =3D &pdev->dev; @@ -617,6 +627,7 @@ static int gpd_fan_probe(struct platform_device *pdev) struct device *hwdev; struct gpd_fan_data *data; const struct gpd_fan_drvdata *match; + int ret; =20 res =3D platform_get_resource(pdev, IORESOURCE_IO, 0); if (!res) @@ -644,6 +655,11 @@ static int gpd_fan_probe(struct platform_device *pdev) dev_set_drvdata(dev, data); =20 gpd_init_ec(data); + + ret =3D devm_add_action_or_reset(dev, gpd_fan_reset_hardware, data); + if (ret) + return ret; + hwdev =3D devm_hwmon_device_register_with_info(dev, DRIVER_NAME, data, @@ -655,19 +671,8 @@ static int gpd_fan_probe(struct platform_device *pdev) return 0; } =20 -static void gpd_fan_remove(struct platform_device *pdev) -{ - struct gpd_fan_data *data =3D dev_get_drvdata(&pdev->dev); - - if (data) { - data->pwm_enable =3D AUTOMATIC; - gpd_set_pwm_enable(data, AUTOMATIC); - } -} - static struct platform_driver gpd_fan_driver =3D { .probe =3D gpd_fan_probe, - .remove =3D gpd_fan_remove, .driver =3D { .name =3D KBUILD_MODNAME, }, --=20 2.25.1