summaryrefslogtreecommitdiffstats
path: root/src/lib/gpsm.c
blob: e2b71d6d4dcdddfa4d4d4ae742edbd347400ad27 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
// $Id: gpsm.c,v 1.2 2014/02/03 01:30:24 daviddu Exp $
// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/gpsm.c,v $
//-----------------------------------------------------------------------------
// *! (C) Copyright International Business Machines Corp. 2013
// *! All Rights Reserved -- Property of IBM
// *! *** IBM Confidential ***
//-----------------------------------------------------------------------------

/// \file gpsm.c
/// \brief Global Pstate Mechanism procedures
///
/// \todo : Should we initialize any/all iVRM delays in gpsm_lpsa_install()?

#include "ssx.h"
#include "pstates.h"
#include "gpe_control.h"
#include "gpsm.h"
#include "vrm.h"

/// The semaphore used to block threads waiting for GPSM protocol actions

SsxSemaphore G_gpsm_protocol_semaphore;


////////////////////////////////////////////////////////////////////////////
// Private Utilities
////////////////////////////////////////////////////////////////////////////

// The mechanical transition to Firmware Pstate Mode - Not a procedure

static void
_gpsm_fw_mode(void)
{
    pmc_mode_reg_t pmr;

    pmr.value = in32(PMC_MODE_REG);
    pmr.fields.enable_hw_pstate_mode = 0;
    pmr.fields.enable_fw_auction_pstate_mode = 0;
    pmr.fields.enable_fw_pstate_mode = 1;
    out32(PMC_MODE_REG, pmr.value);
}


// The mechanical transition to Firmware Auction Pstate Mode - Not a procedure

static void
_gpsm_fw_auction_mode(void)
{
    pmc_mode_reg_t pmr;

    pmr.value = in32(PMC_MODE_REG);
    pmr.fields.enable_hw_pstate_mode = 0;
    pmr.fields.enable_fw_auction_pstate_mode = 1;
    pmr.fields.enable_fw_pstate_mode = 0;
    out32(PMC_MODE_REG, pmr.value);
}


// The mechanical transition to Hardware Pstate Mode - Not a procedure.
// Disable voltage change via safe_mode_without_spivid 
// before enter hw mode to prevent possible glitch that 
// hw momentarily flush turbo to pstate actual, 
// After enter hw mode, we will enable back the spivid

static void
_gpsm_hw_mode(void)
{
    pmc_mode_reg_t pmr;

    if (!gpsm_dcm_slave_p()) {
        pmr.value = in32(PMC_MODE_REG);
        pmr.fields.safe_mode_without_spivid = 1;
        out32(PMC_MODE_REG, pmr.value);
    }

    pmr.value = in32(PMC_MODE_REG);
    pmr.fields.enable_hw_pstate_mode = 1;
    pmr.fields.enable_fw_auction_pstate_mode = 0;
    pmr.fields.enable_fw_pstate_mode = 0;
    out32(PMC_MODE_REG, pmr.value);

    if (!gpsm_dcm_slave_p()) {
        pmr.value = in32(PMC_MODE_REG);
        pmr.fields.safe_mode_without_spivid = 0;
        out32(PMC_MODE_REG, pmr.value);
    }

}

    
////////////////////////////////////////////////////////////////////////////
// Private Sub-Procedures
////////////////////////////////////////////////////////////////////////////

// By definition, quiescing the GPSM always leaves the system in firmware
// Pstate mode.  This is necessary for a consistent specification due to the
// fact that in general, Hardware Pstate mode can not be quiesced without
// leaving that mode.

// To quiesce the GPSM in firmware or firmware auction mode requires waiting
// for both the voltage and frequency changes to be complete.  Note that they
// will never be ongoing simultaneously. This predicate is used for all
// firmware-mode quiesce, even though normally only one part or the other
// (voltage/protocol) is active.
//
// Recall that PMC interrupts are level-low, so if ongoing status is clear,
// the operation is ongoing.

static int
gpsm_fw_quiesce(void)
{
    int rc = 0;
    
    if (!ssx_irq_status_get(PGP_IRQ_PMC_PROTOCOL_ONGOING)) {
        ssx_irq_enable(PGP_IRQ_PMC_PROTOCOL_ONGOING);
        rc = ssx_semaphore_pend(&G_gpsm_protocol_semaphore, SSX_WAIT_FOREVER);
    }

    if ((!rc) && !ssx_irq_status_get(PGP_IRQ_PMC_VOLTAGE_CHANGE_ONGOING)) {
        ssx_irq_enable(PGP_IRQ_PMC_VOLTAGE_CHANGE_ONGOING);
        rc = ssx_semaphore_pend(&G_gpsm_protocol_semaphore, SSX_WAIT_FOREVER);
    }

    if (!rc) {
        _gpsm_fw_mode();
    }

    return rc;
}


// To quiesce the GPSM in hardware mode requires waiting for any ongoing
// Pstate change to be complete.  Note that there is no guarantee that this
// condition will ever be true in general unless something external to PMC
// ensures that Global bids stop coming in to the GPSM.  An alternative used
// here is to 'lock' the GPSM temporarily by setting the rail bounds min and
// max to the current Global Pstate Actual.  The GPSM will eventually quiesce
// at the global actual, and we can safely move to Firmware Pstate mode and
// release the lock.
//
// Recall that PMC 'ongoing' interrupts are level-low, so if ongoing status is
// clear, the operation is ongoing.

static int
gpsm_hw_quiesce(void)
{
    int rc = 0;
    pmc_rail_bounds_register_t prbr, original_prbr;
    pmc_pstate_monitor_and_ctrl_reg_t ppmacr;

    ppmacr.value = in32(PMC_PSTATE_MONITOR_AND_CTRL_REG);
    
    original_prbr.value = prbr.value = in32(PMC_RAIL_BOUNDS_REGISTER);
    prbr.fields.pmin_rail = ppmacr.fields.gpsa;
    prbr.fields.pmax_rail = ppmacr.fields.gpsa;
    out32(PMC_RAIL_BOUNDS_REGISTER, prbr.value);

    rc = _gpsm_hw_quiesce();
    
    if (!rc) {
        _gpsm_fw_mode();
        out32(PMC_RAIL_BOUNDS_REGISTER, original_prbr.value);
    }

    return rc;
}

////////////////////////////////////////////////////////////////////////////
// Public Predicates
////////////////////////////////////////////////////////////////////////////

/// Is the Global Pstate Mechanism quiesced?
///
/// This predicate can only truly be answered 'true' if we are not in
/// hardware Pstate mode.  
///
/// \retval 0 Either we're in Hardware Pstate Mode, or a Voltage/Frequency
/// operation is ongoing.
///
/// \retval 1 We're not in Hardware Pstate Mode and no Voltage/Frequency
/// operation is ongoing.

int
gpsm_quiesced_p(void)
{
    return !(gpsm_hw_mode_p() ||
             !ssx_irq_status_get(PGP_IRQ_PMC_PROTOCOL_ONGOING) ||
             !ssx_irq_status_get(PGP_IRQ_PMC_VOLTAGE_CHANGE_ONGOING));
}

/// Predicate: Is the PMC in hardware Pstate mode?
///
/// \returns 0/1

int
gpsm_hw_mode_p(void)
{
    pmc_mode_reg_t pmr;

    pmr.value = in32(PMC_MODE_REG);
    return (pmr.fields.enable_hw_pstate_mode != 0);
}


/// Predicate: Is the PMC in firmware auction Pstate mode?
///
/// \returns 0/1

int
gpsm_fw_auction_mode_p(void)
{
    pmc_mode_reg_t pmr;

    pmr.value = in32(PMC_MODE_REG);
    return (pmr.fields.enable_fw_auction_pstate_mode != 0);
}


/// Predicate: Is the PMC in firmware Pstate mode?
///
/// \returns 0/1

int
gpsm_fw_mode_p(void)
{
    pmc_mode_reg_t pmr;

    pmr.value = in32(PMC_MODE_REG);
    return (pmr.fields.enable_fw_pstate_mode != 0);
}


/// Predicate: Is the chip configured as a DCM?
///
/// \returns 0/1

int
gpsm_dcm_mode_p(void)
{
    pmc_mode_reg_t pmc_mode_reg;
    pmc_mode_reg.value = in32(PMC_MODE_REG);
    return pmc_mode_reg.fields.enable_interchip_interface;
}


/// Predicate: Is the chip configured as a DCM Slave?
///
/// \returns 0/1

int
gpsm_dcm_master_p(void)
{
    pmc_mode_reg_t pmc_mode_reg;
    pmc_mode_reg.value = in32(PMC_MODE_REG);
    return 
        pmc_mode_reg.fields.enable_interchip_interface &&
        pmc_mode_reg.fields.interchip_mode;
}


/// Predicate: Is the chip configured as a DCM Slave?
///
/// \returns 0/1

int
gpsm_dcm_slave_p(void)
{
    pmc_mode_reg_t pmc_mode_reg;
    pmc_mode_reg.value = in32(PMC_MODE_REG);
    return 
        pmc_mode_reg.fields.enable_interchip_interface &&
        (pmc_mode_reg.fields.interchip_mode == 0);
}
    

        

////////////////////////////////////////////////////////////////////////////
// Procedures
////////////////////////////////////////////////////////////////////////////

/// Recover the GlobalPstateTable object from the PMC
///
/// \note It is assumed that the pointer to the Global Pstate table installed
/// in the PMC is actually a pointer to a complete GlobalPstateTable object
/// (which contains a Global Pstate table as its first element).
///
/// \returns A pointer to the currently active GlobalPstateTable object.

GlobalPstateTable*
gpsm_gpst(void)
{
    pmc_parameter_reg1_t ppr1;

    ppr1.value = in32(PMC_PARAMETER_REG1);
    return (GlobalPstateTable*)
        (ppr1.fields.ba_sram_pstate_table << GLOBAL_PSTATE_TABLE_ALIGNMENT);
}


/// Quiesce the GPSM to firmware mode from any other mode
///
/// At the exit of this procedure, the PMC will be in Firmware Pstate Mode and
/// there will be no ongoing voltage or frequency transitions.
int
gpsm_quiesce(void)
{
    int rc;

    if (gpsm_hw_mode_p()) {
        rc = gpsm_hw_quiesce();
    } else {
        rc = gpsm_fw_quiesce();
    }

    return rc;
}

/// Quiesce the GPSM in Hardware Pstate Mode
///
/// In general there is no guarantee that the GPSM will ever quiesce, or
/// remain quiesced in Hardware Pstate Mode unless something like the
/// procedure in gpsm_hw_quiesce() is used.  This procedure is provided for
/// the benefit of applications that are in complete control of Pstates
/// (including idle state Pstates) to simply wait for the Pstate protocol to
/// quiesce, without quiescing and entering Firmware Pstate mode like
/// gpsm_hw_quiesce(). 

int
_gpsm_hw_quiesce(void)
{
    int rc;

    do {
        rc = 0;

        if (!gpsm_hw_mode_p()) {
            rc = -GPSM_ILLEGAL_MODE_HW_QUIESCE;
            break;
        }

        if (!ssx_irq_status_get(PGP_IRQ_PMC_PROTOCOL_ONGOING)) {
            ssx_irq_enable(PGP_IRQ_PMC_PROTOCOL_ONGOING);
            rc = ssx_semaphore_pend(&G_gpsm_protocol_semaphore, 
                                    SSX_WAIT_FOREVER);
            if (rc) break;
        }
    } while (0);
    
    return rc;
}


/// Change to GPSM firmware mode from any mode

int
gpsm_fw_mode(void)
{
    return gpsm_quiesce();
}


/// Change to GPSM firmware auction mode from any mode

int
gpsm_fw_auction_mode(void)
{
    int rc;

    rc = gpsm_quiesce();
    if (!rc) {
        _gpsm_fw_auction_mode();
    }
    return rc;
}


/// Change to Hardware Pstate Mode
///
/// The (unchecked) assumption behind this procedure is that the caller has
/// run through Pstate intialization and enablement, and the system is in a
/// state where the entry to Hardware Pstate Mode is safe once the GPSM is
/// quiesced. 

int
gpsm_hw_mode(void)
{
    int rc;

    TRACE_GPSM(TRACE_GPSM_HW_MODE);

    rc = gpsm_quiesce();
    if (!rc) {
        _gpsm_hw_mode();
    }
    return rc;
}


/// The default GPSM auction procedure
///
/// The default auction returns the value of
/// PMC_HARDWARE_AUCTION_PSTATE_REG.haps.

Pstate
gpsm_default_auction(void)
{
    pmc_hardware_auction_pstate_reg_t phapr;

    phapr.value = in32(PMC_HARDWARE_AUCTION_PSTATE_REG);
    return phapr.fields.haps;
}


/// Update a user-supplied vector of Pstates with the current Global bid of
/// each core.
///
/// \param[out] o_bids A vector of Pstates; The vector must be large enough to
/// hold the bid of every possible core.
///
/// This routine is provided for use by non-default Global Pstate auction
/// procedures.

void
gpsm_get_global_bids(Pstate *o_bids)
{
    // This takes advantage of the implicit layout of the
    // PMC_CORE_PSTATE_REG<n>.

    uint32_t *bids32 = (uint32_t *)o_bids;

    bids32[0] = in32(PMC_CORE_PSTATE_REG0);
    bids32[1] = in32(PMC_CORE_PSTATE_REG1);
    bids32[2] = in32(PMC_CORE_PSTATE_REG2);
    bids32[3] = in32(PMC_CORE_PSTATE_REG3);
}


/// Update a current Global bid of each core from a user supplied vector.
///
/// \param[in] i_bids An array of Global Pstate bids.
///
/// This routine is provided for use by test procedures; there is likely no
/// product-level energy management application for this procedure.

void
gpsm_set_global_bids(const Pstate *i_bids)
{
    // This takes advantage of the implicit layout of the
    // PMC_CORE_PSTATE_REG<n>.

    uint32_t *bids32 = (uint32_t *)i_bids;

    out32(PMC_CORE_PSTATE_REG0, bids32[0]);
    out32(PMC_CORE_PSTATE_REG1, bids32[1]);
    out32(PMC_CORE_PSTATE_REG2, bids32[2]);
    out32(PMC_CORE_PSTATE_REG3, bids32[3]);
}

    
/// Application-controlled Global Actual Broadcast
///
/// \param[in] i_pstate The Global Actual Pstate to broadcast
///
/// \param[in] i_entry A gpst_entry_t containing the information to be used by
/// the iVRM. If iVRM are not enabled then \a entry can be initialized to 0.
///
/// This API is provided for advanced applications to have complete control
/// over a firmware-mode Global Actual broadcast.  There is no error
/// checking. Most applications in Firware Pstate mode will use the
/// higher-level gpsm_broadcast_global_actual() API.

void
_gpsm_broadcast_global_actual(const Pstate i_pstate, 
                              const gpst_entry_t i_entry)
{
    pmc_pstate_monitor_and_ctrl_reg_t ppmacr;
    pmc_eff_global_actual_voltage_reg_t pegavr;

    ppmacr.value = 0;
    ppmacr.fields.gpsa = i_pstate;
    out32(PMC_PSTATE_MONITOR_AND_CTRL_REG, ppmacr.value);

    pegavr.value = 0;
    pegavr.fields.maxreg_vdd = i_entry.fields.maxreg_vdd;
    pegavr.fields.maxreg_vcs = i_entry.fields.maxreg_vcs;
    pegavr.fields.eff_evid_vdd = i_entry.fields.evid_vdd_eff; 
    pegavr.fields.eff_evid_vcs = i_entry.fields.evid_vcs_eff; 
    out32(PMC_EFF_GLOBAL_ACTUAL_VOLTAGE_REG, pegavr.value);

    TRACE_GPSM(TRACE_GPSM_BROADCAST_GLOBAL_ACTUAL);
}


/// Broadcast the Global Actual Pstate in firmware Pstate mode.
///
/// \param[in] i_gpst An initialized GlobalPstateTable structure used to
/// define the legal Pstate range, and to provide the voltage settings
/// (maxreg_vxx and eff_evid_vxx) for the internal VRM.
///
/// \param[in] i_pstate The pstate specfiying the Global Actual Pstate to be
/// broadast to the core chiplets.
///
/// \param[in] i_bias This is a signed bias used to obtain the voltage Pstate,
/// <em> in addition to the \a undervolting_bias already built into the Pstate
/// table </em>. The iVRM information sent with the Global Actual Pstate comes
/// from the \a pstate - \a undervolting_bias + \a bias entry of the Pstate
/// table.
///
/// This API can be used in firware Pstate mode to broadcast a Global Actual
/// Pstate and iVRM settings to the core chiplets. The API also supports
/// optional under/over-volting.  The requested Pstate will be broadcast along
/// with the voltage information from the associated Pstate table entry. 
///
/// Under/over-volting is specified by setting the \a bias to a non-0
/// (signed) value.  For example, to undervfolt by one Pstate (if possible),
/// call the API with \a bias = -1.
///
/// This API always waits for the Global Pstate Machine to quiesce before
/// proceeding with the Global Actual broadcast. Therefore it can only be
/// called from thread mode, or from a non-thread mode guaranteed by the
/// caller to have quiesced.
///
/// \note The application can use the _gpsm_broadcast_global_actual() API for
/// complete control over the information transmitted to the cores.
///
/// The following return codes are not considered errors:
///
/// \retval 0 Success
///
/// \retval -GPST_PSTATE_CLIPPED_HIGH_GPSM_BGA The requested Pstate does not 
/// exist in the table. The maximum Pstate entry in the table has been 
/// broadcast as the voltage Pstate.
///
/// \retval -GPST_PSTATE_CLIPPED_LOW_GPSM_BGA The requested Pstate does not 
/// exist in the table. The minimum Pstate entry in the table has been 
/// broadcast as the voltage Pstate.
///
/// The following return codes are considered errors:
///
/// \retval -GPSM_INVALID_OBJECT The Global Pstate Table is either null (0) or
/// otherwise invalid.
///
/// \retval -GPSM_INVALID_ARGUMENT One or more arguments are invalid or
/// inconsistent in some way.
///
/// \retval -GPSM_ILLEGAL_MODE_BGA The PMC is not in firmware pstate mode.
///
/// This API may also return errors from the SSX semaphore operations that
/// implement the wait for quiescence.

int
gpsm_broadcast_global_actual(const GlobalPstateTable* i_gpst, 
                             const Pstate i_pstate, 
                             const int i_bias)
{
    int rc, bias_rc, entry_rc;
    gpst_entry_t entry;
    Pstate voltage_pstate;

    do {

        if (!gpsm_fw_mode_p()) {
            rc = GPSM_ILLEGAL_MODE_BGA;
            break;
        }

        // Bias the pstate, fetch the Pstate entry, quiesce and broadcast.
        // bias_pstate() only returns saturation warnings. These are turned
        // into bounds warnings if necessary (indicating that the Pstate
        // saturated but the PMAX or PMIN was also a legal entry in the
        // table).

        bias_rc = bias_pstate(i_pstate, i_bias, &voltage_pstate);
        entry_rc = gpst_entry(i_gpst, voltage_pstate, 0, &entry);
        if (entry_rc && 
            (entry_rc != -GPST_PSTATE_CLIPPED_LOW_GPST_ENTRY) &&
            (entry_rc != -GPST_PSTATE_CLIPPED_HIGH_GPST_ENTRY)) {
            rc = entry_rc;
            break;
        }

        rc = gpsm_quiesce();
        if (rc) break;

        _gpsm_broadcast_global_actual(i_pstate, entry);

        if (entry_rc != 0) {
            rc = entry_rc;
        } else if (bias_rc == -PSTATE_OVERFLOW_BIAS_PS) {
            rc = -GPST_PSTATE_CLIPPED_HIGH_GPSM_BGA;
        } else if (bias_rc == -PSTATE_UNDERFLOW_BIAS_PS) {
            rc = -GPST_PSTATE_CLIPPED_LOW_GPSM_BGA;
        }
    } while (0);

    return rc;
}


OpenPOWER on IntegriCloud