summaryrefslogtreecommitdiffstats
path: root/pk/ppe42/ppe42_scom.c
blob: e9fe2e6ce1e2fa00c125cbd652fae3de2a5c5858 (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
//-----------------------------------------------------------------------------
// *! (C) Copyright International Business Machines Corp. 2015
// *! All Rights Reserved -- Property of IBM
// *! *** IBM Confidential ***
//-----------------------------------------------------------------------------

/// \file   ppe42_scom.c
/// \brief  Lowest level PK SCOM definitions. 
///
/// Currently these SCOM functions are only optimized for functionality, not
/// speed. Speed optimization will be done when we have full compiler support
/// for the low-level stvd and lvd SCOM OPs.
///
/// A FAPI-lite SCOM can call these PK SCOM functions.
///
/// Comment:
/// - No need to poll for SCOM completion, nor return error code of SCOM fails.
///   A SCOM fail will cause the GPE to hang if configured to do so. But do we
///   necessarily have to do this?  Wouldn't a gentle recovery from a SCOM fail
///   be preferred?

#include "pk.h"
#include "ppe42_scom.h"


uint32_t putscom_abs(uint32_t i_address, uint64_t i_data)
{

    // CMO-Declaring variables tied to specific registers enables us to protect 
    // the SCOM data and address variables used in the new stvd and lvd 64-bit
    // scom instructions. This protection is needed since the new instructions'
    // operands are not yet properly considered by the compiler.
    // Note that l_dataH is used to represent the "beginning", i.e. High-order,
    // part of the 64-bit data in d8 (i.e., r8+r9). 
    uint32_t register l_dataH asm("r8")=0;
    uint32_t register l_dataL asm("r9")=0;
    uint32_t register l_addr_eff asm("r10")=0;
    uint32_t register l_scratch asm("r31")=0;
    
    l_addr_eff = i_address;
    l_dataH = (uint32_t)(i_data>>32);
    l_dataL = (uint32_t)(i_data);    

    // CMO-The following sequence forces usage of l_dataH/L and l_addr_eff
    // and thus the population of them as well. 
    // Further note that unless l_dataH/L are placed right before the following
    // sequence, more specifically, if they're placed at the top of putscom(),
    // r8, or l_dataH, might be overwritten in the if(chiplet_id) section.
    // Secondly, we test l_addr_eff for non-zero through the CR0 register 
    // (which was populated in the "mr." instruction.) This is to convince the 
    // compiler that we actually used l_addr_eff for something. 
    // At present the test result causes no action except to execute the stvd 
    // instruction in either case.
    asm volatile ( \
        "mr. %0, %1 \n" \
        : "=r"(l_scratch) \
        : "r"(l_dataH) );
    asm volatile ( \
        "mr. %0, %1 \n" \
        : "=r"(l_scratch) \
        : "r"(l_dataL) );
    asm volatile ( \
        "mr. %0, %1 \n" \
        : "=r"(l_scratch) \
        : "r"(l_addr_eff) );
    asm volatile ( \
        "beq 0x4 \n" );

    // CMO-This instruction is not fully supported by the compiler (as of
    // 20150108):
    // - Works: It is correctly translated into the proper OP code
    //            format.
    // - Works not: The compiler does not seem to recognize the usage
    //              of the two l_xyz variables in that it doesn't
    //              know prior to this command that the registers that
    //              contain the values of l_xyz need to be protected
    //              up to this point. Thus, we are forced to use those
    //              two l_xyz variables in some dummy instructions just
    //              before this point in order to fake protection.    
    asm volatile ( \
        "stvd %[data], 0(%[effective_address]) \n" \
        : [data]"=r"(l_dataH) \
        : [effective_address]"r"(l_addr_eff) );

    // CMO-TBD
    // Check PIB response code in 0x00001007(17:19)
    // Translate PIB rc to PK rc
    // Does this rc get reset to zero on success?
    // Do we need to check this rc prior to issuing the SCOM?

    return 0;
}

uint32_t _putscom( uint32_t i_chiplet_id, uint32_t i_address, uint64_t i_data)
{
    uint32_t  l_rc=0;
    uint32_t  l_cid=0;
    uint32_t  l_addr_eff=0;

    if (i_chiplet_id)
    {
        // Accommodate two different ways of supplying the chiplet ID:
        //   0xNN000000: Only bits in high-order two nibbles : Valid
        //   0x000000NN: Only bits in low-order two nibbles :  Valid
        //
        if ((i_chiplet_id & 0xFF000000) == i_chiplet_id)
        {
            // Valid: Chiplet ID in high-order two nibbles.
            l_cid = i_chiplet_id;
        }
        else if ((i_chiplet_id & 0x000000FF) == i_chiplet_id)
        {
            // Valid: Chiplet ID in low-order two nibbles. Convert to high-order.
            l_cid = i_chiplet_id << 24;
        }
        else
        {
            // Invalid: Invalid type of chiplet ID
            PK_TRACE("putscom() : Invalid value of i_chiplet_id (=0x%08X)",i_chiplet_id);
            return 1; //CMO-improve Return sensible rc here.
        }

        l_addr_eff = (i_address & 0x00FFFFFF) | l_cid;
    }
    else
    {
        // Chiplet ID is zero. Accept address as is.
        // This is useful for PIB addresses and non-EX chiplets, and even for
        //   EX chiplets if the fully qualified EX chiplet addr is already known.
        l_addr_eff = i_address;

    }
    
    l_rc = putscom_abs(l_addr_eff, i_data);


    return l_rc;
}


uint32_t getscom_abs( uint32_t i_address, uint64_t *o_data)
{
    
    // CMO-Declaring variables tied to specific registers enables us to protect 
    // the SCOM data and address variables used in the new stvd and lvd 64-bit
    // data instructions. This protection is needed since the new instructions
    // are not yet properly considered by the compiler.
    // Note that l_dataH is used to represent the "beginning", i.e. High-order,
    // part of the 64-bit data in d8 (i.e., r8+r9). 
    uint32_t register l_dataH asm("r8")=0;
    uint32_t register l_dataL asm("r9")=0;
    uint32_t register l_addr_eff asm("r10")=0;
    uint32_t register l_scratch asm("r31")=0;

    
    l_addr_eff = i_address;
    
    // CMO-The following sequence forces usage of l_addr_eff and thus the
    // population of it as well. 
    // Secondly, we test l_addr_eff for non-zero through the CR0 register 
    // (which was populated in the "mr." instruction.) This is to convince the 
    // compiler that we actually used l_addr_eff for something. 
    // At present the test result causes no action except to execute the lvd 
    // instruction in either case.
    asm volatile ( \
        "mr. %0, %1 \n" \
        : "=r"(l_scratch) \
        : "r"(l_addr_eff) );
    asm volatile ( \
        "beq 0x4 \n" );

    asm volatile ( \
        "lvd %[data], 0(%[effective_address]) \n" \
        : [data]"=r"(l_dataH) \
        : [effective_address]"r"(l_addr_eff) );

    // CMO-The following sequence moves the read data, in l_dataH/L, into the
    // 64-bit o_data location.
    asm volatile ( \
        "stw %0, 0(%1) \n" \
        : "=r"(l_dataH) \
        : "r"(o_data) );
    asm volatile ( \
        "stw %0, 4(%1) \n" \
        : "=r"(l_dataL) \
        : "r"(o_data) );
    
    // CMO-TBD
    // Check PIB response code in 0x00001007(17:19)
    // Translate PIB rc to PK rc

    return 0;
}

uint32_t _getscom( uint32_t i_chiplet_id, uint32_t i_address, uint64_t *o_data)
{
    uint32_t  l_rc=0;
    uint32_t  l_cid=0;
    uint32_t  l_addr_eff=0;

    if (i_chiplet_id)
    {
        // Accommodate two different ways of supplying the chiplet ID:
        //   0xNN000000: Only bits in high-order two nibbles : Valid
        //   0x000000NN: Only bits in low-order two nibbles :  Valid
        //
        if ((i_chiplet_id & 0xFF000000) == i_chiplet_id)
        {
            // Valid: Chiplet ID in high-order two nibbles.
            l_cid = i_chiplet_id;
        }
        else if ((i_chiplet_id & 0x000000FF) == i_chiplet_id)
        {
            // Valid: Chiplet ID in low-order two nibbles. Convert to high-order.
            l_cid = i_chiplet_id << 24;
        }
        else
        {
            // Invalid: Invalid type of chiplet ID
            PK_TRACE("getscom() : Invalid value of i_chiplet_id (=0x%08X)",i_chiplet_id);
            return 1; //CMO-improve Return sensible rc here.
        }

        l_addr_eff = (i_address & 0x00FFFFFF) | l_cid;
    }
    else
    {
        // Chiplet ID is zero. Accept address as is.
        // This is useful for PIB addresses and non-EX chiplets, and even for
        //   EX chiplets if the fully qualified EX chiplet addr is already known.
        l_addr_eff = i_address;
    }

    l_rc = getscom_abs(l_addr_eff, o_data);
    
    // CMO-TBD
    // Check PIB response code in 0x00001007(17:19)
    // Translate PIB rc to PK rc

    return l_rc;
}
OpenPOWER on IntegriCloud