Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
SPIFlash.cxx
Go to the documentation of this file.
1
36//#define LOGLEVEL INFO
37
39
40#include <fcntl.h>
41#include <spi/spidev.h>
42#include <stropts.h>
43#include <sys/stat.h>
44#include <sys/types.h>
45#include <unistd.h>
46
47#include "os/OS.hxx"
48#include "utils/logging.h"
49
53{
54public:
56 : mu_(mu)
57 {
58 if (mu_)
59 {
60 mu_->lock();
61 }
62 }
63
65 {
66 if (mu_)
67 {
68 mu_->unlock();
69 }
70 }
71
72private:
73 OSMutex *mu_;
74};
75
76#define LockIfExists(l) int error_omitted_mutex_lock_variable[-1]
77
78void SPIFlash::init(const char *dev_name)
79{
80 spiFd_ = ::open(dev_name, O_RDWR);
81 HASSERT(spiFd_ >= 0);
82
83 uint8_t spi_bpw = 8;
84 int ret;
85 ret = ::ioctl(spiFd_, SPI_IOC_WR_MODE, &cfg_->spiMode_);
86 HASSERT(ret == 0);
87 ret = ::ioctl(spiFd_, SPI_IOC_WR_BITS_PER_WORD, &spi_bpw);
88 HASSERT(ret == 0);
89 ret = ::ioctl(spiFd_, SPI_IOC_WR_MAX_SPEED_HZ, &cfg_->speedHz_);
90 HASSERT(ret == 0);
91}
92
93void SPIFlash::get_id(char id_out[3])
94{
96 struct spi_ioc_transfer xfer[2] = {0, 0};
97 xfer[0].tx_buf = (uintptr_t)&cfg_->idCommand_;
98 xfer[0].len = 1;
99 xfer[1].rx_buf = (uintptr_t)id_out;
100 xfer[1].len = 3;
101 xfer[1].cs_change = true;
102 ::ioctl(spiFd_, SPI_IOC_MESSAGE(2), xfer);
103}
104
105void SPIFlash::read(uint32_t addr, void *buf, size_t len)
106{
108 struct spi_ioc_transfer xfer[2] = {0, 0};
109 uint8_t rdreq[5];
110 rdreq[0] = cfg_->readCommand_;
111 rdreq[1] = (addr >> 16) & 0xff;
112 rdreq[2] = (addr >> 8) & 0xff;
113 rdreq[3] = (addr)&0xff;
114 rdreq[4] = 0;
115 xfer[0].tx_buf = (uintptr_t)rdreq;
116 xfer[0].len = 4 + cfg_->readNeedsStuffing_;
117 xfer[1].rx_buf = (uintptr_t)buf;
118 xfer[1].len = len;
119 xfer[1].cs_change = true;
120 ::ioctl(spiFd_, SPI_IOC_MESSAGE(2), xfer);
121
122 auto db = (const uint8_t *)buf;
123 LOG(INFO, "read [%x]=%02x%02x%02x%02x, %u bytes success", (unsigned)addr,
124 db[0], db[1], db[2], db[3], len);
125}
126
127void SPIFlash::write(uint32_t addr, const void *buf, size_t size_bytes)
128{
130 const size_t page_size = (~cfg_->pageSizeMask_) + 1;
131 const uint8_t *d = (const uint8_t *)buf;
132 while (size_bytes)
133 {
134 size_t len = std::min(page_size, size_bytes);
135 if ((addr & cfg_->pageSizeMask_) !=
136 ((addr + len - 1) & cfg_->pageSizeMask_))
137 {
138 // Anything that overflows to the next page we don't write now.
139 len -= (addr + len) & ~cfg_->pageSizeMask_;
140 }
141
142 HASSERT((addr & cfg_->pageSizeMask_) ==
143 ((addr + len - 1) & cfg_->pageSizeMask_));
144
145 struct spi_ioc_transfer xfer[3] = {0, 0, 0};
146 uint8_t wreq[4];
147 wreq[0] = cfg_->writeCommand_;
148 wreq[1] = (addr >> 16) & 0xff;
149 wreq[2] = (addr >> 8) & 0xff;
150 wreq[3] = addr & 0xff;
151 xfer[0].tx_buf = (uintptr_t)&cfg_->writeEnableCommand_;
152 xfer[0].len = 1;
153 xfer[0].cs_change = true;
154 xfer[1].tx_buf = (uintptr_t)wreq;
155 xfer[1].len = 4;
156 xfer[2].tx_buf = (uintptr_t)d;
157 xfer[2].len = len;
158 xfer[2].cs_change = true;
159 ::ioctl(spiFd_, SPI_IOC_MESSAGE(3), xfer);
160
161 unsigned waitcount = wait_for_write();
162 LOG(VERBOSE,
163 "write [%x]=%02x%02x%02x%02x, %u bytes success after %u iter",
164 (unsigned)addr, d[0], d[1], d[2], d[3], len, waitcount);
165
166 size_bytes -= len;
167 addr += len;
168 d += len;
169 }
170}
171
173{
174 // Now we wait for the write to be complete.
175 unsigned waitcount = 0;
176 while (true)
177 {
178 struct spi_ioc_transfer sxfer = {0};
179 uint8_t streq[2];
180 streq[0] = cfg_->statusReadCommand_;
181 streq[1] = 0xFF;
182 sxfer.tx_buf = (uintptr_t)streq;
183 sxfer.rx_buf = (uintptr_t)streq;
184 sxfer.len = 2;
185 sxfer.cs_change = true;
186 ::ioctl(spiFd_, SPI_IOC_MESSAGE(1), &sxfer);
187
188 if ((streq[1] & cfg_->statusWritePendingBit_) == 0)
189 {
190 return waitcount;
191 }
192 waitcount++;
193 }
194}
195
196void SPIFlash::erase(uint32_t addr, size_t len)
197{
198 size_t end = addr + len;
199 while (addr < end)
200 {
201 struct spi_ioc_transfer xfer[2] = {0, 0};
202 uint8_t ereq[4];
203 ereq[0] = cfg_->eraseCommand_;
204 ereq[1] = (addr >> 16) & 0xff;
205 ereq[2] = (addr >> 8) & 0xff;
206 ereq[3] = (addr)&0xff;
207 xfer[0].tx_buf = (uintptr_t)&cfg_->writeEnableCommand_;
208 xfer[0].len = 1;
209 xfer[0].cs_change = true;
210 xfer[1].tx_buf = (uintptr_t)ereq;
211 xfer[1].len = 4;
212 xfer[1].cs_change = true;
213
214 ::ioctl(spiFd_, SPI_IOC_MESSAGE(2), &xfer);
215
216 unsigned waitcount = wait_for_write();
217 LOG(INFO, "erase at %x, success after %u iter", (unsigned)addr,
218 waitcount);
219
220 addr += cfg_->sectorSize_;
221 }
222}
223
225{
226 struct spi_ioc_transfer xfer[2] = {0, 0};
227 xfer[0].tx_buf = (uintptr_t)&cfg_->writeEnableCommand_;
228 xfer[0].len = 1;
229 xfer[0].cs_change = true;
230 xfer[1].tx_buf = (uintptr_t)&cfg_->chipEraseCommand_;
231 xfer[1].len = 1;
232 xfer[1].cs_change = true;
233
234 ::ioctl(spiFd_, SPI_IOC_MESSAGE(2), &xfer);
235
236 unsigned waitcount = wait_for_write();
237 LOG(INFO, "chip-erase, success after %u iter", waitcount);
238}
int ioctl(int fd, unsigned long int key,...)
Request and ioctl transaction.
Definition Fileio.cxx:452
Conditional OSMutexLock which can handle a nullptr as mutex (in which case it does not lock anything)...
Definition SPIFlash.cxx:53
This class provides a mutex API.
Definition OS.hxx:427
void lock()
Lock a mutex.
Definition OS.hxx:446
void unlock()
Unlock a mutex.
Definition OS.hxx:453
void write(uint32_t addr, const void *buf, size_t len)
Performs write to the device.
Definition SPIFlash.cxx:127
void init(const char *dev_name)
Opens the SPI bus.
Definition SPIFlash.cxx:78
void chip_erase()
Erases the entire device.
Definition SPIFlash.cxx:224
void get_id(char id_out[3])
Fetches the identification bytes form the SPIFlash.
Definition SPIFlash.cxx:93
void read(uint32_t addr, void *buf, size_t len)
Reads data from the device.
Definition SPIFlash.cxx:105
void erase(uint32_t addr, size_t len)
Erases sector(s) of the device.
Definition SPIFlash.cxx:196
OSMutex * lock_
Lock that protects accesses to the flash chip.
Definition SPIFlash.hxx:173
int spiFd_
File descriptor for the opened SPI bus.
Definition SPIFlash.hxx:176
const SPIFlashConfig * cfg_
Configuration.
Definition SPIFlash.hxx:170
unsigned wait_for_write()
Waits until write is complete.
Definition SPIFlash.cxx:172
#define LOG(level, message...)
Conditionally write a message to the logging output.
Definition logging.h:99
static const int VERBOSE
Loglevel that is usually not printed, reporting debugging information.
Definition logging.h:59
static const int INFO
Loglevel that is printed by default, reporting some status information.
Definition logging.h:57
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
uint8_t statusReadCommand_
Command to use for status register read.
Definition SPIFlash.hxx:92
uint8_t chipEraseCommand_
Command to use for chip erase.
Definition SPIFlash.hxx:89
uint8_t spiMode_
SPI mode to use.
Definition SPIFlash.hxx:76
uint32_t pageSizeMask_
A page program operation might wrap around a page.
Definition SPIFlash.hxx:73
uint8_t readNeedsStuffing_
Set this to 1 if the read command needs a dummy byte after the address.
Definition SPIFlash.hxx:98
uint8_t statusWritePendingBit_
Which bit to check in the status register for write complete.
Definition SPIFlash.hxx:95
uint32_t speedHz_
Use this frequency to talk to SPI.
Definition SPIFlash.hxx:61
uint8_t writeCommand_
Command to use for writes.
Definition SPIFlash.hxx:85
uint32_t sectorSize_
How many bytes is an erase sector.
Definition SPIFlash.hxx:64
uint8_t idCommand_
Command to use for get identification bytes.
Definition SPIFlash.hxx:79
uint8_t readCommand_
Command to use for reads.
Definition SPIFlash.hxx:81
uint8_t writeEnableCommand_
Command sent out before each write/erase command.
Definition SPIFlash.hxx:83
uint8_t eraseCommand_
Command to use for sector erases.
Definition SPIFlash.hxx:87