/* * et.c - Driver for EMCO Timers * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CONFIG_PPC_5xxx_IPBFREQ 66000 #define ET_VERSION "0.1" #define ET_NAME "et" #define ET_MSG "et: " #undef ET_EXCLUSIVE_OPEN MODULE_LICENSE("GPL"); MODULE_AUTHOR("Frank Bennett"); MODULE_DESCRIPTION("Emco Timers Driver"); #define ET_MAJOR 190 static int major = ET_MAJOR; MODULE_PARM(major,"i"); MODULE_PARM_DESC(major, "Device major number (default=190)"); static struct mpc5xxx_gpt *gpt = (struct mpc5xxx_gpt *)MPC5xxx_GPT; // static struct mpc5xxx_gpio *gpio = (struct mpc5xxx_gpio *)MPC5xxx_GPIO; static struct mpc5xxx_intr *intr = (struct mpc5xxx_intr *)MPC5xxx_INTR; static unsigned long turbine_per; static DECLARE_WAIT_QUEUE_HEAD(et_wait); /* * ET timer related definitions: * */ #define ET_POUT 0 // Pulse Out #define ET_KP_ACTIVE 1 // not KeyPad active (low) IN see gui.c #define ET_TDIR 2 // Turbine DIR IN #define ET_TIN 3 // Turbine PULSE IN #define ET_52k 4 // start SAR convert #define ET_RLY1 5 // RELAY 1 active low OUT #define ET_RLY2 6 // RELAY 2 active low OUT #define ET_BKL 7 // LCD Backlite OUT #define ET_DELAY_US 10 /* in micro-seconds */ #define ET_PORT_TO_IRQ(port) (13 + port) #define ET_IRQ_TO_PORT(irq) (irq - 13) #ifdef ET_EXCLUSICE_OPEN static char et_is_open = 0; #endif static spinlock_t et_lock = SPIN_LOCK_UNLOCKED; static void et_interrupt (int irq, void *dev_id, struct pt_regs * regs) { int port = ET_IRQ_TO_PORT(irq); unsigned long stat; printk(KERN_INFO "et_interrupt IRQ %d port %d main_stat %x gpt.sr %x \n", irq, port, intr->main_status, gpt[port].sr ); if( port == ET_TIN ) { // Clear pending interrupt stat = in_be32(&gpt[port].sr) & 0xffff; out_be32(&gpt[port].sr, MPC5xxx_GPT_SR_CAPT); printk(KERN_INFO "et_int irq=%d port=%d stat %0lx \n", irq, port, stat); // wake_up_interruptible(&et_wait); } else { printk(KERN_ERR ET_MSG "Unexpected IRQ %d received", irq); } spin_lock (&et_lock); turbine_per = in_be32(&gpt[port].sr) >> 16; spin_unlock (&et_lock); } #ifdef CONFIG_PROC_FS static int et_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { *eof = 1; return count; } static int et_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) { if (count > 128) return -EINVAL; return count; } #endif /* * Device file operations */ static int et_open(struct inode *inode, struct file *file) { #ifdef ET_EXCLUSICE_OPEN if (et_is_open != 0) return -EBUSY; et_is_open = 1; #endif spin_lock_irq (&et_lock); turbine_per = 0L; spin_unlock_irq (&et_lock); return 0; } static int et_close(struct inode *inode, struct file *file) { #ifdef ET_EXCLUSICE_OPEN et_is_open = 0; #endif return 0; } static int et_write (struct file *file, u8 *buf, size_t count, loff_t *ppos) { printk(KERN_DEBUG " et_write \n" ); return 0; } static size_t et_read (struct file *file, char *buf, size_t count, loff_t *ppos) { DECLARE_WAITQUEUE(wait, current); unsigned long data; ssize_t retval; printk(KERN_DEBUG " et_read \n" ); if (count != sizeof (unsigned int) && count != sizeof (unsigned long)) return -EINVAL; // add_wait_queue(&et_wait, &wait); while (1) { __set_current_state(TASK_INTERRUPTIBLE); spin_lock_irq (&et_lock); data = turbine_per; turbine_per = 0L; spin_unlock_irq (&et_lock); if (data != 0) break; if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN; goto out; } if (signal_pending(current)) { retval = -ERESTARTSYS; goto out; } schedule(); } if (count == sizeof(unsigned int)) retval = put_user(data, (unsigned int *)buf); else retval = put_user(data, (unsigned long *)buf); if (!retval) retval = count; out: current->state = TASK_RUNNING; // remove_wait_queue(&et_wait, &wait); return retval; } static int et_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { unsigned long data; // printk(KERN_DEBUG "et_ioctl %04x %08lx\n", cmd, arg); switch (cmd) { case ET_POUT: case ET_52k: case ET_BKL: case ET_RLY1: case ET_RLY2: out_be32(&gpt[cmd].cr, (arg & 0xffff0000) |1); /* width, update_now */ out_be32(&gpt[cmd].cir, ((CONFIG_PPC_5xxx_IPBFREQ / 1000) << 16) | (arg&0xffff) ); // prescale, period break; case ET_TIN: data = turbine_per; turbine_per = 0L; return copy_to_user((void *)arg, &data, sizeof data) ? -EFAULT : 0; break; case ET_TDIR: case ET_KP_ACTIVE: data = (gpt[cmd].sr & MPC5xxx_GPT_SR_PIN)>>8; return copy_to_user((void *)arg, &data, sizeof data) ? -EFAULT : 0; //case ET_RLY1: //case ET_RLY2: // if( arg == 1L) // out_be32(&gpt[cmd].emsr, MPC5xxx_GPT_EMSR_OUTPUT_1 | MPC5xxx_GPT_EMSR_INTERNAL); // else // out_be32(&gpt[cmd].emsr, MPC5xxx_GPT_EMSR_OUTPUT_0 | MPC5xxx_GPT_EMSR_INTERNAL); // break; } return 0; } static struct file_operations et_fops = { owner: THIS_MODULE, open: et_open, release: et_close, read: et_read, write: et_write, ioctl: et_ioctl, }; static void et_init_gpt(void) { unsigned int per; /* Pulse Out - Timer 0 */ out_be32(&gpt[ET_POUT].emsr, MPC5xxx_GPT_EMSR_PWM | MPC5xxx_GPT_EMSR_CE | MPC5xxx_GPT_EMSR_CONTINOUS); /* Turbine in - GPIO Timer 1 */ turbine_per = 0L; out_be32(&gpt[ET_TIN].cir, ( 66<<16) | 1 ); /* prescale(16), period(16) */ out_be32(&gpt[ET_TIN].emsr, MPC5xxx_GPT_EMSR_INP_CAPTURE | MPC5xxx_GPT_EMSR_ICT_FALLING | MPC5xxx_GPT_EMSR_INT_ENABLE); /* Turbine dir - GPIO Timer 2 */ out_be32(&gpt[ET_TDIR].emsr, MPC5xxx_GPT_EMSR_INP_CAPTURE | MPC5xxx_GPT_EMSR_ICT_FALLING ); /* 52k ref - GPIO Timer 3 */ per = 40 ; // 40 usec out_be32(&gpt[ET_52k].cr, (per/2)<<16 | 1); /* width, update_now */ out_be32(&gpt[ET_52k].cir, ( 33<<16) | per ); /* prescale(16), period(16) */ out_be32(&gpt[ET_52k].emsr, MPC5xxx_GPT_EMSR_PWM | MPC5xxx_GPT_EMSR_CE | MPC5xxx_GPT_EMSR_CONTINOUS); /* KP_active - GPIO Timer 4 */ out_be32(&gpt[ET_KP_ACTIVE].emsr, MPC5xxx_GPT_EMSR_INP_CAPTURE | MPC5xxx_GPT_EMSR_ICT_FALLING ); /* Relay 1 - GPIO Timer 5 */ out_be32(&gpt[ET_RLY1].emsr, MPC5xxx_GPT_EMSR_PWM | MPC5xxx_GPT_EMSR_CE | MPC5xxx_GPT_EMSR_CONTINOUS); // MPC5xxx_GPT_EMSR_OUTPUT_1 | MPC5xxx_GPT_EMSR_INTERNAL); /* Relay 2 - GPIO Timer 5 */ out_be32(&gpt[ET_RLY2].emsr, MPC5xxx_GPT_EMSR_PWM | MPC5xxx_GPT_EMSR_CE | MPC5xxx_GPT_EMSR_CONTINOUS); // MPC5xxx_GPT_EMSR_OUTPUT_1 | MPC5xxx_GPT_EMSR_INTERNAL); /* LCD BL Lite- GPIO Timer 7 */ per = 4000 ; // 4 msec out_be32(&gpt[ET_BKL].cr, (per/4)<<16 | 1); /* width, update_now */ out_be32(&gpt[ET_BKL].cir, ( 66<<16) | per ); /* prescale(16), period(16) */ out_be32(&gpt[ET_BKL].emsr, MPC5xxx_GPT_EMSR_PWM | MPC5xxx_GPT_EMSR_CE | MPC5xxx_GPT_EMSR_CONTINOUS); } static int __init et_init (void) { #ifdef CONFIG_PROC_FS struct proc_dir_entry * proc; #endif int ret = -ENODEV; ret = register_chrdev(ET_MAJOR, ET_NAME, &et_fops); if (ret < 0) { printk(KERN_ERR ET_MSG "Couldn't register " ET_NAME " driver\n"); goto abort; } if (major == 0) major = ret; /* dynamic */ #ifdef CONFIG_PROC_FS proc = create_proc_entry(ET_NAME, S_IFREG | S_IRUGO, NULL); if (proc == NULL) { printk(KERN_ERR ET_MSG "failed to create /proc/"ET_NAME"\n"); goto abort_unregister; } proc->read_proc = et_read_proc; proc->write_proc = et_write_proc; #endif if (request_irq(ET_PORT_TO_IRQ(ET_TIN), et_interrupt, 0, "et Trubine", NULL)) { printk(KERN_ERR ET_MSG "couldn't register interrupts\n"); goto abort_remove_proc; } et_init_gpt(); printk(KERN_INFO "ET Display Driver v%s loaded ET_TIN irq %d\n", ET_VERSION, ET_PORT_TO_IRQ(ET_TIN) ); intr->main_mask &= ~(0x000000ff); /* enable timer ints */ printk(KERN_INFO "et_init 00 intr->per_mask %08x\n", intr->per_mask); printk(KERN_INFO "et_init 10 intr->ctrl %08x\n", intr->ctrl); printk(KERN_INFO "et_init 14 intr->main_mask %08x\n", intr->main_mask); printk(KERN_INFO "et_init 24 intr->enc_stat %08x\n", intr->enc_status); printk(KERN_INFO "et_init 28 intr->crit_stat %08x\n", intr->crit_status); printk(KERN_INFO "et_init 2c intr->main_stat %08x\n", intr->main_status); printk(KERN_INFO "et_init 30 intr->per_stat %08x\n", intr->per_status); printk(KERN_INFO "et_init 38 intr->per_error %08x\n", intr->per_error); sti(); /* volatile u32 per_mask; // INTR + 0x00 volatile u32 per_pri1; // INTR + 0x04 volatile u32 per_pri2; // INTR + 0x08 volatile u32 per_pri3; // INTR + 0x0c volatile u32 ctrl; // INTR + 0x10 volatile u32 main_mask; // INTR + 0x14 volatile u32 main_pri1; // INTR + 0x18 volatile u32 main_pri2; // INTR + 0x1c volatile u32 reserved1; // INTR + 0x20 volatile u32 enc_status; // INTR + 0x24 volatile u32 crit_status; // INTR + 0x28 volatile u32 main_status; // INTR + 0x2c volatile u32 per_status; // INTR + 0x30 volatile u32 reserved2; // INTR + 0x34 volatile u32 per_error; // INTR + 0x38 */ return 0; abort_remove_proc: remove_proc_entry(ET_NAME, NULL); abort_unregister: unregister_chrdev(major, ET_NAME); abort: return ret; } static void __devexit et_cleanup (void) { free_irq(ET_PORT_TO_IRQ(ET_TIN), NULL); remove_proc_entry(ET_NAME, NULL); unregister_chrdev(major, ET_NAME); printk(KERN_INFO "ET Display Driver v%s unloaded\n", ET_VERSION); } EXPORT_NO_SYMBOLS; module_init(et_init) module_exit(et_cleanup)