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 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 "); +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 +#include + +/* 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 */