16
16
* The user should refer to the vendor technical documentation to get details
17
17
* about the supported events.
18
18
*
19
- * Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
19
+ * Copyright (c) 2022-2023 , NVIDIA CORPORATION & AFFILIATES. All rights reserved.
20
20
*
21
21
*/
22
22
26
26
#include <linux/interrupt.h>
27
27
#include <linux/io-64-nonatomic-lo-hi.h>
28
28
#include <linux/module.h>
29
+ #include <linux/mutex.h>
29
30
#include <linux/perf_event.h>
30
31
#include <linux/platform_device.h>
31
32
32
33
#include "arm_cspmu.h"
33
- #include "nvidia_cspmu.h"
34
34
35
35
#define PMUNAME "arm_cspmu"
36
36
#define DRVNAME "arm-cs-arch-pmu"
112
112
*/
113
113
#define HILOHI_MAX_POLL 1000
114
114
115
- /* JEDEC-assigned JEP106 identification code */
116
- #define ARM_CSPMU_IMPL_ID_NVIDIA 0x36B
117
-
118
115
static unsigned long arm_cspmu_cpuhp_state ;
119
116
117
+ static DEFINE_MUTEX (arm_cspmu_lock );
118
+
120
119
static struct acpi_apmt_node * arm_cspmu_apmt_node (struct device * dev )
121
120
{
122
121
return * (struct acpi_apmt_node * * )dev_get_platdata (dev );
@@ -373,27 +372,37 @@ static struct attribute_group arm_cspmu_cpumask_attr_group = {
373
372
.attrs = arm_cspmu_cpumask_attrs ,
374
373
};
375
374
376
- struct impl_match {
377
- u32 pmiidr ;
378
- u32 mask ;
379
- int (* impl_init_ops )(struct arm_cspmu * cspmu );
380
- };
381
-
382
- static const struct impl_match impl_match [] = {
375
+ static struct arm_cspmu_impl_match impl_match [] = {
383
376
{
384
- .pmiidr = ARM_CSPMU_IMPL_ID_NVIDIA ,
385
- .mask = ARM_CSPMU_PMIIDR_IMPLEMENTER ,
386
- .impl_init_ops = nv_cspmu_init_ops
377
+ .module_name = "nvidia_cspmu" ,
378
+ .pmiidr_val = ARM_CSPMU_IMPL_ID_NVIDIA ,
379
+ .pmiidr_mask = ARM_CSPMU_PMIIDR_IMPLEMENTER ,
380
+ .module = NULL ,
381
+ .impl_init_ops = NULL ,
387
382
},
388
- {}
383
+ {0 }
389
384
};
390
385
386
+ static struct arm_cspmu_impl_match * arm_cspmu_impl_match_get (u32 pmiidr )
387
+ {
388
+ struct arm_cspmu_impl_match * match = impl_match ;
389
+
390
+ for (; match -> pmiidr_val ; match ++ ) {
391
+ u32 mask = match -> pmiidr_mask ;
392
+
393
+ if ((match -> pmiidr_val & mask ) == (pmiidr & mask ))
394
+ return match ;
395
+ }
396
+
397
+ return NULL ;
398
+ }
399
+
391
400
static int arm_cspmu_init_impl_ops (struct arm_cspmu * cspmu )
392
401
{
393
- int ret ;
402
+ int ret = 0 ;
394
403
struct arm_cspmu_impl_ops * impl_ops = & cspmu -> impl .ops ;
395
404
struct acpi_apmt_node * apmt_node = arm_cspmu_apmt_node (cspmu -> dev );
396
- const struct impl_match * match = impl_match ;
405
+ struct arm_cspmu_impl_match * match ;
397
406
398
407
/*
399
408
* Get PMU implementer and product id from APMT node.
@@ -405,17 +414,36 @@ static int arm_cspmu_init_impl_ops(struct arm_cspmu *cspmu)
405
414
readl (cspmu -> base0 + PMIIDR );
406
415
407
416
/* Find implementer specific attribute ops. */
408
- for (; match -> pmiidr ; match ++ ) {
409
- const u32 mask = match -> mask ;
417
+ match = arm_cspmu_impl_match_get (cspmu -> impl .pmiidr );
418
+
419
+ /* Load implementer module and initialize the callbacks. */
420
+ if (match ) {
421
+ mutex_lock (& arm_cspmu_lock );
422
+
423
+ if (match -> impl_init_ops ) {
424
+ /* Prevent unload until PMU registration is done. */
425
+ if (try_module_get (match -> module )) {
426
+ cspmu -> impl .module = match -> module ;
427
+ cspmu -> impl .match = match ;
428
+ ret = match -> impl_init_ops (cspmu );
429
+ if (ret )
430
+ module_put (match -> module );
431
+ } else {
432
+ WARN (1 , "arm_cspmu failed to get module: %s\n" ,
433
+ match -> module_name );
434
+ ret = - EINVAL ;
435
+ }
436
+ } else {
437
+ request_module_nowait (match -> module_name );
438
+ ret = - EPROBE_DEFER ;
439
+ }
410
440
411
- if ((match -> pmiidr & mask ) == (cspmu -> impl .pmiidr & mask )) {
412
- ret = match -> impl_init_ops (cspmu );
413
- if (ret )
414
- return ret ;
441
+ mutex_unlock (& arm_cspmu_lock );
415
442
416
- break ;
417
- }
418
- }
443
+ if (ret )
444
+ return ret ;
445
+ } else
446
+ cspmu -> impl .module = THIS_MODULE ;
419
447
420
448
/* Use default callbacks if implementer doesn't provide one. */
421
449
CHECK_DEFAULT_IMPL_OPS (impl_ops , get_event_attrs );
@@ -478,11 +506,6 @@ arm_cspmu_alloc_attr_group(struct arm_cspmu *cspmu)
478
506
struct attribute_group * * attr_groups = NULL ;
479
507
struct device * dev = cspmu -> dev ;
480
508
const struct arm_cspmu_impl_ops * impl_ops = & cspmu -> impl .ops ;
481
- int ret ;
482
-
483
- ret = arm_cspmu_init_impl_ops (cspmu );
484
- if (ret )
485
- return NULL ;
486
509
487
510
cspmu -> identifier = impl_ops -> get_identifier (cspmu );
488
511
cspmu -> name = impl_ops -> get_name (cspmu );
@@ -1149,7 +1172,7 @@ static int arm_cspmu_register_pmu(struct arm_cspmu *cspmu)
1149
1172
1150
1173
cspmu -> pmu = (struct pmu ){
1151
1174
.task_ctx_nr = perf_invalid_context ,
1152
- .module = THIS_MODULE ,
1175
+ .module = cspmu -> impl . module ,
1153
1176
.pmu_enable = arm_cspmu_enable ,
1154
1177
.pmu_disable = arm_cspmu_disable ,
1155
1178
.event_init = arm_cspmu_event_init ,
@@ -1196,11 +1219,17 @@ static int arm_cspmu_device_probe(struct platform_device *pdev)
1196
1219
if (ret )
1197
1220
return ret ;
1198
1221
1199
- ret = arm_cspmu_register_pmu (cspmu );
1222
+ ret = arm_cspmu_init_impl_ops (cspmu );
1200
1223
if (ret )
1201
1224
return ret ;
1202
1225
1203
- return 0 ;
1226
+ ret = arm_cspmu_register_pmu (cspmu );
1227
+
1228
+ /* Matches arm_cspmu_init_impl_ops() above. */
1229
+ if (cspmu -> impl .module != THIS_MODULE )
1230
+ module_put (cspmu -> impl .module );
1231
+
1232
+ return ret ;
1204
1233
}
1205
1234
1206
1235
static int arm_cspmu_device_remove (struct platform_device * pdev )
@@ -1300,6 +1329,75 @@ static void __exit arm_cspmu_exit(void)
1300
1329
cpuhp_remove_multi_state (arm_cspmu_cpuhp_state );
1301
1330
}
1302
1331
1332
+ int arm_cspmu_impl_register (const struct arm_cspmu_impl_match * impl_match )
1333
+ {
1334
+ struct arm_cspmu_impl_match * match ;
1335
+ int ret = 0 ;
1336
+
1337
+ match = arm_cspmu_impl_match_get (impl_match -> pmiidr_val );
1338
+
1339
+ if (match ) {
1340
+ mutex_lock (& arm_cspmu_lock );
1341
+
1342
+ if (!match -> impl_init_ops ) {
1343
+ match -> module = impl_match -> module ;
1344
+ match -> impl_init_ops = impl_match -> impl_init_ops ;
1345
+ } else {
1346
+ /* Broken match table may contain non-unique entries */
1347
+ WARN (1 , "arm_cspmu backend already registered for module: %s, pmiidr: 0x%x, mask: 0x%x\n" ,
1348
+ match -> module_name ,
1349
+ match -> pmiidr_val ,
1350
+ match -> pmiidr_mask );
1351
+
1352
+ ret = - EINVAL ;
1353
+ }
1354
+
1355
+ mutex_unlock (& arm_cspmu_lock );
1356
+
1357
+ if (!ret )
1358
+ ret = driver_attach (& arm_cspmu_driver .driver );
1359
+ } else {
1360
+ pr_err ("arm_cspmu reg failed, unable to find a match for pmiidr: 0x%x\n" ,
1361
+ impl_match -> pmiidr_val );
1362
+
1363
+ ret = - EINVAL ;
1364
+ }
1365
+
1366
+ return ret ;
1367
+ }
1368
+ EXPORT_SYMBOL_GPL (arm_cspmu_impl_register );
1369
+
1370
+ static int arm_cspmu_match_device (struct device * dev , const void * match )
1371
+ {
1372
+ struct arm_cspmu * cspmu = platform_get_drvdata (to_platform_device (dev ));
1373
+
1374
+ return (cspmu && cspmu -> impl .match == match ) ? 1 : 0 ;
1375
+ }
1376
+
1377
+ void arm_cspmu_impl_unregister (const struct arm_cspmu_impl_match * impl_match )
1378
+ {
1379
+ struct device * dev ;
1380
+ struct arm_cspmu_impl_match * match ;
1381
+
1382
+ match = arm_cspmu_impl_match_get (impl_match -> pmiidr_val );
1383
+
1384
+ if (WARN_ON (!match ))
1385
+ return ;
1386
+
1387
+ /* Unbind the driver from all matching backend devices. */
1388
+ while ((dev = driver_find_device (& arm_cspmu_driver .driver , NULL ,
1389
+ match , arm_cspmu_match_device )))
1390
+ device_release_driver (dev );
1391
+
1392
+ mutex_lock (& arm_cspmu_lock );
1393
+
1394
+ match -> module = NULL ;
1395
+ match -> impl_init_ops = NULL ;
1396
+
1397
+ mutex_unlock (& arm_cspmu_lock );
1398
+ }
1399
+ EXPORT_SYMBOL_GPL (arm_cspmu_impl_unregister );
1400
+
1303
1401
module_init (arm_cspmu_init );
1304
1402
module_exit (arm_cspmu_exit );
1305
1403
0 commit comments