1010#include <linux/acpi.h>
1111#include <linux/bits.h>
1212#include <linux/bitfield.h>
13+ #include <linux/cleanup.h>
1314#include <linux/debugfs.h>
1415#include <linux/device.h>
1516#include <linux/device/driver.h>
1617#include <linux/errno.h>
1718#include <linux/hwmon.h>
1819#include <linux/kernel.h>
1920#include <linux/module.h>
21+ #include <linux/mutex.h>
2022#include <linux/printk.h>
2123#include <linux/rwsem.h>
2224#include <linux/types.h>
@@ -76,8 +78,13 @@ enum msi_wmi_platform_method {
7678 MSI_PLATFORM_GET_WMI = 0x1d ,
7779};
7880
79- struct msi_wmi_platform_debugfs_data {
81+ struct msi_wmi_platform_data {
8082 struct wmi_device * wdev ;
83+ struct mutex wmi_lock ; /* Necessary when calling WMI methods */
84+ };
85+
86+ struct msi_wmi_platform_debugfs_data {
87+ struct msi_wmi_platform_data * data ;
8188 enum msi_wmi_platform_method method ;
8289 struct rw_semaphore buffer_lock ; /* Protects debugfs buffer */
8390 size_t length ;
@@ -132,8 +139,9 @@ static int msi_wmi_platform_parse_buffer(union acpi_object *obj, u8 *output, siz
132139 return 0 ;
133140}
134141
135- static int msi_wmi_platform_query (struct wmi_device * wdev , enum msi_wmi_platform_method method ,
136- u8 * input , size_t input_length , u8 * output , size_t output_length )
142+ static int msi_wmi_platform_query (struct msi_wmi_platform_data * data ,
143+ enum msi_wmi_platform_method method , u8 * input ,
144+ size_t input_length , u8 * output , size_t output_length )
137145{
138146 struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER , NULL };
139147 struct acpi_buffer in = {
@@ -147,9 +155,15 @@ static int msi_wmi_platform_query(struct wmi_device *wdev, enum msi_wmi_platform
147155 if (!input_length || !output_length )
148156 return - EINVAL ;
149157
150- status = wmidev_evaluate_method (wdev , 0x0 , method , & in , & out );
151- if (ACPI_FAILURE (status ))
152- return - EIO ;
158+ /*
159+ * The ACPI control method responsible for handling the WMI method calls
160+ * is not thread-safe. Because of this we have to do the locking ourself.
161+ */
162+ scoped_guard (mutex , & data -> wmi_lock ) {
163+ status = wmidev_evaluate_method (data -> wdev , 0x0 , method , & in , & out );
164+ if (ACPI_FAILURE (status ))
165+ return - EIO ;
166+ }
153167
154168 obj = out .pointer ;
155169 if (!obj )
@@ -170,13 +184,13 @@ static umode_t msi_wmi_platform_is_visible(const void *drvdata, enum hwmon_senso
170184static int msi_wmi_platform_read (struct device * dev , enum hwmon_sensor_types type , u32 attr ,
171185 int channel , long * val )
172186{
173- struct wmi_device * wdev = dev_get_drvdata (dev );
187+ struct msi_wmi_platform_data * data = dev_get_drvdata (dev );
174188 u8 input [32 ] = { 0 };
175189 u8 output [32 ];
176190 u16 value ;
177191 int ret ;
178192
179- ret = msi_wmi_platform_query (wdev , MSI_PLATFORM_GET_FAN , input , sizeof (input ), output ,
193+ ret = msi_wmi_platform_query (data , MSI_PLATFORM_GET_FAN , input , sizeof (input ), output ,
180194 sizeof (output ));
181195 if (ret < 0 )
182196 return ret ;
@@ -231,7 +245,7 @@ static ssize_t msi_wmi_platform_write(struct file *fp, const char __user *input,
231245 return ret ;
232246
233247 down_write (& data -> buffer_lock );
234- ret = msi_wmi_platform_query (data -> wdev , data -> method , payload , data -> length , data -> buffer ,
248+ ret = msi_wmi_platform_query (data -> data , data -> method , payload , data -> length , data -> buffer ,
235249 data -> length );
236250 up_write (& data -> buffer_lock );
237251
@@ -277,17 +291,17 @@ static void msi_wmi_platform_debugfs_remove(void *data)
277291 debugfs_remove_recursive (dir );
278292}
279293
280- static void msi_wmi_platform_debugfs_add (struct wmi_device * wdev , struct dentry * dir ,
294+ static void msi_wmi_platform_debugfs_add (struct msi_wmi_platform_data * drvdata , struct dentry * dir ,
281295 const char * name , enum msi_wmi_platform_method method )
282296{
283297 struct msi_wmi_platform_debugfs_data * data ;
284298 struct dentry * entry ;
285299
286- data = devm_kzalloc (& wdev -> dev , sizeof (* data ), GFP_KERNEL );
300+ data = devm_kzalloc (& drvdata -> wdev -> dev , sizeof (* data ), GFP_KERNEL );
287301 if (!data )
288302 return ;
289303
290- data -> wdev = wdev ;
304+ data -> data = drvdata ;
291305 data -> method = method ;
292306 init_rwsem (& data -> buffer_lock );
293307
@@ -298,90 +312,91 @@ static void msi_wmi_platform_debugfs_add(struct wmi_device *wdev, struct dentry
298312
299313 entry = debugfs_create_file (name , 0600 , dir , data , & msi_wmi_platform_debugfs_fops );
300314 if (IS_ERR (entry ))
301- devm_kfree (& wdev -> dev , data );
315+ devm_kfree (& drvdata -> wdev -> dev , data );
302316}
303317
304- static void msi_wmi_platform_debugfs_init (struct wmi_device * wdev )
318+ static void msi_wmi_platform_debugfs_init (struct msi_wmi_platform_data * data )
305319{
306320 struct dentry * dir ;
307321 char dir_name [64 ];
308322 int ret , method ;
309323
310- scnprintf (dir_name , ARRAY_SIZE (dir_name ), "%s-%s" , DRIVER_NAME , dev_name (& wdev -> dev ));
324+ scnprintf (dir_name , ARRAY_SIZE (dir_name ), "%s-%s" , DRIVER_NAME , dev_name (& data -> wdev -> dev ));
311325
312326 dir = debugfs_create_dir (dir_name , NULL );
313327 if (IS_ERR (dir ))
314328 return ;
315329
316- ret = devm_add_action_or_reset (& wdev -> dev , msi_wmi_platform_debugfs_remove , dir );
330+ ret = devm_add_action_or_reset (& data -> wdev -> dev , msi_wmi_platform_debugfs_remove , dir );
317331 if (ret < 0 )
318332 return ;
319333
320334 for (method = MSI_PLATFORM_GET_PACKAGE ; method <= MSI_PLATFORM_GET_WMI ; method ++ )
321- msi_wmi_platform_debugfs_add (wdev , dir , msi_wmi_platform_debugfs_names [method - 1 ],
335+ msi_wmi_platform_debugfs_add (data , dir , msi_wmi_platform_debugfs_names [method - 1 ],
322336 method );
323337}
324338
325- static int msi_wmi_platform_hwmon_init (struct wmi_device * wdev )
339+ static int msi_wmi_platform_hwmon_init (struct msi_wmi_platform_data * data )
326340{
327341 struct device * hdev ;
328342
329- hdev = devm_hwmon_device_register_with_info (& wdev -> dev , "msi_wmi_platform" , wdev ,
343+ hdev = devm_hwmon_device_register_with_info (& data -> wdev -> dev , "msi_wmi_platform" , data ,
330344 & msi_wmi_platform_chip_info , NULL );
331345
332346 return PTR_ERR_OR_ZERO (hdev );
333347}
334348
335- static int msi_wmi_platform_ec_init (struct wmi_device * wdev )
349+ static int msi_wmi_platform_ec_init (struct msi_wmi_platform_data * data )
336350{
337351 u8 input [32 ] = { 0 };
338352 u8 output [32 ];
339353 u8 flags ;
340354 int ret ;
341355
342- ret = msi_wmi_platform_query (wdev , MSI_PLATFORM_GET_EC , input , sizeof (input ), output ,
356+ ret = msi_wmi_platform_query (data , MSI_PLATFORM_GET_EC , input , sizeof (input ), output ,
343357 sizeof (output ));
344358 if (ret < 0 )
345359 return ret ;
346360
347361 flags = output [MSI_PLATFORM_EC_FLAGS_OFFSET ];
348362
349- dev_dbg (& wdev -> dev , "EC RAM version %lu.%lu\n" ,
363+ dev_dbg (& data -> wdev -> dev , "EC RAM version %lu.%lu\n" ,
350364 FIELD_GET (MSI_PLATFORM_EC_MAJOR_MASK , flags ),
351365 FIELD_GET (MSI_PLATFORM_EC_MINOR_MASK , flags ));
352- dev_dbg (& wdev -> dev , "EC firmware version %.28s\n" ,
366+ dev_dbg (& data -> wdev -> dev , "EC firmware version %.28s\n" ,
353367 & output [MSI_PLATFORM_EC_VERSION_OFFSET ]);
354368
355369 if (!(flags & MSI_PLATFORM_EC_IS_TIGERLAKE )) {
356370 if (!force )
357371 return - ENODEV ;
358372
359- dev_warn (& wdev -> dev , "Loading on a non-Tigerlake platform\n" );
373+ dev_warn (& data -> wdev -> dev , "Loading on a non-Tigerlake platform\n" );
360374 }
361375
362376 return 0 ;
363377}
364378
365- static int msi_wmi_platform_init (struct wmi_device * wdev )
379+ static int msi_wmi_platform_init (struct msi_wmi_platform_data * data )
366380{
367381 u8 input [32 ] = { 0 };
368382 u8 output [32 ];
369383 int ret ;
370384
371- ret = msi_wmi_platform_query (wdev , MSI_PLATFORM_GET_WMI , input , sizeof (input ), output ,
385+ ret = msi_wmi_platform_query (data , MSI_PLATFORM_GET_WMI , input , sizeof (input ), output ,
372386 sizeof (output ));
373387 if (ret < 0 )
374388 return ret ;
375389
376- dev_dbg (& wdev -> dev , "WMI interface version %u.%u\n" ,
390+ dev_dbg (& data -> wdev -> dev , "WMI interface version %u.%u\n" ,
377391 output [MSI_PLATFORM_WMI_MAJOR_OFFSET ],
378392 output [MSI_PLATFORM_WMI_MINOR_OFFSET ]);
379393
380394 if (output [MSI_PLATFORM_WMI_MAJOR_OFFSET ] != MSI_WMI_PLATFORM_INTERFACE_VERSION ) {
381395 if (!force )
382396 return - ENODEV ;
383397
384- dev_warn (& wdev -> dev , "Loading despite unsupported WMI interface version (%u.%u)\n" ,
398+ dev_warn (& data -> wdev -> dev ,
399+ "Loading despite unsupported WMI interface version (%u.%u)\n" ,
385400 output [MSI_PLATFORM_WMI_MAJOR_OFFSET ],
386401 output [MSI_PLATFORM_WMI_MINOR_OFFSET ]);
387402 }
@@ -391,19 +406,31 @@ static int msi_wmi_platform_init(struct wmi_device *wdev)
391406
392407static int msi_wmi_platform_probe (struct wmi_device * wdev , const void * context )
393408{
409+ struct msi_wmi_platform_data * data ;
394410 int ret ;
395411
396- ret = msi_wmi_platform_init (wdev );
412+ data = devm_kzalloc (& wdev -> dev , sizeof (* data ), GFP_KERNEL );
413+ if (!data )
414+ return - ENOMEM ;
415+
416+ data -> wdev = wdev ;
417+ dev_set_drvdata (& wdev -> dev , data );
418+
419+ ret = devm_mutex_init (& wdev -> dev , & data -> wmi_lock );
420+ if (ret < 0 )
421+ return ret ;
422+
423+ ret = msi_wmi_platform_init (data );
397424 if (ret < 0 )
398425 return ret ;
399426
400- ret = msi_wmi_platform_ec_init (wdev );
427+ ret = msi_wmi_platform_ec_init (data );
401428 if (ret < 0 )
402429 return ret ;
403430
404- msi_wmi_platform_debugfs_init (wdev );
431+ msi_wmi_platform_debugfs_init (data );
405432
406- return msi_wmi_platform_hwmon_init (wdev );
433+ return msi_wmi_platform_hwmon_init (data );
407434}
408435
409436static const struct wmi_device_id msi_wmi_platform_id_table [] = {
0 commit comments