* [PATCH] I2C: Fix twl4030 timeouts on omap3430
@ 2008-03-28 8:41 Tony Lindgren
2008-03-31 10:43 ` Tony Lindgren
0 siblings, 1 reply; 11+ messages in thread
From: Tony Lindgren @ 2008-03-28 8:41 UTC (permalink / raw)
To: linux-omap
[-- Attachment #1: Type: text/plain, Size: 294 bytes --]
Hi all,
This helps with the annoying I2C timeouts. Does anybody have an idea
why the twl4030 chip does not like doing multiple transfers in a row?
To me the only difference seems to be that clocks are idled between
writing the twl4030 register and reading the register value.
Regards,
Tony
[-- Attachment #2: i2c-twl4030-timeout.patch --]
[-- Type: text/x-diff, Size: 1640 bytes --]
>From 25c4c8f449819cb6f40b59ed1a9b25ebcc7cd72e Mon Sep 17 00:00:00 2001
From: Tony Lindgren <tony@atomide.com>
Date: Thu, 27 Mar 2008 19:05:30 +0200
Subject: [PATCH] I2C: Fix twl4030 timeouts on omap3430
For some reason doing a twl4030 write-read cycle can hang the I2C bus
on omap3430. And doing the write and read separately in twl4030_i2c_read()
seems to fix the problem...
Not intended for applying, just a temporary workaround.
diff --git a/drivers/i2c/chips/twl4030-core.c b/drivers/i2c/chips/twl4030-core.c
index ded86e7..62868b0 100644
--- a/drivers/i2c/chips/twl4030-core.c
+++ b/drivers/i2c/chips/twl4030-core.c
@@ -327,6 +327,7 @@ int twl4030_i2c_read(u8 mod_no, u8 * value, u8 reg, u8 num_bytes)
return -EPERM;
}
mutex_lock(&twl->xfer_lock);
+
/* [MSG1] fill the register address data */
msg = &twl->xfer_msg[0];
msg->addr = twl->address;
@@ -334,18 +335,25 @@ int twl4030_i2c_read(u8 mod_no, u8 * value, u8 reg, u8 num_bytes)
msg->flags = 0; /* Read the register value */
val = twl4030_map[mod_no].base + reg;
msg->buf = &val;
+ ret = i2c_transfer(twl->client.adapter, twl->xfer_msg, 1);
+ if (ret < 0)
+ goto out;
+
/* [MSG2] fill the data rx buffer */
msg = &twl->xfer_msg[1];
msg->addr = twl->address;
msg->flags = I2C_M_RD; /* Read the register value */
msg->len = num_bytes; /* only n bytes */
msg->buf = value;
- ret = i2c_transfer(twl->client.adapter, twl->xfer_msg, 2);
+ ret = i2c_transfer(twl->client.adapter, twl->xfer_msg, 1);
+
+out:
mutex_unlock(&twl->xfer_lock);
/* i2cTransfer returns num messages.translate it pls.. */
if (ret >= 0)
ret = 0;
+
return ret;
}
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH] I2C: Fix twl4030 timeouts on omap3430
[not found] ` <1206704800-6768-4-git-send-email-tony@atomide.com>
@ 2008-03-28 11:46 ` Tony Lindgren
2008-03-28 11:46 ` Tony Lindgren
2008-03-28 11:48 ` Tony Lindgren
0 siblings, 2 replies; 11+ messages in thread
From: Tony Lindgren @ 2008-03-28 11:46 UTC (permalink / raw)
To: linux-omap; +Cc: Tony Lindgren
For some reason doing a twl4030 write-read cycle can hang the I2C bus
on omap3430. And doing the write and read separately in twl4030_i2c_read()
seems to fix the problem...
Not intended for applying, just a temporary workaround.
diff --git a/drivers/i2c/chips/twl4030-core.c b/drivers/i2c/chips/twl4030-core.c
index ded86e7..62868b0 100644
--- a/drivers/i2c/chips/twl4030-core.c
+++ b/drivers/i2c/chips/twl4030-core.c
@@ -327,6 +327,7 @@ int twl4030_i2c_read(u8 mod_no, u8 * value, u8 reg, u8 num_bytes)
return -EPERM;
}
mutex_lock(&twl->xfer_lock);
+
/* [MSG1] fill the register address data */
msg = &twl->xfer_msg[0];
msg->addr = twl->address;
@@ -334,18 +335,25 @@ int twl4030_i2c_read(u8 mod_no, u8 * value, u8 reg, u8 num_bytes)
msg->flags = 0; /* Read the register value */
val = twl4030_map[mod_no].base + reg;
msg->buf = &val;
+ ret = i2c_transfer(twl->client.adapter, twl->xfer_msg, 1);
+ if (ret < 0)
+ goto out;
+
/* [MSG2] fill the data rx buffer */
msg = &twl->xfer_msg[1];
msg->addr = twl->address;
msg->flags = I2C_M_RD; /* Read the register value */
msg->len = num_bytes; /* only n bytes */
msg->buf = value;
- ret = i2c_transfer(twl->client.adapter, twl->xfer_msg, 2);
+ ret = i2c_transfer(twl->client.adapter, twl->xfer_msg, 1);
+
+out:
mutex_unlock(&twl->xfer_lock);
/* i2cTransfer returns num messages.translate it pls.. */
if (ret >= 0)
ret = 0;
+
return ret;
}
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH] I2C: Fix twl4030 timeouts on omap3430
2008-03-28 11:46 ` [PATCH] I2C: Fix twl4030 timeouts on omap3430 Tony Lindgren
@ 2008-03-28 11:46 ` Tony Lindgren
2008-03-28 11:49 ` Tony Lindgren
2008-03-28 11:48 ` Tony Lindgren
1 sibling, 1 reply; 11+ messages in thread
From: Tony Lindgren @ 2008-03-28 11:46 UTC (permalink / raw)
To: linux-omap; +Cc: Richard Woodruff, Tony Lindgren
ARM: OMAP: Use posted mode for dmtimer
This patch adds the use of write posting for the timer. Previously, every
write could lock the requestor for almost 3x32KHz cycles. This patch only
synchronizes before writes and reads instead of after them and it does
it on per register basis. Doing it this way there is some chance to hide
some of the sync latency. It also removes some needless reads when
non-posted mode is there. With out this fix the read/writes take almost
2% CPU load @500MHz just waiting on tick timer registers.
Also define new 34xx only registers.
Signed-off-by: Richard Woodruff <r-woodruff2@ti.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
--- a/arch/arm/plat-omap/dmtimer.c
+++ b/arch/arm/plat-omap/dmtimer.c
@@ -38,34 +38,113 @@
#include <asm/arch/irqs.h>
/* register offsets */
-#define OMAP_TIMER_ID_REG 0x00
-#define OMAP_TIMER_OCP_CFG_REG 0x10
-#define OMAP_TIMER_SYS_STAT_REG 0x14
-#define OMAP_TIMER_STAT_REG 0x18
-#define OMAP_TIMER_INT_EN_REG 0x1c
-#define OMAP_TIMER_WAKEUP_EN_REG 0x20
-#define OMAP_TIMER_CTRL_REG 0x24
-#define OMAP_TIMER_COUNTER_REG 0x28
-#define OMAP_TIMER_LOAD_REG 0x2c
-#define OMAP_TIMER_TRIGGER_REG 0x30
-#define OMAP_TIMER_WRITE_PEND_REG 0x34
-#define OMAP_TIMER_MATCH_REG 0x38
-#define OMAP_TIMER_CAPTURE_REG 0x3c
-#define OMAP_TIMER_IF_CTRL_REG 0x40
-
-/* timer control reg bits */
-#define OMAP_TIMER_CTRL_GPOCFG (1 << 14)
-#define OMAP_TIMER_CTRL_CAPTMODE (1 << 13)
-#define OMAP_TIMER_CTRL_PT (1 << 12)
-#define OMAP_TIMER_CTRL_TCM_LOWTOHIGH (0x1 << 8)
-#define OMAP_TIMER_CTRL_TCM_HIGHTOLOW (0x2 << 8)
-#define OMAP_TIMER_CTRL_TCM_BOTHEDGES (0x3 << 8)
-#define OMAP_TIMER_CTRL_SCPWM (1 << 7)
-#define OMAP_TIMER_CTRL_CE (1 << 6) /* compare enable */
-#define OMAP_TIMER_CTRL_PRE (1 << 5) /* prescaler enable */
-#define OMAP_TIMER_CTRL_PTV_SHIFT 2 /* how much to shift the prescaler value */
-#define OMAP_TIMER_CTRL_AR (1 << 1) /* auto-reload enable */
-#define OMAP_TIMER_CTRL_ST (1 << 0) /* start timer */
+#define _OMAP_TIMER_ID_OFFSET 0x00
+#define _OMAP_TIMER_OCP_CFG_OFFSET 0x10
+#define _OMAP_TIMER_SYS_STAT_OFFSET 0x14
+#define _OMAP_TIMER_STAT_OFFSET 0x18
+#define _OMAP_TIMER_INT_EN_OFFSET 0x1c
+#define _OMAP_TIMER_WAKEUP_EN_OFFSET 0x20
+#define _OMAP_TIMER_CTRL_OFFSET 0x24
+#define OMAP_TIMER_CTRL_GPOCFG (1 << 14)
+#define OMAP_TIMER_CTRL_CAPTMODE (1 << 13)
+#define OMAP_TIMER_CTRL_PT (1 << 12)
+#define OMAP_TIMER_CTRL_TCM_LOWTOHIGH (0x1 << 8)
+#define OMAP_TIMER_CTRL_TCM_HIGHTOLOW (0x2 << 8)
+#define OMAP_TIMER_CTRL_TCM_BOTHEDGES (0x3 << 8)
+#define OMAP_TIMER_CTRL_SCPWM (1 << 7)
+#define OMAP_TIMER_CTRL_CE (1 << 6) /* compare enable */
+#define OMAP_TIMER_CTRL_PRE (1 << 5) /* prescaler enable */
+#define OMAP_TIMER_CTRL_PTV_SHIFT 2 /* prescaler value shift */
+#define OMAP_TIMER_CTRL_POSTED (1 << 2)
+#define OMAP_TIMER_CTRL_AR (1 << 1) /* auto-reload enable */
+#define OMAP_TIMER_CTRL_ST (1 << 0) /* start timer */
+#define _OMAP_TIMER_COUNTER_OFFSET 0x28
+#define _OMAP_TIMER_LOAD_OFFSET 0x2c
+#define _OMAP_TIMER_TRIGGER_OFFSET 0x30
+#define _OMAP_TIMER_WRITE_PEND_OFFSET 0x34
+#define WP_NONE 0 /* no write pending bit */
+#define WP_TCLR (1 << 0)
+#define WP_TCRR (1 << 1)
+#define WP_TLDR (1 << 2)
+#define WP_TTGR (1 << 3)
+#define WP_TMAR (1 << 4)
+#define WP_TPIR (1 << 5)
+#define WP_TNIR (1 << 6)
+#define WP_TCVR (1 << 7)
+#define WP_TOCR (1 << 8)
+#define WP_TOWR (1 << 9)
+#define _OMAP_TIMER_MATCH_OFFSET 0x38
+#define _OMAP_TIMER_CAPTURE_OFFSET 0x3c
+#define _OMAP_TIMER_IF_CTRL_OFFSET 0x40
+#define _OMAP_TIMER_CAPTURE2_OFFSET 0x44 /* TCAR2, 34xx only */
+#define _OMAP_TIMER_TICK_POS_OFFSET 0x48 /* TPIR, 34xx only */
+#define _OMAP_TIMER_TICK_NEG_OFFSET 0x4c /* TNIR, 34xx only */
+#define _OMAP_TIMER_TICK_COUNT_OFFSET 0x50 /* TCVR, 34xx only */
+#define _OMAP_TIMER_TICK_INT_MASK_SET_OFFSET 0x54 /* TOCR, 34xx only */
+#define _OMAP_TIMER_TICK_INT_MASK_COUNT_OFFSET 0x58 /* TOWR, 34xx only */
+
+/* register offsets with the write pending bit encoded */
+#define WPSHIFT 16
+
+#define OMAP_TIMER_ID_REG (_OMAP_TIMER_ID_OFFSET \
+ | (WP_NONE << WPSHIFT))
+
+#define OMAP_TIMER_OCP_CFG_REG (_OMAP_TIMER_OCP_CFG_OFFSET \
+ | (WP_NONE << WPSHIFT))
+
+#define OMAP_TIMER_SYS_STAT_REG (_OMAP_TIMER_SYS_STAT_OFFSET \
+ | (WP_NONE << WPSHIFT))
+
+#define OMAP_TIMER_STAT_REG (_OMAP_TIMER_STAT_OFFSET \
+ | (WP_NONE << WPSHIFT))
+
+#define OMAP_TIMER_INT_EN_REG (_OMAP_TIMER_INT_EN_OFFSET \
+ | (WP_NONE << WPSHIFT))
+
+#define OMAP_TIMER_WAKEUP_EN_REG (_OMAP_TIMER_WAKEUP_EN_OFFSET \
+ | (WP_NONE << WPSHIFT))
+
+#define OMAP_TIMER_CTRL_REG (_OMAP_TIMER_CTRL_OFFSET \
+ | (WP_TCLR << WPSHIFT))
+
+#define OMAP_TIMER_COUNTER_REG (_OMAP_TIMER_COUNTER_OFFSET \
+ | (WP_TCRR << WPSHIFT))
+
+#define OMAP_TIMER_LOAD_REG (_OMAP_TIMER_LOAD_OFFSET \
+ | (WP_TLDR << WPSHIFT))
+
+#define OMAP_TIMER_TRIGGER_REG (_OMAP_TIMER_TRIGGER_OFFSET \
+ | (WP_TTGR << WPSHIFT))
+
+#define OMAP_TIMER_WRITE_PEND_REG (_OMAP_TIMER_WRITE_PEND_OFFSET \
+ | (WP_NONE << WPSHIFT))
+
+#define OMAP_TIMER_MATCH_REG (_OMAP_TIMER_MATCH_OFFSET \
+ | (WP_TMAR << WPSHIFT))
+
+#define OMAP_TIMER_CAPTURE_REG (_OMAP_TIMER_CAPTURE_OFFSET \
+ | (WP_NONE << WPSHIFT))
+
+#define OMAP_TIMER_IF_CTRL_REG (_OMAP_TIMER_IF_CTRL_OFFSET \
+ | (WP_NONE << WPSHIFT))
+
+#define OMAP_TIMER_CAPTURE2_REG (_OMAP_TIMER_CAPTURE2_OFFSET \
+ | (WP_NONE << WPSHIFT))
+
+#define OMAP_TIMER_TICK_POS_REG (_OMAP_TIMER_TICK_POS_OFFSET \
+ | (WP_TPIR << WPSHIFT))
+
+#define OMAP_TIMER_TICK_NEG_REG (_OMAP_TIMER_TICK_NEG_OFFSET \
+ | (WP_TNIR << WPSHIFT))
+
+#define OMAP_TIMER_TICK_COUNT_REG (_OMAP_TIMER_TICK_COUNT_OFFSET \
+ | (WP_TCVR << WPSHIFT))
+
+#define OMAP_TIMER_TICK_INT_MASK_SET_REG \
+ (_OMAP_TIMER_TICK_INT_MASK_SET_OFFSET | (WP_TOCR << WPSHIFT))
+
+#define OMAP_TIMER_TICK_INT_MASK_COUNT_REG \
+ (_OMAP_TIMER_TICK_INT_MASK_COUNT_OFFSET | (WP_TOWR << WPSHIFT))
struct omap_dm_timer {
unsigned long phys_base;
@@ -76,6 +155,7 @@ struct omap_dm_timer {
void __iomem *io_base;
unsigned reserved:1;
unsigned enabled:1;
+ unsigned posted:1;
};
#ifdef CONFIG_ARCH_OMAP1
@@ -181,16 +261,34 @@ static struct clk **dm_source_clocks;
static spinlock_t dm_timer_lock;
-static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, int reg)
+/*
+ * Reads timer registers in posted and non-posted mode. The posted mode bit
+ * is encoded in reg. Note that in posted mode write pending bit must be
+ * checked. Otherwise a read of a non completed write will produce an error.
+ */
+static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, u32 reg)
{
- return readl(timer->io_base + reg);
+ if (timer->posted)
+ while (readl(timer->io_base + (OMAP_TIMER_WRITE_PEND_REG & 0xff))
+ & (reg >> WPSHIFT))
+ cpu_relax();
+ return readl(timer->io_base + (reg & 0xff));
}
-static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, int reg, u32 value)
+/*
+ * Writes timer registers in posted and non-posted mode. The posted mode bit
+ * is encoded in reg. Note that in posted mode the write pending bit must be
+ * checked. Otherwise a write on a register which has a pending write will be
+ * lost.
+ */
+static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg,
+ u32 value)
{
- writel(value, timer->io_base + reg);
- while (omap_dm_timer_read_reg(timer, OMAP_TIMER_WRITE_PEND_REG))
- ;
+ if (timer->posted)
+ while (readl(timer->io_base + (OMAP_TIMER_WRITE_PEND_REG & 0xff))
+ & (reg >> WPSHIFT))
+ cpu_relax();
+ writel(value, timer->io_base + (reg & 0xff));
}
static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer)
@@ -217,17 +315,23 @@ static void omap_dm_timer_reset(struct omap_dm_timer *timer)
}
omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
- /* Set to smart-idle mode */
l = omap_dm_timer_read_reg(timer, OMAP_TIMER_OCP_CFG_REG);
- l |= 0x02 << 3;
-
- if (cpu_class_is_omap2() && timer == &dm_timers[0]) {
- /* Enable wake-up only for GPT1 on OMAP2 CPUs*/
+ l |= 0x02 << 3; /* Set to smart-idle mode */
+ l |= 0x2 << 8; /* Set clock activity to perserve f-clock on idle */
+
+ /*
+ * Enable wake-up only for GPT1 on OMAP2 CPUs.
+ * FIXME: All timers should have wake-up enabled and clear
+ * PRCM status.
+ */
+ if (cpu_class_is_omap2() && (timer == &dm_timers[0]))
l |= 1 << 2;
- /* Non-posted mode */
- omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0);
- }
omap_dm_timer_write_reg(timer, OMAP_TIMER_OCP_CFG_REG, l);
+
+ /* Match hardware reset default of posted mode */
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG,
+ OMAP_TIMER_CTRL_POSTED);
+ timer->posted = 1;
}
static void omap_dm_timer_prepare(struct omap_dm_timer *timer)
@@ -434,6 +538,11 @@ void omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload,
l &= ~OMAP_TIMER_CTRL_AR;
omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
+
+ /* REVISIT: hw feature, ttgr overtaking tldr? */
+ while (readl(timer->io_base + (OMAP_TIMER_WRITE_PEND_REG & 0xff)))
+ cpu_relax();
+
omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
}
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] I2C: Fix twl4030 timeouts on omap3430
2008-03-28 11:46 ` [PATCH] I2C: Fix twl4030 timeouts on omap3430 Tony Lindgren
2008-03-28 11:46 ` Tony Lindgren
@ 2008-03-28 11:48 ` Tony Lindgren
1 sibling, 0 replies; 11+ messages in thread
From: Tony Lindgren @ 2008-03-28 11:48 UTC (permalink / raw)
To: linux-omap
* Tony Lindgren <tony@atomide.com> [080328 13:47]:
> For some reason doing a twl4030 write-read cycle can hang the I2C bus
> on omap3430. And doing the write and read separately in twl4030_i2c_read()
> seems to fix the problem...
>
> Not intended for applying, just a temporary workaround.
Uh, please ignore this one, git-send-email weirdness where it sends
all files ending in .patch.. The series should only contain two patches.
Tony
>
> diff --git a/drivers/i2c/chips/twl4030-core.c b/drivers/i2c/chips/twl4030-core.c
> index ded86e7..62868b0 100644
> --- a/drivers/i2c/chips/twl4030-core.c
> +++ b/drivers/i2c/chips/twl4030-core.c
> @@ -327,6 +327,7 @@ int twl4030_i2c_read(u8 mod_no, u8 * value, u8 reg, u8 num_bytes)
> return -EPERM;
> }
> mutex_lock(&twl->xfer_lock);
> +
> /* [MSG1] fill the register address data */
> msg = &twl->xfer_msg[0];
> msg->addr = twl->address;
> @@ -334,18 +335,25 @@ int twl4030_i2c_read(u8 mod_no, u8 * value, u8 reg, u8 num_bytes)
> msg->flags = 0; /* Read the register value */
> val = twl4030_map[mod_no].base + reg;
> msg->buf = &val;
> + ret = i2c_transfer(twl->client.adapter, twl->xfer_msg, 1);
> + if (ret < 0)
> + goto out;
> +
> /* [MSG2] fill the data rx buffer */
> msg = &twl->xfer_msg[1];
> msg->addr = twl->address;
> msg->flags = I2C_M_RD; /* Read the register value */
> msg->len = num_bytes; /* only n bytes */
> msg->buf = value;
> - ret = i2c_transfer(twl->client.adapter, twl->xfer_msg, 2);
> + ret = i2c_transfer(twl->client.adapter, twl->xfer_msg, 1);
> +
> +out:
> mutex_unlock(&twl->xfer_lock);
>
> /* i2cTransfer returns num messages.translate it pls.. */
> if (ret >= 0)
> ret = 0;
> +
> return ret;
> }
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] I2C: Fix twl4030 timeouts on omap3430
2008-03-28 11:46 ` Tony Lindgren
@ 2008-03-28 11:49 ` Tony Lindgren
0 siblings, 0 replies; 11+ messages in thread
From: Tony Lindgren @ 2008-03-28 11:49 UTC (permalink / raw)
To: linux-omap; +Cc: Richard Woodruff
* Tony Lindgren <tony@atomide.com> [080328 13:47]:
> ARM: OMAP: Use posted mode for dmtimer
>
> This patch adds the use of write posting for the timer. Previously, every
> write could lock the requestor for almost 3x32KHz cycles. This patch only
> synchronizes before writes and reads instead of after them and it does
> it on per register basis. Doing it this way there is some chance to hide
> some of the sync latency. It also removes some needless reads when
> non-posted mode is there. With out this fix the read/writes take almost
> 2% CPU load @500MHz just waiting on tick timer registers.
>
> Also define new 34xx only registers.
Please ignore this patch too.
Tony
>
> Signed-off-by: Richard Woodruff <r-woodruff2@ti.com>
> Signed-off-by: Tony Lindgren <tony@atomide.com>
>
> --- a/arch/arm/plat-omap/dmtimer.c
> +++ b/arch/arm/plat-omap/dmtimer.c
> @@ -38,34 +38,113 @@
> #include <asm/arch/irqs.h>
>
> /* register offsets */
> -#define OMAP_TIMER_ID_REG 0x00
> -#define OMAP_TIMER_OCP_CFG_REG 0x10
> -#define OMAP_TIMER_SYS_STAT_REG 0x14
> -#define OMAP_TIMER_STAT_REG 0x18
> -#define OMAP_TIMER_INT_EN_REG 0x1c
> -#define OMAP_TIMER_WAKEUP_EN_REG 0x20
> -#define OMAP_TIMER_CTRL_REG 0x24
> -#define OMAP_TIMER_COUNTER_REG 0x28
> -#define OMAP_TIMER_LOAD_REG 0x2c
> -#define OMAP_TIMER_TRIGGER_REG 0x30
> -#define OMAP_TIMER_WRITE_PEND_REG 0x34
> -#define OMAP_TIMER_MATCH_REG 0x38
> -#define OMAP_TIMER_CAPTURE_REG 0x3c
> -#define OMAP_TIMER_IF_CTRL_REG 0x40
> -
> -/* timer control reg bits */
> -#define OMAP_TIMER_CTRL_GPOCFG (1 << 14)
> -#define OMAP_TIMER_CTRL_CAPTMODE (1 << 13)
> -#define OMAP_TIMER_CTRL_PT (1 << 12)
> -#define OMAP_TIMER_CTRL_TCM_LOWTOHIGH (0x1 << 8)
> -#define OMAP_TIMER_CTRL_TCM_HIGHTOLOW (0x2 << 8)
> -#define OMAP_TIMER_CTRL_TCM_BOTHEDGES (0x3 << 8)
> -#define OMAP_TIMER_CTRL_SCPWM (1 << 7)
> -#define OMAP_TIMER_CTRL_CE (1 << 6) /* compare enable */
> -#define OMAP_TIMER_CTRL_PRE (1 << 5) /* prescaler enable */
> -#define OMAP_TIMER_CTRL_PTV_SHIFT 2 /* how much to shift the prescaler value */
> -#define OMAP_TIMER_CTRL_AR (1 << 1) /* auto-reload enable */
> -#define OMAP_TIMER_CTRL_ST (1 << 0) /* start timer */
> +#define _OMAP_TIMER_ID_OFFSET 0x00
> +#define _OMAP_TIMER_OCP_CFG_OFFSET 0x10
> +#define _OMAP_TIMER_SYS_STAT_OFFSET 0x14
> +#define _OMAP_TIMER_STAT_OFFSET 0x18
> +#define _OMAP_TIMER_INT_EN_OFFSET 0x1c
> +#define _OMAP_TIMER_WAKEUP_EN_OFFSET 0x20
> +#define _OMAP_TIMER_CTRL_OFFSET 0x24
> +#define OMAP_TIMER_CTRL_GPOCFG (1 << 14)
> +#define OMAP_TIMER_CTRL_CAPTMODE (1 << 13)
> +#define OMAP_TIMER_CTRL_PT (1 << 12)
> +#define OMAP_TIMER_CTRL_TCM_LOWTOHIGH (0x1 << 8)
> +#define OMAP_TIMER_CTRL_TCM_HIGHTOLOW (0x2 << 8)
> +#define OMAP_TIMER_CTRL_TCM_BOTHEDGES (0x3 << 8)
> +#define OMAP_TIMER_CTRL_SCPWM (1 << 7)
> +#define OMAP_TIMER_CTRL_CE (1 << 6) /* compare enable */
> +#define OMAP_TIMER_CTRL_PRE (1 << 5) /* prescaler enable */
> +#define OMAP_TIMER_CTRL_PTV_SHIFT 2 /* prescaler value shift */
> +#define OMAP_TIMER_CTRL_POSTED (1 << 2)
> +#define OMAP_TIMER_CTRL_AR (1 << 1) /* auto-reload enable */
> +#define OMAP_TIMER_CTRL_ST (1 << 0) /* start timer */
> +#define _OMAP_TIMER_COUNTER_OFFSET 0x28
> +#define _OMAP_TIMER_LOAD_OFFSET 0x2c
> +#define _OMAP_TIMER_TRIGGER_OFFSET 0x30
> +#define _OMAP_TIMER_WRITE_PEND_OFFSET 0x34
> +#define WP_NONE 0 /* no write pending bit */
> +#define WP_TCLR (1 << 0)
> +#define WP_TCRR (1 << 1)
> +#define WP_TLDR (1 << 2)
> +#define WP_TTGR (1 << 3)
> +#define WP_TMAR (1 << 4)
> +#define WP_TPIR (1 << 5)
> +#define WP_TNIR (1 << 6)
> +#define WP_TCVR (1 << 7)
> +#define WP_TOCR (1 << 8)
> +#define WP_TOWR (1 << 9)
> +#define _OMAP_TIMER_MATCH_OFFSET 0x38
> +#define _OMAP_TIMER_CAPTURE_OFFSET 0x3c
> +#define _OMAP_TIMER_IF_CTRL_OFFSET 0x40
> +#define _OMAP_TIMER_CAPTURE2_OFFSET 0x44 /* TCAR2, 34xx only */
> +#define _OMAP_TIMER_TICK_POS_OFFSET 0x48 /* TPIR, 34xx only */
> +#define _OMAP_TIMER_TICK_NEG_OFFSET 0x4c /* TNIR, 34xx only */
> +#define _OMAP_TIMER_TICK_COUNT_OFFSET 0x50 /* TCVR, 34xx only */
> +#define _OMAP_TIMER_TICK_INT_MASK_SET_OFFSET 0x54 /* TOCR, 34xx only */
> +#define _OMAP_TIMER_TICK_INT_MASK_COUNT_OFFSET 0x58 /* TOWR, 34xx only */
> +
> +/* register offsets with the write pending bit encoded */
> +#define WPSHIFT 16
> +
> +#define OMAP_TIMER_ID_REG (_OMAP_TIMER_ID_OFFSET \
> + | (WP_NONE << WPSHIFT))
> +
> +#define OMAP_TIMER_OCP_CFG_REG (_OMAP_TIMER_OCP_CFG_OFFSET \
> + | (WP_NONE << WPSHIFT))
> +
> +#define OMAP_TIMER_SYS_STAT_REG (_OMAP_TIMER_SYS_STAT_OFFSET \
> + | (WP_NONE << WPSHIFT))
> +
> +#define OMAP_TIMER_STAT_REG (_OMAP_TIMER_STAT_OFFSET \
> + | (WP_NONE << WPSHIFT))
> +
> +#define OMAP_TIMER_INT_EN_REG (_OMAP_TIMER_INT_EN_OFFSET \
> + | (WP_NONE << WPSHIFT))
> +
> +#define OMAP_TIMER_WAKEUP_EN_REG (_OMAP_TIMER_WAKEUP_EN_OFFSET \
> + | (WP_NONE << WPSHIFT))
> +
> +#define OMAP_TIMER_CTRL_REG (_OMAP_TIMER_CTRL_OFFSET \
> + | (WP_TCLR << WPSHIFT))
> +
> +#define OMAP_TIMER_COUNTER_REG (_OMAP_TIMER_COUNTER_OFFSET \
> + | (WP_TCRR << WPSHIFT))
> +
> +#define OMAP_TIMER_LOAD_REG (_OMAP_TIMER_LOAD_OFFSET \
> + | (WP_TLDR << WPSHIFT))
> +
> +#define OMAP_TIMER_TRIGGER_REG (_OMAP_TIMER_TRIGGER_OFFSET \
> + | (WP_TTGR << WPSHIFT))
> +
> +#define OMAP_TIMER_WRITE_PEND_REG (_OMAP_TIMER_WRITE_PEND_OFFSET \
> + | (WP_NONE << WPSHIFT))
> +
> +#define OMAP_TIMER_MATCH_REG (_OMAP_TIMER_MATCH_OFFSET \
> + | (WP_TMAR << WPSHIFT))
> +
> +#define OMAP_TIMER_CAPTURE_REG (_OMAP_TIMER_CAPTURE_OFFSET \
> + | (WP_NONE << WPSHIFT))
> +
> +#define OMAP_TIMER_IF_CTRL_REG (_OMAP_TIMER_IF_CTRL_OFFSET \
> + | (WP_NONE << WPSHIFT))
> +
> +#define OMAP_TIMER_CAPTURE2_REG (_OMAP_TIMER_CAPTURE2_OFFSET \
> + | (WP_NONE << WPSHIFT))
> +
> +#define OMAP_TIMER_TICK_POS_REG (_OMAP_TIMER_TICK_POS_OFFSET \
> + | (WP_TPIR << WPSHIFT))
> +
> +#define OMAP_TIMER_TICK_NEG_REG (_OMAP_TIMER_TICK_NEG_OFFSET \
> + | (WP_TNIR << WPSHIFT))
> +
> +#define OMAP_TIMER_TICK_COUNT_REG (_OMAP_TIMER_TICK_COUNT_OFFSET \
> + | (WP_TCVR << WPSHIFT))
> +
> +#define OMAP_TIMER_TICK_INT_MASK_SET_REG \
> + (_OMAP_TIMER_TICK_INT_MASK_SET_OFFSET | (WP_TOCR << WPSHIFT))
> +
> +#define OMAP_TIMER_TICK_INT_MASK_COUNT_REG \
> + (_OMAP_TIMER_TICK_INT_MASK_COUNT_OFFSET | (WP_TOWR << WPSHIFT))
>
> struct omap_dm_timer {
> unsigned long phys_base;
> @@ -76,6 +155,7 @@ struct omap_dm_timer {
> void __iomem *io_base;
> unsigned reserved:1;
> unsigned enabled:1;
> + unsigned posted:1;
> };
>
> #ifdef CONFIG_ARCH_OMAP1
> @@ -181,16 +261,34 @@ static struct clk **dm_source_clocks;
>
> static spinlock_t dm_timer_lock;
>
> -static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, int reg)
> +/*
> + * Reads timer registers in posted and non-posted mode. The posted mode bit
> + * is encoded in reg. Note that in posted mode write pending bit must be
> + * checked. Otherwise a read of a non completed write will produce an error.
> + */
> +static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, u32 reg)
> {
> - return readl(timer->io_base + reg);
> + if (timer->posted)
> + while (readl(timer->io_base + (OMAP_TIMER_WRITE_PEND_REG & 0xff))
> + & (reg >> WPSHIFT))
> + cpu_relax();
> + return readl(timer->io_base + (reg & 0xff));
> }
>
> -static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, int reg, u32 value)
> +/*
> + * Writes timer registers in posted and non-posted mode. The posted mode bit
> + * is encoded in reg. Note that in posted mode the write pending bit must be
> + * checked. Otherwise a write on a register which has a pending write will be
> + * lost.
> + */
> +static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg,
> + u32 value)
> {
> - writel(value, timer->io_base + reg);
> - while (omap_dm_timer_read_reg(timer, OMAP_TIMER_WRITE_PEND_REG))
> - ;
> + if (timer->posted)
> + while (readl(timer->io_base + (OMAP_TIMER_WRITE_PEND_REG & 0xff))
> + & (reg >> WPSHIFT))
> + cpu_relax();
> + writel(value, timer->io_base + (reg & 0xff));
> }
>
> static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer)
> @@ -217,17 +315,23 @@ static void omap_dm_timer_reset(struct omap_dm_timer *timer)
> }
> omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
>
> - /* Set to smart-idle mode */
> l = omap_dm_timer_read_reg(timer, OMAP_TIMER_OCP_CFG_REG);
> - l |= 0x02 << 3;
> -
> - if (cpu_class_is_omap2() && timer == &dm_timers[0]) {
> - /* Enable wake-up only for GPT1 on OMAP2 CPUs*/
> + l |= 0x02 << 3; /* Set to smart-idle mode */
> + l |= 0x2 << 8; /* Set clock activity to perserve f-clock on idle */
> +
> + /*
> + * Enable wake-up only for GPT1 on OMAP2 CPUs.
> + * FIXME: All timers should have wake-up enabled and clear
> + * PRCM status.
> + */
> + if (cpu_class_is_omap2() && (timer == &dm_timers[0]))
> l |= 1 << 2;
> - /* Non-posted mode */
> - omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0);
> - }
> omap_dm_timer_write_reg(timer, OMAP_TIMER_OCP_CFG_REG, l);
> +
> + /* Match hardware reset default of posted mode */
> + omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG,
> + OMAP_TIMER_CTRL_POSTED);
> + timer->posted = 1;
> }
>
> static void omap_dm_timer_prepare(struct omap_dm_timer *timer)
> @@ -434,6 +538,11 @@ void omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload,
> l &= ~OMAP_TIMER_CTRL_AR;
> omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
> omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
> +
> + /* REVISIT: hw feature, ttgr overtaking tldr? */
> + while (readl(timer->io_base + (OMAP_TIMER_WRITE_PEND_REG & 0xff)))
> + cpu_relax();
> +
> omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
> }
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] I2C: Fix twl4030 timeouts on omap3430
2008-03-28 8:41 [PATCH] I2C: Fix twl4030 timeouts on omap3430 Tony Lindgren
@ 2008-03-31 10:43 ` Tony Lindgren
2008-03-31 14:30 ` Tony Lindgren
0 siblings, 1 reply; 11+ messages in thread
From: Tony Lindgren @ 2008-03-31 10:43 UTC (permalink / raw)
To: linux-omap
* Tony Lindgren <tony@atomide.com> [080328 10:41]:
> Hi all,
>
> This helps with the annoying I2C timeouts. Does anybody have an idea
> why the twl4030 chip does not like doing multiple transfers in a row?
>
> To me the only difference seems to be that clocks are idled between
> writing the twl4030 register and reading the register value.
I'll push this today with a REVISIT comment added.
Tony
> From 25c4c8f449819cb6f40b59ed1a9b25ebcc7cd72e Mon Sep 17 00:00:00 2001
> From: Tony Lindgren <tony@atomide.com>
> Date: Thu, 27 Mar 2008 19:05:30 +0200
> Subject: [PATCH] I2C: Fix twl4030 timeouts on omap3430
>
> For some reason doing a twl4030 write-read cycle can hang the I2C bus
> on omap3430. And doing the write and read separately in twl4030_i2c_read()
> seems to fix the problem...
>
> Not intended for applying, just a temporary workaround.
>
> diff --git a/drivers/i2c/chips/twl4030-core.c b/drivers/i2c/chips/twl4030-core.c
> index ded86e7..62868b0 100644
> --- a/drivers/i2c/chips/twl4030-core.c
> +++ b/drivers/i2c/chips/twl4030-core.c
> @@ -327,6 +327,7 @@ int twl4030_i2c_read(u8 mod_no, u8 * value, u8 reg, u8 num_bytes)
> return -EPERM;
> }
> mutex_lock(&twl->xfer_lock);
> +
> /* [MSG1] fill the register address data */
> msg = &twl->xfer_msg[0];
> msg->addr = twl->address;
> @@ -334,18 +335,25 @@ int twl4030_i2c_read(u8 mod_no, u8 * value, u8 reg, u8 num_bytes)
> msg->flags = 0; /* Read the register value */
> val = twl4030_map[mod_no].base + reg;
> msg->buf = &val;
> + ret = i2c_transfer(twl->client.adapter, twl->xfer_msg, 1);
> + if (ret < 0)
> + goto out;
> +
> /* [MSG2] fill the data rx buffer */
> msg = &twl->xfer_msg[1];
> msg->addr = twl->address;
> msg->flags = I2C_M_RD; /* Read the register value */
> msg->len = num_bytes; /* only n bytes */
> msg->buf = value;
> - ret = i2c_transfer(twl->client.adapter, twl->xfer_msg, 2);
> + ret = i2c_transfer(twl->client.adapter, twl->xfer_msg, 1);
> +
> +out:
> mutex_unlock(&twl->xfer_lock);
>
> /* i2cTransfer returns num messages.translate it pls.. */
> if (ret >= 0)
> ret = 0;
> +
> return ret;
> }
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] I2C: Fix twl4030 timeouts on omap3430
2008-03-31 10:43 ` Tony Lindgren
@ 2008-03-31 14:30 ` Tony Lindgren
2008-04-01 12:43 ` Tony Lindgren
0 siblings, 1 reply; 11+ messages in thread
From: Tony Lindgren @ 2008-03-31 14:30 UTC (permalink / raw)
To: linux-omap
* Tony Lindgren <tony@atomide.com> [080331 13:43]:
> * Tony Lindgren <tony@atomide.com> [080328 10:41]:
> > Hi all,
> >
> > This helps with the annoying I2C timeouts. Does anybody have an idea
> > why the twl4030 chip does not like doing multiple transfers in a row?
> >
> > To me the only difference seems to be that clocks are idled between
> > writing the twl4030 register and reading the register value.
>
> I'll push this today with a REVISIT comment added.
Looks like this kills twl4030 interrupts, so I've reverted it.
Tony
> > From 25c4c8f449819cb6f40b59ed1a9b25ebcc7cd72e Mon Sep 17 00:00:00 2001
> > From: Tony Lindgren <tony@atomide.com>
> > Date: Thu, 27 Mar 2008 19:05:30 +0200
> > Subject: [PATCH] I2C: Fix twl4030 timeouts on omap3430
> >
> > For some reason doing a twl4030 write-read cycle can hang the I2C bus
> > on omap3430. And doing the write and read separately in twl4030_i2c_read()
> > seems to fix the problem...
> >
> > Not intended for applying, just a temporary workaround.
> >
> > diff --git a/drivers/i2c/chips/twl4030-core.c b/drivers/i2c/chips/twl4030-core.c
> > index ded86e7..62868b0 100644
> > --- a/drivers/i2c/chips/twl4030-core.c
> > +++ b/drivers/i2c/chips/twl4030-core.c
> > @@ -327,6 +327,7 @@ int twl4030_i2c_read(u8 mod_no, u8 * value, u8 reg, u8 num_bytes)
> > return -EPERM;
> > }
> > mutex_lock(&twl->xfer_lock);
> > +
> > /* [MSG1] fill the register address data */
> > msg = &twl->xfer_msg[0];
> > msg->addr = twl->address;
> > @@ -334,18 +335,25 @@ int twl4030_i2c_read(u8 mod_no, u8 * value, u8 reg, u8 num_bytes)
> > msg->flags = 0; /* Read the register value */
> > val = twl4030_map[mod_no].base + reg;
> > msg->buf = &val;
> > + ret = i2c_transfer(twl->client.adapter, twl->xfer_msg, 1);
> > + if (ret < 0)
> > + goto out;
> > +
> > /* [MSG2] fill the data rx buffer */
> > msg = &twl->xfer_msg[1];
> > msg->addr = twl->address;
> > msg->flags = I2C_M_RD; /* Read the register value */
> > msg->len = num_bytes; /* only n bytes */
> > msg->buf = value;
> > - ret = i2c_transfer(twl->client.adapter, twl->xfer_msg, 2);
> > + ret = i2c_transfer(twl->client.adapter, twl->xfer_msg, 1);
> > +
> > +out:
> > mutex_unlock(&twl->xfer_lock);
> >
> > /* i2cTransfer returns num messages.translate it pls.. */
> > if (ret >= 0)
> > ret = 0;
> > +
> > return ret;
> > }
> >
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] I2C: Fix twl4030 timeouts on omap3430
2008-03-31 14:30 ` Tony Lindgren
@ 2008-04-01 12:43 ` Tony Lindgren
2008-04-01 13:00 ` Peter 'p2' De Schrijver
0 siblings, 1 reply; 11+ messages in thread
From: Tony Lindgren @ 2008-04-01 12:43 UTC (permalink / raw)
To: linux-omap
* Tony Lindgren <tony@atomide.com> [080331 17:30]:
> * Tony Lindgren <tony@atomide.com> [080331 13:43]:
> > * Tony Lindgren <tony@atomide.com> [080328 10:41]:
> > > Hi all,
> > >
> > > This helps with the annoying I2C timeouts. Does anybody have an idea
> > > why the twl4030 chip does not like doing multiple transfers in a row?
> > >
> > > To me the only difference seems to be that clocks are idled between
> > > writing the twl4030 register and reading the register value.
> >
> > I'll push this today with a REVISIT comment added.
>
> Looks like this kills twl4030 interrupts, so I've reverted it.
After looking into this problem a bit more, looks like twl4030 reads
to anything in "POWER ID" (modules 0x10 and higher) will hang twl4030
eventually and I2C controller gets stuck in mode where STP never clears.
Repeated reads to "USB ID", "AUD ID" or "AUX ID" will not hang twl4030.
Tony
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] I2C: Fix twl4030 timeouts on omap3430
2008-04-01 12:43 ` Tony Lindgren
@ 2008-04-01 13:00 ` Peter 'p2' De Schrijver
2008-04-01 13:38 ` Tony Lindgren
0 siblings, 1 reply; 11+ messages in thread
From: Peter 'p2' De Schrijver @ 2008-04-01 13:00 UTC (permalink / raw)
To: ext Tony Lindgren; +Cc: linux-omap
On Tue, Apr 01, 2008 at 03:43:56PM +0300, ext Tony Lindgren wrote:
> * Tony Lindgren <tony@atomide.com> [080331 17:30]:
> > * Tony Lindgren <tony@atomide.com> [080331 13:43]:
> > > * Tony Lindgren <tony@atomide.com> [080328 10:41]:
> > > > Hi all,
> > > >
> > > > This helps with the annoying I2C timeouts. Does anybody have an idea
> > > > why the twl4030 chip does not like doing multiple transfers in a row?
> > > >
> > > > To me the only difference seems to be that clocks are idled between
> > > > writing the twl4030 register and reading the register value.
> > >
> > > I'll push this today with a REVISIT comment added.
> >
> > Looks like this kills twl4030 interrupts, so I've reverted it.
>
> After looking into this problem a bit more, looks like twl4030 reads
> to anything in "POWER ID" (modules 0x10 and higher) will hang twl4030
> eventually and I2C controller gets stuck in mode where STP never clears.
>
> Repeated reads to "USB ID", "AUD ID" or "AUX ID" will not hang twl4030.
>
I remember seeing something similar when doing the powerbutton code.
Klaus Pedersen found out that leaving CFG_BOOT to its reset value solved
the problem. Unfortunately this breaks MADC and USB afaics, so it's not
a real solution. CFG_BOOT is programmed in power_companion_init().
Cheers,
Peter.
--
goa is a state of mind
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] I2C: Fix twl4030 timeouts on omap3430
2008-04-01 13:00 ` Peter 'p2' De Schrijver
@ 2008-04-01 13:38 ` Tony Lindgren
2008-04-01 14:04 ` Tony Lindgren
0 siblings, 1 reply; 11+ messages in thread
From: Tony Lindgren @ 2008-04-01 13:38 UTC (permalink / raw)
To: Peter 'p2' De Schrijver; +Cc: linux-omap
* Peter 'p2' De Schrijver <peter.de-schrijver@nokia.com> [080401 16:01]:
> On Tue, Apr 01, 2008 at 03:43:56PM +0300, ext Tony Lindgren wrote:
> > * Tony Lindgren <tony@atomide.com> [080331 17:30]:
> > > * Tony Lindgren <tony@atomide.com> [080331 13:43]:
> > > > * Tony Lindgren <tony@atomide.com> [080328 10:41]:
> > > > > Hi all,
> > > > >
> > > > > This helps with the annoying I2C timeouts. Does anybody have an idea
> > > > > why the twl4030 chip does not like doing multiple transfers in a row?
> > > > >
> > > > > To me the only difference seems to be that clocks are idled between
> > > > > writing the twl4030 register and reading the register value.
> > > >
> > > > I'll push this today with a REVISIT comment added.
> > >
> > > Looks like this kills twl4030 interrupts, so I've reverted it.
> >
> > After looking into this problem a bit more, looks like twl4030 reads
> > to anything in "POWER ID" (modules 0x10 and higher) will hang twl4030
> > eventually and I2C controller gets stuck in mode where STP never clears.
> >
> > Repeated reads to "USB ID", "AUD ID" or "AUX ID" will not hang twl4030.
> >
>
> I remember seeing something similar when doing the powerbutton code.
> Klaus Pedersen found out that leaving CFG_BOOT to its reset value solved
> the problem. Unfortunately this breaks MADC and USB afaics, so it's not
> a real solution. CFG_BOOT is programmed in power_companion_init().
Great, this helped a lot! Looks like power_companion_init() tries to get
osc_ck without checking for clkg_get() return value, and osc_ck does not
exist in clock34xx.h. So R_CFG_BOOT gets set to default 26MHz value :)
Will post a patch when available.
Tony
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] I2C: Fix twl4030 timeouts on omap3430
2008-04-01 13:38 ` Tony Lindgren
@ 2008-04-01 14:04 ` Tony Lindgren
0 siblings, 0 replies; 11+ messages in thread
From: Tony Lindgren @ 2008-04-01 14:04 UTC (permalink / raw)
To: Peter 'p2' De Schrijver; +Cc: linux-omap
[-- Attachment #1: Type: text/plain, Size: 1843 bytes --]
* Tony Lindgren <tony@atomide.com> [080401 16:39]:
> * Peter 'p2' De Schrijver <peter.de-schrijver@nokia.com> [080401 16:01]:
> > On Tue, Apr 01, 2008 at 03:43:56PM +0300, ext Tony Lindgren wrote:
> > > * Tony Lindgren <tony@atomide.com> [080331 17:30]:
> > > > * Tony Lindgren <tony@atomide.com> [080331 13:43]:
> > > > > * Tony Lindgren <tony@atomide.com> [080328 10:41]:
> > > > > > Hi all,
> > > > > >
> > > > > > This helps with the annoying I2C timeouts. Does anybody have an idea
> > > > > > why the twl4030 chip does not like doing multiple transfers in a row?
> > > > > >
> > > > > > To me the only difference seems to be that clocks are idled between
> > > > > > writing the twl4030 register and reading the register value.
> > > > >
> > > > > I'll push this today with a REVISIT comment added.
> > > >
> > > > Looks like this kills twl4030 interrupts, so I've reverted it.
> > >
> > > After looking into this problem a bit more, looks like twl4030 reads
> > > to anything in "POWER ID" (modules 0x10 and higher) will hang twl4030
> > > eventually and I2C controller gets stuck in mode where STP never clears.
> > >
> > > Repeated reads to "USB ID", "AUD ID" or "AUX ID" will not hang twl4030.
> > >
> >
> > I remember seeing something similar when doing the powerbutton code.
> > Klaus Pedersen found out that leaving CFG_BOOT to its reset value solved
> > the problem. Unfortunately this breaks MADC and USB afaics, so it's not
> > a real solution. CFG_BOOT is programmed in power_companion_init().
>
> Great, this helped a lot! Looks like power_companion_init() tries to get
> osc_ck without checking for clkg_get() return value, and osc_ck does not
> exist in clock34xx.h. So R_CFG_BOOT gets set to default 26MHz value :)
>
> Will post a patch when available.
Thanks again, here's a patch, I've pushed it also.
Tony
[-- Attachment #2: i2c-twl4030-clock.patch --]
[-- Type: text/x-diff, Size: 1317 bytes --]
From: Tony Lindgren <tony@atomide.com>
Date: Tue, 1 Apr 2008 16:58:02 +0300
Subject: [PATCH] I2C: Fix twl4030 clock init
Without this patch twl4030 clock can get programmed to incorrect rate
which can eventually hang twl4030 reads. Also minor formatting fixes.
Signed-off-by: Tony Lindgren <tony@atomide.com>
--- a/drivers/i2c/chips/twl4030-core.c
+++ b/drivers/i2c/chips/twl4030-core.c
@@ -672,11 +672,20 @@ static int power_companion_init(void)
u32 rate, ctrl = HFCLK_FREQ_26_MHZ;
int e = 0;
- osc = clk_get(NULL,"osc_ck");
+ if (cpu_is_omap2430())
+ osc = clk_get(NULL, "osc_ck");
+ else
+ osc = clk_get(NULL, "osc_sys_ck");
+ if (IS_ERR(osc)) {
+ printk(KERN_ERR "Skipping twl3040 internal clock init and "
+ "using bootloader value (unknown osc rate)\n");
+ return 0;
+ }
+
rate = clk_get_rate(osc);
clk_put(osc);
- switch(rate) {
+ switch (rate) {
case 19200000 : ctrl = HFCLK_FREQ_19p2_MHZ; break;
case 26000000 : ctrl = HFCLK_FREQ_26_MHZ; break;
case 38400000 : ctrl = HFCLK_FREQ_38p4_MHZ; break;
@@ -684,7 +693,7 @@ static int power_companion_init(void)
ctrl |= HIGH_PERF_SQ;
e |= unprotect_pm_master();
- /* effect->MADC+USB ck en */
+ /* effect->MADC+USB ck en */
e |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, ctrl, R_CFG_BOOT);
e |= protect_pm_master();
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2008-04-01 14:05 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-03-28 8:41 [PATCH] I2C: Fix twl4030 timeouts on omap3430 Tony Lindgren
2008-03-31 10:43 ` Tony Lindgren
2008-03-31 14:30 ` Tony Lindgren
2008-04-01 12:43 ` Tony Lindgren
2008-04-01 13:00 ` Peter 'p2' De Schrijver
2008-04-01 13:38 ` Tony Lindgren
2008-04-01 14:04 ` Tony Lindgren
-- strict thread matches above, loose matches on Subject: below --
2008-03-28 11:46 [PATCH 0/2] Add sram34xx.S Tony Lindgren
2008-03-28 11:46 ` [PATCH 1/2] ARM: OMAP3: Add 34xx SRAM functions Tony Lindgren
2008-03-28 11:46 ` [PATCH 2/2] ARCH: OMAP3: Make SRAM code from TI CDP compile and work Tony Lindgren
[not found] ` <1206704800-6768-4-git-send-email-tony@atomide.com>
2008-03-28 11:46 ` [PATCH] I2C: Fix twl4030 timeouts on omap3430 Tony Lindgren
2008-03-28 11:46 ` Tony Lindgren
2008-03-28 11:49 ` Tony Lindgren
2008-03-28 11:48 ` Tony Lindgren
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox