From: Troy Kisky <troy.kisky-Q5RJGjKts06CY9SHAMCTRUEOCMrvLtNR@public.gmane.org>
To: Kevin Hilman <khilman-1D3HCaltpLuhEniVeURVKkEOCMrvLtNR@public.gmane.org>
Cc: Jon Povey <jon.povey-Ean/AyPsLtfkYMGBc/C6ZA@public.gmane.org>,
davinci-linux-open-source-VycZQUHpC/PFrsHnngEfi1aTQe2KTcn/@public.gmane.org,
linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Subject: Re: [PATCH] i2c: davinci: Fix race when setting up for TX
Date: Thu, 16 Sep 2010 13:28:50 -0700 [thread overview]
Message-ID: <4C927E02.10905@boundarydevices.com> (raw)
In-Reply-To: <87pqwda8ub.fsf-1D3HCaltpLuhEniVeURVKkEOCMrvLtNR@public.gmane.org>
On 9/16/2010 10:37 AM, Kevin Hilman wrote:
> Jon Povey <jon.povey-Ean/AyPsLtfkYMGBc/C6ZA@public.gmane.org> writes:
>
>> When setting up to transmit, a race exists between the ISR and
>> i2c_davinci_xfer_msg() trying to load the first byte and adjust counters.
>> This is mostly visible for transmits > 1 byte long.
>>
>> The ISR may run at any time after the mode register has been set.
>> While we are setting up and loading the first byte, protect this critical
>> section from the ISR with a spinlock.
>>
>> The RX path or zero-length transmits do not need this locking.
>>
>> Tested on DM355 connected to Techwell TW2836 and Wolfson WM8985
>>
>> Signed-off-by: Jon Povey <jon.povey-Ean/AyPsLtfkYMGBc/C6ZA@public.gmane.org>
>
> This looks like a good fix.
>
> Anyone else care to test on other platforms and add a 'Tested-by'?
>
> Thanks,
>
> Kevin
>
>> ---
>> I suspect this hasn't shown up for others using single-byte transmits as the
>> interrupt tends to either run entirely before or entirely after this block
>> in i2c_davinci_xfer_msg():
>>
>> /*
>> * First byte should be set here, not after interrupt,
>> * because transmit-data-ready interrupt can come before
>> * NACK-interrupt during sending of previous message and
>> * ICDXR may have wrong data
>> */
>> if ((!(msg->flags & I2C_M_RD)) && dev->buf_len) {
>> davinci_i2c_write_reg(dev, DAVINCI_I2C_DXR_REG, *dev->buf++);
>> dev->buf_len--;
>> }
>>
>> Often the entire message would be sent before that test was executed
>> (observed with LED wiggling and a logic analyser), so dev->buf_len would
>> be untrue and things merrily went on their way. That seems to be counter
>> to the intent in the comment.
>>
>> I tried some fiddling around reordering the register loads but couldn't
>> get things reliable so stuck in a spinlock. Better solutions welcome.
>>
>> P.S.: Having run into the the bus reset code a lot during testing, I
>> am pretty sure that that generic_i2c_clock_pulse() does NOTHING due to
>> pinmuxing, at least on DM355, it is misleading and may be better
>> replaced with a comment saying "It would be great to toggle SCL here".
>>
>> drivers/i2c/busses/i2c-davinci.c | 21 ++++++++++++++++++++-
>> 1 files changed, 20 insertions(+), 1 deletions(-)
>>
>> diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c
>> index 2222c87..43aa55d 100644
>> --- a/drivers/i2c/busses/i2c-davinci.c
>> +++ b/drivers/i2c/busses/i2c-davinci.c
>> @@ -107,6 +107,7 @@ struct davinci_i2c_dev {
>> u8 *buf;
>> size_t buf_len;
>> int irq;
>> + spinlock_t lock;
>> int stop;
>> u8 terminate;
>> struct i2c_adapter adapter;
>> @@ -312,6 +313,8 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
>> u32 flag;
>> u16 w;
>> int r;
>> + unsigned long flags;
>> + int preload = 0;
>>
>> if (!pdata)
>> pdata = &davinci_i2c_platform_data_default;
>> @@ -347,6 +350,15 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
>> flag &= ~DAVINCI_I2C_MDR_STP;
>> }
>>
>> + /*
>> + * When transmitting, lock ISR out to avoid it racing on the buffer and
>> + * DXR register before we are done
>> + */
>> + if ((!(msg->flags & I2C_M_RD)) && dev->buf_len) {
>> + preload = 1;
>> + spin_lock_irqsave(&dev->lock, flags);
>> + }
>> +
>> /* Enable receive or transmit interrupts */
>> w = davinci_i2c_read_reg(dev, DAVINCI_I2C_IMR_REG);
Maybe you can write 0 to IMR here, and move this interrupt enable (IMR) write to after the DXR write.
That would seem a better patch.
>> if (msg->flags & I2C_M_RD)
>> @@ -366,13 +378,15 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
>> * NACK-interrupt during sending of previous message and
>> * ICDXR may have wrong data
>> */
>> - if ((!(msg->flags & I2C_M_RD)) && dev->buf_len) {
>> + if (preload) {
>> davinci_i2c_write_reg(dev, DAVINCI_I2C_DXR_REG, *dev->buf++);
>> dev->buf_len--;
>> + spin_unlock_irqrestore(&dev->lock, flags);
>> }
>>
>> r = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
>> dev->adapter.timeout);
>> +
>> if (r == 0) {
>> dev_err(dev->dev, "controller timed out\n");
>> i2c_recover_bus(dev);
>> @@ -490,6 +504,8 @@ static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id)
>> int count = 0;
>> u16 w;
>>
>> + spin_lock(&dev->lock);
>> +
>> while ((stat = davinci_i2c_read_reg(dev, DAVINCI_I2C_IVR_REG))) {
>> dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat);
>> if (count++ == 100) {
>> @@ -579,6 +595,8 @@ static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id)
>> }
>> }
>>
>> + spin_unlock(&dev->lock);
>> +
>> return count ? IRQ_HANDLED : IRQ_NONE;
>> }
>>
>> @@ -662,6 +680,7 @@ static int davinci_i2c_probe(struct platform_device *pdev)
>> goto err_release_region;
>> }
>>
>> + spin_lock_init(&dev->lock);
>> init_completion(&dev->cmd_complete);
>> #ifdef CONFIG_CPU_FREQ
>> init_completion(&dev->xfr_complete);
> _______________________________________________
> Davinci-linux-open-source mailing list
> Davinci-linux-open-source-VycZQUHpC/PFrsHnngEfi1aTQe2KTcn/@public.gmane.org
> http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source
>
next prev parent reply other threads:[~2010-09-16 20:28 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-09-16 10:21 [PATCH] i2c: davinci: Fix race when setting up for TX Jon Povey
[not found] ` <1284632480-24035-1-git-send-email-jon.povey-Ean/AyPsLtfkYMGBc/C6ZA@public.gmane.org>
2010-09-16 17:37 ` Kevin Hilman
[not found] ` <87pqwda8ub.fsf-1D3HCaltpLuhEniVeURVKkEOCMrvLtNR@public.gmane.org>
2010-09-16 20:28 ` Troy Kisky [this message]
-- strict thread matches above, loose matches on Subject: below --
2010-06-22 7:31 Jon Povey
[not found] ` <1277191864-5024-1-git-send-email-jon.povey-Ean/AyPsLtfkYMGBc/C6ZA@public.gmane.org>
2010-06-22 12:17 ` Sudhakar Rajashekhara
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4C927E02.10905@boundarydevices.com \
--to=troy.kisky-q5rjgjkts06cy9shamctrueocmrvltnr@public.gmane.org \
--cc=davinci-linux-open-source-VycZQUHpC/PFrsHnngEfi1aTQe2KTcn/@public.gmane.org \
--cc=jon.povey-Ean/AyPsLtfkYMGBc/C6ZA@public.gmane.org \
--cc=khilman-1D3HCaltpLuhEniVeURVKkEOCMrvLtNR@public.gmane.org \
--cc=linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox