@@ -62,76 +62,96 @@ int I2CEEBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size)
6262 // Check the address and size fit onto the chip.
6363 MBED_ASSERT (is_valid_read (addr, size));
6464
65- _i2c-> start ( );
65+ auto *charBuffer = reinterpret_cast < char *>(buffer );
6666
67- if (!_i2c-> write (_i2c_addr | 0 )) {
68- return BD_ERROR_DEVICE_ERROR;
69- }
67+ auto const handler = [&]( const bd_addr_t &pagedStart, const bd_size_t &pagedLength, const uint8_t &page) -> int
68+ {
69+ _i2c-> start ();
7070
71- if (!_address_is_eight_bit && !_i2c->write ((char )(addr >> 8 ))) {
72- return BD_ERROR_DEVICE_ERROR;
73- }
71+ auto const pagedDeviceAddress = get_paged_device_address (page);
7472
75- if (!_i2c->write (( char )(addr & 0xff ) )) {
76- return BD_ERROR_DEVICE_ERROR;
77- }
73+ if (!_i2c->write (pagedDeviceAddress )) {
74+ return BD_ERROR_DEVICE_ERROR;
75+ }
7876
79- _i2c->stop ();
77+ if (!_address_is_eight_bit && !_i2c->write ((char )(pagedStart >> 8u ))) {
78+ return BD_ERROR_DEVICE_ERROR;
79+ }
8080
81- auto err = _sync ();
82- if (err) {
83- return err;
84- }
81+ if (!_i2c->write ((char )(pagedStart & 0xffu ))) {
82+ return BD_ERROR_DEVICE_ERROR;
83+ }
8584
86- if (0 != _i2c->read (_i2c_addr, static_cast <char *>(buffer), size)) {
87- return BD_ERROR_DEVICE_ERROR;
88- }
85+ _i2c->stop ();
8986
90- return 0 ;
87+ auto err = _sync ();
88+ if (err) {
89+ return err;
90+ }
91+
92+ if (0 != _i2c->read (_i2c_addr, charBuffer, pagedLength)) {
93+ return BD_ERROR_DEVICE_ERROR;
94+ }
95+
96+ charBuffer += size;
97+
98+ return BD_ERROR_OK;
99+ };
100+
101+ return do_paged (addr, size, handler);
91102}
92103
93104int I2CEEBlockDevice::program (const void *buffer, bd_addr_t addr, bd_size_t size)
94105{
95106 // Check the addr and size fit onto the chip.
96107 MBED_ASSERT (is_valid_program (addr, size));
97108
98- // While we have some more data to write.
99- while (size > 0 ) {
100- uint32_t off = addr % _block;
101- uint32_t chunk = (off + size < _block) ? size : (_block - off);
109+ auto const *charBuffer = reinterpret_cast <char const *>(buffer);
102110
103- _i2c->start ();
111+ auto const handler = [&](const bd_addr_t &pagedStart, const bd_size_t &pagedLength, const uint8_t &page) -> int
112+ {
113+ // While we have some more data to write.
114+ while (size > 0 ) {
115+ uint32_t off = addr % _block;
116+ uint32_t chunk = (off + size < _block) ? size : (_block - off);
104117
105- if (!_i2c->write (_i2c_addr | 0 )) {
106- return BD_ERROR_DEVICE_ERROR;
107- }
118+ _i2c->start ();
108119
109- if (!_address_is_eight_bit && !_i2c->write ((char )(addr >> 8 ))) {
110- return BD_ERROR_DEVICE_ERROR;
111- }
120+ auto const pagedDeviceAddress = get_paged_device_address (page);
112121
113- if (!_i2c->write (( char )(addr & 0xff ) )) {
114- return BD_ERROR_DEVICE_ERROR;
115- }
122+ if (!_i2c->write (pagedDeviceAddress )) {
123+ return BD_ERROR_DEVICE_ERROR;
124+ }
116125
117- for ( unsigned i = 0 ; i < chunk; i++ ) {
118- _i2c-> write ( static_cast < const char *>(buffer)[i]) ;
119- }
126+ if (!_address_is_eight_bit && !_i2c-> write (( char )(pagedStart >> 8u )) ) {
127+ return BD_ERROR_DEVICE_ERROR ;
128+ }
120129
121- _i2c->stop ();
130+ if (!_i2c->write ((char )(addr & 0xffu ))) {
131+ return BD_ERROR_DEVICE_ERROR;
132+ }
122133
123- int err = _sync ();
134+ for (unsigned i = 0 ; i < chunk; i++) {
135+ _i2c->write (charBuffer[i]);
136+ }
124137
125- if (err) {
126- return err;
138+ _i2c->stop ();
139+
140+ int err = _sync ();
141+
142+ if (err) {
143+ return err;
144+ }
145+
146+ addr += chunk;
147+ size -= chunk;
148+ charBuffer += chunk;
127149 }
128150
129- addr += chunk;
130- size -= chunk;
131- buffer = static_cast <const char *>(buffer) + chunk;
132- }
151+ return BD_ERROR_OK;
152+ };
133153
134- return 0 ;
154+ return do_paged (addr, size, handler) ;
135155}
136156
137157int I2CEEBlockDevice::erase (bd_addr_t addr, bd_size_t size)
@@ -180,3 +200,74 @@ const char *I2CEEBlockDevice::get_type() const
180200{
181201 return " I2CEE" ;
182202}
203+
204+ int I2CEEBlockDevice::do_paged (const bd_addr_t &startAddress,
205+ const bd_size_t &length,
206+ const paged_handler &handler)
207+ {
208+ // This helper is only used for eight bit mode.
209+ if (!this ->_address_is_eight_bit ) {
210+ return handler (startAddress, length, 0 );
211+ }
212+
213+ auto currentStartAddress = startAddress;
214+
215+ auto const pageSize = 256 ;
216+ bd_size_t lengthDone = 0 ;
217+ while (lengthDone != length)
218+ {
219+ /* Integer division => Round down */
220+ uint8_t const currentPage = currentStartAddress / 256 ;
221+ bd_addr_t const nextPageBegin = (currentPage + 1 ) * pageSize;
222+ bd_addr_t const currentReadEndAddressExclusive = std::min (nextPageBegin, startAddress + length);
223+ bd_size_t const currentLength = currentReadEndAddressExclusive - currentStartAddress;
224+ bd_addr_t const pagedBegin = currentStartAddress - (currentPage * pageSize);
225+
226+ auto const handlerReturn = handler (pagedBegin, currentLength, currentPage);
227+ if (handlerReturn != BD_ERROR_OK)
228+ {
229+ return handlerReturn;
230+ }
231+
232+ currentStartAddress = currentReadEndAddressExclusive;
233+ lengthDone += currentLength;
234+ }
235+
236+ return BD_ERROR_OK;
237+ }
238+
239+ uint8_t I2CEEBlockDevice::get_paged_device_address (const uint8_t &page)
240+ {
241+ if (!this ->_address_is_eight_bit )
242+ {
243+ return this ->_i2c_addr ;
244+ }
245+ else
246+ {
247+ // This method uses a dynamically created bit mask for the page given.
248+ // This ensures compatibility with all sizes of ICs.
249+ // E. g. the 512K variants have two user address bits and one page bit.
250+ // We don't want to forcefully override the two user address bits.
251+
252+ // Create a mask to cover all bits required to set page
253+ // i starts at one because the LSB is used for R/W in I2C
254+ uint8_t i = 1 ;
255+ uint8_t addressMask = 0 ;
256+ auto p = page;
257+ while (p != 0u )
258+ {
259+ addressMask |= (1u << i);
260+ p >>= 1u ;
261+ i++;
262+ }
263+
264+ uint8_t pagedDeviceAddress = this ->_i2c_addr & static_cast <uint8_t >(~addressMask);
265+ // Assert page < 0b111, because we don't have
266+ // more bits for page encoding
267+ // Don't actually write 0b111, this is a nonstandard extension.
268+ MBED_ASSERT (page < 0x7 );
269+ pagedDeviceAddress |= static_cast <uint8_t >(page << 1u );
270+
271+ return pagedDeviceAddress;
272+ }
273+ }
0 commit comments