Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

platform/x86: lenovo-wmi-{capdata,other}: Support multiple Capability Data

The current implementation are heavily bound to capdata01. Rewrite it so
that it is suitable to utilize other Capability Data as well.

No functional change intended.

Signed-off-by: Rong Zhang <i@rong.moe>
Reviewed-by: Derek J. Clark <derekjohn.clark@gmail.com>
Tested-by: Derek J. Clark <derekjohn.clark@gmail.com>
Link: https://patch.msgid.link/20260120182104.163424-4-i@rong.moe
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

authored by

Rong Zhang and committed by
Ilpo Järvinen
4ff1a029 f28d76b1

+196 -60
+179 -54
drivers/platform/x86/lenovo/wmi-capdata.c
··· 12 12 * 13 13 * Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> 14 14 * - Initial implementation (formerly named lenovo-wmi-capdata01) 15 + * 16 + * Copyright (C) 2025 Rong Zhang <i@rong.moe> 17 + * - Unified implementation 15 18 */ 16 19 20 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 21 + 17 22 #include <linux/acpi.h> 23 + #include <linux/bug.h> 18 24 #include <linux/cleanup.h> 19 25 #include <linux/component.h> 20 26 #include <linux/container_of.h> 21 27 #include <linux/device.h> 28 + #include <linux/dev_printk.h> 29 + #include <linux/err.h> 22 30 #include <linux/export.h> 23 31 #include <linux/gfp_types.h> 24 32 #include <linux/module.h> ··· 34 26 #include <linux/mutex_types.h> 35 27 #include <linux/notifier.h> 36 28 #include <linux/overflow.h> 29 + #include <linux/stddef.h> 37 30 #include <linux/types.h> 38 31 #include <linux/wmi.h> 39 32 ··· 45 36 #define ACPI_AC_CLASS "ac_adapter" 46 37 #define ACPI_AC_NOTIFY_STATUS 0x80 47 38 39 + enum lwmi_cd_type { 40 + LENOVO_CAPABILITY_DATA_01, 41 + }; 42 + 43 + #define LWMI_CD_TABLE_ITEM(_type) \ 44 + [_type] = { \ 45 + .name = #_type, \ 46 + .type = _type, \ 47 + } 48 + 49 + static const struct lwmi_cd_info { 50 + const char *name; 51 + enum lwmi_cd_type type; 52 + } lwmi_cd_table[] = { 53 + LWMI_CD_TABLE_ITEM(LENOVO_CAPABILITY_DATA_01), 54 + }; 55 + 48 56 struct lwmi_cd_priv { 49 57 struct notifier_block acpi_nb; /* ACPI events */ 50 58 struct wmi_device *wdev; ··· 70 44 71 45 struct cd_list { 72 46 struct mutex list_mutex; /* list R/W mutex */ 47 + enum lwmi_cd_type type; 73 48 u8 count; 74 - struct capdata01 data[]; 49 + 50 + union { 51 + DECLARE_FLEX_ARRAY(struct capdata01, cd01); 52 + }; 75 53 }; 54 + 55 + static struct wmi_driver lwmi_cd_driver; 56 + 57 + /** 58 + * lwmi_cd_match() - Match rule for the master driver. 59 + * @dev: Pointer to the capability data parent device. 60 + * @type: Pointer to capability data type (enum lwmi_cd_type *) to match. 61 + * 62 + * Return: int. 63 + */ 64 + static int lwmi_cd_match(struct device *dev, void *type) 65 + { 66 + struct lwmi_cd_priv *priv; 67 + 68 + if (dev->driver != &lwmi_cd_driver.driver) 69 + return false; 70 + 71 + priv = dev_get_drvdata(dev); 72 + return priv->list->type == *(enum lwmi_cd_type *)type; 73 + } 74 + 75 + /** 76 + * lwmi_cd_match_add_all() - Add all match rule for the master driver. 77 + * @master: Pointer to the master device. 78 + * @matchptr: Pointer to the returned component_match pointer. 79 + * 80 + * Adds all component matches to the list stored in @matchptr for the @master 81 + * device. @matchptr must be initialized to NULL. 82 + */ 83 + void lwmi_cd_match_add_all(struct device *master, struct component_match **matchptr) 84 + { 85 + int i; 86 + 87 + if (WARN_ON(*matchptr)) 88 + return; 89 + 90 + for (i = 0; i < ARRAY_SIZE(lwmi_cd_table); i++) { 91 + component_match_add(master, matchptr, lwmi_cd_match, 92 + (void *)&lwmi_cd_table[i].type); 93 + if (IS_ERR(*matchptr)) 94 + return; 95 + } 96 + } 97 + EXPORT_SYMBOL_NS_GPL(lwmi_cd_match_add_all, "LENOVO_WMI_CAPDATA"); 76 98 77 99 /** 78 100 * lwmi_cd_component_bind() - Bind component to master device. 79 101 * @cd_dev: Pointer to the lenovo-wmi-capdata driver parent device. 80 102 * @om_dev: Pointer to the lenovo-wmi-other driver parent device. 81 - * @data: cd_list object pointer used to return the capability data. 103 + * @data: lwmi_cd_binder object pointer used to return the capability data. 82 104 * 83 105 * On lenovo-wmi-other's master bind, provide a pointer to the local capdata 84 106 * list. This is used to call lwmi_cd*_get_data to look up attribute data ··· 138 64 struct device *om_dev, void *data) 139 65 { 140 66 struct lwmi_cd_priv *priv = dev_get_drvdata(cd_dev); 141 - struct cd_list **cd_list = data; 67 + struct lwmi_cd_binder *binder = data; 142 68 143 - *cd_list = priv->list; 69 + switch (priv->list->type) { 70 + case LENOVO_CAPABILITY_DATA_01: 71 + binder->cd01_list = priv->list; 72 + break; 73 + default: 74 + return -EINVAL; 75 + } 144 76 145 77 return 0; 146 78 } ··· 155 75 .bind = lwmi_cd_component_bind, 156 76 }; 157 77 158 - /** 159 - * lwmi_cd01_get_data - Get the data of the specified attribute 78 + /* 79 + * lwmi_cd*_get_data - Get the data of the specified attribute 160 80 * @list: The lenovo-wmi-capdata pointer to its cd_list struct. 161 81 * @attribute_id: The capdata attribute ID to be found. 162 - * @output: Pointer to a capdata01 struct to return the data. 82 + * @output: Pointer to a capdata* struct to return the data. 163 83 * 164 - * Retrieves the capability data 01 struct pointer for the given 165 - * attribute for its specified thermal mode. 84 + * Retrieves the capability data struct pointer for the given 85 + * attribute. 166 86 * 167 87 * Return: 0 on success, or -EINVAL. 168 88 */ 169 - int lwmi_cd01_get_data(struct cd_list *list, u32 attribute_id, struct capdata01 *output) 170 - { 171 - u8 idx; 172 - 173 - guard(mutex)(&list->list_mutex); 174 - for (idx = 0; idx < list->count; idx++) { 175 - if (list->data[idx].id != attribute_id) 176 - continue; 177 - memcpy(output, &list->data[idx], sizeof(list->data[idx])); 178 - return 0; 89 + #define DEF_LWMI_CDXX_GET_DATA(_cdxx, _cd_type, _output_t) \ 90 + int lwmi_##_cdxx##_get_data(struct cd_list *list, u32 attribute_id, _output_t *output) \ 91 + { \ 92 + u8 idx; \ 93 + \ 94 + if (WARN_ON(list->type != _cd_type)) \ 95 + return -EINVAL; \ 96 + \ 97 + guard(mutex)(&list->list_mutex); \ 98 + for (idx = 0; idx < list->count; idx++) { \ 99 + if (list->_cdxx[idx].id != attribute_id) \ 100 + continue; \ 101 + memcpy(output, &list->_cdxx[idx], sizeof(list->_cdxx[idx])); \ 102 + return 0; \ 103 + } \ 104 + return -EINVAL; \ 179 105 } 180 106 181 - return -EINVAL; 182 - } 107 + DEF_LWMI_CDXX_GET_DATA(cd01, LENOVO_CAPABILITY_DATA_01, struct capdata01); 183 108 EXPORT_SYMBOL_NS_GPL(lwmi_cd01_get_data, "LENOVO_WMI_CAPDATA"); 184 109 185 110 /** ··· 197 112 */ 198 113 static int lwmi_cd_cache(struct lwmi_cd_priv *priv) 199 114 { 115 + size_t size; 200 116 int idx; 117 + void *p; 118 + 119 + switch (priv->list->type) { 120 + case LENOVO_CAPABILITY_DATA_01: 121 + p = &priv->list->cd01[0]; 122 + size = sizeof(priv->list->cd01[0]); 123 + break; 124 + default: 125 + return -EINVAL; 126 + } 201 127 202 128 guard(mutex)(&priv->list->list_mutex); 203 - for (idx = 0; idx < priv->list->count; idx++) { 129 + for (idx = 0; idx < priv->list->count; idx++, p += size) { 204 130 union acpi_object *ret_obj __free(kfree) = NULL; 205 131 206 132 ret_obj = wmidev_block_query(priv->wdev, idx); ··· 219 123 return -ENODEV; 220 124 221 125 if (ret_obj->type != ACPI_TYPE_BUFFER || 222 - ret_obj->buffer.length < sizeof(priv->list->data[idx])) 126 + ret_obj->buffer.length < size) 223 127 continue; 224 128 225 - memcpy(&priv->list->data[idx], ret_obj->buffer.pointer, 226 - ret_obj->buffer.length); 129 + memcpy(p, ret_obj->buffer.pointer, size); 227 130 } 228 131 229 132 return 0; ··· 231 136 /** 232 137 * lwmi_cd_alloc() - Allocate a cd_list struct in drvdata 233 138 * @priv: lenovo-wmi-capdata driver data. 139 + * @type: The type of capability data. 234 140 * 235 141 * Allocate a cd_list struct large enough to contain data from all WMI data 236 142 * blocks provided by the interface. 237 143 * 238 144 * Return: 0 on success, or an error. 239 145 */ 240 - static int lwmi_cd_alloc(struct lwmi_cd_priv *priv) 146 + static int lwmi_cd_alloc(struct lwmi_cd_priv *priv, enum lwmi_cd_type type) 241 147 { 242 148 struct cd_list *list; 243 149 size_t list_size; 244 150 int count, ret; 245 151 246 152 count = wmidev_instance_count(priv->wdev); 247 - list_size = struct_size(list, data, count); 153 + 154 + switch (type) { 155 + case LENOVO_CAPABILITY_DATA_01: 156 + list_size = struct_size(list, cd01, count); 157 + break; 158 + default: 159 + return -EINVAL; 160 + } 248 161 249 162 list = devm_kzalloc(&priv->wdev->dev, list_size, GFP_KERNEL); 250 163 if (!list) ··· 262 159 if (ret) 263 160 return ret; 264 161 162 + list->type = type; 265 163 list->count = count; 266 164 priv->list = list; 267 165 ··· 272 168 /** 273 169 * lwmi_cd_setup() - Cache all WMI data block information 274 170 * @priv: lenovo-wmi-capdata driver data. 171 + * @type: The type of capability data. 275 172 * 276 173 * Allocate a cd_list struct large enough to contain data from all WMI data 277 174 * blocks provided by the interface. Then loop through each data block and ··· 280 175 * 281 176 * Return: 0 on success, or an error code. 282 177 */ 283 - static int lwmi_cd_setup(struct lwmi_cd_priv *priv) 178 + static int lwmi_cd_setup(struct lwmi_cd_priv *priv, enum lwmi_cd_type type) 284 179 { 285 180 int ret; 286 181 287 - ret = lwmi_cd_alloc(priv); 182 + ret = lwmi_cd_alloc(priv, type); 288 183 if (ret) 289 184 return ret; 290 185 ··· 340 235 341 236 static int lwmi_cd_probe(struct wmi_device *wdev, const void *context) 342 237 { 238 + const struct lwmi_cd_info *info = context; 343 239 struct lwmi_cd_priv *priv; 344 240 int ret; 241 + 242 + if (!info) 243 + return -EINVAL; 345 244 346 245 priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); 347 246 if (!priv) ··· 354 245 priv->wdev = wdev; 355 246 dev_set_drvdata(&wdev->dev, priv); 356 247 357 - ret = lwmi_cd_setup(priv); 248 + ret = lwmi_cd_setup(priv, info->type); 358 249 if (ret) 359 - return ret; 250 + goto out; 360 251 361 - priv->acpi_nb.notifier_call = lwmi_cd01_notifier_call; 252 + switch (info->type) { 253 + case LENOVO_CAPABILITY_DATA_01: 254 + priv->acpi_nb.notifier_call = lwmi_cd01_notifier_call; 362 255 363 - ret = register_acpi_notifier(&priv->acpi_nb); 364 - if (ret) 365 - return ret; 256 + ret = register_acpi_notifier(&priv->acpi_nb); 257 + if (ret) 258 + goto out; 366 259 367 - ret = devm_add_action_or_reset(&wdev->dev, lwmi_cd01_unregister, &priv->acpi_nb); 368 - if (ret) 369 - return ret; 260 + ret = devm_add_action_or_reset(&wdev->dev, lwmi_cd01_unregister, 261 + &priv->acpi_nb); 262 + if (ret) 263 + goto out; 370 264 371 - return component_add(&wdev->dev, &lwmi_cd_component_ops); 265 + ret = component_add(&wdev->dev, &lwmi_cd_component_ops); 266 + goto out; 267 + default: 268 + return -EINVAL; 269 + } 270 + out: 271 + if (ret) { 272 + dev_err(&wdev->dev, "failed to register %s: %d\n", 273 + info->name, ret); 274 + } else { 275 + dev_dbg(&wdev->dev, "registered %s with %u items\n", 276 + info->name, priv->list->count); 277 + } 278 + return ret; 372 279 } 373 280 374 281 static void lwmi_cd_remove(struct wmi_device *wdev) 375 282 { 376 - component_del(&wdev->dev, &lwmi_cd_component_ops); 283 + struct lwmi_cd_priv *priv = dev_get_drvdata(&wdev->dev); 284 + 285 + switch (priv->list->type) { 286 + case LENOVO_CAPABILITY_DATA_01: 287 + component_del(&wdev->dev, &lwmi_cd_component_ops); 288 + break; 289 + default: 290 + WARN_ON(1); 291 + } 377 292 } 378 293 294 + #define LWMI_CD_WDEV_ID(_type) \ 295 + .guid_string = _type##_GUID, \ 296 + .context = &lwmi_cd_table[_type], 297 + 379 298 static const struct wmi_device_id lwmi_cd_id_table[] = { 380 - { LENOVO_CAPABILITY_DATA_01_GUID, NULL }, 299 + { LWMI_CD_WDEV_ID(LENOVO_CAPABILITY_DATA_01) }, 381 300 {} 382 301 }; 383 302 ··· 420 283 .no_singleton = true, 421 284 }; 422 285 423 - /** 424 - * lwmi_cd01_match() - Match rule for the master driver. 425 - * @dev: Pointer to the capability data 01 parent device. 426 - * @data: Unused void pointer for passing match criteria. 427 - * 428 - * Return: int. 429 - */ 430 - int lwmi_cd01_match(struct device *dev, void *data) 431 - { 432 - return dev->driver == &lwmi_cd_driver.driver; 433 - } 434 - EXPORT_SYMBOL_NS_GPL(lwmi_cd01_match, "LENOVO_WMI_CAPDATA"); 435 - 436 286 module_wmi_driver(lwmi_cd_driver); 437 287 438 288 MODULE_DEVICE_TABLE(wmi, lwmi_cd_id_table); 439 289 MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>"); 290 + MODULE_AUTHOR("Rong Zhang <i@rong.moe>"); 440 291 MODULE_DESCRIPTION("Lenovo Capability Data WMI Driver"); 441 292 MODULE_LICENSE("GPL");
+6 -1
drivers/platform/x86/lenovo/wmi-capdata.h
··· 7 7 8 8 #include <linux/types.h> 9 9 10 + struct component_match; 10 11 struct device; 11 12 struct cd_list; 12 13 ··· 20 19 u32 max_value; 21 20 }; 22 21 22 + struct lwmi_cd_binder { 23 + struct cd_list *cd01_list; 24 + }; 25 + 26 + void lwmi_cd_match_add_all(struct device *master, struct component_match **matchptr); 23 27 int lwmi_cd01_get_data(struct cd_list *list, u32 attribute_id, struct capdata01 *output); 24 - int lwmi_cd01_match(struct device *dev, void *data); 25 28 26 29 #endif /* !_LENOVO_WMI_CAPDATA_H_ */
+11 -5
drivers/platform/x86/lenovo/wmi-other.c
··· 579 579 static int lwmi_om_master_bind(struct device *dev) 580 580 { 581 581 struct lwmi_om_priv *priv = dev_get_drvdata(dev); 582 - struct cd_list *tmp_list; 582 + struct lwmi_cd_binder binder = {}; 583 583 int ret; 584 584 585 - ret = component_bind_all(dev, &tmp_list); 585 + ret = component_bind_all(dev, &binder); 586 586 if (ret) 587 587 return ret; 588 588 589 - priv->cd01_list = tmp_list; 589 + priv->cd01_list = binder.cd01_list; 590 590 if (!priv->cd01_list) 591 591 return -ENODEV; 592 592 ··· 623 623 if (!priv) 624 624 return -ENOMEM; 625 625 626 + /* Sentinel for on-demand ida_free(). */ 627 + priv->ida_id = -EIDRM; 628 + 626 629 priv->wdev = wdev; 627 630 dev_set_drvdata(&wdev->dev, priv); 628 631 629 - component_match_add(&wdev->dev, &master_match, lwmi_cd01_match, NULL); 632 + lwmi_cd_match_add_all(&wdev->dev, &master_match); 630 633 if (IS_ERR(master_match)) 631 634 return PTR_ERR(master_match); 632 635 ··· 642 639 struct lwmi_om_priv *priv = dev_get_drvdata(&wdev->dev); 643 640 644 641 component_master_del(&wdev->dev, &lwmi_om_master_ops); 645 - ida_free(&lwmi_om_ida, priv->ida_id); 642 + 643 + /* No IDA to free if the driver is never bound to its components. */ 644 + if (priv->ida_id >= 0) 645 + ida_free(&lwmi_om_ida, priv->ida_id); 646 646 } 647 647 648 648 static const struct wmi_device_id lwmi_other_id_table[] = {