3737#include <proto/alib.h>
3838#include <dos/dosextens.h>
3939#include "../../ndkcompat.h"
40-
41- /* Use mirrored addresses for writes to work around 68030 write-allocation bug.
42- * The MMU must be configured to make the write-mirror non-cacheable.
43- * For now however, we disable write-allocation instead of using different
44- * addresses.
45- */
46- #define SPI_PORT_READ_HOLD_OFFS 0x7FFFE0
47- #define SPI_PORT_READ_END_OFFS 0x7FFFF0
48- #define SPI_PORT_WRITE_HOLD_OFFS 0x7FFFC0
49- #define SPI_PORT_WRITE_END_OFFS 0x7FFFD0
40+ #include "spi.h"
5041
5142/* A4092 board identification (matches main.c) */
5243#define MANUF_ID_COMMODORE_BRAUNSCHWEIG 513
@@ -64,256 +55,6 @@ struct ConfigDev* FindConfigDev(struct ConfigDev*, LONG, LONG);
6455
6556#define MAX_TRANSFER_SIZE (8 * 1024 * 1024)
6657
67- static inline uint32_t pack_nibble_word (uint8_t b )
68- {
69- uint32_t hi = ((uint32_t )((b >> 4 ) & 0x0F )) << 28 ; /* bits 28..31 */
70- uint32_t lo = ((uint32_t )( b & 0x0F )) << 12 ; /* bits 12..15 */
71- return hi | lo ;
72- }
73- static inline uint8_t unpack_nibble_word (uint32_t w )
74- {
75- uint8_t hi = (uint8_t )((w >> 28 ) & 0x0F );
76- uint8_t lo = (uint8_t )((w >> 12 ) & 0x0F );
77- return (uint8_t )((hi << 4 ) | lo );
78- }
79- static inline void mmio_write_hold (uint32_t base , uint8_t v )
80- {
81- volatile uint32_t * p = (volatile uint32_t * )(uintptr_t )(base + SPI_PORT_WRITE_HOLD_OFFS );
82- * p = pack_nibble_word (v );
83- }
84- static inline void mmio_write_end (uint32_t base , uint8_t v )
85- {
86- volatile uint32_t * p = (volatile uint32_t * )(uintptr_t )(base + SPI_PORT_WRITE_END_OFFS );
87- * p = pack_nibble_word (v );
88- }
89- static inline uint8_t mmio_read_hold (uint32_t base )
90- {
91- volatile uint32_t * p = (volatile uint32_t * )(uintptr_t )(base + SPI_PORT_READ_HOLD_OFFS );
92- uint32_t w = * p ;
93- return unpack_nibble_word (w );
94- }
95- static inline uint8_t mmio_read_end (uint32_t base )
96- {
97- volatile uint32_t * p = (volatile uint32_t * )(uintptr_t )(base + SPI_PORT_READ_END_OFFS );
98- uint32_t w = * p ;
99- return unpack_nibble_word (w );
100- }
101-
102- /* ===== SPI commands / constants ===== */
103- enum {
104- SPI_CMD_WREN = 0x06 ,
105- SPI_CMD_RDSR1 = 0x05 ,
106- SPI_CMD_WRSR = 0x01 ,
107- SPI_CMD_RDSR2 = 0x35 ,
108- SPI_CMD_RDID = 0x9F ,
109- SPI_CMD_READ = 0x03 ,
110- SPI_CMD_PP = 0x02 ,
111- SPI_CMD_SE_4K = 0x20 ,
112- SPI_CMD_BE64K = 0xD8 ,
113- };
114-
115- #define SR1_WIP 0x01
116- #define SR1_WEL 0x02
117- #define SR1_BP_MASK 0x1C
118-
119- #define SR2_QE 0x02
120-
121- #define SPI_PAGE_SIZE 256u
122- #define SPI_BLOCK_SIZE 65536u
123-
124- /* ===== low-level bytewise helpers ===== */
125- static inline void spi_tx_hold (uint32_t base , uint8_t v ) { mmio_write_hold (base , v ); }
126- static inline void spi_tx_end (uint32_t base , uint8_t v ) {
127- mmio_write_end (base , v );
128- //for (volatile int i = 0; i < 200; i++);
129- }
130- static inline uint8_t spi_rx_hold (uint32_t base ) {
131- return mmio_read_hold (base );
132- }
133- static inline uint8_t spi_rx_end (uint32_t base ) {
134- return mmio_read_end (base );
135- }
136-
137- /* ===== status / waits ===== */
138- static void spi_write_enable (uint32_t base )
139- {
140- spi_tx_end (base , SPI_CMD_WREN );
141- }
142- static uint8_t spi_read_sr1 (uint32_t base )
143- {
144- spi_tx_hold (base , SPI_CMD_RDSR1 );
145- return spi_rx_end (base );
146- }
147- static uint8_t spi_read_sr2 (uint32_t base )
148- {
149- spi_tx_hold (base , SPI_CMD_RDSR2 );
150- return spi_rx_end (base );
151- }
152- static bool spi_wait_wip_clear (uint32_t base , uint32_t max_iters )
153- {
154- const uint32_t settle_polls = 4096 ;
155- bool saw_wip = false;
156-
157- /* Allow the SPI engine to finish clocking the opcode before polling aggressively. */
158- for (uint32_t i = 0 ; i < settle_polls ; ++ i ) {
159- uint8_t sr = spi_read_sr1 (base );
160- if (sr & SR1_WIP ) {
161- saw_wip = true;
162- break ;
163- }
164- }
165-
166- if (!saw_wip ) {
167- fprintf (stderr , "SPI: command never asserted WIP (SR1=%02X)\n" , spi_read_sr1 (base ));
168- return false;
169- }
170-
171- for (uint32_t i = 0 ; i < max_iters ; ++ i ) {
172- if ((spi_read_sr1 (base ) & SR1_WIP ) == 0 ) {
173- return true;
174- }
175- }
176-
177- fprintf (stderr , "SPI: timeout waiting for WIP clear (SR1=%02X)\n" , spi_read_sr1 (base ));
178- return false;
179- }
180- static bool spi_wait_wel_set (uint32_t base , uint32_t max_iters )
181- {
182- while (max_iters -- ) {
183- if (spi_read_sr1 (base ) & SR1_WEL ) {
184- return true;
185- }
186- }
187- return false;
188- }
189-
190- static bool spi_write_status (uint32_t base , uint8_t sr1 , uint8_t sr2 )
191- {
192- spi_write_enable (base );
193- if (!spi_wait_wel_set (base , 1000 )) {
194- fprintf (stderr , "ERROR: WEL not set before status write\n" );
195- return false;
196- }
197- spi_tx_hold (base , SPI_CMD_WRSR );
198- spi_tx_hold (base , sr1 );
199- spi_tx_end (base , sr2 );
200- if (!spi_wait_wip_clear (base , 100000 )) {
201- fprintf (stderr , "ERROR: status register write timed out\n" );
202- return false;
203- }
204- return true;
205- }
206-
207- static bool spi_clear_block_protect (uint32_t base )
208- {
209- uint8_t sr1 = spi_read_sr1 (base );
210- uint8_t sr2 = spi_read_sr2 (base );
211- if ((sr1 & SR1_BP_MASK ) == 0 ) {
212- return true;
213- }
214- uint8_t new_sr1 = sr1 & (uint8_t )~SR1_BP_MASK ;
215- printf ("SPI flash reports block protection (SR1=%02X SR2=%02X); clearing BP bits...\n" , sr1 , sr2 );
216- if (!spi_write_status (base , new_sr1 , sr2 )) {
217- fprintf (stderr , "ERROR: failed to clear block protection bits\n" );
218- return false;
219- }
220- uint8_t verify1 = spi_read_sr1 (base );
221- uint8_t verify2 = spi_read_sr2 (base );
222- if (verify1 & SR1_BP_MASK ) {
223- fprintf (stderr , "ERROR: block protection bits still set (SR1=%02X SR2=%02X). Check WP# pin.\n" , verify1 , verify2 );
224- return false;
225- }
226- return true;
227- }
228-
229- /* ===== single ops ===== */
230- static void spi_read_id (uint32_t base , uint8_t * mfg , uint8_t * type , uint8_t * cap )
231- {
232- spi_tx_hold (base , SPI_CMD_RDID );
233- if (mfg ) * mfg = spi_rx_hold (base );
234- if (type ) * type = spi_rx_hold (base );
235- if (cap ) * cap = spi_rx_end (base );
236- }
237- static bool spi_block_erase (uint32_t base , uint32_t baddr )
238- {
239- spi_write_enable (base );
240- if (!spi_wait_wel_set (base , 1000 )) {
241- fprintf (stderr , "ERROR: WEL not set before erase at 0x%08lX\n" , (unsigned long )baddr );
242- return false;
243- }
244- spi_tx_hold (base , SPI_CMD_BE64K );
245- spi_tx_hold (base , (baddr >> 16 ) & 0xFF );
246- spi_tx_hold (base , (baddr >> 8 ) & 0xFF );
247- spi_tx_end (base , baddr & 0xFF );
248- return spi_wait_wip_clear (base , 2000000 );
249- }
250- static bool spi_sector_erase_4k (uint32_t base , uint32_t baddr )
251- {
252- spi_write_enable (base );
253- if (!spi_wait_wel_set (base , 1000 )) {
254- fprintf (stderr , "ERROR: WEL not set before 4K erase at 0x%08lX\n" , (unsigned long )baddr );
255- return false;
256- }
257- spi_tx_hold (base , SPI_CMD_SE_4K );
258- spi_tx_hold (base , (baddr >> 16 ) & 0xFF );
259- spi_tx_hold (base , (baddr >> 8 ) & 0xFF );
260- spi_tx_end (base , baddr & 0xFF );
261- return spi_wait_wip_clear (base , 2000000 );
262- }
263-
264- static bool spi_page_program (uint32_t base , uint32_t addr , const uint8_t * data , size_t len )
265- {
266- if (!len || len > SPI_PAGE_SIZE ) return false;
267- spi_write_enable (base );
268- if (!spi_wait_wel_set (base , 1000 )) {
269- fprintf (stderr , "ERROR: WEL not set before program at 0x%08lX\n" , (unsigned long )addr );
270- return false;
271- }
272- spi_tx_hold (base , SPI_CMD_PP );
273- spi_tx_hold (base , (addr >> 16 ) & 0xFF );
274- spi_tx_hold (base , (addr >> 8 ) & 0xFF );
275- spi_tx_hold (base , addr & 0xFF );
276- for (size_t i = 0 ; i + 1 < len ; ++ i ) spi_tx_hold (base , data [i ]);
277- spi_tx_end (base , data [len - 1 ]);
278- return spi_wait_wip_clear (base , 100000 );
279- }
280-
281- /* ===== streaming helpers ===== */
282- static bool spi_read_buf (uint32_t base , uint32_t addr , uint8_t * out , size_t len )
283- {
284- if (!len ) return true;
285- spi_tx_hold (base , SPI_CMD_READ );
286- spi_tx_hold (base , (addr >> 16 ) & 0xFF );
287- spi_tx_hold (base , (addr >> 8 ) & 0xFF );
288- spi_tx_hold (base , addr & 0xFF );
289- for (size_t i = 0 ; i + 1 < len ; ++ i ) out [i ] = spi_rx_hold (base );
290- out [len - 1 ] = spi_rx_end (base );
291- return true;
292- }
293- static bool spi_write_buf_pagewise (uint32_t base , uint32_t addr , const uint8_t * in , size_t len )
294- {
295- while (len ) {
296- uint32_t page_off = addr & (SPI_PAGE_SIZE - 1 );
297- uint32_t page_room = SPI_PAGE_SIZE - page_off ;
298- size_t chunk = len < page_room ? len : page_room ;
299- if (!spi_page_program (base , addr , in , chunk )) return false;
300- addr += chunk ; in += chunk ; len -= chunk ;
301- }
302- return true;
303- }
304- static bool spi_erase_range_blocks (uint32_t base , uint32_t addr , size_t len ,
305- void (* progress )(size_t done , size_t total ))
306- {
307- uint32_t start = (addr / SPI_BLOCK_SIZE ) * SPI_BLOCK_SIZE ;
308- uint32_t end = ((addr + (uint32_t )len - 1 ) / SPI_BLOCK_SIZE ) * SPI_BLOCK_SIZE ;
309- size_t total = (end - start ) / SPI_BLOCK_SIZE + 1 , k = 0 ;
310- for (uint32_t b = start ; b <= end ; b += SPI_BLOCK_SIZE , ++ k ) {
311- if (progress ) progress (k , total );
312- if (!spi_block_erase (base , b )) return false;
313- }
314- if (progress ) progress (total , total );
315- return true;
316- }
31758static bool spi_verify_buf (uint32_t base , uint32_t addr , const uint8_t * ref , size_t len ,
31859 void (* progress )(size_t done , size_t total ))
31960{
@@ -806,6 +547,7 @@ static void usage(const char *argv0)
806547 printf (
807548"Usage:\n"
808549" %s id\n"
550+ " %s uid\n"
809551" %s read <addr> <len> <outfile>\n"
810552" %s erase <addr> <len> [--verify]\n"
811553" %s write <addr> <infile> [--verify]\n"
@@ -823,7 +565,7 @@ static void usage(const char *argv0)
823565" - 'writemfg' programs manufacturing data from a config file.\n"
824566" - 'readmfg' reads manufacturing data from flash to a config file.\n"
825567" - 'dumpmfg' displays manufacturing data on the console.\n"
826- , argv0 , argv0 , argv0 , argv0 , argv0 , argv0 , argv0 , argv0 , argv0 , argv0 );
568+ , argv0 , argv0 , argv0 , argv0 , argv0 , argv0 , argv0 , argv0 , argv0 , argv0 , argv0 );
827569}
828570
829571/* parse CSV of hex bytes for 'patch' */
@@ -891,6 +633,15 @@ static int cmd_id(uint32_t base)
891633 return 0 ;
892634}
893635
636+ static int cmd_uid (uint32_t base )
637+ {
638+ uint8_t id [8 ];
639+ spi_read_unique_id (base , id );
640+ printf ("Unique ID: %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n" ,
641+ id [0 ], id [1 ], id [2 ], id [3 ], id [4 ], id [5 ], id [6 ], id [7 ]);
642+ return 0 ;
643+ }
644+
894645static int cmd_read (uint32_t base , uint32_t addr , size_t len , const char * outfile )
895646{
896647 if (len > MAX_TRANSFER_SIZE ) { fprintf (stderr ,"len too large (max %d)\n" , MAX_TRANSFER_SIZE ); return 2 ; }
@@ -932,7 +683,7 @@ static int cmd_write(uint32_t base, const char *saddr, const char *infile, bool
932683 size_t done = 0 ;
933684 while (done < len ) {
934685 size_t chunk = (len - done ) > 4096 ? 4096 : (len - done );
935- if (!spi_write_buf_pagewise (base , addr + done , img + done , chunk )) {
686+ if (!spi_write_buf_pagewise (base , addr + done , img + done , chunk , NULL )) {
936687 fprintf (stderr ,"program failed at +0x%zx\n" , done );
937688 free (img ); return 3 ;
938689 }
@@ -970,7 +721,7 @@ static int cmd_patch(uint32_t base, const char *saddr, const char *hexlist)
970721 if (!parse_hexbytes (hexlist , & bytes , & n ) || n == 0 ) { free (bytes ); fprintf (stderr ,"bad hex bytes\n" ); return 2 ; }
971722 if (!spi_clear_block_protect (base )) { free (bytes ); return 3 ; }
972723 printf ("Patching %zu byte(s) at 0x%08lX (assumes erased)...\n" , n , (unsigned long )addr );
973- bool ok = spi_write_buf_pagewise (base , addr , bytes , n );
724+ bool ok = spi_write_buf_pagewise (base , addr , bytes , n , NULL );
974725 free (bytes );
975726 if (!ok ) { fprintf (stderr ,"patch failed\n" ); return 3 ; }
976727 printf ("Patch OK\n" ); return 0 ;
@@ -1039,7 +790,7 @@ static int cmd_writemfg(uint32_t base, const char *config_file)
1039790 }
1040791
1041792 /* Write 256 bytes */
1042- if (!spi_write_buf_pagewise (base , MFG_FLASH_OFFSET , (const uint8_t * )& mfg , sizeof (mfg ))) {
793+ if (!spi_write_buf_pagewise (base , MFG_FLASH_OFFSET , (const uint8_t * )& mfg , sizeof (mfg ), NULL )) {
1043794 fprintf (stderr , "ERROR: flash write failed\n" );
1044795 return 3 ;
1045796 }
@@ -1154,6 +905,9 @@ int main(int argc, char **argv)
1154905 if (strcmp (cmd ,"id" )== 0 ) {
1155906 return cmd_id (base );
1156907 }
908+ else if (strcmp (cmd ,"uid" )== 0 ) {
909+ return cmd_uid (base );
910+ }
1157911 else if (strcmp (cmd ,"read" )== 0 ) {
1158912 if (argi + 2 >= argc ) { usage (argv [0 ]); return 1 ; }
1159913 uint32_t addr ; size_t len ;
0 commit comments