From df3060fc33865e2e48f62be6093d6851f31834af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ronald=20Tschal=C3=A4r?= Date: Mon, 28 Aug 2017 00:04:18 -0700 Subject: [PATCH] applespi: Drain outstading spi commands before removing module. This ensures our callbacks are not called after we've been removed, and it also ensures that writes have been fully processed (including the associated response being received), thereby resolving occasional errors when removing and re-inserting this module. --- applespi.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/applespi.c b/applespi.c index b73b45c..baf0e4b 100644 --- a/applespi.c +++ b/applespi.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) @@ -231,6 +232,11 @@ struct applespi_data { unsigned cmd_log_mask; struct led_classdev backlight_info; + + bool drain; + wait_queue_head_t drain_complete; + bool read_active; + bool write_active; }; static const unsigned char applespi_scancodes[] = { @@ -557,6 +563,7 @@ static int applespi_setup_spi(struct applespi_data *applespi) return sts; spin_lock_init(&applespi->cmd_msg_lock); + init_waitqueue_head(&applespi->drain_complete); return 0; } @@ -624,6 +631,10 @@ applespi_send_cmd_msg(struct applespi_data *applespi) u16 crc; int sts; + /* check if draining */ + if (applespi->drain) + return 0; + /* check whether send is in progress */ if (applespi->cmd_msg_queued) return 0; @@ -696,10 +707,13 @@ applespi_send_cmd_msg(struct applespi_data *applespi) sts = applespi_async(applespi, &applespi->wr_m, applespi_async_write_complete); - if (sts != 0) + if (sts != 0) { pr_warn("Error queueing async write to device: %d\n", sts); - else + } else { applespi->cmd_msg_queued = true; + applespi->write_active = true; + } + return sts; } @@ -969,6 +983,7 @@ static void applespi_got_data(struct applespi_data *applespi) { struct keyboard_protocol *keyboard_protocol; + unsigned long flags; keyboard_protocol = (struct keyboard_protocol*) applespi->rx_buffer; if (keyboard_protocol->packet_type == PACKET_TYPE_READ && @@ -1010,6 +1025,19 @@ applespi_got_data(struct applespi_data *applespi) */ udelay(SPI_RW_CHG_DLY); + /* handle draining */ + spin_lock_irqsave(&applespi->cmd_msg_lock, flags); + + applespi->read_active = false; + if (keyboard_protocol->packet_type == PACKET_TYPE_WRITE) + applespi->write_active = false; + + if (applespi->drain && !applespi->write_active) + wake_up_all(&applespi->drain_complete); + + spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); + + /* notify write complete */ if (keyboard_protocol->packet_type == PACKET_TYPE_WRITE) applespi_cmd_msg_complete(applespi); } @@ -1029,6 +1057,7 @@ static void applespi_async_read_complete(void *context) static u32 applespi_notify(acpi_handle gpe_device, u32 gpe, void *context) { struct applespi_data *applespi = context; + int sts; unsigned long flags; debug_print(DBG_RD_IRQ, "--- %s ---------------------------\n", @@ -1038,7 +1067,13 @@ static u32 applespi_notify(acpi_handle gpe_device, u32 gpe, void *context) applespi_setup_spi_message(&applespi->rd_m, 2, &applespi->dl_t, &applespi->rd_t); spin_lock_irqsave(&applespi->cmd_msg_lock, flags); - applespi_async(applespi, &applespi->rd_m, applespi_async_read_complete); + + sts = applespi_async(applespi, &applespi->rd_m, applespi_async_read_complete); + if (sts != 0) + pr_warn("Error queueing async read to device: %d\n", sts); + else + applespi->read_active = true; + spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); return ACPI_INTERRUPT_HANDLED; @@ -1236,10 +1271,30 @@ static int applespi_probe(struct spi_device *spi) static int applespi_remove(struct spi_device *spi) { struct applespi_data *applespi = spi_get_drvdata(spi); + unsigned long flags; + + /* wait for all outstanding writes to finish */ + spin_lock_irqsave(&applespi->cmd_msg_lock, flags); + + applespi->drain = true; + wait_event_lock_irq(applespi->drain_complete, !applespi->write_active, + applespi->cmd_msg_lock); + spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); + + /* shut things down */ acpi_disable_gpe(NULL, applespi->gpe); acpi_remove_gpe_handler(NULL, applespi->gpe, applespi_notify); + /* wait for all outstanding reads to finish */ + spin_lock_irqsave(&applespi->cmd_msg_lock, flags); + + wait_event_lock_irq(applespi->drain_complete, !applespi->read_active, + applespi->cmd_msg_lock); + + spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags); + + /* done */ pr_info("spi-device remove done: %s\n", dev_name(&spi->dev)); return 0; }