3535
3636#define DID_VID_ADDR 0xD40F00
3737
38+ static uint8_t mode_to_nbits (enum libhoth_spi_mode mode ) {
39+ switch (mode ) {
40+ case LIBHOTH_SPI_MODE_DUAL :
41+ return 2 ;
42+ case LIBHOTH_SPI_MODE_QUAD :
43+ return 4 ;
44+ case LIBHOTH_SPI_MODE_SINGLE :
45+ default :
46+ return 1 ;
47+ }
48+ }
49+
50+ static uint8_t mode_to_read_opcode (enum libhoth_spi_mode mode ) {
51+ switch (mode ) {
52+ case LIBHOTH_SPI_MODE_DUAL :
53+ return SPI_NOR_OPCODE_DUAL_READ ;
54+ case LIBHOTH_SPI_MODE_QUAD :
55+ return SPI_NOR_OPCODE_QUAD_READ ;
56+ case LIBHOTH_SPI_MODE_SINGLE :
57+ default :
58+ return SPI_NOR_OPCODE_SLOW_READ ;
59+ }
60+ }
61+
62+ static uint8_t mode_to_write_opcode (enum libhoth_spi_mode mode ) {
63+ switch (mode ) {
64+ case LIBHOTH_SPI_MODE_QUAD :
65+ return SPI_NOR_OPCODE_QUAD_PAGE_PROGRAM ;
66+ case LIBHOTH_SPI_MODE_SINGLE :
67+ case LIBHOTH_SPI_MODE_DUAL :
68+ default :
69+ return SPI_NOR_OPCODE_PAGE_PROGRAM ;
70+ }
71+ }
72+
3873static int spi_nor_address (uint8_t * buf , uint32_t address ,
3974 bool address_mode_4b ) {
4075 if (address_mode_4b ) {
@@ -128,7 +163,8 @@ static int spi_nor_write_enable(const int fd) {
128163 return LIBHOTH_OK ;
129164}
130165
131- static int spi_nor_write (int fd , bool address_mode_4b , unsigned int address ,
166+ static int spi_nor_write (int fd , bool address_mode_4b ,
167+ enum libhoth_spi_mode mode , unsigned int address ,
132168 const void * data , size_t data_len ,
133169 uint32_t device_busy_wait_timeout ,
134170 uint32_t device_busy_wait_check_interval ) {
@@ -147,7 +183,7 @@ static int spi_nor_write(int fd, bool address_mode_4b, unsigned int address,
147183 uint8_t rq_buf [5 ] = {0 }; // 1 for command opcode, 4 (max) for address
148184
149185 // Page Program OPCODE + Address
150- rq_buf [0 ] = SPI_NOR_OPCODE_PAGE_PROGRAM ;
186+ rq_buf [0 ] = mode_to_write_opcode ( mode ) ;
151187 int address_len = spi_nor_address (& rq_buf [1 ], address , address_mode_4b );
152188 xfer [0 ] = (struct spi_ioc_transfer ){
153189 .tx_buf = (unsigned long )rq_buf ,
@@ -163,6 +199,11 @@ static int spi_nor_write(int fd, bool address_mode_4b, unsigned int address,
163199 .len = chunk_send_size ,
164200 };
165201
202+ if (mode != LIBHOTH_SPI_MODE_SINGLE ) {
203+ uint8_t nbits = mode_to_nbits (mode );
204+ xfer [1 ].tx_nbits = nbits ;
205+ }
206+
166207 status = ioctl (fd , SPI_IOC_MESSAGE (2 ), xfer );
167208 if (status < 0 ) {
168209 return LIBHOTH_ERR_FAIL ;
@@ -180,15 +221,16 @@ static int spi_nor_write(int fd, bool address_mode_4b, unsigned int address,
180221 return LIBHOTH_OK ;
181222}
182223
183- static int spi_nor_read (int fd , bool address_mode_4b , unsigned int address ,
224+ static int spi_nor_read (int fd , bool address_mode_4b ,
225+ enum libhoth_spi_mode mode , unsigned int address ,
184226 void * data , size_t data_len ) {
185227 if (fd < 0 || !data || !data_len ) return LIBHOTH_ERR_INVALID_PARAMETER ;
186228
187229 uint8_t rd_request [5 ] = {0 };
188230 struct spi_ioc_transfer xfer [2 ] = {0 };
189231
190232 // Read OPCODE and mailbox address
191- rd_request [0 ] = SPI_NOR_OPCODE_SLOW_READ ;
233+ rd_request [0 ] = mode_to_read_opcode ( mode ) ;
192234 int address_len = spi_nor_address (& rd_request [1 ], address , address_mode_4b );
193235 xfer [0 ] = (struct spi_ioc_transfer ){
194236 .tx_buf = (unsigned long )rd_request ,
@@ -201,6 +243,11 @@ static int spi_nor_read(int fd, bool address_mode_4b, unsigned int address,
201243 .len = data_len ,
202244 };
203245
246+ if (mode != LIBHOTH_SPI_MODE_SINGLE ) {
247+ uint8_t nbits = mode_to_nbits (mode );
248+ xfer [1 ].rx_nbits = nbits ;
249+ }
250+
204251 int status = ioctl (fd , SPI_IOC_MESSAGE (2 ), xfer );
205252 if (status < 0 ) {
206253 return LIBHOTH_ERR_FAIL ;
@@ -281,6 +328,7 @@ int libhoth_spi_open(const struct libhoth_spi_device_init_options* options,
281328 spi_dev -> fd = fd ;
282329 spi_dev -> mailbox_address = options -> mailbox ;
283330 spi_dev -> address_mode_4b = true;
331+ spi_dev -> mode = options -> operation_mode ;
284332 spi_dev -> device_busy_wait_timeout = options -> device_busy_wait_timeout ;
285333 spi_dev -> device_busy_wait_check_interval =
286334 options -> device_busy_wait_check_interval ;
@@ -311,12 +359,52 @@ int libhoth_spi_open(const struct libhoth_spi_device_init_options* options,
311359 }
312360 }
313361
314- if (options -> mode ) {
315- const uint8_t mode = (uint8_t )options -> mode ;
316- if (ioctl (fd , SPI_IOC_WR_MODE , & mode ) < 0 ) {
362+ uint32_t mode = options -> mode ;
363+ if (ioctl (fd , SPI_IOC_RD_MODE32 , & spi_dev -> original_mode ) < 0 ) {
364+ status = LIBHOTH_ERR_FAIL ;
365+ goto err_out ;
366+ }
367+
368+ // TODO(michaelfield): Readback the SFDP and check that quadmode is
369+ // supported. If not, then we should fail unless the user explicitly
370+ // requested to force quadmode.
371+
372+ if (options -> operation_mode == LIBHOTH_SPI_MODE_QUAD ) {
373+ // Quadmode spi needs to use 32-bit mode flags
374+ mode |= (SPI_TX_QUAD | SPI_RX_QUAD );
375+
376+ if (ioctl (fd , SPI_IOC_WR_MODE32 , & mode ) < 0 ) {
317377 status = LIBHOTH_ERR_FAIL ;
318378 goto err_out ;
319379 }
380+ } else if (options -> operation_mode == LIBHOTH_SPI_MODE_DUAL ) {
381+ // Dualmode spi needs to use 32-bit mode flags
382+ mode |= (SPI_TX_DUAL | SPI_RX_DUAL );
383+
384+ if (ioctl (fd , SPI_IOC_WR_MODE32 , & mode ) < 0 ) {
385+ status = LIBHOTH_ERR_FAIL ;
386+ goto err_out ;
387+ }
388+ } else {
389+ // Set the mode anyways.
390+ // There is a failure mode wherein a bad mode setting will stick.
391+ // Even if mode is zero, we still want to write it.
392+ if (ioctl (fd , SPI_IOC_WR_MODE32 , & mode ) < 0 ) {
393+ status = LIBHOTH_ERR_FAIL ;
394+ goto err_out ;
395+ }
396+ }
397+
398+ // read back the mode, and verify that it is what we expect.
399+ uint32_t read_mode ;
400+ if (ioctl (fd , SPI_IOC_RD_MODE32 , & read_mode ) < 0 ) {
401+ status = LIBHOTH_ERR_FAIL ;
402+ goto err_out ;
403+ }
404+
405+ if (read_mode != mode ) {
406+ status = LIBHOTH_ERR_FAIL ;
407+ goto err_out ;
320408 }
321409
322410 if (options -> speed ) {
@@ -353,7 +441,7 @@ int libhoth_spi_send_request(struct libhoth_device* dev, const void* request,
353441 struct libhoth_spi_device * spi_dev =
354442 (struct libhoth_spi_device * )dev -> user_ctx ;
355443
356- return spi_nor_write (spi_dev -> fd , spi_dev -> address_mode_4b ,
444+ return spi_nor_write (spi_dev -> fd , spi_dev -> address_mode_4b , spi_dev -> mode ,
357445 spi_dev -> mailbox_address , request , request_size ,
358446 spi_dev -> device_busy_wait_timeout ,
359447 spi_dev -> device_busy_wait_check_interval );
@@ -377,7 +465,7 @@ int libhoth_spi_receive_response(struct libhoth_device* dev, void* response,
377465 (struct libhoth_spi_device * )dev -> user_ctx ;
378466
379467 // Read Header From Mailbox
380- status = spi_nor_read (spi_dev -> fd , spi_dev -> address_mode_4b ,
468+ status = spi_nor_read (spi_dev -> fd , spi_dev -> address_mode_4b , spi_dev -> mode ,
381469 spi_dev -> mailbox_address , response ,
382470 sizeof (struct hoth_host_response ));
383471 if (status != LIBHOTH_OK ) {
@@ -397,7 +485,7 @@ int libhoth_spi_receive_response(struct libhoth_device* dev, void* response,
397485 if (host_response .data_len > 0 ) {
398486 // Read remainder of data based on header length
399487 uint8_t * const data_start = (uint8_t * )response + total_bytes ;
400- status = spi_nor_read (spi_dev -> fd , spi_dev -> address_mode_4b ,
488+ status = spi_nor_read (spi_dev -> fd , spi_dev -> address_mode_4b , spi_dev -> mode ,
401489 spi_dev -> mailbox_address + total_bytes , data_start ,
402490 host_response .data_len );
403491 if (status != LIBHOTH_OK ) {
@@ -467,7 +555,7 @@ int libhoth_spi_send_and_receive_response(struct libhoth_device* dev,
467555
468556 // Page Program OPCODE + Mailbox Address
469557 uint8_t pp_buf [5 ] = {0 };
470- pp_buf [0 ] = SPI_NOR_OPCODE_PAGE_PROGRAM ;
558+ pp_buf [0 ] = mode_to_write_opcode ( spi_dev -> mode ) ;
471559 int address_len = spi_nor_address (& pp_buf [1 ], address , address_mode_4b );
472560 xfer [1 ] = (struct spi_ioc_transfer ){
473561 .tx_buf = (unsigned long )pp_buf ,
@@ -481,11 +569,15 @@ int libhoth_spi_send_and_receive_response(struct libhoth_device* dev,
481569 .cs_change = 1 ,
482570 };
483571
572+ if (spi_dev -> mode != LIBHOTH_SPI_MODE_SINGLE ) {
573+ uint8_t nbits = mode_to_nbits (spi_dev -> mode );
574+ xfer [2 ].tx_nbits = nbits ;
575+ }
484576 // Wait for status register is handled by the spidev driver.
485577
486578 // Read opcode + Mailbox Address
487579 uint8_t rd_buf [5 ] = {0 };
488- rd_buf [0 ] = SPI_NOR_OPCODE_SLOW_READ ;
580+ rd_buf [0 ] = mode_to_read_opcode ( spi_dev -> mode ) ;
489581 address_len = spi_nor_address (& rd_buf [1 ], address , address_mode_4b );
490582 xfer [3 ] = (struct spi_ioc_transfer ){
491583 .tx_buf = (unsigned long )rd_buf ,
@@ -498,6 +590,11 @@ int libhoth_spi_send_and_receive_response(struct libhoth_device* dev,
498590 .len = max_response_size ,
499591 };
500592
593+ if (spi_dev -> mode != LIBHOTH_SPI_MODE_SINGLE ) {
594+ uint8_t nbits = mode_to_nbits (spi_dev -> mode );
595+ xfer [4 ].rx_nbits = nbits ;
596+ }
597+
501598 int rc = LIBHOTH_OK ;
502599 int status = ioctl (spi_dev -> fd , SPI_IOC_MESSAGE (5 ), xfer );
503600 if (status < 0 ) {
@@ -564,6 +661,12 @@ int libhoth_spi_close(struct libhoth_device* dev) {
564661
565662 struct libhoth_spi_device * spi_dev =
566663 (struct libhoth_spi_device * )dev -> user_ctx ;
664+ // Mode settings can be sticky! Restore the original mode to ensure any bad
665+ // modesetting does not presist across processes.
666+ if (ioctl (spi_dev -> fd , SPI_IOC_WR_MODE32 , & spi_dev -> original_mode ) < 0 ) {
667+ // We can't do much here, but we should at least close the fd.
668+ perror ("Failed to restore SPI mode" );
669+ }
567670 close (spi_dev -> fd );
568671 free (dev -> user_ctx );
569672 return LIBHOTH_OK ;
0 commit comments