Subject: [PATCH 2.6.11.6] CPM2 Timers API

From: Jason McMullan <[email-masked]>
Date: Thu, 31 Mar 2005 14:54:46 -0500

Two patches:

cpm-timer.patch:

	The base timers API for CPM2 timers

cpm-timer.mpc85xx-devices.patch:

	MPC85xx support

-- 
Jason McMullan <[email-masked]>
TimeSys Corporation

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 <[email-masked]>

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 */
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 <[email-masked]>

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 <[email-masked]>");
+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 */