@@ -56,6 +56,8 @@ static int spec_idx;
5656 */
5757struct spi_dt_spec spec_copies [5 ];
5858
59+
60+
5961/*
6062 ********************
6163 * SPI test buffers *
@@ -125,6 +127,64 @@ static void to_display_format(const uint8_t *src, size_t size, char *dst)
125127 }
126128}
127129
130+ #if DT_NODE_HAS_PROP (DT_PATH (zephyr_user ), cs_loopback_gpios )
131+
132+ static const struct gpio_dt_spec cs_loopback_gpio =
133+ GPIO_DT_SPEC_GET_OR (DT_PATH (zephyr_user ), cs_loopback_gpios , {0 });
134+ static struct gpio_callback cs_cb_data ;
135+ static K_SEM_DEFINE (cs_sem , 0 , UINT_MAX ) ;
136+
137+ static void spi_loopback_gpio_cs_loopback_prepare (void )
138+ {
139+ k_sem_reset (& cs_sem );
140+ }
141+
142+ static int spi_loopback_gpio_cs_loopback_check (int expected_triggers )
143+ {
144+ return k_sem_count_get (& cs_sem ) != expected_triggers ;
145+ }
146+
147+ static void cs_callback (const struct device * port ,
148+ struct gpio_callback * cb ,
149+ gpio_port_pins_t pins )
150+ {
151+ ARG_UNUSED (port );
152+ ARG_UNUSED (cb );
153+ ARG_UNUSED (pins );
154+
155+ /* Give semaphore to indicate CS triggered */
156+ k_sem_give (& cs_sem );
157+ }
158+
159+ static int spi_loopback_gpio_cs_loopback_init (void )
160+ {
161+ const struct gpio_dt_spec * gpio = & cs_loopback_gpio ;
162+ int ret ;
163+
164+ if (!gpio_is_ready_dt (gpio )) {
165+ return - ENODEV ;
166+ }
167+
168+ ret = gpio_pin_configure_dt (gpio , GPIO_INPUT );
169+ if (ret ) {
170+ return ret ;
171+ }
172+
173+ ret = gpio_pin_interrupt_configure_dt (gpio , GPIO_INT_EDGE_BOTH );
174+ if (ret ) {
175+ return ret ;
176+ }
177+
178+ gpio_init_callback (& cs_cb_data , cs_callback , BIT (gpio -> pin ));
179+
180+ return gpio_add_callback (gpio -> port , & cs_cb_data );
181+ }
182+ #else
183+ #define spi_loopback_gpio_cs_loopback_init (...) (0)
184+ #define spi_loopback_gpio_cs_loopback_prepare (...)
185+ #define spi_loopback_gpio_cs_loopback_check (...) (0)
186+ #endif
187+
128188/* just a wrapper of the driver transceive call with ztest error assert */
129189static void spi_loopback_transceive (struct spi_dt_spec * const spec ,
130190 const struct spi_buf_set * const tx ,
@@ -133,12 +193,15 @@ static void spi_loopback_transceive(struct spi_dt_spec *const spec,
133193 int ret ;
134194
135195 zassert_ok (pm_device_runtime_get (spec -> bus ));
196+ spi_loopback_gpio_cs_loopback_prepare ();
136197 ret = spi_transceive_dt (spec , tx , rx );
137198 if (ret == - EINVAL || ret == - ENOTSUP ) {
138199 TC_PRINT ("Spi config invalid for this controller - skip\n" );
139200 goto out ;
140201 }
141202 zassert_ok (ret , "SPI transceive failed, code %d" , ret );
203+ /* There should be two CS triggers during the transaction, start and end */
204+ zassert_false (spi_loopback_gpio_cs_loopback_check (2 ));
142205out :
143206 zassert_ok (pm_device_runtime_put (spec -> bus ));
144207}
@@ -820,6 +883,66 @@ ZTEST(spi_extra_api_features, test_spi_lock_release)
820883 lock_spec -> config .operation &= ~SPI_LOCK_ON ;
821884}
822885
886+ ZTEST (spi_extra_api_features , test_spi_hold_on_cs )
887+ {
888+ const struct spi_buf_set tx = spi_loopback_setup_xfer (tx_bufs_pool , 1 ,
889+ buffer_tx , BUF_SIZE );
890+ const struct spi_buf_set rx = spi_loopback_setup_xfer (rx_bufs_pool , 1 ,
891+ NULL , BUF_SIZE );
892+ struct spi_dt_spec * hold_spec = & spi_slow ;
893+ int ret = 0 ;
894+
895+ hold_spec -> config .operation |= SPI_HOLD_ON_CS ;
896+
897+ spi_loopback_gpio_cs_loopback_prepare ();
898+ ret = spi_transceive_dt (hold_spec , & tx , & rx );
899+ if (ret == - ENOTSUP || ret == - EINVAL ) {
900+ TC_PRINT ("SPI hold on CS not supported" );
901+ ret = 0 ;
902+ goto early_exit ;
903+ } else if (ret ) {
904+ goto early_exit ;
905+ }
906+ /* Should get start assertion is 1 CS edge but no end */
907+ if (spi_loopback_gpio_cs_loopback_check (1 )) {
908+ ret = - EIO ;
909+ goto early_exit ;
910+ }
911+
912+ spi_loopback_gpio_cs_loopback_prepare ();
913+ ret = spi_transceive_dt (hold_spec , & tx , & rx );
914+ if (ret ) {
915+ goto early_exit ;
916+ }
917+ /* CS is already asserted, and we still have hold on, so no edges */
918+ if (spi_loopback_gpio_cs_loopback_check (0 )) {
919+ ret = - EIO ;
920+ goto early_exit ;
921+ }
922+
923+ hold_spec -> config .operation &= ~SPI_HOLD_ON_CS ;
924+
925+ spi_loopback_gpio_cs_loopback_prepare ();
926+ ret = spi_transceive_dt (hold_spec , & tx , & rx );
927+ if (ret ) {
928+ goto early_exit ;
929+ }
930+ /* This time we don't have hold flag but it starts held, so only end trigger */
931+ if (spi_loopback_gpio_cs_loopback_check (1 )) {
932+ ret = - EIO ;
933+ goto early_exit ;
934+ }
935+
936+ /* now just do a normal transfer to make sure there was no leftover effects */
937+ spi_loopback_transceive (hold_spec , & tx , & rx );
938+
939+ return ;
940+
941+ early_exit :
942+ hold_spec -> config .operation &= ~SPI_HOLD_ON_CS ;
943+ zassert_false (ret , "SPI transceive failed, code %d" , ret );
944+ }
945+
823946/*
824947 *************************
825948 * Test suite definition *
@@ -859,6 +982,7 @@ static void run_after_lock(void *unused)
859982 spi_release_dt (& spi_slow );
860983 spi_slow .config .operation &= ~SPI_LOCK_ON ;
861984 spi_fast .config .operation &= ~SPI_LOCK_ON ;
985+ spi_slow .config .operation &= ~SPI_HOLD_ON_CS ;
862986}
863987
864988ZTEST_SUITE (spi_loopback , NULL , spi_loopback_setup , NULL , NULL , run_after_suite );
@@ -891,6 +1015,8 @@ void test_main(void)
8911015 K_PRIO_COOP (7 ), 0 , K_NO_WAIT );
8921016#endif
8931017
1018+ zassert_false (spi_loopback_gpio_cs_loopback_init ());
1019+
8941020 ztest_run_all (NULL , false, ARRAY_SIZE (loopback_specs ), 1 );
8951021
8961022#if (CONFIG_SPI_ASYNC )
0 commit comments