summaryrefslogtreecommitdiff
path: root/memories/spi-flash/at26.c
blob: 4ac80c2852828e3e4da7885d706e0f2c3bb44ada (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
/* ----------------------------------------------------------------------------
 *         ATMEL Microcontroller Software Support
 * ----------------------------------------------------------------------------
 * Copyright (c) 2008, Atmel Corporation
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the disclaimer below.
 *
 * Atmel's name may not be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ----------------------------------------------------------------------------
 */

//------------------------------------------------------------------------------
//         Headers
//------------------------------------------------------------------------------

#include "at26.h"
#include <board.h>
#include <utility/assert.h>

//------------------------------------------------------------------------------
//         Internal definitions
//------------------------------------------------------------------------------

/// SPI clock frequency used in Hz.
#if defined (BOARD_AT26F004)
// SPI runs at a faster frequency for devices with smaller throughput
#define SPCK            10000000
#else
#define SPCK            1000000
#endif

/// SPI chip select configuration value.
#define CSR             (AT91C_SPI_NCPHA | \
                         SPID_CSR_DLYBCT(BOARD_MCK, 100) | \
                         SPID_CSR_DLYBS(BOARD_MCK, 5) | \
                         SPID_CSR_SCBR(BOARD_MCK, SPCK))

/// Number of recognized dataflash.
#define NUMDATAFLASH    (sizeof(at26Devices) / sizeof(At26Desc))

//------------------------------------------------------------------------------
//         Internal variables
//------------------------------------------------------------------------------

/// Array of recognized serial firmware dataflash chips.
static const At26Desc at26Devices[] = {
    // name, Jedec ID, size, page size, block size, block erase command
    {"AT25DF041A" , 0x0001441F, 512 * 1024 ,     256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    {"AT25DF161"  , 0x0002461F, 2 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    {"AT26F004"   , 0x0000041F,      512 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    {"AT26DF081A" , 0x0001451F, 1 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    {"AT26DF0161" , 0x0000461F, 2 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    {"AT26DF161A" , 0x0001461F, 2 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    {"AT26DF321" ,  0x0000471F, 4 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    {"AT25DF512B" , 0x0001651F, 64 * 1024,       256, 32 * 1024, AT26_BLOCK_ERASE_32K},
    {"AT25DF512B" , 0x0000651F, 64 * 1024,       256, 32 * 1024, AT26_BLOCK_ERASE_32K},
    {"AT25DF021"  , 0x0000431F, 256 * 1024,      256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    {"AT26DF641" ,  0x0000481F, 8 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    // Manufacturer: ST
    {"M25P05"     , 0x00102020,       64 * 1024, 256, 32 * 1024, AT26_BLOCK_ERASE_64K},
    {"M25P10"     , 0x00112020,      128 * 1024, 256, 32 * 1024, AT26_BLOCK_ERASE_64K},
    {"M25P20"     , 0x00122020,      256 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    {"M25P40"     , 0x00132020,      512 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    {"M25P80"     , 0x00142020, 1 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    {"M25P16"     , 0x00152020, 2 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    {"M25P32"     , 0x00162020, 4 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    {"M25P64"     , 0x00172020, 8 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    // Manufacturer: Windbond
    {"W25X10"     , 0x001130EF,      128 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    {"W25X20"     , 0x001230EF,      256 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    {"W25X40"     , 0x001330EF,      512 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    {"W25X80"     , 0x001430EF, 1 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    // Manufacturer: Macronix
    {"MX25L512"   , 0x001020C2,       64 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    {"MX25L3205"  , 0x001620C2, 4 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    {"MX25L6405"  , 0x001720C2, 8 * 1024 * 1024, 256, 64 * 1024, AT26_BLOCK_ERASE_64K},
    // Other
    {"SST25VF512" , 0x000048BF,       64 * 1024, 256, 32 * 1024, AT26_BLOCK_ERASE_32K}
};

//------------------------------------------------------------------------------
//         Exported functions
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
/// Initializes an AT26 driver instance with the given SPI driver and chip
/// select value.
/// \param pAt26  Pointer to an AT26 driver instance.
/// \param pSpid  Pointer to an SPI driver instance.
/// \param cs  Chip select value to communicate with the serial flash.
//------------------------------------------------------------------------------
void AT26_Configure(At26 *pAt26, Spid *pSpid, unsigned char cs)
{
    SpidCmd *pCommand;

    SANITY_CHECK(pAt26);
    SANITY_CHECK(pSpid);
    SANITY_CHECK(cs < 4);

    // Configure the SPI chip select for the serial flash
    SPID_ConfigureCS(pSpid, cs, CSR);

    // Initialize the AT26 fields
    pAt26->pSpid = pSpid;
    pAt26->pDesc = 0;

    // Initialize the command structure
    pCommand = &(pAt26->command);
    pCommand->pCmd = (unsigned char *) pAt26->pCmdBuffer;
    pCommand->callback = 0;
    pCommand->pArgument = 0;
    pCommand->spiCs = cs;
}

//------------------------------------------------------------------------------
/// Returns 1 if the serial flash driver is currently busy executing a command;
/// otherwise returns 0.
/// \param pAt26  Pointer to an At26 driver instance.
//------------------------------------------------------------------------------
unsigned char AT26_IsBusy(At26 *pAt26)
{
    return SPID_IsBusy(pAt26->pSpid);
}

//------------------------------------------------------------------------------
/// Sends a command to the serial flash through the SPI. The command is made up
/// of two parts: the first is used to transmit the command byte and optionally,
/// address and dummy bytes. The second part is the data to send or receive.
/// This function does not block: it returns as soon as the transfer has been
/// started. An optional callback can be invoked to notify the end of transfer.
/// Return 0 if successful; otherwise, returns AT26_ERROR_BUSY if the AT26
/// driver is currently executing a command, or AT26_ERROR_SPI if the command
/// cannot be sent because of a SPI error.
/// \param pAt26  Pointer to an At26 driver instance.
/// \param cmd  Command byte.
/// \param cmdSize  Size of command (command byte + address bytes + dummy bytes).
/// \param pData Data buffer.
/// \param dataSize  Number of bytes to send/receive.
/// \param address  Address to transmit.
/// \param callback  Optional user-provided callback to invoke at end of transfer.
/// \param pArgument  Optional argument to the callback function.
//------------------------------------------------------------------------------
unsigned char AT26_SendCommand(
    At26 *pAt26,
    unsigned char cmd,
    unsigned char cmdSize,
    unsigned char *pData,
    unsigned int dataSize,
    unsigned int address,
    SpidCallback callback,
    void *pArgument)

{
    SpidCmd *pCommand;

    SANITY_CHECK(pAt26);

    // Check if the SPI driver is available
    if (AT26_IsBusy(pAt26)) {

        return AT26_ERROR_BUSY;
    }

    // Store command and address in command buffer
    pAt26->pCmdBuffer[0] = (cmd & 0x000000FF)
                           | ((address & 0x0000FF) << 24)
                           | ((address & 0x00FF00) << 8)
                           | ((address & 0xFF0000) >> 8);

    // Update the SPI transfer descriptor
    pCommand = &(pAt26->command);
     pCommand->cmdSize = cmdSize;
     pCommand->pData = pData;
     pCommand->dataSize = dataSize;
     pCommand->callback = callback;
     pCommand->pArgument = pArgument;

     // Start the SPI transfer
     if (SPID_SendCommand(pAt26->pSpid, pCommand)) {

         return AT26_ERROR_SPI;
     }

     return 0;
}

//------------------------------------------------------------------------------
/// Tries to detect a serial firmware flash device given its JEDEC identifier.
/// The JEDEC id can be retrieved by sending the correct command to the device.
/// Returns the corresponding AT26 descriptor if found; otherwise returns 0.
/// \param pAt26  Pointer to an AT26 driver instance.
/// \param jedecId  JEDEC identifier of device.
//------------------------------------------------------------------------------
const At26Desc * AT26_FindDevice(At26 *pAt26, unsigned int jedecId)
{
    unsigned int i = 0;

    SANITY_CHECK(pAt26);

    // Search if device is recognized
    pAt26->pDesc = 0;
    while ((i < NUMDATAFLASH) && !(pAt26->pDesc)) {

        if (jedecId == at26Devices[i].jedecId) {

            pAt26->pDesc = &(at26Devices[i]);
        }

        i++;
    }

    return pAt26->pDesc;
}

personal git repositories of Harald Welte. Your mileage may vary