summaryrefslogtreecommitdiffstats
path: root/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/mcbist.C
blob: 1b38ed539a132280172023726b1266cec47994ec (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
/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/memory/lib/mcbist/mcbist.C $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2019                        */
/* [+] 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                                                     */

///
/// @file mcbist.C
/// @brief Run and manage the MCBIST engine
///
// *HWP HWP Owner: Stephen Glancy <sglancy@us.ibm.com>
// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com>
// *HWP Team: Memory
// *HWP Level: 3
// *HWP Consumed by: FSP:HB

#include <lib/shared/nimbus_defaults.H>
#include <fapi2.H>
#include <lib/mss_attribute_accessors.H>
#include <lib/mcbist/mcbist.H>
#include <lib/workarounds/mcbist_workarounds.H>
#include <generic/memory/lib/utils/pos.H>

using fapi2::TARGET_TYPE_MCBIST;
using fapi2::TARGET_TYPE_MCA;
using fapi2::TARGET_TYPE_MCS;

namespace mss
{

///
/// @brief Gets the attribute for freq
/// @param[in] const ref to the target
/// @param[out] uint64_t& reference to store the value
/// @note Generated by gen_accessors.pl generate_mc_port_params
/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK
/// @note  Frequency of this memory channel in MT/s (Mega Transfers per second)
///
template<>
fapi2::ReturnCode freq<mss::mc_type::NIMBUS, fapi2::TARGET_TYPE_MCBIST>(const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>&
        i_target, uint64_t& o_value)
{
    return mss::freq(i_target, o_value);
}

const std::pair<uint64_t, uint64_t> mcbistTraits<>::address_pairs[] =
{
    { START_ADDRESS_0, END_ADDRESS_0 },
    { START_ADDRESS_1, END_ADDRESS_1 },
    { START_ADDRESS_2, END_ADDRESS_2 },
    { START_ADDRESS_3, END_ADDRESS_3 },
};

const std::vector< mss::mcbist::op_type > mcbistTraits<>::FIFO_MODE_REQUIRED_OP_TYPES =
{
    mss::mcbist::op_type::WRITE            ,
    mss::mcbist::op_type::READ             ,
    mss::mcbist::op_type::READ_WRITE       ,
    mss::mcbist::op_type::WRITE_READ       ,
    mss::mcbist::op_type::READ_WRITE_READ  ,
    mss::mcbist::op_type::READ_WRITE_WRITE ,
    mss::mcbist::op_type::RAND_SEQ         ,
    mss::mcbist::op_type::READ_READ_WRITE  ,
};

namespace mcbist
{

///
/// @brief Get a list of ports involved in the program
/// Specialization for program<>
/// @param[in] i_target the target for this program
/// @return vector of port targets
///
template<>
std::vector<fapi2::Target<fapi2::TARGET_TYPE_MCA>>
        program<>::get_port_list( const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target ) const
{
    using TT = mss::mcbistTraits<>;

    std::vector<fapi2::Target<fapi2::TARGET_TYPE_MCA>> l_list;

    fapi2::buffer<uint64_t> l_ports_selected;
    // extract port sel to left of l_ports_selected so port relatve pos maps to bit number
    iv_control.extract<TT::PORT_SEL, TT::PORT_SEL_LEN>(l_ports_selected);

    for (const auto& p : find_targets<fapi2::TARGET_TYPE_MCA>(i_target))
    {
        if (l_ports_selected.getBit(mss::relative_pos<fapi2::TARGET_TYPE_MCBIST>(p)))
        {
            l_list.push_back(p);
        }
    }

    return l_list;
}


///
/// @brief Read entries from MCBIST Read Buffer (RB) array
/// Specialization for fapi2::TARGET_TYPE_MCA
/// @param[in] i_target the target to effect
/// @param[in] i_start_addr the array address to read first
/// @param[in] i_num_entries the number of array entries to read
/// @param[out] o_data vector of output data
/// @param[out] o_ecc_data vector of ecc data
/// @return FAPI2_RC_SUCCSS iff ok
///
template<>
fapi2::ReturnCode read_rb_array(const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target,
                                const uint64_t i_start_addr,
                                const uint64_t i_num_entries,
                                std::vector< fapi2::buffer<uint64_t> >& o_data,
                                std::vector< fapi2::buffer<uint64_t> >& o_ecc_data)
{
    using TT = mcbistTraits<DEFAULT_MC_TYPE, fapi2::TARGET_TYPE_MCS>;

    fapi2::buffer<uint64_t> l_control_value;
    fapi2::buffer<uint64_t> l_data;
    uint64_t l_array_addr = i_start_addr;

    const auto& l_mcs = mss::find_target<TARGET_TYPE_MCS>(i_target);

    // Clear out any stale values from output vectors
    o_data.clear();
    o_ecc_data.clear();

    // set BUFFER according to port position within MCS
    l_control_value.writeBit<TT::RB_BUFFER_SEL>(mss::relative_pos<fapi2::TARGET_TYPE_MCS>(i_target))
    // set start address
    .insertFromRight<TT::RB_ADDRESS, TT::RB_ADDRESS_LEN>(l_array_addr)
    // enable the auto increment bit
    .setBit<TT::RB_AUTOINC>();

    FAPI_INF("Setting the RB array access control register.");
    FAPI_TRY( mss::putScom(l_mcs, TT::RD_BUF_CTL_REG, l_control_value) );

    for (uint8_t l_index = 0; l_index < i_num_entries; ++l_index)
    {
        // Note that since we enabled AUTOINC above, reading ECC_REG will increment
        // the array pointer so the next DATA_REG read will read the next array entry
        FAPI_TRY( mss::getScom(l_mcs, TT::RD_BUF_DATA_REG, l_data) );
        FAPI_INF("RB data index %d is: 0x%016lx", l_array_addr, l_data);
        o_data.push_back(l_data);

        // Need to read ecc register to increment array index
        FAPI_TRY( mss::getScom(l_mcs, TT::RD_BUF_ECC_REG, l_data) );
        o_ecc_data.push_back(l_data);
        ++l_array_addr;

        // Array address automatically rolls over if we go beyond NUM_COMPARE_LOG_ENTRIES
        if (l_array_addr >= TT::NUM_COMPARE_LOG_ENTRIES)
        {
            l_array_addr = 0;
        }

    }

fapi_try_exit:
    return fapi2::current_err;
}

///
/// @brief Checks if broadcast mode is capable of being enabled on this vector of targets
/// @param[in] i_targets the vector of targets to analyze - specialization for MCA target type
/// @return l_capable - yes iff these vector of targets are broadcast capable
///
template<>
const mss::states is_broadcast_capable(const std::vector<fapi2::Target<fapi2::TARGET_TYPE_MCA>>& i_targets)
{
    // If we don't have MCA's exit out
    if(i_targets.size() == 0)
    {
        FAPI_INF("No MCA's found. Not broadcast capable, exiting...");
        return mss::states::NO;
    }

    // Now the fun begins
    // First, get the number of DIMM's on the 0th MCA
    const uint64_t l_first_mca_num_dimm = mss::count_dimm(i_targets[0]);

    // Now, find if we have any MCA's that have a different number of DIMM's
    // Note: starts on the next MCA target due to the fact that something always equals itself
    const auto l_mca_it = std::find_if(i_targets.begin() + 1,
                                       i_targets.end(),
                                       [l_first_mca_num_dimm]( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_rhs) -> bool
    {
        // Count the number of DIMM and compare to the expected
        const uint64_t l_num_dimm = mss::count_dimm(i_rhs);

        // If they're different, we found the MCA that is different
        return (l_first_mca_num_dimm != l_num_dimm);
    });

    // If no MCA was found that have a different number of DIMMs (aka a different drop), then we are broadcast capable
    if(l_mca_it == i_targets.end())
    {
        const auto l_mcbist = mss::find_target<fapi2::TARGET_TYPE_MCBIST>(i_targets[0]);
        FAPI_INF("MCA vector from %s has the same number of DIMMs per port: %lu - is broadcast capable",
                 mss::c_str(l_mcbist), l_first_mca_num_dimm);
        return mss::states::YES;
    }

    // Otherwise, note the MCA that differs and return that this is not broadcast capable
    FAPI_INF("MCA %s differs on the number of DIMMs per port: (number of DIMMs from i_targets[0] %lu, found %lu) - is NOT broadcast capable",
             mss::c_str(*l_mca_it), l_first_mca_num_dimm, mss::count_dimm(*l_mca_it));
    return mss::states::NO;
}

///
/// @brief Checks if broadcast mode is capable of being enabled on this target
/// @param[in] i_target the target to effect
/// @param[in] i_bc_force attribute's value to force off broadcast mode
/// @param[in] i_bc_enable attribute's value to enable or disable broadcast mode
/// @param[in] i_chip_bc_capable true if the chip is BC capable
/// @return o_capable - yes iff these vector of targets are broadcast capable
///
const mss::states is_broadcast_capable_helper(const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target,
        const uint8_t i_bc_force,
        const uint8_t i_bc_enable,
        const bool i_chip_bc_capable)
{
    // First off, check if we need to disable broadcast mode due to a chip size bug
    // Note: the bug check is decidedly more complicated than the EC check, but we'll just disable BC mode out of safety concerns
    // Better to go slow and steady and initialize the chip properly than to go fast and leave the memory initialized poorly
    if( !i_chip_bc_capable )
    {
        FAPI_INF("%s A chip bug prevents broadcast mode. Chip is not brodcast capable", mss::c_str(i_target));
        return mss::states::NO;
    }

    // If BC mode is forced off, then we're done
    if( i_bc_force == fapi2::ENUM_ATTR_MSS_MRW_FORCE_BCMODE_OFF_YES )
    {
        FAPI_INF("%s MRW attribute has broadcast mode forced off", mss::c_str(i_target));
        return mss::states::NO;
    }

    // Now check the override broadcast mode capable attribute
    if( i_bc_enable == fapi2::ENUM_ATTR_MSS_OVERRIDE_MEMDIAGS_BCMODE_DISABLE )
    {
        FAPI_INF("%s attribute has broadcast mode disabled", mss::c_str(i_target));
        return mss::states::NO;
    }

    // Otherwise, our chip and attributes allow us to be BC capable
    FAPI_INF("%s chip and attributes allow for BC mode", mss::c_str(i_target));
    return mss::states::YES;
}

///
/// @brief Checks if broadcast mode is capable of being enabled on this target
/// @param[in] i_target the target to effect - specialization for MCBIST target type
/// @return l_capable - yes iff these vector of targets are broadcast capable
///
template< >
const mss::states is_broadcast_capable(const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target)
{
    // Chip is BC capable IFF the MCBIST end of rank bug is not present
    const auto l_chip_bc_capable = !mss::chip_ec_feature_mcbist_end_of_rank(i_target);

    // If BC mode is disabled in the MRW, then it's disabled here
    uint8_t l_bc_mode_enable = 0;
    uint8_t l_bc_mode_force_off = 0;
    FAPI_TRY(mss::override_memdiags_bcmode(l_bc_mode_enable));
    FAPI_TRY(mss::mrw_force_bcmode_off(l_bc_mode_force_off));

    // Check if the chip and attributes allows memdiags/mcbist to be in broadcast mode
    {
        const auto l_state = is_broadcast_capable_helper(i_target, l_bc_mode_force_off, l_bc_mode_enable, l_chip_bc_capable);

        if(l_state == mss::states::NO)
        {
            return l_state;
        }
    }

    // Now that we are guaranteed to have a chip that could run broadcast mode and the system is allowed to do so,
    // do the following steps to check whether our config is broadcast capable:
    {
        // Steps to determine if this MCBIST is broadcastable
        // 1) Check the number of DIMM's on each MCA - true only if they all match
        // 2) Check that all of the DIMM kinds are equal - if they are, then we can do broadcast mode
        // 3) if both 1 and 2 are true, then broadcast capable, otherwise false

        // 1) Check the number of DIMM's on each MCA - if they don't match, then no
        const auto l_mca_check = is_broadcast_capable(mss::find_targets<fapi2::TARGET_TYPE_MCA>(i_target));

        // 2) Check that all of the DIMM kinds are equal - if they are, then we can do broadcast mode
        const auto l_dimms = mss::find_targets<fapi2::TARGET_TYPE_DIMM>(i_target);
        const auto l_dimm_kinds = mss::dimm::kind<>::vector(l_dimms);
        const auto l_dimm_kind_check = is_broadcast_capable(l_dimm_kinds);

        // 3) if both 1/2 are true, then broadcastable, otherwise false
        const auto l_capable = ((l_mca_check == mss::states::YES) && (l_dimm_kind_check == mss::states::YES)) ?
                               mss::states::YES : mss::states::NO;

        FAPI_INF("%s %s broadcast capable", mss::c_str(i_target), (l_capable == mss::states::YES) ? "is" : "is not");
        return l_capable;
    }

fapi_try_exit:
    FAPI_ERR("%s failed to get an MRW attribute, an egregious error. Returning NOT broadcast capable",
             mss::c_str(i_target));
    return mss::states::NO;
}

///
/// @brief Checks if broadcast mode is capable of being enabled on this vector of targets
/// @param[in] i_target the target to effect
/// @return l_capable - yes iff these vector of targets are broadcast capable
///
const mss::states is_broadcast_capable(const std::vector<mss::dimm::kind<>>& i_kinds)
{
    // If we don't have any DIMM kinds exit out
    if(i_kinds.size() == 0)
    {
        FAPI_INF("No DIMM kinds are located. Not broadcast capable, exiting...");
        return mss::states::NO;
    }

    // Now the fun begins
    // First, get the starting kind - the 0th kind in the vector
    const auto l_expected_kind = i_kinds[0];

    // Now, find if we have any kinds that differ from our first kind
    // Note: starts on the next DIMM kind due to the fact that something always equals itself
    const auto l_kind_it = std::find_if(i_kinds.begin() + 1,
                                        i_kinds.end(), [&l_expected_kind]( const mss::dimm::kind<>& i_rhs) -> bool
    {
        // If they're different, we found a DIMM that is differs
        return (l_expected_kind != i_rhs);
    });

    // If no DIMM kind was found that differs, then we are broadcast capable
    if(l_kind_it == i_kinds.end())
    {
        FAPI_INF("DIMM kinds vector starting with %s has the kinds for all DIMM's - is broadcast capable",
                 mss::c_str(l_expected_kind.iv_target));
        return mss::states::YES;
    }

    // Otherwise, note the MCA that differs and return that this is not broadcast capable
    FAPI_INF("DIMM kinds vector differs with %s has the kinds for all DIMM's - is not broadcast capable",
             mss::c_str(l_kind_it->iv_target));
    return mss::states::NO;
}

///
/// @brief Configures all of the ports for broadcast mode
/// @param[in] i_target the target to effect - MCBIST specialization
/// @param[out] o_port_select - the configuration of the selected ports
/// @return FAPI2_RC_SUCCSS iff ok
///
template< >
fapi2::ReturnCode setup_broadcast_port_select(const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target,
        uint64_t& o_port_select)
{
    // Starting location - the MCBIST type takes in ports as a right justified uint64_t value
    // As we have 4 ports per-MCBIST, they occupy 60-63
    // START contains the starting offset for each port
    constexpr uint64_t START = 60;

    // Loops through all the ports and adds them to the broadcast value
    fapi2::buffer<uint64_t> l_port_select;
    fapi2::current_err = fapi2::FAPI2_RC_SUCCESS;

    for(const auto& l_mca : mss::find_targets<fapi2::TARGET_TYPE_MCA>(i_target))
    {
        // Configures each port individually
        const uint64_t l_offset = mss::relative_pos<TARGET_TYPE_MCBIST>(l_mca);
        FAPI_TRY(l_port_select.setBit(START +  l_offset),
                 "%s setBit error for relative pos of %lu",
                 mss::c_str(l_mca), l_offset);
    }

    // Assigns the returned value
    o_port_select = l_port_select;

fapi_try_exit:
    return fapi2::current_err;
}

///
/// @brief Enables broadcast mode
/// @param[in] i_target the target to effect - MCBIST specialization
/// @param[in,out] io_program the mcbist::program - MCBIST specialization
/// @return FAPI2_RC_SUCCSS iff ok
///
template< >
fapi2::ReturnCode enable_broadcast_mode(const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target,
                                        mcbist::program<>& io_program)
{
    // constexpr's for beautification
    constexpr bool ENABLE = true;
    constexpr auto BROADCAST_SYNC_ENABLE = mss::states::ON;
    constexpr auto MAX_BROADCAST_SYNC_WAIT = mss::mcbist::broadcast_timebase::TB_COUNT_128;

    // First, enable broadcast mode
    io_program.change_maint_broadcast_mode(ENABLE);

    // Second, configure broadcast mode on all enabled ports
    uint64_t l_port_select = 0;
    FAPI_TRY(setup_broadcast_port_select(i_target, l_port_select));
    io_program.select_ports(l_port_select);

    // Finally, enable broadcast sync mode and max out the wait to avoid timeout issues
    io_program.broadcast_sync_enable(BROADCAST_SYNC_ENABLE);
    io_program.change_broadcast_timebase(MAX_BROADCAST_SYNC_WAIT);

fapi_try_exit:
    return fapi2::current_err;
}

///
/// @brief Configures broadcast mode, if it is needed
/// @param[in] i_target the target to effect
/// @param[in,out] io_program the mcbist::program
/// @return FAPI2_RC_SUCCSS iff ok
///
template<>
fapi2::ReturnCode configure_broadcast_mode(const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target,
        mcbist::program<>& io_program)
{
    // If we're not capable to do broadcast mode on this target, exit out
    const auto l_broadcast_capable = is_broadcast_capable(i_target);

    if(l_broadcast_capable == mss::states::NO)
    {
        FAPI_INF("%s is not broadcast capable, skipping enablement of broadcast mode", mss::c_str(i_target));
        return fapi2::FAPI2_RC_SUCCESS;
    }

    // Enable broadcast mode
    FAPI_INF("%s is broadcast capable, enabling broadcast mode", mss::c_str(i_target));
    return enable_broadcast_mode(i_target, io_program);
}

///
/// @brief Clear the errors helper. Chip can specialise this
/// function to put any necessary workaround in it.
/// @return fapi2::ReturnCode, FAPI2_RC_SUCCESS iff OK
///
template< >
fapi2::ReturnCode clear_error_helper( const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target,
                                      const program<>& i_program )
{
    // Implement any mcbist work-arounds.
    // I'm going to do the unthinkable here - and cast away the const of the mcbist program input.
    // The work arounds need to change this, and so it needs to not be const. However, I don't want
    // to risk general const-correctness by changing the input parameter to non-const. So, I use
    // const_cast<> (ducks out of the way of the flying adjectives.) These are work-arounds ...
    FAPI_TRY( workarounds::mcbist::end_of_rank(i_target, const_cast<program<>&>(i_program)) );

    FAPI_TRY( clear_errors(i_target) );

fapi_try_exit:
    return fapi2::current_err;
}

} // namespace MCBIST

// Note: outside of the mcbist namespace

///
/// @brief Dump the registers of an mcbist
/// @param[in] i_target, the mcbist in question
/// @return fapi2::FAPI2_RC_SUCCESS if ok
///
template<>
fapi2::ReturnCode dump_regs( const fapi2::Target<TARGET_TYPE_MCBIST>& i_target )
{
    return fapi2::current_err;
}


} // namespace
OpenPOWER on IntegriCloud