* RE: [PATCH] : Fix annoying 'too much work for irq72' serial port watermark error.
@ 2006-03-23 19:29 Woodruff, Richard
2006-03-23 19:46 ` Tony Lindgren
0 siblings, 1 reply; 3+ messages in thread
From: Woodruff, Richard @ 2006-03-23 19:29 UTC (permalink / raw)
To: tony; +Cc: linux-omap-open-source
[-- Attachment #1: Type: text/plain, Size: 1462 bytes --]
Perhaps this version is better. It avoids a possible double entry to
transmit_chars() and removes the need for an #ifdef OMAPxyz.
Regards,
Richard W.
> I was just trying out a different PRCM set on 2420/2430 and the
annoying
> serial 'too much work' interrupt started up again even with an SCR=8.
> I've attached a patch which fixes the problem. At fastest PRCM sets
> timing some issues have popped up which have been masked before.
>
> Note: This patch is a consolidated on with the other patches I had
> previously sent which have not been applied, as this is what I tested
> with. The timer version in this patch is good, and I validated
outputs
> using the emulator at function entry/exits. Some of the earlier
> versions had overflow issues with nano-seconds.
>
> This patch now allows the OMAP_SCR to go back to 0, though it works
> either way. This issue was this: A THR interrupt can be generated
> which has status in the IIR, but without status in the LSR. This is
> because the TX watermark interrupt happens when the FIFO and SR are
NOT
> empty. The previous logic assumed at least the FIFO was empty before
> calling the TX routine. For a watermark this is not the case. The
fix
> I added to this code checks the SSR (supplementary status register) to
> see if there is room in the FIFO. Given we know how it was loaded
there
> will be room if this shows it is not empty.
>
> Regards,
> Richard W.
[-- Attachment #2: serial_fix_timer_irq.diff --]
[-- Type: application/octet-stream, Size: 13397 bytes --]
diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h
index 6c78d47..2781dfb 100644
--- a/arch/arm/mach-omap2/clock.h
+++ b/arch/arm/mach-omap2/clock.h
@@ -1062,7 +1062,7 @@ static struct clk gpt2_ick = {
.parent = &l4_ck,
.flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
.enable_reg = (void __iomem *)&CM_ICLKEN1_CORE, /* Bit4 */
- .enable_bit = 0,
+ .enable_bit = 4,
.recalc = &omap2_followparent_recalc,
};
diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c
index d7baff6..79a45bd 100644
--- a/arch/arm/mach-omap2/irq.c
+++ b/arch/arm/mach-omap2/irq.c
@@ -26,6 +26,11 @@
#define INTC_MIR_CLEAR0 0x0088
#define INTC_MIR_SET0 0x008c
+#define sync_p_port_write() \
+ ({ int dv = 0; \
+ __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 4" : : "r" (dv)); \
+ })
+
/*
* OMAP2 has a number of different interrupt controllers, each interrupt
* controller is identified as its own "bank". Register definitions are
@@ -61,6 +66,7 @@ static struct omap_irq_bank {
static void omap_ack_irq(unsigned int irq)
{
omap_writel(0x1, irq_banks[0].base_reg + INTC_CONTROL);
+ sync_p_port_write();
}
static void omap_mask_irq(unsigned int irq)
@@ -74,6 +80,7 @@ static void omap_mask_irq(unsigned int i
}
omap_writel(1 << irq, irq_banks[0].base_reg + INTC_MIR_SET0 + offset);
+ sync_p_port_write();
}
static void omap_unmask_irq(unsigned int irq)
@@ -87,6 +94,7 @@ static void omap_unmask_irq(unsigned int
}
omap_writel(1 << irq, irq_banks[0].base_reg + INTC_MIR_CLEAR0 + offset);
+ sync_p_port_write();
}
static void omap_mask_ack_irq(unsigned int irq)
diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c
index 24dd374..bec3bde 100644
--- a/arch/arm/mach-omap2/serial.c
+++ b/arch/arm/mach-omap2/serial.c
@@ -82,7 +82,8 @@ static inline void serial_write_reg(stru
static inline void __init omap_serial_reset(struct plat_serial8250_port *p)
{
serial_write_reg(p, UART_OMAP_MDR1, 0x07);
- serial_write_reg(p, UART_OMAP_SCR, 0x08);
+// serial_write_reg(p, UART_OMAP_SCR, 0x08); /* wartermark broke */
+ serial_write_reg(p, UART_OMAP_SCR, 0x00); /* watermark fixed */
serial_write_reg(p, UART_OMAP_MDR1, 0x00);
serial_write_reg(p, UART_OMAP_SYSC, 0x01);
}
diff --git a/arch/arm/mach-omap2/timer-gp.c b/arch/arm/mach-omap2/timer-gp.c
index 1d2f5ac..dc56a7b 100644
--- a/arch/arm/mach-omap2/timer-gp.c
+++ b/arch/arm/mach-omap2/timer-gp.c
@@ -8,8 +8,7 @@
* Juha Yrjölä <juha.yrjola@nokia.com>
*
* Some parts based off of TI's 24xx code:
- *
- * Copyright (C) 2004 Texas Instruments, Inc.
+ * Copyright (C) 2006 Texas Instruments, Inc.
*
* Roughly modelled after the OMAP1 MPU timer code.
*
@@ -26,6 +25,7 @@
#include <asm/mach/time.h>
#include <asm/delay.h>
#include <asm/io.h>
+#include <asm/div64.h>
#define OMAP2_GP_TIMER1_BASE 0x48028000
#define OMAP2_GP_TIMER2_BASE 0x4802a000
@@ -40,7 +40,17 @@
#define GP_TIMER_TLDR 0x2c
#define GP_TIMER_TSICR 0x40
+/* system ticker */
#define OS_TIMER_NR 1 /* GP timer 2 */
+#define OS_TIMER_NR_IRQ INT_24XX_GPTIMER2
+/* gettimeoffset and sched_clock */
+#define OS_TIMER_OFF 2 /* GP timer 3 */
+#define OS_TIMER_OFF_IRQ INT_24XX_GPTIMER3
+/* Use sys_ck as source */
+#define GP_SRC_CLK "sys_ck"
+
+#define TIMER_ON 1
+#define TIMER_OFF 0
static unsigned long timer_base[] = {
IO_ADDRESS(OMAP2_GP_TIMER1_BASE),
@@ -49,6 +59,13 @@ static unsigned long timer_base[] = {
IO_ADDRESS(OMAP2_GP_TIMER4_BASE),
};
+struct sys_timer omap_timer;
+static u32 omap_mpu_timer_last; /* Last processed system timer interrupt */
+static u32 gpticks_per_msec;
+static u32 gpticks_mask_per_jiffy; /* clarify & simplify isr calculation */
+unsigned long long num_jiffies_in_ov_in_ns; /* # jiffies in 0xffffffff ticks */
+unsigned long long tot_jiffies_in_ns = 0; /* total jiffies in ns */
+
static inline unsigned int timer_read_reg(int nr, unsigned int reg)
{
return __raw_readl(timer_base[nr] + reg);
@@ -60,7 +77,7 @@ static inline void timer_write_reg(int n
}
/* Note that we always enable the clock prescale divider bit */
-static inline void omap2_gp_timer_start(int nr, unsigned long load_val)
+static inline void omap2_gp_timer_start(int nr, unsigned long load_val, int interrupt)
{
unsigned int tmp;
@@ -68,13 +85,27 @@ static inline void omap2_gp_timer_start(
timer_write_reg(nr, GP_TIMER_TLDR, tmp);
timer_write_reg(nr, GP_TIMER_TCRR, tmp);
- timer_write_reg(nr, GP_TIMER_TIER, 1 << 1);
+ timer_write_reg(nr, GP_TIMER_TIER, interrupt << 1);
timer_write_reg(nr, GP_TIMER_TCLR, (1 << 5) | (1 << 1) | 1);
}
+/*
+ * Elapsed time between interrupts is calculated using timerX (x=2).
+ * Latency during the interrupt is calculated using timerY (y=3).
+ * Both timer2 and timer3 counting at sys_ck/2.
+ */
static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id,
struct pt_regs *regs)
{
+ u32 now, lat;
+
+ /* get current tick time stamp */
+ now = timer_read_reg(OS_TIMER_OFF, GP_TIMER_TCRR);
+
+ /* get ticks since autoreload from 0xfffffxyz - 0xfffff000*/
+ lat = timer_read_reg(OS_TIMER_NR, GP_TIMER_TCRR) - gpticks_mask_per_jiffy;
+ omap_mpu_timer_last = now - lat;
+
write_seqlock(&xtime_lock);
timer_write_reg(OS_TIMER_NR, GP_TIMER_TISR, 1 << 1);
@@ -85,43 +116,165 @@ static irqreturn_t omap2_gp_timer_interr
return IRQ_HANDLED;
}
+static int omap2_gp_clkctrl(u32 nr, unsigned long *rate, u32 on_off,
+ char *n_src)
+{
+ struct clk *src_ck, *gpt_fck, *gpt_ick;
+ char n_fck[16], n_ick[16];
+
+ ++nr;
+ snprintf(n_fck, sizeof(n_fck), "gpt%d_fck", nr);
+ snprintf(n_ick, sizeof(n_ick), "gpt%d_ick", nr);
+
+ src_ck = clk_get(NULL, n_src);
+ gpt_fck = clk_get(NULL, n_fck);
+ gpt_ick = clk_get(NULL, n_ick);
+
+ if (IS_ERR(src_ck) || IS_ERR(gpt_fck) || IS_ERR(gpt_ick)){
+ printk(KERN_ERR "Serious problem starting system GPT timer \n");
+ return 0;
+ }
+ if(on_off){
+ clk_set_parent(gpt_fck, src_ck);
+ clk_enable(gpt_fck);
+ clk_enable(gpt_ick);
+ if(rate != NULL)
+ *rate = clk_get_rate(src_ck);
+ } else {
+ clk_disable(gpt_fck);
+ clk_disable(gpt_ick);
+ }
+ clk_put(src_ck);
+ clk_put(gpt_fck);
+ clk_put(gpt_ick);
+ return 1;
+}
+
static struct irqaction omap2_gp_timer_irq = {
.name = "gp timer",
.flags = SA_INTERRUPT,
.handler = omap2_gp_timer_interrupt,
};
-static void __init omap2_gp_timer_init(void)
+/* inits up timer for 100 ticks / sec the time base is assumed to be in
+ * the MHz range. The usual source will be sys_ck divided by 2
+ */
+static u32 omap2_gp_timer_init(int nr)
{
- struct clk * sys_ck;
- u32 tick_period = 120000;
+ unsigned long tick_period, gprate;
u32 l;
+ /* enable timer clocks & set parent to sys_ck */
+ omap2_gp_clkctrl(nr, &gprate, TIMER_ON, GP_SRC_CLK);
+
+ gprate /= 2; /* Min timer prescale divider is 2 */
+ gpticks_per_msec = gprate / 1000; /* gpticks/s -> gpticks/ms */
+
+ tick_period = gprate / HZ; /* num of gpticks in 1 system tick (jiffy) */
+ tick_period -= 1;
+ gpticks_mask_per_jiffy = 0xffffffff - tick_period;
+
/* Reset clock and prescale value */
- timer_write_reg(OS_TIMER_NR, GP_TIMER_TCLR, 0);
+ timer_write_reg(nr, GP_TIMER_TCLR, 0);
+
+ l = timer_read_reg(nr, GP_TIMER_TIDR);
+ printk(KERN_INFO "OMAP2 GP timer (HW version %d.%d)\n",
+ (l >> 4) & 0x0f, l & 0x0f);
- sys_ck = clk_get(NULL, "sys_ck");
- if (IS_ERR(sys_ck))
- printk(KERN_ERR "Could not get sys_ck\n");
- else {
- clk_enable(sys_ck);
- tick_period = clk_get_rate(sys_ck) / 100;
- clk_put(sys_ck);
+ setup_irq(OS_TIMER_NR_IRQ, &omap2_gp_timer_irq);
+
+ omap2_gp_timer_start(nr, tick_period, 1);
+
+ return gprate;
+}
+
+static irqreturn_t omap2_gpt_off_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ tot_jiffies_in_ns += num_jiffies_in_ov_in_ns;
+ timer_write_reg(OS_TIMER_OFF, GP_TIMER_TISR, 1 << 1);
+ return IRQ_HANDLED;
+}
+
+static struct irqaction omap2_gpt_off_irq = {
+ .name = "gp timer overflow",
+ .flags = SA_INTERRUPT,
+ .handler = omap2_gpt_off_interrupt,
+};
+
+static void omap2_gp_timer_offset_init(int nr, unsigned long *rate)
+{
+ if(omap2_gp_clkctrl(nr, rate, TIMER_ON, GP_SRC_CLK)){
+ timer_write_reg(nr, GP_TIMER_TCLR, 0);
+ omap2_gp_timer_start(nr, 0xffffffff, 1);
+ setup_irq(OS_TIMER_OFF_IRQ, &omap2_gpt_off_irq);
}
+}
- tick_period /= 2; /* Minimum prescale divider is 2 */
- tick_period -= 1;
+/* useful for small offset values */
+static u32 machinecycles_to_usecs(u32 gpticks)
+{
+ return (((gpticks * 2 * 1000)/ gpticks_per_msec) + 1) >> 1;
+}
+
+/* useful for large offset timer values */
+static unsigned long long machinecycles_to_ns(u32 gpticks)
+{
+ unsigned long long nsecs;
- l = timer_read_reg(OS_TIMER_NR, GP_TIMER_TIDR);
- printk(KERN_INFO "OMAP2 GP timer (HW version %d.%d)\n",
- (l >> 4) & 0x0f, l & 0x0f);
+ /* gpticks/ticks-per-msec = msec
+ * msec * 1000000 = nsec
+ */
+ nsecs = (u64)gpticks * 1000000;
+ do_div(nsecs, gpticks_per_msec);
+ return nsecs;
+}
+
+/*
+ * Scheduler clock - returns total jiffies expressed in nanosec.
+ */
+unsigned long long sched_clock(void)
+{
+ u32 elapsed_cycles;
+ unsigned long long elapsed_ns;
+
+ elapsed_cycles = timer_read_reg(OS_TIMER_OFF, GP_TIMER_TCRR);
+ elapsed_ns = machinecycles_to_ns(elapsed_cycles);
+ return (tot_jiffies_in_ns + elapsed_ns);
+}
+
+/* return elapsed usecs since last system timer ISR */
+static unsigned long omap2_gettimeoffset(void)
+{
+ u32 now, elapsed_usec, elapsed_cycles;
+
+ now = timer_read_reg(OS_TIMER_OFF, GP_TIMER_TCRR);
+ elapsed_cycles = now - omap_mpu_timer_last;
+ elapsed_usec = machinecycles_to_usecs(elapsed_cycles);
+ return(elapsed_usec);
+}
+
+static void __init omap2_timer_init(void)
+{
+ u32 gprate;
+ u32 num_ns_in_jiffy;
- setup_irq(38, &omap2_gp_timer_irq);
+ omap2_gp_timer_offset_init(OS_TIMER_OFF, NULL);
+ gprate = omap2_gp_timer_init(OS_TIMER_NR);
- omap2_gp_timer_start(OS_TIMER_NR, tick_period);
+ /* 0xffffffff * 1/gprate = seconds in overflow = siof
+ * siof * HZ (jiffies in a second) = num of jiffies in overflow = njiof
+ * njiof * ns in a jiffy (1000000000/HZ) = num_jiffies_in_ov_in_ns
+ */
+ num_jiffies_in_ov_in_ns = 0xffffffff * HZ;
+ do_div(num_jiffies_in_ov_in_ns, gprate); /* jiffies in 0xffffffff ticks */
+ num_ns_in_jiffy = 1000000000 / HZ; /* num ns in a jiffy */
+ num_jiffies_in_ov_in_ns *= (u64)num_ns_in_jiffy;
+ omap_timer.offset = omap2_gettimeoffset;
}
struct sys_timer omap_timer = {
- .init = omap2_gp_timer_init,
+ .init = omap2_timer_init,
+ .offset = NULL,
};
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index 8b1fc5a..40c6d41 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -1268,7 +1268,8 @@ static unsigned int check_modem_status(s
* This handles the interrupt from one port.
*/
static inline void
-serial8250_handle_port(struct uart_8250_port *up, struct pt_regs *regs)
+serial8250_handle_port(struct uart_8250_port *up, struct pt_regs *regs,
+ unsigned int iir)
{
unsigned int status;
@@ -1281,7 +1282,11 @@ serial8250_handle_port(struct uart_8250_
if (status & UART_LSR_DR)
receive_chars(up, &status, regs);
check_modem_status(up);
- if (status & UART_LSR_THRE)
+
+ /* load space in tx fifo if entry from THRI-watermark
+ * or if THRE shows empty.
+ */
+ if ((status & UART_LSR_THRE) || (iir & UART_IIR_THRI))
transmit_chars(up);
spin_unlock(&up->port.lock);
@@ -1320,7 +1325,7 @@ static irqreturn_t serial8250_interrupt(
iir = serial_in(up, UART_IIR);
if (!(iir & UART_IIR_NO_INT)) {
- serial8250_handle_port(up, regs);
+ serial8250_handle_port(up, regs, iir);
handled = 1;
@@ -1421,7 +1426,7 @@ static void serial8250_timeout(unsigned
iir = serial_in(up, UART_IIR);
if (!(iir & UART_IIR_NO_INT))
- serial8250_handle_port(up, NULL);
+ serial8250_handle_port(up, NULL, iir);
timeout = up->port.timeout;
timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
diff --git a/include/asm-arm/arch-omap/irqs.h b/include/asm-arm/arch-omap/irqs.h
index 42098d9..458b026 100644
--- a/include/asm-arm/arch-omap/irqs.h
+++ b/include/asm-arm/arch-omap/irqs.h
@@ -242,6 +242,8 @@
#define INT_24XX_GPIO_BANK2 30
#define INT_24XX_GPIO_BANK3 31
#define INT_24XX_GPIO_BANK4 32
+#define INT_24XX_GPTIMER2 38
+#define INT_24XX_GPTIMER3 39
#define INT_24XX_MCBSP1_IRQ_TX 59
#define INT_24XX_MCBSP1_IRQ_RX 60
#define INT_24XX_MCBSP2_IRQ_TX 62
diff --git a/include/asm-arm/arch-omap/timex.h b/include/asm-arm/arch-omap/timex.h
index 21f2e36..1d89679 100644
--- a/include/asm-arm/arch-omap/timex.h
+++ b/include/asm-arm/arch-omap/timex.h
@@ -34,8 +34,12 @@
*/
#ifdef CONFIG_OMAP_32K_TIMER
#define CLOCK_TICK_RATE (CONFIG_OMAP_32K_TIMER_HZ)
+#elif defined(CONFIG_MACH_OMAP_H4)
+#define CLOCK_TICK_RATE (6000000) /* sys_clk/2 */
+#elif defined(CONFIG_MACH_OMAP_2430SDP)
+#define CLOCK_TICK_RATE (6500000)
#else
-#define CLOCK_TICK_RATE (HZ * 100000UL)
+#define CLOCK_TICK_RATE (6000000)
#endif
#endif /* __ASM_ARCH_OMAP_TIMEX_H */
diff --git a/include/linux/serial_reg.h b/include/linux/serial_reg.h
index 3c8a6aa..93df0cc 100644
--- a/include/linux/serial_reg.h
+++ b/include/linux/serial_reg.h
@@ -321,5 +321,8 @@
#define UART_OMAP_SYSC 0x15 /* System configuration register */
#define UART_OMAP_SYSS 0x16 /* System status register */
+#define UART_SSR_TX_FIFO_FULL 0x1
+#define UART_SSR_RX_CTS_WU_STS 0x2
+
#endif /* _LINUX_SERIAL_REG_H */
[-- Attachment #3: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH] : Fix annoying 'too much work for irq72' serial port watermark error.
2006-03-23 19:29 [PATCH] : Fix annoying 'too much work for irq72' serial port watermark error Woodruff, Richard
@ 2006-03-23 19:46 ` Tony Lindgren
0 siblings, 0 replies; 3+ messages in thread
From: Tony Lindgren @ 2006-03-23 19:46 UTC (permalink / raw)
To: Woodruff, Richard; +Cc: linux-omap-open-source
* Woodruff, Richard <r-woodruff2@ti.com> [060323 11:30]:
> Perhaps this version is better. It avoids a possible double entry to
> transmit_chars() and removes the need for an #ifdef OMAPxyz.
OK, let's test this on various boards.
> > I was just trying out a different PRCM set on 2420/2430 and the
> annoying
> > serial 'too much work' interrupt started up again even with an SCR=8.
> > I've attached a patch which fixes the problem. At fastest PRCM sets
> > timing some issues have popped up which have been masked before.
> >
> > Note: This patch is a consolidated on with the other patches I had
> > previously sent which have not been applied, as this is what I tested
> > with. The timer version in this patch is good, and I validated
> outputs
> > using the emulator at function entry/exits. Some of the earlier
> > versions had overflow issues with nano-seconds.
> >
> > This patch now allows the OMAP_SCR to go back to 0, though it works
> > either way. This issue was this: A THR interrupt can be generated
> > which has status in the IIR, but without status in the LSR. This is
> > because the TX watermark interrupt happens when the FIFO and SR are
> NOT
> > empty. The previous logic assumed at least the FIFO was empty before
> > calling the TX routine. For a watermark this is not the case. The
> fix
> > I added to this code checks the SSR (supplementary status register) to
> > see if there is room in the FIFO. Given we know how it was loaded
> there
> > will be room if this shows it is not empty.
Thanks, I'll take a look at the timer code soonish.
Tony
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH] : Fix annoying 'too much work for irq72' serial port watermark error.
@ 2006-03-23 17:53 Woodruff, Richard
0 siblings, 0 replies; 3+ messages in thread
From: Woodruff, Richard @ 2006-03-23 17:53 UTC (permalink / raw)
To: tony; +Cc: linux-omap-open-source
[-- Attachment #1: Type: text/plain, Size: 1261 bytes --]
Tony,
I was just trying out a different PRCM set on 2420/2430 and the annoying
serial 'too much work' interrupt started up again even with an SCR=8.
I've attached a patch which fixes the problem. At fastest PRCM sets
timing some issues have popped up which have been masked before.
Note: This patch is a consolidated on with the other patches I had
previously sent which have not been applied, as this is what I tested
with. The timer version in this patch is good, and I validated outputs
using the emulator at function entry/exits. Some of the earlier
versions had overflow issues with nano-seconds.
This patch now allows the OMAP_SCR to go back to 0, though it works
either way. This issue was this: A THR interrupt can be generated
which has status in the IIR, but without status in the LSR. This is
because the TX watermark interrupt happens when the FIFO and SR are NOT
empty. The previous logic assumed at least the FIFO was empty before
calling the TX routine. For a watermark this is not the case. The fix
I added to this code checks the SSR (supplementary status register) to
see if there is room in the FIFO. Given we know how it was loaded there
will be room if this shows it is not empty.
Regards,
Richard W.
[-- Attachment #2: serial_fix_timer_irq.diff --]
[-- Type: application/octet-stream, Size: 12882 bytes --]
diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h
index 6c78d47..2781dfb 100644
--- a/arch/arm/mach-omap2/clock.h
+++ b/arch/arm/mach-omap2/clock.h
@@ -1062,7 +1062,7 @@ static struct clk gpt2_ick = {
.parent = &l4_ck,
.flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
.enable_reg = (void __iomem *)&CM_ICLKEN1_CORE, /* Bit4 */
- .enable_bit = 0,
+ .enable_bit = 4,
.recalc = &omap2_followparent_recalc,
};
diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c
index d7baff6..79a45bd 100644
--- a/arch/arm/mach-omap2/irq.c
+++ b/arch/arm/mach-omap2/irq.c
@@ -26,6 +26,11 @@
#define INTC_MIR_CLEAR0 0x0088
#define INTC_MIR_SET0 0x008c
+#define sync_p_port_write() \
+ ({ int dv = 0; \
+ __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 4" : : "r" (dv)); \
+ })
+
/*
* OMAP2 has a number of different interrupt controllers, each interrupt
* controller is identified as its own "bank". Register definitions are
@@ -61,6 +66,7 @@ static struct omap_irq_bank {
static void omap_ack_irq(unsigned int irq)
{
omap_writel(0x1, irq_banks[0].base_reg + INTC_CONTROL);
+ sync_p_port_write();
}
static void omap_mask_irq(unsigned int irq)
@@ -74,6 +80,7 @@ static void omap_mask_irq(unsigned int i
}
omap_writel(1 << irq, irq_banks[0].base_reg + INTC_MIR_SET0 + offset);
+ sync_p_port_write();
}
static void omap_unmask_irq(unsigned int irq)
@@ -87,6 +94,7 @@ static void omap_unmask_irq(unsigned int
}
omap_writel(1 << irq, irq_banks[0].base_reg + INTC_MIR_CLEAR0 + offset);
+ sync_p_port_write();
}
static void omap_mask_ack_irq(unsigned int irq)
diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c
index 24dd374..bec3bde 100644
--- a/arch/arm/mach-omap2/serial.c
+++ b/arch/arm/mach-omap2/serial.c
@@ -82,7 +82,8 @@ static inline void serial_write_reg(stru
static inline void __init omap_serial_reset(struct plat_serial8250_port *p)
{
serial_write_reg(p, UART_OMAP_MDR1, 0x07);
- serial_write_reg(p, UART_OMAP_SCR, 0x08);
+// serial_write_reg(p, UART_OMAP_SCR, 0x08); /* wartermark broke */
+ serial_write_reg(p, UART_OMAP_SCR, 0x00); /* watermark fixed */
serial_write_reg(p, UART_OMAP_MDR1, 0x00);
serial_write_reg(p, UART_OMAP_SYSC, 0x01);
}
diff --git a/arch/arm/mach-omap2/timer-gp.c b/arch/arm/mach-omap2/timer-gp.c
index 1d2f5ac..dc56a7b 100644
--- a/arch/arm/mach-omap2/timer-gp.c
+++ b/arch/arm/mach-omap2/timer-gp.c
@@ -8,8 +8,7 @@
* Juha Yrjölä <juha.yrjola@nokia.com>
*
* Some parts based off of TI's 24xx code:
- *
- * Copyright (C) 2004 Texas Instruments, Inc.
+ * Copyright (C) 2006 Texas Instruments, Inc.
*
* Roughly modelled after the OMAP1 MPU timer code.
*
@@ -26,6 +25,7 @@
#include <asm/mach/time.h>
#include <asm/delay.h>
#include <asm/io.h>
+#include <asm/div64.h>
#define OMAP2_GP_TIMER1_BASE 0x48028000
#define OMAP2_GP_TIMER2_BASE 0x4802a000
@@ -40,7 +40,17 @@
#define GP_TIMER_TLDR 0x2c
#define GP_TIMER_TSICR 0x40
+/* system ticker */
#define OS_TIMER_NR 1 /* GP timer 2 */
+#define OS_TIMER_NR_IRQ INT_24XX_GPTIMER2
+/* gettimeoffset and sched_clock */
+#define OS_TIMER_OFF 2 /* GP timer 3 */
+#define OS_TIMER_OFF_IRQ INT_24XX_GPTIMER3
+/* Use sys_ck as source */
+#define GP_SRC_CLK "sys_ck"
+
+#define TIMER_ON 1
+#define TIMER_OFF 0
static unsigned long timer_base[] = {
IO_ADDRESS(OMAP2_GP_TIMER1_BASE),
@@ -49,6 +59,13 @@ static unsigned long timer_base[] = {
IO_ADDRESS(OMAP2_GP_TIMER4_BASE),
};
+struct sys_timer omap_timer;
+static u32 omap_mpu_timer_last; /* Last processed system timer interrupt */
+static u32 gpticks_per_msec;
+static u32 gpticks_mask_per_jiffy; /* clarify & simplify isr calculation */
+unsigned long long num_jiffies_in_ov_in_ns; /* # jiffies in 0xffffffff ticks */
+unsigned long long tot_jiffies_in_ns = 0; /* total jiffies in ns */
+
static inline unsigned int timer_read_reg(int nr, unsigned int reg)
{
return __raw_readl(timer_base[nr] + reg);
@@ -60,7 +77,7 @@ static inline void timer_write_reg(int n
}
/* Note that we always enable the clock prescale divider bit */
-static inline void omap2_gp_timer_start(int nr, unsigned long load_val)
+static inline void omap2_gp_timer_start(int nr, unsigned long load_val, int interrupt)
{
unsigned int tmp;
@@ -68,13 +85,27 @@ static inline void omap2_gp_timer_start(
timer_write_reg(nr, GP_TIMER_TLDR, tmp);
timer_write_reg(nr, GP_TIMER_TCRR, tmp);
- timer_write_reg(nr, GP_TIMER_TIER, 1 << 1);
+ timer_write_reg(nr, GP_TIMER_TIER, interrupt << 1);
timer_write_reg(nr, GP_TIMER_TCLR, (1 << 5) | (1 << 1) | 1);
}
+/*
+ * Elapsed time between interrupts is calculated using timerX (x=2).
+ * Latency during the interrupt is calculated using timerY (y=3).
+ * Both timer2 and timer3 counting at sys_ck/2.
+ */
static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id,
struct pt_regs *regs)
{
+ u32 now, lat;
+
+ /* get current tick time stamp */
+ now = timer_read_reg(OS_TIMER_OFF, GP_TIMER_TCRR);
+
+ /* get ticks since autoreload from 0xfffffxyz - 0xfffff000*/
+ lat = timer_read_reg(OS_TIMER_NR, GP_TIMER_TCRR) - gpticks_mask_per_jiffy;
+ omap_mpu_timer_last = now - lat;
+
write_seqlock(&xtime_lock);
timer_write_reg(OS_TIMER_NR, GP_TIMER_TISR, 1 << 1);
@@ -85,43 +116,165 @@ static irqreturn_t omap2_gp_timer_interr
return IRQ_HANDLED;
}
+static int omap2_gp_clkctrl(u32 nr, unsigned long *rate, u32 on_off,
+ char *n_src)
+{
+ struct clk *src_ck, *gpt_fck, *gpt_ick;
+ char n_fck[16], n_ick[16];
+
+ ++nr;
+ snprintf(n_fck, sizeof(n_fck), "gpt%d_fck", nr);
+ snprintf(n_ick, sizeof(n_ick), "gpt%d_ick", nr);
+
+ src_ck = clk_get(NULL, n_src);
+ gpt_fck = clk_get(NULL, n_fck);
+ gpt_ick = clk_get(NULL, n_ick);
+
+ if (IS_ERR(src_ck) || IS_ERR(gpt_fck) || IS_ERR(gpt_ick)){
+ printk(KERN_ERR "Serious problem starting system GPT timer \n");
+ return 0;
+ }
+ if(on_off){
+ clk_set_parent(gpt_fck, src_ck);
+ clk_enable(gpt_fck);
+ clk_enable(gpt_ick);
+ if(rate != NULL)
+ *rate = clk_get_rate(src_ck);
+ } else {
+ clk_disable(gpt_fck);
+ clk_disable(gpt_ick);
+ }
+ clk_put(src_ck);
+ clk_put(gpt_fck);
+ clk_put(gpt_ick);
+ return 1;
+}
+
static struct irqaction omap2_gp_timer_irq = {
.name = "gp timer",
.flags = SA_INTERRUPT,
.handler = omap2_gp_timer_interrupt,
};
-static void __init omap2_gp_timer_init(void)
+/* inits up timer for 100 ticks / sec the time base is assumed to be in
+ * the MHz range. The usual source will be sys_ck divided by 2
+ */
+static u32 omap2_gp_timer_init(int nr)
{
- struct clk * sys_ck;
- u32 tick_period = 120000;
+ unsigned long tick_period, gprate;
u32 l;
+ /* enable timer clocks & set parent to sys_ck */
+ omap2_gp_clkctrl(nr, &gprate, TIMER_ON, GP_SRC_CLK);
+
+ gprate /= 2; /* Min timer prescale divider is 2 */
+ gpticks_per_msec = gprate / 1000; /* gpticks/s -> gpticks/ms */
+
+ tick_period = gprate / HZ; /* num of gpticks in 1 system tick (jiffy) */
+ tick_period -= 1;
+ gpticks_mask_per_jiffy = 0xffffffff - tick_period;
+
/* Reset clock and prescale value */
- timer_write_reg(OS_TIMER_NR, GP_TIMER_TCLR, 0);
+ timer_write_reg(nr, GP_TIMER_TCLR, 0);
+
+ l = timer_read_reg(nr, GP_TIMER_TIDR);
+ printk(KERN_INFO "OMAP2 GP timer (HW version %d.%d)\n",
+ (l >> 4) & 0x0f, l & 0x0f);
- sys_ck = clk_get(NULL, "sys_ck");
- if (IS_ERR(sys_ck))
- printk(KERN_ERR "Could not get sys_ck\n");
- else {
- clk_enable(sys_ck);
- tick_period = clk_get_rate(sys_ck) / 100;
- clk_put(sys_ck);
+ setup_irq(OS_TIMER_NR_IRQ, &omap2_gp_timer_irq);
+
+ omap2_gp_timer_start(nr, tick_period, 1);
+
+ return gprate;
+}
+
+static irqreturn_t omap2_gpt_off_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ tot_jiffies_in_ns += num_jiffies_in_ov_in_ns;
+ timer_write_reg(OS_TIMER_OFF, GP_TIMER_TISR, 1 << 1);
+ return IRQ_HANDLED;
+}
+
+static struct irqaction omap2_gpt_off_irq = {
+ .name = "gp timer overflow",
+ .flags = SA_INTERRUPT,
+ .handler = omap2_gpt_off_interrupt,
+};
+
+static void omap2_gp_timer_offset_init(int nr, unsigned long *rate)
+{
+ if(omap2_gp_clkctrl(nr, rate, TIMER_ON, GP_SRC_CLK)){
+ timer_write_reg(nr, GP_TIMER_TCLR, 0);
+ omap2_gp_timer_start(nr, 0xffffffff, 1);
+ setup_irq(OS_TIMER_OFF_IRQ, &omap2_gpt_off_irq);
}
+}
- tick_period /= 2; /* Minimum prescale divider is 2 */
- tick_period -= 1;
+/* useful for small offset values */
+static u32 machinecycles_to_usecs(u32 gpticks)
+{
+ return (((gpticks * 2 * 1000)/ gpticks_per_msec) + 1) >> 1;
+}
+
+/* useful for large offset timer values */
+static unsigned long long machinecycles_to_ns(u32 gpticks)
+{
+ unsigned long long nsecs;
- l = timer_read_reg(OS_TIMER_NR, GP_TIMER_TIDR);
- printk(KERN_INFO "OMAP2 GP timer (HW version %d.%d)\n",
- (l >> 4) & 0x0f, l & 0x0f);
+ /* gpticks/ticks-per-msec = msec
+ * msec * 1000000 = nsec
+ */
+ nsecs = (u64)gpticks * 1000000;
+ do_div(nsecs, gpticks_per_msec);
+ return nsecs;
+}
+
+/*
+ * Scheduler clock - returns total jiffies expressed in nanosec.
+ */
+unsigned long long sched_clock(void)
+{
+ u32 elapsed_cycles;
+ unsigned long long elapsed_ns;
+
+ elapsed_cycles = timer_read_reg(OS_TIMER_OFF, GP_TIMER_TCRR);
+ elapsed_ns = machinecycles_to_ns(elapsed_cycles);
+ return (tot_jiffies_in_ns + elapsed_ns);
+}
+
+/* return elapsed usecs since last system timer ISR */
+static unsigned long omap2_gettimeoffset(void)
+{
+ u32 now, elapsed_usec, elapsed_cycles;
+
+ now = timer_read_reg(OS_TIMER_OFF, GP_TIMER_TCRR);
+ elapsed_cycles = now - omap_mpu_timer_last;
+ elapsed_usec = machinecycles_to_usecs(elapsed_cycles);
+ return(elapsed_usec);
+}
+
+static void __init omap2_timer_init(void)
+{
+ u32 gprate;
+ u32 num_ns_in_jiffy;
- setup_irq(38, &omap2_gp_timer_irq);
+ omap2_gp_timer_offset_init(OS_TIMER_OFF, NULL);
+ gprate = omap2_gp_timer_init(OS_TIMER_NR);
- omap2_gp_timer_start(OS_TIMER_NR, tick_period);
+ /* 0xffffffff * 1/gprate = seconds in overflow = siof
+ * siof * HZ (jiffies in a second) = num of jiffies in overflow = njiof
+ * njiof * ns in a jiffy (1000000000/HZ) = num_jiffies_in_ov_in_ns
+ */
+ num_jiffies_in_ov_in_ns = 0xffffffff * HZ;
+ do_div(num_jiffies_in_ov_in_ns, gprate); /* jiffies in 0xffffffff ticks */
+ num_ns_in_jiffy = 1000000000 / HZ; /* num ns in a jiffy */
+ num_jiffies_in_ov_in_ns *= (u64)num_ns_in_jiffy;
+ omap_timer.offset = omap2_gettimeoffset;
}
struct sys_timer omap_timer = {
- .init = omap2_gp_timer_init,
+ .init = omap2_timer_init,
+ .offset = NULL,
};
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index 8b1fc5a..ffbc4f9 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -1281,9 +1281,21 @@ serial8250_handle_port(struct uart_8250_
if (status & UART_LSR_DR)
receive_chars(up, &status, regs);
check_modem_status(up);
- if (status & UART_LSR_THRE)
+ if (status & UART_LSR_THRE) /* handle fifo empty */
transmit_chars(up);
+#ifdef CONFIG_MACH_OMAP_GENERIC
+ /*
+ * THR rasied. The fifo is NOT empty, but below watermark.
+ * For OMAP The FIFO is 64 but sized as 32, with watermark
+ * at 32. There is always room for 32. This really applies
+ * to more than OMAP but not varients all have the SSR.
+ */
+ status = serial_inp(up, UART_OMAP_SSR); /* handle watermark interrupt */
+ if (!(status & UART_SSR_TX_FIFO_FULL))
+ transmit_chars(up);
+#endif
+
spin_unlock(&up->port.lock);
}
diff --git a/include/asm-arm/arch-omap/irqs.h b/include/asm-arm/arch-omap/irqs.h
index 42098d9..458b026 100644
--- a/include/asm-arm/arch-omap/irqs.h
+++ b/include/asm-arm/arch-omap/irqs.h
@@ -242,6 +242,8 @@
#define INT_24XX_GPIO_BANK2 30
#define INT_24XX_GPIO_BANK3 31
#define INT_24XX_GPIO_BANK4 32
+#define INT_24XX_GPTIMER2 38
+#define INT_24XX_GPTIMER3 39
#define INT_24XX_MCBSP1_IRQ_TX 59
#define INT_24XX_MCBSP1_IRQ_RX 60
#define INT_24XX_MCBSP2_IRQ_TX 62
diff --git a/include/asm-arm/arch-omap/timex.h b/include/asm-arm/arch-omap/timex.h
index 21f2e36..1d89679 100644
--- a/include/asm-arm/arch-omap/timex.h
+++ b/include/asm-arm/arch-omap/timex.h
@@ -34,8 +34,12 @@
*/
#ifdef CONFIG_OMAP_32K_TIMER
#define CLOCK_TICK_RATE (CONFIG_OMAP_32K_TIMER_HZ)
+#elif defined(CONFIG_MACH_OMAP_H4)
+#define CLOCK_TICK_RATE (6000000) /* sys_clk/2 */
+#elif defined(CONFIG_MACH_OMAP_2430SDP)
+#define CLOCK_TICK_RATE (6500000)
#else
-#define CLOCK_TICK_RATE (HZ * 100000UL)
+#define CLOCK_TICK_RATE (6000000)
#endif
#endif /* __ASM_ARCH_OMAP_TIMEX_H */
diff --git a/include/linux/serial_reg.h b/include/linux/serial_reg.h
index 3c8a6aa..93df0cc 100644
--- a/include/linux/serial_reg.h
+++ b/include/linux/serial_reg.h
@@ -321,5 +321,8 @@
#define UART_OMAP_SYSC 0x15 /* System configuration register */
#define UART_OMAP_SYSS 0x16 /* System status register */
+#define UART_SSR_TX_FIFO_FULL 0x1
+#define UART_SSR_RX_CTS_WU_STS 0x2
+
#endif /* _LINUX_SERIAL_REG_H */
[-- Attachment #3: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2006-03-23 19:46 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-03-23 19:29 [PATCH] : Fix annoying 'too much work for irq72' serial port watermark error Woodruff, Richard
2006-03-23 19:46 ` Tony Lindgren
-- strict thread matches above, loose matches on Subject: below --
2006-03-23 17:53 Woodruff, Richard
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox