summaryrefslogtreecommitdiffstats
path: root/src/usr/pnor/pnordd.H
blob: 0ce538b4d1b8095d6de641fc2a2cfd7062669898 (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
//  IBM_PROLOG_BEGIN_TAG
//  This is an automatically generated prolog.
//
//  $Source: src/usr/pnor/pnordd.H $
//
//  IBM CONFIDENTIAL
//
//  COPYRIGHT International Business Machines Corp. 2011
//
//  p1
//
//  Object Code Only (OCO) source materials
//  Licensed Internal Code Source Materials
//  IBM HostBoot Licensed Internal Code
//
//  The source code for this program is not published or other-
//  wise divested of its trade secrets, irrespective of what has
//  been deposited with the U.S. Copyright Office.
//
//  Origin: 30
//
//  IBM_PROLOG_END
#ifndef __PNOR_PNORDD_H
#define __PNOR_PNORDD_H

#include <limits.h>

/** @file pnordd.H
 *  @brief Provides the interfaces to the PNOR Device Driver
 */

/**
 *  @brief  PNOR Device Driver Class
 *     Provides access to the PNOR flash via the ECCB/LPC/SPI hardware
 */
class PnorDD
{

  public:
    /**
     * @brief Performs a PNOR Read Operation
     *
     * @parm o_buffer  Buffer to read data into
     * @parm io_buflen  Input: Number of bytes to read, 
     *       Output: Number of bytes actually read
     * @parm i_address  Offset into flash to read
     *
     * @return Error from operation
     */
    errlHndl_t readFlash(void* o_buffer,
                         size_t& io_buflen,
                         uint64_t i_address);

    /**
     * @brief Performs a PNOR Write Operation
     *
     * @parm i_buffer  Buffer to write data from
     * @parm io_buflen  Input: Number of bytes to write, 
     *       Output: Number of bytes actually written
     * @parm i_address  Offset into flash to write
     *
     * @return Error from operation
     */
    errlHndl_t writeFlash(void* i_buffer,
                          size_t& io_buflen,
                          uint64_t i_address);


  protected:
    enum PnorMode_t {
        MODEL_UNKNOWN, //Invalid
        MODEL_MEMCPY, //No LPC logic, just do memcpy into cache area
        MODEL_LPC_MEM, //Break into 32-bit LPC ops but use memcpy into cache area
        MODEL_FLAT_ECCB, //Use ECCB scoms to drive LPC, flat memory map behind ECCB, no SPI
        MODEL_REAL, //Code for real hardware or complete sim model
    };

    /**
     * @brief Constructor
     */
    PnorDD( PnorMode_t i_mode = MODEL_UNKNOWN );


    /**
     * @brief Destructor
     */
    ~PnorDD();

    /**
     * @brief Verify flash request is in appropriate address range
     *
     * @i_address  Flash address being operated on
     * @i_length  Length of chunk being operated on
     *
     * @return Error if requested address range is invalid
     */
    errlHndl_t verifyFlashAddressRange(uint64_t i_address,
                                       size_t& i_length);

    /**
     * @brief LPC HC Registers
     *    These are offsets within the LPC Host Controller Register Space
     */
    enum LpcRegAddr {
        LPC_REG_BAR0 = 0x00, /**< BAR0 : OPB register */
        LPC_REG_BAR1 = 0x04, /**< BAR1 : LPC I/O space */
        LPC_REG_BAR2 = 0x08, /**< BAR2 : LPC Memory space */
        LPC_REG_BAR3 = 0x0C, /**< BAR3 : LPC Firmware space */
    };
    
    /**
     * @brief Read a LPC Host Controller Register
     *
     * @parm i_addr  Register address, relative to the
     *           LPC Host Controller Register Space
     * @parm o_data  Buffer to read data into
     *
     * @return Error from operation
     */
    errlHndl_t readRegLPC(LpcRegAddr i_addr,
                          uint32_t& o_data);

    /**
     * @brief Write a LPC Host Controller Register
     *
     * @parm i_addr  Register address, relative to the
     *           LPC Host Controller Register Space
     * @parm o_data  Data to write
     *
     * @return Error from operation
     */
    errlHndl_t writeRegLPC(LpcRegAddr i_addr,
                           uint32_t i_data);

    /**
     * @brief SPI Registers
     *    These are offsets within the SPI Register Space
     */
    enum SpiRegAddr {
        SPI_REG_CMD     = 0x40, /**< CMD : Command */
        SPI_REG_ADR     = 0x44, /**< ADR : Address */
        SPI_REG_ERASMS  = 0x48, /**< ERASMS : Small Erase Block Size */
        SPI_REG_ERASLGS = 0x4C, /**< ERALGS : Large Erase Block Size */
        SPI_REG_ADRCBF  = 0x80, /**< ADRCBF : First Intf NOR Addr Offset */
        SPI_REG_ADRCMF  = 0x84, /**< ADRCMF : First Intf NOR Allocation */
        SPI_REG_ADRCBS  = 0x88, /**< ADRCBS : Second Intf NOR Addr Offset */
        SPI_REG_ADRCMS  = 0x8C, /**< ADRCMS : Second Intf NOR Allocation */
    };

    /**
     * @brief SPI Op Codes
     *    OP Codes for the SPI Command Register
     */
    enum SpiOpCodes {
        SPI_OP_READRAW    = 0x03, /**< Read Raw */
        SPI_OP_WRITERAW   = 0x02, /**< Write Raw */
        SPI_OP_ERASM      = 0x32, /**< Erase Small */
        SPI_OP_ERALG      = 0x34, /**< Erase Large */
        SPI_OP_ENWRITPROT = 0x53, /**< Enable WRite Protect */
        SPI_OP_CHIPID     = 0x1F, /**< Get Chip ID */
        SPI_OP_STATUS     = 0x05, /**< Get Status */
        SPI_OP_TURNOFF    = 0x5E, /**< Turn Off */
        SPI_OP_TURNON     = 0x50, /**< Turn On */
        SPI_OP_ABORT      = 0x6F, /**< Super-Abort */
        SPI_OP_START4BA   = 0x37, /**< Start 4BA */
        SPI_OP_END4BA     = 0x69, /**< End 4BA */
    };

    /**
     * @brief Read a SPI Register
     *
     * @parm i_addr  Register address, relative to the SPI engine
     * @parm o_data  Buffer to read data into
     *
     * @return Error from operation
     */
    errlHndl_t readRegSPI(SpiRegAddr i_addr,
                          uint32_t& o_data);

    /**
     * @brief Write a SPI Register
     *
     * @parm i_addr  Register address, relative to the SPI engine
     * @parm o_data  Data to write
     *
     * @return Error from operation
     */
    errlHndl_t writeRegSPI(SpiRegAddr i_addr,
                           uint32_t i_data);


    /**
     * @brief Some general constants
     *
     */
    enum {
        LPCHC_FW_SPACE  = 0xF0000000, /**< LPC Host Controller FW Space */
        LPCHC_MEM_SPACE = 0xE0000000, /**< LPC Host Controller Mem Space */
        LPCHC_IO_SPACE  = 0xD0010000, /**< LPC Host Controller I/O Space */
        LPCHC_REG_SPACE = 0xC0012000, /**< LPC Host Ctlr Register Space */

        LPC_DIRECT_READ_OFFSET = 0xFC000000,
        LPC_SPI_REG_OFFSET = 0xF0000C00,
        LPC_TOP_OF_FLASH_OFFSET = 0xFFFFFFFF,

        ECCB_CTL_REG  = 0x000B0020, /**< ECCB Control Reg (FW) */
        ECCB_STAT_REG = 0x000B0022, /**< ECCB Status Reg (FW) */
        ECCB_DATA_REG = 0x000B0023, /**< ECCB Data Reg (FW) */
        //ECCB_CTL_REG  = 0x00090020, /**< ECCB Control Reg (FW) */
        //ECCB_STAT_REG = 0x00090022, /**< ECCB Status Reg (FW) */
        //ECCB_DATA_REG = 0x00090023, /**< ECCB Data Reg (FW) */

        // Default Values to set for all operations
        // 1101.0100.0000.000x.0000.0001.0000.0000.<address>
        LPC_CTL_REG_DEFAULT = 0xD400010000000000,

        LPC_STAT_REG_ERROR_MASK = 0xFC0000000007F700, /**< Error Bits */

        PNORSIZE = 4 * MEGABYTE, //@fixme - read from TOC instead
        ERASESIZE_BYTES = 4 * KILOBYTE, /**< Minimum Erase Block (bytes) */
        ERASESIZE_WORD32 = ERASESIZE_BYTES/(sizeof(uint32_t)), /**< Erase Block (32-bit words) */

        ECCB_POLL_TIME_NS = 400000, /**< max time from Manfred Walz is 400ms */
        ECCB_POLL_INCR_NS = 10, /**< minimum increment during poll */
    };


    /**
     * @brief Read an address from LPC space
     *
     * @parm i_addr  Absolute LPC Address
     * @parm o_data  Buffer to read data into
     *
     * @return Error from operation
     */
    errlHndl_t readLPC(uint32_t i_addr,
                       uint32_t& o_data);

    /**
     * @brief Write an address from LPC space
     *
     * @parm i_addr  Absolute LPC Address
     * @parm o_data  Data to write
     *
     * @return Error from operation
     */
    errlHndl_t writeLPC(uint32_t i_addr,
                        uint32_t i_data);

    /**
     * @brief Erase a block of flash
     *
     * @parm i_address  Offset into flash to erase, aligned to erase block
     *
     * @return Error from operation
     */
    errlHndl_t eraseFlash(uint32_t i_address);

    /**
     * @brief Compare the existing data in 1 erase block of the flash with
     *   the incoming data and write or erase as needed
     *
     * @parm i_targetAddr  Starting flash address to write
     * @parm i_wordsToWrite  Number of 32-bit words to write
     * @parm i_data  Buffer of data to write
     *
     * @return Error from operation
     */
    errlHndl_t compareAndWriteBlock(uint32_t i_targetAddr,
                                    uint32_t i_wordsToWrite,
                                    uint32_t* i_data);

    /**
     * @brief Determine the nearest flash address aligned to an erase block
     *
     * @parm i_address  Offset into flash
     *
     * @return Block-aligned flash address
     */
    uint32_t findEraseBlock(uint32_t i_address)
    {
        return (i_address - i_address%ERASESIZE_BYTES);
    };

    /**
     * @brief Determine the number of erase blocks that are included in
     *    the given range
     *
     * @parm i_address  Offset into flash
     * @parm i_byteSize  Number of bytes in range
     *
     * @return Number of full or partial erase blocks 
     */
    uint32_t getNumAffectedBlocks(uint32_t i_address,
                                  size_t i_byteSize)
    {
        uint32_t blocks = 0;
        uint32_t addr = i_address;
        while( findEraseBlock(addr) < (i_address+i_byteSize) )
        {
            blocks++;
            addr += ERASESIZE_BYTES;
        }
        return blocks;
    };


    /**
     * @brief  ECCB Control Register Layout
     */
    union ControlReg_t
    {
        uint64_t data64;
        struct
        {
            // unused sections should be set to zero
            uint64_t magic1      : 8;  /**< 0:7 = b11010100 per spec */
            uint64_t unused1     : 7;  /**< 8:14 */
            uint64_t read_op     : 1;  /**< 15 = set for read operation */
            uint64_t unused2     : 7;  /**< 16:22 */
            uint64_t addr_len    : 3;  /**< 23:25 = 100 means 4 byte */
            uint64_t unused3     : 6;  /**< 26:31 */
            uint64_t address     : 32; /**< 32:63 = LPC Address */
        };

        ControlReg_t() : data64(LPC_CTL_REG_DEFAULT) {};            
    };

    /**
     * @brief  ECCB Status Register Layout
     */
    union StatusReg_t
    {
        uint64_t data64;
        struct
        {
            uint64_t pib_errors  : 6;  /**< 0:5  */
            uint64_t read_data   : 32; /**< 6:37 */
            uint64_t unused1     : 6;  /**< 38:43 */
            uint64_t busy        : 1;  /**< 44 = Operation Busy */
            uint64_t errors1     : 7;  /**< 45:51 */
            uint64_t op_done     : 1;  /**< 52 */
            uint64_t errors2     : 3;  /**< 53:55 */
            uint64_t unused2     : 8;  /**< 56:63 */
        };
        StatusReg_t() : data64(0) {};
    };

  private: // Variables
    /**
     * @brief Mutex to prevent concurrent PNOR accesses
     */
    mutex_t iv_mutex;

    /**
     * @brief Track PNOR erases for wear monitoring
     *    (making this static so tools can find it easier)
     *    track writes by page (=erase block)
     */
    uint8_t iv_erases[PNORSIZE/ERASESIZE_BYTES];

    /** 
     * @brief Determine how much of the PNOR logic to use,
     *   this is required due to different model functionality
     *   in the current VPO and Simics models
     */
    PnorMode_t iv_mode;

    // Needed for testcases
    friend class PnorDdTest;
};


#endif
OpenPOWER on IntegriCloud