/* Matthias Schoeldgen (robot@schoeldgen.de) 
 * Feb 2001 Software for robot using MC68EZ328 in UCSimm
 * a driver for detecting speed and waypoints of a robot
 * using the system timer 
 * connected to a general purpose irq input is a interrupter wheel.
 * we measure speed = interrupts per second
 * count the interrupts for waypoint measurements.
 * (Odometer). 
 * Mar 2002:  We now are using 2 sensors of the hall type, connected 
 * to PD7 and PD6. PD7 generates irq7 , PD6 generates irq 3
 * first successful run on Feb., 21 , 2001
 * heavily modified , but still derived
 * and with shame stolen from 
 *	SoftDog	0.04:	A Software Watchdog Device
 *
 *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
 *
 *	Email us for quotes on Linux software and driver development. 
 *
 *			-----------------------
 *
 *	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.
 *
 *			-----------------------
 */
#include <linux/module.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/proc_fs.h>
#include <linux/miscdevice.h>
#include <asm/MC68EZ328.h>
#include <asm/machdep.h>
#include <linux/interrupt.h>
#include <asm/irq.h>

#define SPEEDY_MINOR	131
#define TIMER_MARGIN	2		/* measuring interval (secs)*/

static int soft_margin = TIMER_MARGIN;	/* in seconds */
static unsigned int way1,way2;
static unsigned int speed1,speed2;
static unsigned int calc1,calc2;

/*
 *	Our timer
 */
 
struct timer_list watchdog_ticktock;
static int timer_alive = 0;

static int speed_read_proc(char *buf, char **start, off_t offset,
			  int len, int *eof, void *private)
{
	int written;
	written = sprintf(buf, "%5i %2i %5i %2i\n",way1,speed1,way2,speed2);
	return written;
}

/* undramatical irq routine. to get the most out of the interrupter wheel
 we use both rising and falling edge
 Wheel 1 */
void mytimer1_irq(int irq, void *dev_id, struct pt_regs * regs)
{
        way1++;
	ICR ^= 0x1000; 	/* toggle polarity */
	ISR |= 0x00080000; /* reset IRQ6 */
}
/* wheel 2 */
void mytimer2_irq(int irq, void *dev_id, struct pt_regs * regs)
{
        way2++;
	ICR ^= 0x2000; 	/* toggle polarity */
	ISR |= 0x00040000; /* reset IRQ3 */
}

/*
 *	this timer fires every (soft_margin) seconds, gets the waypoint
 * from the irq routine and sees , how many where counted
 */
 
static void watchdog_fire(unsigned long data)
{
	del_timer(&watchdog_ticktock);
	watchdog_ticktock.expires=jiffies + (soft_margin * HZ);
	add_timer(&watchdog_ticktock);
	speed1 = way1 - calc1;
	calc1 = way1;
	speed2 = way2 - calc2;
	calc2 = way2;
}

static void speedy_release(struct inode *inode, struct file *file)
{
/* not used by the tachometer */
}

static ssize_t speedy_write(struct file *file, const char *data, size_t len, loff_t *ppos)
{
	/*
	 *	Reset variables
	 */
	if (ppos != &file->f_pos)
		return -ESPIPE;
	if (len) {
		way1 = way2 = 0;
		speed1 = speed2 = 0;
		calc1 = calc2 = 0;
	return 1;
	}
	return 0;
}

/*new structure of ops for linux 2.4.x */
static struct file_operations speedy_fops=
{
    owner:	THIS_MODULE,		/* Seek */
    write:	speedy_write,	/* Write */
};

static struct miscdevice speedy_miscdev=
{
	SPEEDY_MINOR,
	"speed",
	&speedy_fops
};

void matze_init(void)
{
/* 
    initing the MC68EZ628 to : IRQ on Port D7,D6 (IRQ6,5)
    machine dependent interrupt register 
*/
    PDDIR &= 0x3f; /* make this an input */
    PDSEL &= 0x3f; /* select  the io function irq*/
    ICR |= 0x0300;  /* edge trigger on irq 6 and irq 3 */
    /* in the IRQ register this is bit 19 */
    if (request_irq(19 ,mytimer1_irq,IRQ_FLG_LOCK, "wheel1",NULL)){
	printk("speed: Error:irq6 not set \n");
	return;
    }	
    if (request_irq(18 ,mytimer2_irq,IRQ_FLG_LOCK, "wheel2",NULL)){
	printk("speed: Error:irq3 not set \n");
	return;
    }
    if (misc_register(&speedy_miscdev)){
	printk("speed:Cannot register misc device");
	return;
	}
    init_timer(&watchdog_ticktock);
	watchdog_ticktock.function=watchdog_fire;
/* start up the timer */
	del_timer(&watchdog_ticktock);
	watchdog_ticktock.expires=jiffies + (soft_margin * HZ);
	add_timer(&watchdog_ticktock);
	timer_alive++;
	create_proc_read_entry("speed", 0, 0, speed_read_proc, NULL);
	printk("Speed and way measuring 2001 DC7UR meas every %d sec\n", soft_margin);
	return;
}	

#ifdef MODULE
int init_module(void)
{
	matze_init();
	return 0;
}

void cleanup_module(void)
{
	misc_deregister(&speedy_miscdev);
}
#endif

