5
5
* Copyright (C) 2018 ARM Ltd.
6
6
*/
7
7
8
+ #include <linux/bits.h>
8
9
#include <linux/of.h>
10
+ #include <linux/io.h>
9
11
#include <linux/platform_device.h>
10
12
#include <linux/pm_opp.h>
11
13
#include <linux/sort.h>
@@ -21,6 +23,7 @@ enum scmi_performance_protocol_cmd {
21
23
PERF_LEVEL_GET = 0x8 ,
22
24
PERF_NOTIFY_LIMITS = 0x9 ,
23
25
PERF_NOTIFY_LEVEL = 0xa ,
26
+ PERF_DESCRIBE_FASTCHANNEL = 0xb ,
24
27
};
25
28
26
29
struct scmi_opp {
@@ -44,6 +47,7 @@ struct scmi_msg_resp_perf_domain_attributes {
44
47
#define SUPPORTS_SET_PERF_LVL (x ) ((x) & BIT(30))
45
48
#define SUPPORTS_PERF_LIMIT_NOTIFY (x ) ((x) & BIT(29))
46
49
#define SUPPORTS_PERF_LEVEL_NOTIFY (x ) ((x) & BIT(28))
50
+ #define SUPPORTS_PERF_FASTCHANNELS (x ) ((x) & BIT(27))
47
51
__le32 rate_limit_us ;
48
52
__le32 sustained_freq_khz ;
49
53
__le32 sustained_perf_level ;
@@ -87,17 +91,56 @@ struct scmi_msg_resp_perf_describe_levels {
87
91
} opp [0 ];
88
92
};
89
93
94
+ struct scmi_perf_get_fc_info {
95
+ __le32 domain ;
96
+ __le32 message_id ;
97
+ };
98
+
99
+ struct scmi_msg_resp_perf_desc_fc {
100
+ __le32 attr ;
101
+ #define SUPPORTS_DOORBELL (x ) ((x) & BIT(0))
102
+ #define DOORBELL_REG_WIDTH (x ) FIELD_GET(GENMASK(2, 1), (x))
103
+ __le32 rate_limit ;
104
+ __le32 chan_addr_low ;
105
+ __le32 chan_addr_high ;
106
+ __le32 chan_size ;
107
+ __le32 db_addr_low ;
108
+ __le32 db_addr_high ;
109
+ __le32 db_set_lmask ;
110
+ __le32 db_set_hmask ;
111
+ __le32 db_preserve_lmask ;
112
+ __le32 db_preserve_hmask ;
113
+ };
114
+
115
+ struct scmi_fc_db_info {
116
+ int width ;
117
+ u64 set ;
118
+ u64 mask ;
119
+ void __iomem * addr ;
120
+ };
121
+
122
+ struct scmi_fc_info {
123
+ void __iomem * level_set_addr ;
124
+ void __iomem * limit_set_addr ;
125
+ void __iomem * level_get_addr ;
126
+ void __iomem * limit_get_addr ;
127
+ struct scmi_fc_db_info * level_set_db ;
128
+ struct scmi_fc_db_info * limit_set_db ;
129
+ };
130
+
90
131
struct perf_dom_info {
91
132
bool set_limits ;
92
133
bool set_perf ;
93
134
bool perf_limit_notify ;
94
135
bool perf_level_notify ;
136
+ bool perf_fastchannels ;
95
137
u32 opp_count ;
96
138
u32 sustained_freq_khz ;
97
139
u32 sustained_perf_level ;
98
140
u32 mult_factor ;
99
141
char name [SCMI_MAX_STR_SIZE ];
100
142
struct scmi_opp opp [MAX_OPPS ];
143
+ struct scmi_fc_info * fc_info ;
101
144
};
102
145
103
146
struct scmi_perf_info {
@@ -162,6 +205,7 @@ scmi_perf_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
162
205
dom_info -> set_perf = SUPPORTS_SET_PERF_LVL (flags );
163
206
dom_info -> perf_limit_notify = SUPPORTS_PERF_LIMIT_NOTIFY (flags );
164
207
dom_info -> perf_level_notify = SUPPORTS_PERF_LEVEL_NOTIFY (flags );
208
+ dom_info -> perf_fastchannels = SUPPORTS_PERF_FASTCHANNELS (flags );
165
209
dom_info -> sustained_freq_khz =
166
210
le32_to_cpu (attr -> sustained_freq_khz );
167
211
dom_info -> sustained_perf_level =
@@ -250,7 +294,7 @@ scmi_perf_describe_levels_get(const struct scmi_handle *handle, u32 domain,
250
294
}
251
295
252
296
static int scmi_perf_limits_set (const struct scmi_handle * handle , u32 domain ,
253
- u32 max_perf , u32 min_perf )
297
+ u32 max_perf , u32 min_perf )
254
298
{
255
299
int ret ;
256
300
struct scmi_xfer * t ;
@@ -273,7 +317,7 @@ static int scmi_perf_limits_set(const struct scmi_handle *handle, u32 domain,
273
317
}
274
318
275
319
static int scmi_perf_limits_get (const struct scmi_handle * handle , u32 domain ,
276
- u32 * max_perf , u32 * min_perf )
320
+ u32 * max_perf , u32 * min_perf )
277
321
{
278
322
int ret ;
279
323
struct scmi_xfer * t ;
@@ -299,7 +343,7 @@ static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain,
299
343
}
300
344
301
345
static int scmi_perf_level_set (const struct scmi_handle * handle , u32 domain ,
302
- u32 level , bool poll )
346
+ u32 level , bool poll )
303
347
{
304
348
int ret ;
305
349
struct scmi_xfer * t ;
@@ -322,7 +366,7 @@ static int scmi_perf_level_set(const struct scmi_handle *handle, u32 domain,
322
366
}
323
367
324
368
static int scmi_perf_level_get (const struct scmi_handle * handle , u32 domain ,
325
- u32 * level , bool poll )
369
+ u32 * level , bool poll )
326
370
{
327
371
int ret ;
328
372
struct scmi_xfer * t ;
@@ -343,6 +387,104 @@ static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain,
343
387
return ret ;
344
388
}
345
389
390
+ static bool scmi_perf_fc_size_is_valid (u32 msg , u32 size )
391
+ {
392
+ if ((msg == PERF_LEVEL_GET || msg == PERF_LEVEL_SET ) && size == 4 )
393
+ return true;
394
+ if ((msg == PERF_LIMITS_GET || msg == PERF_LIMITS_SET ) && size == 8 )
395
+ return true;
396
+ return false;
397
+ }
398
+
399
+ static void
400
+ scmi_perf_domain_desc_fc (const struct scmi_handle * handle , u32 domain ,
401
+ u32 message_id , void __iomem * * p_addr ,
402
+ struct scmi_fc_db_info * * p_db )
403
+ {
404
+ int ret ;
405
+ u32 flags ;
406
+ u64 phys_addr ;
407
+ u8 size ;
408
+ void __iomem * addr ;
409
+ struct scmi_xfer * t ;
410
+ struct scmi_fc_db_info * db ;
411
+ struct scmi_perf_get_fc_info * info ;
412
+ struct scmi_msg_resp_perf_desc_fc * resp ;
413
+
414
+ if (!p_addr )
415
+ return ;
416
+
417
+ ret = scmi_xfer_get_init (handle , PERF_DESCRIBE_FASTCHANNEL ,
418
+ SCMI_PROTOCOL_PERF ,
419
+ sizeof (* info ), sizeof (* resp ), & t );
420
+ if (ret )
421
+ return ;
422
+
423
+ info = t -> tx .buf ;
424
+ info -> domain = cpu_to_le32 (domain );
425
+ info -> message_id = cpu_to_le32 (message_id );
426
+
427
+ ret = scmi_do_xfer (handle , t );
428
+ if (ret )
429
+ goto err_xfer ;
430
+
431
+ resp = t -> rx .buf ;
432
+ flags = le32_to_cpu (resp -> attr );
433
+ size = le32_to_cpu (resp -> chan_size );
434
+ if (!scmi_perf_fc_size_is_valid (message_id , size ))
435
+ goto err_xfer ;
436
+
437
+ phys_addr = le32_to_cpu (resp -> chan_addr_low );
438
+ phys_addr |= (u64 )le32_to_cpu (resp -> chan_addr_high ) << 32 ;
439
+ addr = devm_ioremap (handle -> dev , phys_addr , size );
440
+ if (!addr )
441
+ goto err_xfer ;
442
+ * p_addr = addr ;
443
+
444
+ if (p_db && SUPPORTS_DOORBELL (flags )) {
445
+ db = devm_kzalloc (handle -> dev , sizeof (* db ), GFP_KERNEL );
446
+ if (!db )
447
+ goto err_xfer ;
448
+
449
+ size = 1 << DOORBELL_REG_WIDTH (flags );
450
+ phys_addr = le32_to_cpu (resp -> db_addr_low );
451
+ phys_addr |= (u64 )le32_to_cpu (resp -> db_addr_high ) << 32 ;
452
+ addr = devm_ioremap (handle -> dev , phys_addr , size );
453
+ if (!addr )
454
+ goto err_xfer ;
455
+
456
+ db -> addr = addr ;
457
+ db -> width = size ;
458
+ db -> set = le32_to_cpu (resp -> db_set_lmask );
459
+ db -> set |= (u64 )le32_to_cpu (resp -> db_set_hmask ) << 32 ;
460
+ db -> mask = le32_to_cpu (resp -> db_preserve_lmask );
461
+ db -> mask |= (u64 )le32_to_cpu (resp -> db_preserve_hmask ) << 32 ;
462
+ * p_db = db ;
463
+ }
464
+ err_xfer :
465
+ scmi_xfer_put (handle , t );
466
+ }
467
+
468
+ static void scmi_perf_domain_init_fc (const struct scmi_handle * handle ,
469
+ u32 domain , struct scmi_fc_info * * p_fc )
470
+ {
471
+ struct scmi_fc_info * fc ;
472
+
473
+ fc = devm_kzalloc (handle -> dev , sizeof (* fc ), GFP_KERNEL );
474
+ if (!fc )
475
+ return ;
476
+
477
+ scmi_perf_domain_desc_fc (handle , domain , PERF_LEVEL_SET ,
478
+ & fc -> level_set_addr , & fc -> level_set_db );
479
+ scmi_perf_domain_desc_fc (handle , domain , PERF_LEVEL_GET ,
480
+ & fc -> level_get_addr , NULL );
481
+ scmi_perf_domain_desc_fc (handle , domain , PERF_LIMITS_SET ,
482
+ & fc -> limit_set_addr , & fc -> limit_set_db );
483
+ scmi_perf_domain_desc_fc (handle , domain , PERF_LIMITS_GET ,
484
+ & fc -> limit_get_addr , NULL );
485
+ * p_fc = fc ;
486
+ }
487
+
346
488
/* Device specific ops */
347
489
static int scmi_dev_domain_id (struct device * dev )
348
490
{
@@ -494,6 +636,9 @@ static int scmi_perf_protocol_init(struct scmi_handle *handle)
494
636
495
637
scmi_perf_domain_attributes_get (handle , domain , dom );
496
638
scmi_perf_describe_levels_get (handle , domain , dom );
639
+
640
+ if (dom -> perf_fastchannels )
641
+ scmi_perf_domain_init_fc (handle , domain , & dom -> fc_info );
497
642
}
498
643
499
644
handle -> perf_ops = & perf_ops ;
0 commit comments