summaryrefslogtreecommitdiffstats
path: root/src/occ_gpe0/firdata/lpc.c
blob: 6544c44ed140893e773342c93cd3e2871c154443 (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
/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/occ_405/firdata/lpc.c $                                   */
/*                                                                        */
/* OpenPOWER OnChipController Project                                     */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2017                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

#include <native.h>
#include <lpc.h>
#include <scom_util.h>

#define    LPCHC_FW_SPACE   0xF0000000 /**< LPC Host Controller FW Space */
#define    LPCHC_MEM_SPACE  0xE0000000 /**< LPC Host Controller Mem Space */
#define    LPCHC_IO_SPACE   0xD0010000 /**< LPC Host Controller I/O Space */
#define    LPCHC_REG_SPACE  0xC0012000 /**< LPC Host Ctlr Register Space */
#define    ECCB_NON_FW_RESET_REG  0x000B0001 /**< ECCB Reset Reg (non-FW) */

#define    LPC_BASE_REG    0x00090040 /**< LPC Base Address Register */
#define    LPC_CMD_REG     0x00090041 /**< LPC Command Register */
#define    LPC_DATA_REG    0x00090042 /**< LPC Data Register */
#define    LPC_STATUS_REG  0x00090043 /**< LPC Status Register */

#define    ECCB_CTL_REG   0x000B0020 /**< ECCB Control Reg (FW) */
#define    ECCB_RESET_REG   0x000B0021 /**< ECCB Reset Reg (FW) */
#define    ECCB_STAT_REG  0x000B0022 /**< ECCB Status Reg (FW) */
#define    ECCB_DATA_REG  0x000B0023 /**< ECCB Data Reg (FW) */

/* Default Values to set for all operations
    1101.0100.0000.000x.0000.0001.0000.0000.<address> */
#define    ECCB_CTL_REG_DEFAULT  0xD400010000000000

/* Error bits: wh_todo comments here */
#define    LPC_STAT_REG_ERROR_MASK  0x0000000000000000 /**< Error Bits */ //wh_todo correctly set mask

/**< OPB LPCM Sync FIR Reg - used to read the FIR*/
#define    OPB_LPCM_FIR_REG  0x01010C00

/**< OPB LPCM Sync FIR Reg WOX_AND - used to clear the FIR */
#define    OPB_LPCM_FIR_WOX_AND_REG  0x01010C01

/**< OPB LPCM Sync FIR Mask Reg WO_OR - used to set the mask */
#define    OPB_LPCM_FIR_MASK_WO_OR_REG  0x01010C05

#define    OPB_LPCM_FIR_ERROR_MASK  0xFF00000000000000 /**< Error Bits MASK */

/* LPCHC reset-related registers */
#define    OPB_MASTER_LS_CONTROL_REG  0x008 /**<OPBM LS Control Reg */
#define    LPCHC_RESET_REG            0x0FC /**<LPC HC Reset Register */

#define    ECCB_RESET_LPC_FAST_RESET  (1ULL << 62) /**< bit 1  Fast reset */

#define    LPC_POLL_TIME_NS  400000 /**< max time should be 400ms */
#define    LPC_POLL_INCR_NS  100000 /**< increase for testing */

#define    LPCHC_SYNC_CYCLE_COUNTER_INFINITE  0xFF000000

/**
 * @brief  LPC Base Register Layout
*/
typedef union
{

    uint64_t data64;

    struct
    {
        /* unused sections should be set to zero */
        uint64_t unused0   : 8;  /**< 0:7 */
        uint64_t base_addr : 24; /**< 8:31 */
        uint64_t unused1   : 31; /**< 32:62 */
        uint64_t disable   : 1;  /**< 63 */
    };
} BaseReg_t;

/**
 * @brief  LPC Control Register Layout
 */
typedef union
{

    uint64_t data64;

    struct
    {
        /* unused sections should be set to zero */
        //       rnw == read not write
        uint64_t rnw      : 1;  /**< 0 = Setting to 1 causes read */
        uint64_t unused0  : 4;  /**< 1:4 */
        uint64_t size     : 7;  /**< 5:11 */
        uint64_t unused1  : 20; /**< 12:31 */
        uint64_t address  : 32; /**< 32:63 = LPC Address */
    };
} CommandReg_t;

/**
 * @brief  LPC Status Register Layout
 */
typedef union
{
    uint64_t data64;
    struct
    {
        uint64_t op_done     : 1;  /**< 0 */
        uint64_t unused0     : 9;  /**< 1:9 */
        uint64_t opb_valid   : 1;  /**< 10 */
        uint64_t opb_ack     : 1;  /**< 11 */
        uint64_t unused1     : 52; /**< 12:63 */
    };
} StatusReg_t;

uint32_t checkAddr(LpcTransType i_type,
                   uint32_t i_addr)
{
    uint32_t full_addr = 0;
    switch ( i_type )
    {
        case LPC_TRANS_IO:
            full_addr = i_addr | LPCHC_IO_SPACE;
            break;
        case LPC_TRANS_FW:
            full_addr = i_addr | LPCHC_FW_SPACE;
            break;
    }
    return full_addr;
}


errorHndl_t pollComplete(CommandReg_t* i_ctrl,
                         StatusReg_t* o_stat)
{
    errorHndl_t l_err = NO_ERROR;

    do {
        uint64_t poll_time = 0;
        uint64_t loop = 0;
        do
        {
            SCOM_Trgt_t l_target;
            l_target.type = TRGT_PROC;
            l_target.isMaster = TRUE;
            l_err = SCOM_getScom(l_target, LPC_STATUS_REG, &(o_stat->data64));

            if( l_err )
            {
                break;
            }

            if( o_stat->op_done )
            {
                break;
            }

            /* Want to start out incrementing by small numbers then get bigger
               to avoid a really tight loop in an error case so we'll increase
               the wait each time through */
            sleep( LPC_POLL_INCR_NS*(++loop) );
            poll_time += LPC_POLL_INCR_NS * loop;
        } while ( poll_time < LPC_POLL_TIME_NS );

        /* Check for hw errors or timeout if no previous logs */
        if( (l_err == NO_ERROR) &&
            ((o_stat->data64 & LPC_STAT_REG_ERROR_MASK)
             || (!o_stat->op_done)) )
        {
            TRAC_ERR( "LpcDD::pollComplete> LPC error or timeout: addr=0x%.8X, status=0x%.8X%.8X",
                       i_ctrl->address, (uint32_t)(o_stat->data64>>32), (uint32_t)o_stat->data64 );
            l_err = -1;
            break;
        }
    } while(0);

    return l_err;

}


/*========================================================*/

errorHndl_t lpc_read( LpcTransType i_type,
                     uint32_t i_addr,
                     uint8_t* o_data,
                     uint32_t i_size )
{
    errorHndl_t l_err = NO_ERROR;
    int32_t l_addr = 0;
    uint64_t l_ret;
    uint32_t l_shift_amount;
    uint64_t l_temp_data;

    do {
        if( o_data == NULL )
        {
            TRACFCOMP( "o_data is NULL!" );
            l_err = -2;
            break;
        }


        /* Generate the full absolute LPC address */
        l_addr = checkAddr( i_type, i_addr );

        /* Setup command */
        CommandReg_t lpc_cmd;
        lpc_cmd.rnw     = 1;  //Indicate read not write
        lpc_cmd.size    = i_size;
        lpc_cmd.address = l_addr;

        /* Execute command via Scom */
        SCOM_Trgt_t l_target;
        l_target.type = TRGT_PROC;
        l_target.isMaster = TRUE;

        //First write the address we want to read from in the
        //LPC_CMD_REG scom address
        l_err = SCOM_putScom(l_target, LPC_CMD_REG, lpc_cmd.data64);
        if(l_err != SUCCESS)
        {
            TRAC_ERR("lpc_read: SCOM_putScom failed to write to LPC_CMD_REG command rc=0x%08x",
                    (uint32_t)l_err);
            break;
        }

        /* Poll for completion */
        StatusReg_t lpc_status;
        l_err = pollComplete( &lpc_cmd, &lpc_status );
        if( l_err ) { break; }

        // Read data from the LPC_DATA_REG
        l_err = SCOM_getScom(l_target, LPC_DATA_REG, &l_ret);
        if(l_err != SUCCESS)
        {
            TRAC_ERR("lpc_read: SCOM_getScom failed rc=0x%08x", (uint32_t)l_err);
            break;
        }

        //The scom returns the data in the byte offset represented by last
        //3 bits of the address. For example, addr 0x21 will have have data
        //starting in byte1 on value returned from scom.
        //addr & 0x7 <-- this gives the byte offset
        //7 - (addr & 0x7) <-- subratcing from 7 as the data is left aligned
        //adding (size - 1) <-- to incorporate reading more than one byte
        //multiply by 8 to convert from byte to bits
        l_shift_amount = (7 - ((l_addr & 0x7) + (i_size-1))) * 8;
        l_temp_data    = l_ret >> l_shift_amount;

        //Had some weird problems with memcpy, that's why typecasting
        //to the size of data asked.
        if (i_size == sizeof(uint8_t))
        {
            uint8_t* l_temp_ptr = (uint8_t*)(o_data);
            *l_temp_ptr = l_temp_data;
        }
        else if (i_size == sizeof(uint16_t))
        {
            uint16_t* l_temp_ptr = (uint16_t*)(o_data);
            *l_temp_ptr = l_temp_data;
        }
        else if (i_size == sizeof(uint32_t))
        {
            uint32_t* l_temp_ptr = (uint32_t*)(o_data);
            *l_temp_ptr = l_temp_data;
        }
        else if (i_size == sizeof(uint64_t))
        {
            uint64_t* l_temp_ptr = (uint64_t*)(o_data);
            *l_temp_ptr = l_temp_data;
        }
        else
        {
            TRAC_ERR("lpc_read: unsupported size length");
            l_err = -1;
            break;
        }

    } while(0);

    return l_err;
}

errorHndl_t lpc_write( LpcTransType i_type,
                      uint32_t i_addr,
                      uint8_t* i_data,
                      uint32_t i_size )
{
    errorHndl_t l_err = NO_ERROR;
    uint32_t l_addr = 0;
    uint64_t l_write_data = 0;
    uint64_t l_data = 0;
    uint32_t l_shift_amount;

    do {
        /* Generate the full absolute LPC address */
        l_addr = checkAddr( i_type, i_addr );

        /* Setup Write Command */
        CommandReg_t lpc_cmd;
        lpc_cmd.rnw     = 0;  //Indicate write
        lpc_cmd.size    = i_size;
        lpc_cmd.address = l_addr;

        /* Setup Write command via Scom */
        SCOM_Trgt_t l_target;
        l_target.type = TRGT_PROC;
        l_target.isMaster = TRUE;

        //First write the address we want to write to in LPC_CMD_REG
        l_err = SCOM_putScom(l_target, LPC_CMD_REG, lpc_cmd.data64);
        if(l_err != SUCCESS)
        {
            TRAC_ERR("ERROR> lpc_write: SCOM_putScom failed to write to LPC_CMD_REG command: rc=0x%08x",
                    (uint32_t)l_err);
            break;
        }

        //There were some weird memcpy problems. That's why, typecasting
        //to the size of data requested to write
        if (i_size == sizeof(uint8_t))
        {
            l_write_data = (*i_data);
        }
        else if (i_size == sizeof(uint16_t))
        {
            uint16_t* l_temp_ptr = (uint16_t*)(i_data);
            l_write_data = *l_temp_ptr;
        }
        else if (i_size == sizeof(uint32_t))
        {
            uint32_t* l_temp_ptr = (uint32_t*)(i_data);
            l_write_data = *l_temp_ptr;
        }
        else if (i_size == sizeof(uint64_t))
        {
            uint64_t* l_temp_ptr = (uint64_t*)(i_data);
            l_write_data = *l_temp_ptr;
        }
        else
        {
            TRAC_ERR("lpc_write: unsupported size length");
            l_err = -1;
            break;
        }

        //The scom expects the data in the byte offset represented by last
        //3 bits of the address. For example, addr 0x21 will have have data
        //starting in byte1 on value returned from scom.
        //addr & 0x7 <-- this gives the byte offset
        //7 - (addr & 0x7) <-- subratcing from 7 as the data in the scom reg
        //is expected to be left aligned
        //adding (size - 1) <-- to incorporate reading more than one byte
        //multiply by 8 to convert from byte to bits
        l_shift_amount = (7 - ((l_addr & 0x7) + (i_size-1))) * 8;
        l_data = (l_write_data << l_shift_amount);

        //Write the value to the LPC_DATA_REG
        l_err = SCOM_putScom(l_target, LPC_DATA_REG, l_data);
        if(l_err != SUCCESS)
        {
            TRAC_ERR("ERROR> lpc_write: SCOM_putScom failed to write to LPC_DATA_REG data: rc=0x%08x",
                    (uint32_t)l_err);
            break;
        }

        /* Poll for completion */
        StatusReg_t lpc_stat;
        l_err = pollComplete( &lpc_cmd, &lpc_stat );
        if( l_err ) { break; }


    } while(0);

    return l_err;
}
OpenPOWER on IntegriCloud