* [PATCH 2.6.11.6] CPM2 Timers API
@ 2005-03-31 19:54 Jason McMullan
  2005-03-31 20:33 ` Jason McMullan
  0 siblings, 1 reply; 3+ messages in thread
From: Jason McMullan @ 2005-03-31 19:54 UTC (permalink / raw)
  To: linuxppc-embedded
[-- Attachment #1.1: Type: text/plain, Size: 207 bytes --]
Two patches:
cpm-timer.patch:
	The base timers API for CPM2 timers
cpm-timer.mpc85xx-devices.patch:
	MPC85xx support
-- 
Jason McMullan <jason.mcmullan@timesys.com>
TimeSys Corporation
[-- Attachment #1.2: cpm-timer.mpc85xx-devices.patch --]
[-- Type: text/x-patch, Size: 4739 bytes --]
Description: MPC85xx support for CPM2 timers
Date: Thu Mar 31 14:49:49 EST 2005
Patches: linux-2.6.12-rc1.bk
Signed-Off-By: Jason McMullan <jason.mcmullan@timesys.com>
Difference summary:
 arch/ppc/syslib/mpc85xx_devices.c |   25 +++++++++++++++++++++++++
 arch/ppc/syslib/mpc85xx_sys.c     |   17 ++++++++++-------
 include/asm-ppc/mpc85xx.h         |    1 +
 3 files changed, 36 insertions(+), 7 deletions(-)
diff -urN -X /home/jmcmullan/dontdiff linux-2.6/arch/ppc/syslib/mpc85xx_devices.c linux-2.6.patch/arch/ppc/syslib/mpc85xx_devices.c
--- linux-2.6/arch/ppc/syslib/mpc85xx_devices.c	2005-03-31 14:06:04.000000000 -0500
+++ linux-2.6.patch/arch/ppc/syslib/mpc85xx_devices.c	2005-03-31 14:42:42.000000000 -0500
@@ -19,6 +19,8 @@
 #include <linux/serial_8250.h>
 #include <linux/fsl_devices.h>
 #include <asm/mpc85xx.h>
+#include <asm/cpm2.h>
+#include <asm/immap_cpm2.h>
 #include <asm/irq.h>
 #include <asm/ppc_sys.h>
 
@@ -61,8 +63,14 @@
 		.iotype		= UPIO_MEM,
 		.flags		= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ,
 	},
+	[2] = {
+		.flags = 0
+	}
 };
 
+#define CPM2_OFFSET(t)	((void *)(&((cpm2_map_t *)NULL)->t)-NULL)
+#define CPM2_END(t)	(CPM2_OFFSET(t)+(sizeof(((cpm2_map_t *)NULL)->t))-1)
+
 struct platform_device ppc_sys_platform_devices[] = {
 	[MPC85xx_TSEC1] = {
 		.name = "fsl-gianfar",
@@ -534,6 +542,23 @@
 			},
 		},
 	},
+	[MPC85xx_CPM_TIMER] = {
+		.name = "fsl-cpm-timer",
+		.id = 0,
+		.num_resources	= 2,
+		.resource = (struct resource[]) {
+			{
+				.start	= CPM2_OFFSET(im_cpmtimer),
+				.end	= CPM2_END(im_cpmtimer),
+				.flags	= IORESOURCE_MEM,
+			},
+			{
+				.start	= SIU_INT_TIMER1,
+				.end	= SIU_INT_TIMER4,
+				.flags	= IORESOURCE_IRQ,
+			},
+		},
+	},
 #endif /* CONFIG_CPM2 */
 };
 
diff -urN -X /home/jmcmullan/dontdiff linux-2.6/arch/ppc/syslib/mpc85xx_sys.c linux-2.6.patch/arch/ppc/syslib/mpc85xx_sys.c
--- linux-2.6/arch/ppc/syslib/mpc85xx_sys.c	2005-03-31 14:06:04.000000000 -0500
+++ linux-2.6.patch/arch/ppc/syslib/mpc85xx_sys.c	2005-03-31 14:46:53.768274801 -0500
@@ -36,7 +36,7 @@
 		.ppc_sys_name	= "8560",
 		.mask 		= 0xFFFF0000,
 		.value 		= 0x80700000,
-		.num_devices	= 19,
+		.num_devices	= 20,
 		.device_list	= (enum ppc_sys_devices[])
 		{
 			MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1,
@@ -46,13 +46,14 @@
 			MPC85xx_CPM_SCC2, MPC85xx_CPM_SCC3, MPC85xx_CPM_SCC4,
 			MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2, MPC85xx_CPM_FCC3,
 			MPC85xx_CPM_MCC1, MPC85xx_CPM_MCC2,
+			MPC85xx_CPM_TIMER,
 		},
 	},
 	{
 		.ppc_sys_name	= "8541",
 		.mask 		= 0xFFFF0000,
 		.value 		= 0x80720000,
-		.num_devices	= 13,
+		.num_devices	= 14,
 		.device_list	= (enum ppc_sys_devices[])
 		{
 			MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1,
@@ -60,13 +61,14 @@
 			MPC85xx_PERFMON, MPC85xx_DUART,
 			MPC85xx_CPM_SPI, MPC85xx_CPM_I2C,
 			MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2,
+			MPC85xx_CPM_TIMER,
 		},
 	},
 	{
 		.ppc_sys_name	= "8541E",
 		.mask 		= 0xFFFF0000,
 		.value 		= 0x807A0000,
-		.num_devices	= 14,
+		.num_devices	= 15,
 		.device_list	= (enum ppc_sys_devices[])
 		{
 			MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1,
@@ -74,13 +76,14 @@
 			MPC85xx_PERFMON, MPC85xx_DUART, MPC85xx_SEC2,
 			MPC85xx_CPM_SPI, MPC85xx_CPM_I2C,
 			MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2,
+			MPC85xx_CPM_TIMER,
 		},
 	},
 	{
 		.ppc_sys_name	= "8555",
 		.mask 		= 0xFFFF0000,
 		.value 		= 0x80710000,
-		.num_devices	= 19,
+		.num_devices	= 20,
 		.device_list	= (enum ppc_sys_devices[])
 		{
 			MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1,
@@ -90,14 +93,14 @@
 			MPC85xx_CPM_SCC2, MPC85xx_CPM_SCC3,
 			MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2,
 			MPC85xx_CPM_SMC1, MPC85xx_CPM_SMC2,
-			MPC85xx_CPM_USB,
+			MPC85xx_CPM_USB, MPC85xx_CPM_TIMER,
 		},
 	},
 	{
 		.ppc_sys_name	= "8555E",
 		.mask 		= 0xFFFF0000,
 		.value 		= 0x80790000,
-		.num_devices	= 20,
+		.num_devices	= 21,
 		.device_list	= (enum ppc_sys_devices[])
 		{
 			MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1,
@@ -107,7 +110,7 @@
 			MPC85xx_CPM_SCC2, MPC85xx_CPM_SCC3,
 			MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2,
 			MPC85xx_CPM_SMC1, MPC85xx_CPM_SMC2,
-			MPC85xx_CPM_USB,
+			MPC85xx_CPM_USB, MPC85xx_CPM_TIMER,
 		},
 	},
 	{	/* default match */
diff -urN -X /home/jmcmullan/dontdiff linux-2.6/include/asm-ppc/mpc85xx.h linux-2.6.patch/include/asm-ppc/mpc85xx.h
--- linux-2.6/include/asm-ppc/mpc85xx.h	2005-03-31 14:17:00.000000000 -0500
+++ linux-2.6.patch/include/asm-ppc/mpc85xx.h	2005-03-31 14:36:06.000000000 -0500
@@ -127,6 +127,7 @@
 	MPC85xx_CPM_MCC2,
 	MPC85xx_CPM_SMC1,
 	MPC85xx_CPM_SMC2,
+	MPC85xx_CPM_TIMER,
 };
 
 #endif /* CONFIG_85xx */
[-- Attachment #1.3: cpm-timer.patch --]
[-- Type: text/x-patch, Size: 16394 bytes --]
Description: CPM2 Timers API
Date: Thu Mar 31 14:49:49 EST 2005
Patches: linux-2.6.12-rc1.bk
Signed-Off-By: Jason McMullan <jason.mcmullan@timesys.com>
Difference summary:
 arch/ppc/Kconfig            |    6 
 arch/ppc/syslib/Makefile    |    1 
 arch/ppc/syslib/cpm_timer.c |  522 ++++++++++++++++++++++++++++++++++++++++++++
 include/asm-ppc/cpm_timer.h |   74 ++++++
 4 files changed, 603 insertions(+)
diff -urN -X /home/jmcmullan/dontdiff linux-2.6/arch/ppc/Kconfig linux-2.6.patch/arch/ppc/Kconfig
--- linux-2.6/arch/ppc/Kconfig	2005-03-31 14:05:48.000000000 -0500
+++ linux-2.6.patch/arch/ppc/Kconfig	2005-03-31 14:50:29.192469198 -0500
@@ -713,6 +713,12 @@
 	  you wish to build a kernel for a machine with a CPM2 coprocessor
 	  on it (826x, 827x, 8560).
 
+config CPM_TIMER
+	tristate "CPM2 Timers"
+	depends on 8260 || 85xx
+	help
+	  Includes kernel support for CPM2 timers
+
 config PPC_CHRP
 	bool
 	depends on PPC_MULTIPLATFORM
diff -urN -X /home/jmcmullan/dontdiff linux-2.6/arch/ppc/syslib/cpm_timer.c linux-2.6.patch/arch/ppc/syslib/cpm_timer.c
--- linux-2.6/arch/ppc/syslib/cpm_timer.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.patch/arch/ppc/syslib/cpm_timer.c	2005-03-31 14:50:29.217463534 -0500
@@ -0,0 +1,522 @@
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include <asm/io.h>
+#include <asm/cpm2.h>
+#include <asm/cpm_timer.h>
+#include <asm/delay.h>
+#include <asm/ppcboot.h>
+
+#define TGCR_CAS	0x8
+#define TGCR_GM		0x8
+#define TGCR_STP	0x2
+#define TGCR_RST	0x1
+
+#define TMR_PS_MASK	0xff00
+#define TMR_CE_MASK	0xffc0
+#define   TMR_CE_DISABLE	0x0000
+#define	  TMR_CE_RISING		0x0040
+#define	  TMR_CE_FALLING	0x0080
+#define	  TMR_CE_BOTH		0x00c0
+#define	TMR_OM		0x0020
+#define TMR_ORI		0x0010
+#define TMR_FRR		0x0008
+#define TMR_ICLK_MASK	0x0006
+#define   TMR_ICLK_CASCADED	0x0000
+#define   TMR_ICLK_INTERNAL	0x0002
+#define   TMR_ICLK_INT_DIV_16	0x0004
+#define	  TMR_ICLK_TIN		0x0006
+#define TMR_GE		0x0001
+
+#define TER_REF		0x0002
+#define TER_CAP		0x0001
+
+/* Timer functions
+ */
+static struct resource cpm_timer_resource = {
+	.name = "CPM Timers",
+	.start = 0,
+	.end   = 3,
+};
+
+static struct cpm_timer_s {
+	char name[16];
+	int irq;
+	struct {
+		volatile uint16_t *tmr;
+		volatile uint16_t *ter;
+	} ctrl;
+	union {
+		struct {
+			volatile uint16_t *trr;
+			volatile uint16_t *tcr;
+			volatile uint16_t *tcn;
+		} r16;
+		struct {
+			volatile uint32_t *trr;
+			volatile uint32_t *tcr;
+			volatile uint32_t *tcn;
+		} r32;
+	} data;
+	cpm_timer_f func;
+	void *func_data;
+	int one_shot;
+	cpm_timer_t tm;
+} cpm_timer[4];
+
+static cpmtimer_cpm2_t *timer_base = NULL;
+
+static irqreturn_t cpm_timer_irq(int irq, void *data, struct pt_regs *regs)
+{
+	struct cpm_timer_s *info = data;
+	cpm_timer_f func;
+	uint16_t events = *info->ctrl.ter;
+	void *func_data;
+
+	func_data = info->func_data;	/* Order is important here! */
+	func = info->func;
+	if (info->func) {
+		func(info->tm, func_data);
+	}
+
+	if (info->one_shot) {
+		*info->ctrl.tmr &= ~TMR_ORI;	/* Disable interrupt */
+		info->func = NULL;
+		info->func_data = NULL;
+	}
+
+	*info->ctrl.ter = events;	/* Clear events */
+
+	return IRQ_HANDLED;
+}
+
+/* Allocate a timer.
+ * Pass a timer id, and a reference to a NULL struct resource *
+ * Returns 0 on success, -errno on error
+ */
+EXPORT_SYMBOL(cpm_timer_alloc);
+int cpm_timer_alloc(int timer_id, cpm_timer_t *ptm)
+{
+	struct resource *tm;
+	int err;
+	uint16_t mask;
+      
+	tm = kmalloc(sizeof(struct resource)+16, GFP_KERNEL);
+	memset(tm,0,sizeof(struct resource)+16);
+	if (tm == NULL)
+		return -ENOMEM;
+
+	tm->start = (timer_id>>4)&0xf;
+	tm->end   = timer_id & 0xf;
+	if (tm->start > 3)
+		return -EINVAL;
+
+	if ((tm->end != tm->start) && 
+	    ((tm->start & 1) || (tm->end != (tm->start+1))))
+	    return -EINVAL;
+
+	err = request_resource(&cpm_timer_resource,tm);
+	if (err < 0) {
+		kfree(tm);
+		return err;
+	}
+
+	*ptm = (cpm_timer_t)(tm);
+
+	tm->name = ((void *)tm) + sizeof(struct resource);
+	if (tm->start == tm->end) {
+		sprintf((char *)tm->name,"timer%ld",tm->start+1);
+	} else {
+		sprintf((char *)tm->name,"timer%ld-%ld",tm->start+1,tm->end+1);
+	}
+
+	err = request_irq(cpm_timer[tm->end].irq,cpm_timer_irq,SA_INTERRUPT,tm->name,&cpm_timer[tm->end]);
+	if (err < 0) {
+		printk(KERN_ERR "CPM Timer %s: Can't allocate IRQ %d.\n",tm->name,cpm_timer[tm->end].irq);
+	}
+
+	/* Step 1 - enable and stop the timer */
+	mask = (TGCR_RST|TGCR_STP) << (4*((tm->end)&1));
+	if (tm->start < 2) {
+		timer_base->cpmt_tgcr1 |= mask;
+	} else {
+		timer_base->cpmt_tgcr2 |= mask;
+	}
+
+	cpm_timer[tm->end].func = NULL;
+
+	*cpm_timer[tm->end].ctrl.tmr = TMR_FRR | TMR_ICLK_INT_DIV_16;
+	if (tm->start != tm->end) {
+		if (tm->start == 0) {
+			timer_base->cpmt_tgcr1 |= (TGCR_CAS << 4);
+		} else if (tm->start == 2) {
+			timer_base->cpmt_tgcr2 |= (TGCR_CAS << 4);
+		}
+		*cpm_timer[tm->start].ctrl.tmr = TMR_FRR | TMR_ICLK_INT_DIV_16;
+	} else {
+		if (tm->start < 2) {
+			timer_base->cpmt_tgcr1 &= ~(TGCR_CAS << 4);
+		} else {
+			timer_base->cpmt_tgcr2 &= ~(TGCR_CAS << 4);
+		}
+	}
+
+	cpm_timer[tm->end].tm = tm;
+
+	cpm_timer_reset(tm);
+
+	return 0;
+}
+
+/* Free timer, using cpm_timer_t from cpm_timer_alloc
+ * cpm_timer_free() will stop the timer, and any cycle or one-shot functions
+ * pending.
+ */
+EXPORT_SYMBOL(cpm_timer_free);
+void cpm_timer_free(cpm_timer_t tm)
+{
+	uint16_t mask;
+
+	cpm_timer_stop(tm);
+	free_irq(cpm_timer[tm->end].irq,&cpm_timer[tm->end]);
+	if (tm->start != tm->end) {
+		if (tm->start == 0) {
+			timer_base->cpmt_tgcr1 &= ~(TGCR_CAS << 4);
+		} else if (tm->start == 2) {
+			timer_base->cpmt_tgcr2 &= ~(TGCR_CAS << 4);
+		}
+	}
+	wmb();
+
+	/* Disable the timer */
+	mask = (TGCR_RST << (4*((tm->start)&1))) |
+	       (TGCR_RST << (4*((tm->end)&1)));
+	if (tm->start < 2) {
+		timer_base->cpmt_tgcr1 &= ~mask;
+	} else {
+		timer_base->cpmt_tgcr2 &= ~mask;
+	}
+	wmb();
+
+	release_resource(tm);
+	kfree(tm);
+}
+
+/* Stop the timer
+ */
+EXPORT_SYMBOL(cpm_timer_stop);
+void cpm_timer_stop(cpm_timer_t tm)
+{
+	uint8_t mask;
+
+	mask = (TGCR_STP << (4*((tm->end)&1)));
+	if (tm->start < 2) {
+		timer_base->cpmt_tgcr1 |= mask;
+	} else {
+		timer_base->cpmt_tgcr2 |= mask;
+	}
+	wmb();
+
+}
+
+/* Start the timer
+ */
+EXPORT_SYMBOL(cpm_timer_start);
+void cpm_timer_start(cpm_timer_t tm)
+{
+	uint8_t mask;
+
+	mask = (TGCR_STP << (4*((tm->end)&1)));
+	if (tm->start < 2) {
+		timer_base->cpmt_tgcr1 &= ~mask;
+	} else {
+		timer_base->cpmt_tgcr2 &= ~mask;
+	}
+	wmb();
+}
+
+/* Reset the timer to 0
+ *
+ * Yes, this code looks rather hokey, but evidently tcn is a write-once
+ * register. 
+ */
+EXPORT_SYMBOL(cpm_timer_reset);
+void cpm_timer_reset(cpm_timer_t tm)
+{
+	int mask;
+	int tmr;
+
+	mask = ((TGCR_STP|TGCR_RST) << (4*((tm->end)&1)));
+	tmr = *cpm_timer[tm->end].ctrl.tmr;
+
+	if (tm->start < 2) {
+		timer_base->cpmt_tgcr1 &= ~mask;
+	} else {
+		timer_base->cpmt_tgcr2 &= ~mask;
+	}
+	wmb();
+
+	if (tm->start == tm->end) {
+		*cpm_timer[tm->start].data.r16.tcn = 0;
+	} else {
+		*cpm_timer[tm->start].data.r32.tcn = 0;
+	}
+	wmb();
+
+	if (tm->start < 2) {
+		timer_base->cpmt_tgcr1 |= mask;
+	} else {
+		timer_base->cpmt_tgcr2 |= mask;
+	}
+	wmb();
+	*cpm_timer[tm->end].ctrl.tmr = tmr;
+}
+
+/* Get the timer resolution, in nanoseconds/tick
+ */
+EXPORT_SYMBOL(cpm_timer_get_resolution);
+unsigned long cpm_timer_get_resolution(cpm_timer_t tm)
+{
+	/* For the mpc85xx, we're just using core frequency/16
+	 */
+	extern unsigned char __res[];
+	bd_t *bd = (bd_t *)__res;
+
+	return bd->bi_busfreq/1000000*16;
+}
+
+/* Get the timer value, in ticks
+ */
+EXPORT_SYMBOL(cpm_timer_get_ticks);
+unsigned long cpm_timer_get_ticks(cpm_timer_t tm)
+{
+	unsigned long ticks;
+
+	rmb();
+	if (tm->end != tm->start)
+		ticks = *cpm_timer[tm->start].data.r32.tcn;
+	else
+		ticks = *cpm_timer[tm->start].data.r16.tcn;
+
+	return ticks;
+}
+
+/* Call a function after so many ticks
+ * The function will be called in interrupt context.
+ *
+ * You cannot call cpm_timer_free() within that context.
+ */
+
+static int cpm_timer_call_common(cpm_timer_t tm, unsigned long ticks, cpm_timer_f func, void *func_data)
+{
+	if (cpm_timer[tm->start].func != NULL)
+		return -EBUSY;
+
+	cpm_timer_reset(tm);
+
+	if (tm->start != tm->end) {
+		*cpm_timer[tm->start].data.r32.trr = ticks;
+	} else {
+		*cpm_timer[tm->start].data.r16.trr = ticks & 0xffff;
+	}
+	wmb();
+
+	cpm_timer[tm->end].func = func;
+	cpm_timer[tm->end].func_data = func_data;
+	wmb();
+
+	*cpm_timer[tm->end].ctrl.tmr |= TMR_ORI;
+	if (tm->start != tm->end) {
+		*cpm_timer[tm->start].ctrl.tmr |= TMR_ORI;
+		*cpm_timer[tm->start].ctrl.ter = 0xffff;
+	}
+	*cpm_timer[tm->end].ctrl.ter = 0xffff;
+
+	wmb();
+
+	return 0;
+}
+
+
+/* This is the one-shot timer.
+ */
+EXPORT_SYMBOL(cpm_timer_call_once);
+int cpm_timer_call_once(cpm_timer_t tm, unsigned long ticks, cpm_timer_f func, void *func_data)
+{
+	int err;
+
+	err = cpm_timer_call_common(tm,ticks,func,func_data);
+	if (err < 0)
+		return err;
+
+	cpm_timer[tm->end].one_shot = 1;
+
+	return 0;
+}
+	
+
+/* This is the repeat-timer function
+ */
+EXPORT_SYMBOL(cpm_timer_call_every);
+int cpm_timer_call_every(cpm_timer_t tm, unsigned long ticks, cpm_timer_f func, void *func_data)
+{
+	int err;
+
+	err = cpm_timer_call_common(tm,ticks,func,func_data);
+	if (err < 0)
+		return err;
+
+	cpm_timer[tm->end].one_shot = 0;
+
+	return 0;
+}
+	
+/* Cancel any pending timer functions
+ */
+EXPORT_SYMBOL(cpm_timer_call_none);
+int cpm_timer_call_none(cpm_timer_t tm)
+{
+	cpm_timer[tm->end].one_shot = 1;
+	wmb();
+	cpm_timer[tm->end].func = NULL;
+	wmb();
+	cpm_timer[tm->end].func_data = NULL;
+	wmb();
+
+	return 0;
+}
+
+#ifdef CONFIG_PROC_FS
+
+extern struct seq_operations resource_op;
+
+static int cpm_timer_open(struct inode *inode, struct file *file)
+{
+	int res = seq_open(file, &resource_op);
+	if (!res) {
+		struct seq_file *m = file->private_data;
+		m->private = &cpm_timer_resource;
+	}
+	return res;
+}
+
+static struct file_operations proc_cpm_timer_operations = {
+	.open		= cpm_timer_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static int __init cpm_timer_proc_init(void)
+{
+	struct proc_dir_entry *entry;
+
+	entry = create_proc_entry("cpm-timer", 0, NULL);
+	if (entry)
+		entry->proc_fops = &proc_cpm_timer_operations;
+	return 0;
+}
+
+static void __exit cpm_timer_proc_exit(void)
+{
+	remove_proc_entry("cpm-timer", NULL);
+}
+
+#endif /* CONFIG_PROC_FS */
+
+static int __devinit cpm_timer_probe(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	int i;
+	struct resource *res,*irqs;
+
+	if (timer_base != NULL)
+		return -EBUSY;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL)
+		return -EINVAL;
+
+	irqs = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res == NULL || ((irqs->end+1)-irqs->start) != 4)
+		return -EINVAL;
+
+	timer_base = ioremap(res->start,sizeof(cpmtimer_cpm2_t));
+	if (timer_base == NULL)
+		return -ENOMEM;
+
+#define CPM_TIMER_C_SETUP(field) do {\
+	cpm_timer[0].ctrl.field = &timer_base->cpmt_##field##1; \
+	cpm_timer[1].ctrl.field = &timer_base->cpmt_##field##2; \
+	cpm_timer[2].ctrl.field = &timer_base->cpmt_##field##3; \
+	cpm_timer[3].ctrl.field = &timer_base->cpmt_##field##4; \
+	} while (0)
+
+#define CPM_TIMER_D_SETUP(field) do {\
+	cpm_timer[0].data.r16.field = &timer_base->cpmt_##field##1; \
+	cpm_timer[1].data.r16.field = &timer_base->cpmt_##field##2; \
+	cpm_timer[2].data.r16.field = &timer_base->cpmt_##field##3; \
+	cpm_timer[3].data.r16.field = &timer_base->cpmt_##field##4; \
+	} while (0)
+
+	CPM_TIMER_C_SETUP(tmr);
+	CPM_TIMER_C_SETUP(ter);
+
+	CPM_TIMER_D_SETUP(trr);
+	CPM_TIMER_D_SETUP(tcr);
+	CPM_TIMER_D_SETUP(tcn);
+
+	for (i = 0; i < 4; i++) {
+		sprintf(cpm_timer[i].name,"CPM Timer %d",i+1);
+		cpm_timer[i].irq = irqs->start+i;
+	}
+
+#ifdef CONFIG_PROC_FS
+	cpm_timer_proc_init();
+#endif
+
+	return 0;
+}
+
+static void __devexit cpm_timer_remove(struct device *dev)
+{
+	iounmap(timer_base);
+#ifdef CONFIG_PROC_FS
+	cpm_timer_proc_exit();
+#endif
+}
+
+static struct device_driver cpm_timer_driver = {
+	.name = "fsl-cpm-timer",
+	.bus = &platform_bus_type,
+	.probe = cpm_timer_probe,
+	.remove = __devexit_p(cpm_timer_remove),
+};
+
+static __init int cpm_timer_init(void)
+{
+	printk(KERN_INFO "cpm-timer: CPM2 Timers\n");
+
+	return driver_register(&cpm_timer_driver);
+}
+
+static __exit void cpm_timer_exit(void)
+{
+	driver_unregister(&cpm_timer_driver);
+}
+
+MODULE_LICENSE("GPL");
+
+MODULE_AUTHOR("Jason McMullan <jason.mcmullan@timesys.com>");
+MODULE_DESCRIPTION("CPM2 timer driver");
+
+module_init(cpm_timer_init);
+module_exit(cpm_timer_exit);
+
+
diff -urN -X /home/jmcmullan/dontdiff linux-2.6/arch/ppc/syslib/Makefile linux-2.6.patch/arch/ppc/syslib/Makefile
--- linux-2.6/arch/ppc/syslib/Makefile	2005-03-31 14:06:03.000000000 -0500
+++ linux-2.6.patch/arch/ppc/syslib/Makefile	2005-03-31 14:50:29.211464894 -0500
@@ -84,6 +84,7 @@
 obj-$(CONFIG_PCI_8260)		+= m8260_pci.o indirect_pci.o
 obj-$(CONFIG_8260_PCI9)		+= m8260_pci_erratum9.o
 obj-$(CONFIG_CPM2)		+= cpm2_common.o cpm2_pic.o
+obj-$(CONFIG_CPM_TIMER)		+= cpm_timer.o
 ifeq ($(CONFIG_PPC_GEN550),y)
 obj-$(CONFIG_KGDB)		+= gen550_kgdb.o gen550_dbg.o
 obj-$(CONFIG_SERIAL_TEXT_DEBUG)	+= gen550_dbg.o
diff -urN -X /home/jmcmullan/dontdiff linux-2.6/include/asm-ppc/cpm_timer.h linux-2.6.patch/include/asm-ppc/cpm_timer.h
--- linux-2.6/include/asm-ppc/cpm_timer.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.patch/include/asm-ppc/cpm_timer.h	2005-03-31 14:50:29.239458550 -0500
@@ -0,0 +1,74 @@
+/* Generic include file for all CPM timer definitions.
+ */
+#ifndef __ASM_CPM_TIMER_H
+#define __ASM_CPM_TIMER_H
+
+#include <linux/config.h>
+#include <linux/ioport.h>
+
+/* Allocate/utilize timers 
+ * There are four 16-bit timers, which can be chained as two 32-bit timers.
+ */
+#define CPM2_TIMER16_1	0x00
+#define CPM2_TIMER16_2	0x11
+#define CPM2_TIMER16_3	0x22
+#define CPM2_TIMER16_4	0x33
+#define CPM2_TIMER32_1	0x01
+#define CPM2_TIMER32_2	0x23
+
+typedef struct resource *cpm_timer_t;
+typedef void (*cpm_timer_f)(cpm_timer_t tm, void *data);
+
+/* Allocate a timer.
+ * Pass a timer id, and a reference to a NULL struct resource *
+ * Returns 0 on success, -errno on error
+ */
+int cpm_timer_alloc(int timer_id, cpm_timer_t *tm);
+
+/* Free timer, using cpm_timer_t from cpm_timer_alloc
+ * cpm_timer_free() will stop the timer, and any cycle or one-shot functions
+ * pending.
+ */
+void cpm_timer_free(cpm_timer_t tm);
+
+/* Stop the timer
+ */
+void cpm_timer_stop(cpm_timer_t tm);
+
+/* Start the timer
+ */
+void cpm_timer_start(cpm_timer_t tm);
+
+/* Reset the timer to 0
+ */
+void cpm_timer_reset(cpm_timer_t tm);
+
+/* Get the timer resolution, in nanoseconds/tick
+ */
+unsigned long cpm_timer_get_resolution(cpm_timer_t tm);
+
+/* Get the timer value, in ticks
+ */
+unsigned long cpm_timer_get_ticks(cpm_timer_t tm);
+
+/* Call a function after so many ticks
+ * The function will be called in interrupt context.
+ *
+ * You cannot call cpm_timer_free() within that context.
+ */
+
+
+/* This is the one-shot timer.
+ */
+int cpm_timer_call_once(cpm_timer_t tm, unsigned long ticks, cpm_timer_f func, void *data);
+
+/* This is the repeat-timer function
+ */
+int cpm_timer_call_every(cpm_timer_t tm, unsigned long ticks, cpm_timer_f func, void *data);
+
+/* Cancel any pending timer functions
+ */
+int cpm_timer_call_none(cpm_timer_t tm);
+
+
+#endif /* __ASM_CPM_TIMER_H */
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply	[flat|nested] 3+ messages in thread- * Re: [PATCH 2.6.11.6] CPM2 Timers API
  2005-03-31 19:54 [PATCH 2.6.11.6] CPM2 Timers API Jason McMullan
@ 2005-03-31 20:33 ` Jason McMullan
  2005-04-01 13:50   ` Jason McMullan
  0 siblings, 1 reply; 3+ messages in thread
From: Jason McMullan @ 2005-03-31 20:33 UTC (permalink / raw)
  To: linuxppc-embedded
[-- Attachment #1.1: Type: text/plain, Size: 187 bytes --]
A brown bag error handling and Lindent patch, to be applied in that
order, to my previous CPM timers patch.
-- 
Jason McMullan <jason.mcmullan@timesys.com>
TimeSys Corporation
[-- Attachment #1.2: cpm-timer.brown-bag.patch --]
[-- Type: text/x-patch, Size: 638 bytes --]
--- linux-2.6.patch/arch/ppc/syslib/cpm_timer.c.orig	2005-03-31 14:50:29.000000000 -0500
+++ linux-2.6.patch/arch/ppc/syslib/cpm_timer.c	2005-03-31 15:01:39.417496033 -0500
@@ -114,12 +114,16 @@
 
 	tm->start = (timer_id>>4)&0xf;
 	tm->end   = timer_id & 0xf;
-	if (tm->start > 3)
+	if (tm->start > 3) {
+		kfree(tm);
 		return -EINVAL;
+	}
 
 	if ((tm->end != tm->start) && 
-	    ((tm->start & 1) || (tm->end != (tm->start+1))))
-	    return -EINVAL;
+	    ((tm->start & 1) || (tm->end != (tm->start+1)))) {
+		kfree(tm);
+		return -EINVAL;
+	}
 
 	err = request_resource(&cpm_timer_resource,tm);
 	if (err < 0) {
[-- Attachment #1.3: cpm-timer.lindent.patch --]
[-- Type: text/x-patch, Size: 7152 bytes --]
--- linux-2.6/arch/ppc/syslib/cpm_timer.c	2005-03-31 15:15:59.100629916 -0500
+++ linux-2.6.patch/arch/ppc/syslib/cpm_timer.c	2005-03-31 15:15:38.838272681 -0500
@@ -42,7 +42,7 @@
 static struct resource cpm_timer_resource = {
 	.name = "CPM Timers",
 	.start = 0,
-	.end   = 3,
+	.end = 3,
 };
 
 static struct cpm_timer_s {
@@ -101,52 +101,56 @@
  * Returns 0 on success, -errno on error
  */
 EXPORT_SYMBOL(cpm_timer_alloc);
-int cpm_timer_alloc(int timer_id, cpm_timer_t *ptm)
+int cpm_timer_alloc(int timer_id, cpm_timer_t * ptm)
 {
 	struct resource *tm;
 	int err;
 	uint16_t mask;
-      
-	tm = kmalloc(sizeof(struct resource)+16, GFP_KERNEL);
-	memset(tm,0,sizeof(struct resource)+16);
+
+	tm = kmalloc(sizeof(struct resource) + 16, GFP_KERNEL);
+	memset(tm, 0, sizeof(struct resource) + 16);
 	if (tm == NULL)
 		return -ENOMEM;
 
-	tm->start = (timer_id>>4)&0xf;
-	tm->end   = timer_id & 0xf;
+	tm->start = (timer_id >> 4) & 0xf;
+	tm->end = timer_id & 0xf;
 	if (tm->start > 3) {
 		kfree(tm);
 		return -EINVAL;
 	}
 
-	if ((tm->end != tm->start) && 
-	    ((tm->start & 1) || (tm->end != (tm->start+1)))) {
+	if ((tm->end != tm->start) &&
+	    ((tm->start & 1) || (tm->end != (tm->start + 1)))) {
 		kfree(tm);
 		return -EINVAL;
 	}
 
-	err = request_resource(&cpm_timer_resource,tm);
+	err = request_resource(&cpm_timer_resource, tm);
 	if (err < 0) {
 		kfree(tm);
 		return err;
 	}
 
-	*ptm = (cpm_timer_t)(tm);
+	*ptm = (cpm_timer_t) (tm);
 
 	tm->name = ((void *)tm) + sizeof(struct resource);
 	if (tm->start == tm->end) {
-		sprintf((char *)tm->name,"timer%ld",tm->start+1);
+		sprintf((char *)tm->name, "timer%ld", tm->start + 1);
 	} else {
-		sprintf((char *)tm->name,"timer%ld-%ld",tm->start+1,tm->end+1);
+		sprintf((char *)tm->name, "timer%ld-%ld", tm->start + 1,
+			tm->end + 1);
 	}
 
-	err = request_irq(cpm_timer[tm->end].irq,cpm_timer_irq,SA_INTERRUPT,tm->name,&cpm_timer[tm->end]);
+	err =
+	    request_irq(cpm_timer[tm->end].irq, cpm_timer_irq, SA_INTERRUPT,
+			tm->name, &cpm_timer[tm->end]);
 	if (err < 0) {
-		printk(KERN_ERR "CPM Timer %s: Can't allocate IRQ %d.\n",tm->name,cpm_timer[tm->end].irq);
+		printk(KERN_ERR "CPM Timer %s: Can't allocate IRQ %d.\n",
+		       tm->name, cpm_timer[tm->end].irq);
 	}
 
 	/* Step 1 - enable and stop the timer */
-	mask = (TGCR_RST|TGCR_STP) << (4*((tm->end)&1));
+	mask = (TGCR_RST | TGCR_STP) << (4 * ((tm->end) & 1));
 	if (tm->start < 2) {
 		timer_base->cpmt_tgcr1 |= mask;
 	} else {
@@ -188,7 +192,7 @@
 	uint16_t mask;
 
 	cpm_timer_stop(tm);
-	free_irq(cpm_timer[tm->end].irq,&cpm_timer[tm->end]);
+	free_irq(cpm_timer[tm->end].irq, &cpm_timer[tm->end]);
 	if (tm->start != tm->end) {
 		if (tm->start == 0) {
 			timer_base->cpmt_tgcr1 &= ~(TGCR_CAS << 4);
@@ -199,8 +203,8 @@
 	wmb();
 
 	/* Disable the timer */
-	mask = (TGCR_RST << (4*((tm->start)&1))) |
-	       (TGCR_RST << (4*((tm->end)&1)));
+	mask = (TGCR_RST << (4 * ((tm->start) & 1))) |
+	    (TGCR_RST << (4 * ((tm->end) & 1)));
 	if (tm->start < 2) {
 		timer_base->cpmt_tgcr1 &= ~mask;
 	} else {
@@ -219,7 +223,7 @@
 {
 	uint8_t mask;
 
-	mask = (TGCR_STP << (4*((tm->end)&1)));
+	mask = (TGCR_STP << (4 * ((tm->end) & 1)));
 	if (tm->start < 2) {
 		timer_base->cpmt_tgcr1 |= mask;
 	} else {
@@ -236,7 +240,7 @@
 {
 	uint8_t mask;
 
-	mask = (TGCR_STP << (4*((tm->end)&1)));
+	mask = (TGCR_STP << (4 * ((tm->end) & 1)));
 	if (tm->start < 2) {
 		timer_base->cpmt_tgcr1 &= ~mask;
 	} else {
@@ -256,7 +260,7 @@
 	int mask;
 	int tmr;
 
-	mask = ((TGCR_STP|TGCR_RST) << (4*((tm->end)&1)));
+	mask = ((TGCR_STP | TGCR_RST) << (4 * ((tm->end) & 1)));
 	tmr = *cpm_timer[tm->end].ctrl.tmr;
 
 	if (tm->start < 2) {
@@ -290,9 +294,9 @@
 	/* For the mpc85xx, we're just using core frequency/16
 	 */
 	extern unsigned char __res[];
-	bd_t *bd = (bd_t *)__res;
+	bd_t *bd = (bd_t *) __res;
 
-	return bd->bi_busfreq/1000000*16;
+	return bd->bi_busfreq / 1000000 * 16;
 }
 
 /* Get the timer value, in ticks
@@ -317,7 +321,8 @@
  * You cannot call cpm_timer_free() within that context.
  */
 
-static int cpm_timer_call_common(cpm_timer_t tm, unsigned long ticks, cpm_timer_f func, void *func_data)
+static int cpm_timer_call_common(cpm_timer_t tm, unsigned long ticks,
+				 cpm_timer_f func, void *func_data)
 {
 	if (cpm_timer[tm->start].func != NULL)
 		return -EBUSY;
@@ -347,15 +352,15 @@
 	return 0;
 }
 
-
 /* This is the one-shot timer.
  */
 EXPORT_SYMBOL(cpm_timer_call_once);
-int cpm_timer_call_once(cpm_timer_t tm, unsigned long ticks, cpm_timer_f func, void *func_data)
+int cpm_timer_call_once(cpm_timer_t tm, unsigned long ticks, cpm_timer_f func,
+			void *func_data)
 {
 	int err;
 
-	err = cpm_timer_call_common(tm,ticks,func,func_data);
+	err = cpm_timer_call_common(tm, ticks, func, func_data);
 	if (err < 0)
 		return err;
 
@@ -363,16 +368,16 @@
 
 	return 0;
 }
-	
 
 /* This is the repeat-timer function
  */
 EXPORT_SYMBOL(cpm_timer_call_every);
-int cpm_timer_call_every(cpm_timer_t tm, unsigned long ticks, cpm_timer_f func, void *func_data)
+int cpm_timer_call_every(cpm_timer_t tm, unsigned long ticks, cpm_timer_f func,
+			 void *func_data)
 {
 	int err;
 
-	err = cpm_timer_call_common(tm,ticks,func,func_data);
+	err = cpm_timer_call_common(tm, ticks, func, func_data);
 	if (err < 0)
 		return err;
 
@@ -380,7 +385,7 @@
 
 	return 0;
 }
-	
+
 /* Cancel any pending timer functions
  */
 EXPORT_SYMBOL(cpm_timer_call_none);
@@ -411,10 +416,10 @@
 }
 
 static struct file_operations proc_cpm_timer_operations = {
-	.open		= cpm_timer_open,
-	.read		= seq_read,
-	.llseek		= seq_lseek,
-	.release	= seq_release,
+	.open = cpm_timer_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release,
 };
 
 static int __init cpm_timer_proc_init(void)
@@ -432,13 +437,13 @@
 	remove_proc_entry("cpm-timer", NULL);
 }
 
-#endif /* CONFIG_PROC_FS */
+#endif				/* CONFIG_PROC_FS */
 
 static int __devinit cpm_timer_probe(struct device *dev)
 {
 	struct platform_device *pdev = to_platform_device(dev);
 	int i;
-	struct resource *res,*irqs;
+	struct resource *res, *irqs;
 
 	if (timer_base != NULL)
 		return -EBUSY;
@@ -448,10 +453,10 @@
 		return -EINVAL;
 
 	irqs = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-	if (res == NULL || ((irqs->end+1)-irqs->start) != 4)
+	if (res == NULL || ((irqs->end + 1) - irqs->start) != 4)
 		return -EINVAL;
 
-	timer_base = ioremap(res->start,sizeof(cpmtimer_cpm2_t));
+	timer_base = ioremap(res->start, sizeof(cpmtimer_cpm2_t));
 	if (timer_base == NULL)
 		return -ENOMEM;
 
@@ -477,8 +482,8 @@
 	CPM_TIMER_D_SETUP(tcn);
 
 	for (i = 0; i < 4; i++) {
-		sprintf(cpm_timer[i].name,"CPM Timer %d",i+1);
-		cpm_timer[i].irq = irqs->start+i;
+		sprintf(cpm_timer[i].name, "CPM Timer %d", i + 1);
+		cpm_timer[i].irq = irqs->start + i;
 	}
 
 #ifdef CONFIG_PROC_FS
@@ -522,5 +527,3 @@
 
 module_init(cpm_timer_init);
 module_exit(cpm_timer_exit);
-
-
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply	[flat|nested] 3+ messages in thread
- * Re: [PATCH 2.6.11.6] CPM2 Timers API
  2005-03-31 20:33 ` Jason McMullan
@ 2005-04-01 13:50   ` Jason McMullan
  0 siblings, 0 replies; 3+ messages in thread
From: Jason McMullan @ 2005-04-01 13:50 UTC (permalink / raw)
  To: linuxppc-embedded
[-- Attachment #1.1: Type: text/plain, Size: 123 bytes --]
And here's the test-case kernel module....
-- 
Jason McMullan <jason.mcmullan@timesys.com>
TimeSys Corporation
[-- Attachment #1.2: cpm_timer_test.c --]
[-- Type: text/x-csrc, Size: 3852 bytes --]
/*
 * CPM Timers test
 *
 * Copyright 2004, Timesys Corp.
 * Jason McMullan <jason.mcmullan@timesys.com>
 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <asm/cpm_timer.h>
static int failed = 0;
static volatile int once, every;
static wait_queue_head_t queue;
#define FAILED(x, args...) do { failed++; printk(KERN_INFO "cpm_timer_test: " x , ## args ); if (tm != NULL) { cpm_timer_stop(tm); cpm_timer_free(tm); } return -EINVAL; } while (0)
static void cpm_timer_test_callback(cpm_timer_t tm, void *data)
{
	if (data == &once) {
		unsigned long val = cpm_timer_get_ticks(tm);
		printk(KERN_INFO "cpm_timer_test: Called once at tick %ld.\n",
		       val);
		once++;
		wmb();
	}
	if (data == &every) {
		unsigned long val = cpm_timer_get_ticks(tm);
		printk(KERN_INFO
		       "cpm_timer_test: Called every(%d), tick %ld.\n", every,
		       val);
		every++;
		if (every > 5)
			cpm_timer_call_none(tm);
		wmb();
	}
	wake_up_interruptible(&queue);
}
static int test_timer(int timer_id)
{
	cpm_timer_t tm = NULL;
	int err;
	unsigned long res, val;
	printk(KERN_INFO "cpm_timer_test: Test timer ID 0x%.2x\n", timer_id);
	err = cpm_timer_alloc(timer_id, &tm);
	if (err < 0)
		FAILED("Can't allocate timer 0x%.2x\n", timer_id);
	res = cpm_timer_get_resolution(tm);
	printk(KERN_INFO "cpm_timer_test: nanoseconds/tick = %ld\n", res);
	val = cpm_timer_get_ticks(tm);
	printk(KERN_INFO "cpm_timer_test: current value = %ld\n", val);
	cpm_timer_stop(tm);
	cpm_timer_reset(tm);
	val = cpm_timer_get_ticks(tm);
	printk(KERN_INFO "cpm_timer_test: value after stop/reset= %ld\n", val);
	if (val != 0)
		FAILED("Timer 0x%.2x didn't stop!\n", timer_id);
	cpm_timer_start(tm);
	udelay(5);
	val = cpm_timer_get_ticks(tm);
	udelay(5);
	res = cpm_timer_get_ticks(tm);
	printk(KERN_INFO "cpm_timer_test: value after  5 us= %ld\n", val);
	printk(KERN_INFO "cpm_timer_test: value after 10 us= %ld\n", res);
	if (val == 0 || res == 0)
		FAILED("Timer 0x%.2x didn't increment!\n", timer_id);
	init_waitqueue_head(&queue);
	once = 0;
	err =
	    cpm_timer_call_once(tm, 50000, cpm_timer_test_callback,
				(void *)&once);
	if (err < 0)
		FAILED("Can't call cpm_timer_call_once for 0x%.2x\n", timer_id);
	printk(KERN_INFO "cpm_timer_test: Waiting for one-shot...\n");
	cpm_timer_start(tm);
	err = wait_event_interruptible(queue, once != 0);
	if (err < 0)
		FAILED("Interrupted sleep for 0x%.2x\n", timer_id);
	every = 0;
	err =
	    cpm_timer_call_every(tm, 50000, cpm_timer_test_callback,
				 (void *)&every);
	if (err < 0)
		FAILED("Can't call cpm_timer_call_every for 0x%.2x\n",
		       timer_id);
	printk(KERN_INFO "cpm_timer_test: Waiting for multi-shot x5...\n");
	cpm_timer_start(tm);
	err = wait_event_interruptible(queue, every > 5);
	if (err < 0)
		FAILED("Interrupted sleep for 0x%.2x\n", timer_id);
	cpm_timer_call_none(tm);
	printk(KERN_INFO "cpm_timer_test: Counted up to %d.\n", every);
	cpm_timer_stop(tm);
	cpm_timer_free(tm);
	return 0;
}
static int cpm_timer_test_init(void)
{
	int err;
	printk(KERN_INFO "CPM2 Timer Test Module.\n");
#define TEST(x) do { err=test_timer(x); if (err != 0) goto bad; } while (0)
	TEST(CPM2_TIMER16_1);
	TEST(CPM2_TIMER16_2);
	TEST(CPM2_TIMER16_3);
	TEST(CPM2_TIMER16_4);
	TEST(CPM2_TIMER32_1);
	TEST(CPM2_TIMER32_2);
      bad:
	if (failed) {
		printk(KERN_INFO "cpm_timer_test: FAILED %d tests\n", failed);
	} else {
		printk(KERN_INFO "cpm_timer_test: PASSED\n");
	}
	return 0;
}
static void cpm_timer_test_exit(void)
{
	printk(KERN_INFO "CPM2 Timer Test Module unloaded.\n");
	return;
}
MODULE_LICENSE("GPL");
module_init(cpm_timer_test_init);
module_exit(cpm_timer_test_exit);
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply	[flat|nested] 3+ messages in thread
 
end of thread, other threads:[~2005-04-01 13:50 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-03-31 19:54 [PATCH 2.6.11.6] CPM2 Timers API Jason McMullan
2005-03-31 20:33 ` Jason McMullan
2005-04-01 13:50   ` Jason McMullan
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).