[PATCH 7/7] spi/pl022: make the chip deselect handling thread safe
From: Virupax Sadashivpetimath virupax.sadashivpetim...@stericsson.com There is a possibility that the pump_message and giveback run in parallel on a SMP system. Both the pump_message and giveback threads work on the same SPI message queue. Results will be in correct if the pump_message gets to work on the queue first. when the pump_message works with the queue, it reads the head of the queue and removes it from the queue. pump_message activates the chip select depending on this message read. This leads to giveback working on the modified queue or a emptied queue. If the queue is empty or if the next message on the queue (which is not the actual next message, as the pump message has removed the next message from the queue) is not for the same SPI device as that Of the previous one, giveback will de-activate the chip select activated by pump_message(), which is wrong. To solve this problem pump_message is made not to run and access the queue until the giveback is done handling the queue. I.e. by making the cur_msg NULL after the giveback has read the queue. Also a state variable has been introduced to keep track of when the CS for next message is already activated and avoid to double-activate it. Signed-off-by: Virupax Sadashivpetimath virupax.sadashivpetim...@stericsson.com Signed-off-by: Linus Walleij linus.wall...@linaro.org --- drivers/spi/spi-pl022.c | 72 --- 1 files changed, 37 insertions(+), 35 deletions(-) diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 13988a3..1cff8ad 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -340,6 +340,10 @@ struct vendor_data { * @cur_msg: Pointer to current spi_message being processed * @cur_transfer: Pointer to current spi_transfer * @cur_chip: pointer to current clients chip(assigned from controller_state) + * @next_msg_cs_active: the next message in the queue has been examined + * and it was found that it uses the same chip select as the previous + * message, so we left it active after the previous transfer, and it's + * active already. * @tx: current position in TX buffer to be read * @tx_end: end position in TX buffer to be read * @rx: current position in RX buffer to be written @@ -373,6 +377,7 @@ struct pl022 { struct spi_message *cur_msg; struct spi_transfer *cur_transfer; struct chip_data*cur_chip; + boolnext_msg_cs_active; void*tx; void*tx_end; void*rx; @@ -445,23 +450,9 @@ static void giveback(struct pl022 *pl022) struct spi_transfer *last_transfer; unsigned long flags; struct spi_message *msg; - void (*curr_cs_control) (u32 command); + pl022-next_msg_cs_active = false; - /* -* This local reference to the chip select function -* is needed because we set curr_chip to NULL -* as a step toward termininating the message. -*/ - curr_cs_control = pl022-cur_chip-cs_control; - spin_lock_irqsave(pl022-queue_lock, flags); - msg = pl022-cur_msg; - pl022-cur_msg = NULL; - pl022-cur_transfer = NULL; - pl022-cur_chip = NULL; - queue_work(pl022-workqueue, pl022-pump_messages); - spin_unlock_irqrestore(pl022-queue_lock, flags); - - last_transfer = list_entry(msg-transfers.prev, + last_transfer = list_entry(pl022-cur_msg-transfers.prev, struct spi_transfer, transfer_list); @@ -473,18 +464,13 @@ static void giveback(struct pl022 *pl022) */ udelay(last_transfer-delay_usecs); - /* -* Drop chip select UNLESS cs_change is true or we are returning -* a message with an error, or next message is for another chip -*/ - if (!last_transfer-cs_change) - curr_cs_control(SSP_CHIP_DESELECT); - else { + if (!last_transfer-cs_change) { struct spi_message *next_msg; - /* Holding of cs was hinted, but we need to make sure -* the next message is for the same chip. Don't waste -* time with the following tests unless this was hinted. + /* +* cs_change was not set. We can keep the chip select +* enabled if there is message in the queue and it is +* for the same spi device. * * We cannot postpone this until pump_messages, because * after calling msg-complete (below) the driver that @@ -501,14 +487,26 @@ static void giveback(struct pl022 *pl022) struct spi_message, queue); spin_unlock_irqrestore(pl022-queue_lock, flags); - /* see
Re: [PATCH 7/7] spi/pl022: make the chip deselect handling thread safe
On 11/22/2011 1:55 PM, Linus WALLEIJ wrote: From: Virupax Sadashivpetimath virupax.sadashivpetim...@stericsson.com There is a possibility that the pump_message and giveback run in parallel on a SMP system. Both the pump_message and giveback threads work on the same SPI message queue. Results will be in correct if the pump_message gets to work on the queue first. when the pump_message works with the queue, it reads the head of the queue and removes it from the queue. pump_message activates the chip select depending on this message read. This leads to giveback working on the modified queue or a emptied queue. If the queue is empty or if the next message on the queue (which is not the actual next message, as the pump message has removed the next message from the queue) is not for the same SPI device as that Of the previous one, giveback will de-activate the chip select activated by pump_message(), which is wrong. To solve this problem pump_message is made not to run and access the queue until the giveback is done handling the queue. I.e. by making the cur_msg NULL after the giveback has read the queue. Also a state variable has been introduced to keep track of when the CS for next message is already activated and avoid to double-activate it. Signed-off-by: Virupax Sadashivpetimath virupax.sadashivpetim...@stericsson.com Signed-off-by: Linus Walleij linus.wall...@linaro.org --- drivers/spi/spi-pl022.c | 72 --- 1 files changed, 37 insertions(+), 35 deletions(-) diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 13988a3..1cff8ad 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -340,6 +340,10 @@ struct vendor_data { * @cur_msg: Pointer to current spi_message being processed * @cur_transfer: Pointer to current spi_transfer * @cur_chip: pointer to current clients chip(assigned from controller_state) + * @next_msg_cs_active: the next message in the queue has been examined + * and it was found that it uses the same chip select as the previous + * message, so we left it active after the previous transfer, and it's + * active already. * @tx: current position in TX buffer to be read * @tx_end: end position in TX buffer to be read * @rx: current position in RX buffer to be written @@ -373,6 +377,7 @@ struct pl022 { struct spi_message *cur_msg; struct spi_transfer *cur_transfer; struct chip_data*cur_chip; + boolnext_msg_cs_active; void*tx; void*tx_end; void*rx; @@ -445,23 +450,9 @@ static void giveback(struct pl022 *pl022) struct spi_transfer *last_transfer; unsigned long flags; struct spi_message *msg; - void (*curr_cs_control) (u32 command); + pl022-next_msg_cs_active = false; - /* - * This local reference to the chip select function - * is needed because we set curr_chip to NULL - * as a step toward termininating the message. - */ - curr_cs_control = pl022-cur_chip-cs_control; - spin_lock_irqsave(pl022-queue_lock, flags); - msg = pl022-cur_msg; - pl022-cur_msg = NULL; - pl022-cur_transfer = NULL; - pl022-cur_chip = NULL; - queue_work(pl022-workqueue, pl022-pump_messages); - spin_unlock_irqrestore(pl022-queue_lock, flags); - - last_transfer = list_entry(msg-transfers.prev, + last_transfer = list_entry(pl022-cur_msg-transfers.prev, struct spi_transfer, transfer_list); @@ -473,18 +464,13 @@ static void giveback(struct pl022 *pl022) */ udelay(last_transfer-delay_usecs); - /* - * Drop chip select UNLESS cs_change is true or we are returning - * a message with an error, or next message is for another chip - */ - if (!last_transfer-cs_change) - curr_cs_control(SSP_CHIP_DESELECT); - else { + if (!last_transfer-cs_change) { struct spi_message *next_msg; - /* Holding of cs was hinted, but we need to make sure - * the next message is for the same chip. Don't waste - * time with the following tests unless this was hinted. + /* + * cs_change was not set. We can keep the chip select + * enabled if there is message in the queue and it is + * for the same spi device. * * We cannot postpone this until pump_messages, because * after calling msg-complete (below) the driver that @@ -501,14 +487,26 @@ static void giveback(struct pl022 *pl022) struct spi_message, queue);