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: 6+ 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
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.