Commit 2c89e159 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Staging: comedi: add c6xdigio driver



Driver for Mechatronic Systems Inc. C6x_DIGIO DSP daughter card

Written by Dan Block, email address unknown

Cc: David Schleef <ds@schleef.org>
Cc: Ian Abbott <abbotti@mev.co.uk>
Cc: Frank Mori Hess <fmhess@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 9a21297d
Loading
Loading
Loading
Loading
+517 −0
Original line number Diff line number Diff line
/*
   comedi/drivers/c6xdigio.c

   Hardware driver for Mechatronic Systems Inc. C6x_DIGIO DSP daughter card.
   (http://robot0.ge.uiuc.edu/~spong/mecha/)

   COMEDI - Linux Control and Measurement Device Interface
   Copyright (C) 1999 Dan Block

   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., 675 Mass Ave, Cambridge, MA 02139, USA.

 */
/*
Driver: c6xdigio
Description: Mechatronic Systems Inc. C6x_DIGIO DSP daughter card
Author: Dan Block
Status: unknown
Devices: [Mechatronic Systems Inc.] C6x_DIGIO DSP daughter card (c6xdigio)
Updated: Sun Nov 20 20:18:34 EST 2005

This driver will not work with a 2.4 kernel.
http://robot0.ge.uiuc.edu/~spong/mecha/

*/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/timex.h>
#include <linux/timer.h>
#include <asm/io.h>
#include <linux/pnp.h>

#include "../comedidev.h"

static u8 ReadByteFromHwPort(unsigned long addr)
{
	u8 result = inb(addr);
	return result;
}

static void WriteByteToHwPort(unsigned long addr, u8 val)
{
	outb_p(val, addr);
}

#define C6XDIGIO_SIZE 3

/*
 * port offsets
 */
#define C6XDIGIO_PARALLEL_DATA 0
#define C6XDIGIO_PARALLEL_STATUS 1
#define C6XDIGIO_PARALLEL_CONTROL 2
struct pwmbitstype {
	unsigned sb0:2;
	unsigned sb1:2;
	unsigned sb2:2;
	unsigned sb3:2;
	unsigned sb4:2;
};
union pwmcmdtype {
	unsigned cmd;		// assuming here that int is 32bit
	struct pwmbitstype bits;
};
struct encbitstype {
	unsigned sb0:3;
	unsigned sb1:3;
	unsigned sb2:3;
	unsigned sb3:3;
	unsigned sb4:3;
	unsigned sb5:3;
	unsigned sb6:3;
	unsigned sb7:3;
};
union encvaluetype {
	unsigned value;
	struct encbitstype bits;
};

#define C6XDIGIO_TIME_OUT 20

static int c6xdigio_attach(comedi_device * dev, comedi_devconfig * it);
static int c6xdigio_detach(comedi_device * dev);
comedi_driver driver_c6xdigio = {
      driver_name:"c6xdigio",
      module:THIS_MODULE,
      attach:c6xdigio_attach,
      detach:c6xdigio_detach,
};

static void C6X_pwmInit(unsigned long baseAddr)
{
	int timeout = 0;

//printk("Inside C6X_pwmInit\n");

	WriteByteToHwPort(baseAddr, 0x70);
	while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
		&& (timeout < C6XDIGIO_TIME_OUT)) {
		timeout++;
	}

	WriteByteToHwPort(baseAddr, 0x74);
	timeout = 0;
	while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
		&& (timeout < C6XDIGIO_TIME_OUT)) {
		timeout++;
	}

	WriteByteToHwPort(baseAddr, 0x70);
	timeout = 0;
	while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
		&& (timeout < C6XDIGIO_TIME_OUT)) {
		timeout++;
	}

	WriteByteToHwPort(baseAddr, 0x0);
	timeout = 0;
	while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
		&& (timeout < C6XDIGIO_TIME_OUT)) {
		timeout++;
	}

}

static void C6X_pwmOutput(unsigned long baseAddr, unsigned channel, int value)
{
	unsigned ppcmd;
	union pwmcmdtype pwm;
	int timeout = 0;
	unsigned tmp;

	//printk("Inside C6X_pwmOutput\n");

	pwm.cmd = value;
	if (pwm.cmd > 498)
		pwm.cmd = 498;
	if (pwm.cmd < 2)
		pwm.cmd = 2;

	if (channel == 0) {
		ppcmd = 0x28;
	} else {		// if channel == 1
		ppcmd = 0x30;
	}			/* endif */

	WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb0);
	tmp = ReadByteFromHwPort(baseAddr + 1);
	while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
		tmp = ReadByteFromHwPort(baseAddr + 1);
		timeout++;
	}

	WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb1 + 0x4);
	timeout = 0;
	tmp = ReadByteFromHwPort(baseAddr + 1);
	while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
		tmp = ReadByteFromHwPort(baseAddr + 1);
		timeout++;
	}

	WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb2);
	tmp = ReadByteFromHwPort(baseAddr + 1);
	while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
		tmp = ReadByteFromHwPort(baseAddr + 1);
		timeout++;
	}

	WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb3 + 0x4);
	timeout = 0;
	tmp = ReadByteFromHwPort(baseAddr + 1);
	while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
		tmp = ReadByteFromHwPort(baseAddr + 1);
		timeout++;
	}

	WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb4);
	tmp = ReadByteFromHwPort(baseAddr + 1);
	while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
		tmp = ReadByteFromHwPort(baseAddr + 1);
		timeout++;
	}

	WriteByteToHwPort(baseAddr, 0x0);
	timeout = 0;
	tmp = ReadByteFromHwPort(baseAddr + 1);
	while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
		tmp = ReadByteFromHwPort(baseAddr + 1);
		timeout++;
	}

}

static int C6X_encInput(unsigned long baseAddr, unsigned channel)
{
	unsigned ppcmd;
	union encvaluetype enc;
	int timeout = 0;
	int tmp;

	//printk("Inside C6X_encInput\n");

	enc.value = 0;
	if (channel == 0) {
		ppcmd = 0x48;
	} else {
		ppcmd = 0x50;
	}
	WriteByteToHwPort(baseAddr, ppcmd);
	tmp = ReadByteFromHwPort(baseAddr + 1);
	while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
		tmp = ReadByteFromHwPort(baseAddr + 1);
		timeout++;
	}

	enc.bits.sb0 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
	WriteByteToHwPort(baseAddr, ppcmd + 0x4);
	timeout = 0;
	tmp = ReadByteFromHwPort(baseAddr + 1);
	while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
		tmp = ReadByteFromHwPort(baseAddr + 1);
		timeout++;
	}
	enc.bits.sb1 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
	WriteByteToHwPort(baseAddr, ppcmd);
	timeout = 0;
	tmp = ReadByteFromHwPort(baseAddr + 1);
	while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
		tmp = ReadByteFromHwPort(baseAddr + 1);
		timeout++;
	}
	enc.bits.sb2 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
	WriteByteToHwPort(baseAddr, ppcmd + 0x4);
	timeout = 0;
	tmp = ReadByteFromHwPort(baseAddr + 1);
	while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
		tmp = ReadByteFromHwPort(baseAddr + 1);
		timeout++;
	}
	enc.bits.sb3 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
	WriteByteToHwPort(baseAddr, ppcmd);
	timeout = 0;
	tmp = ReadByteFromHwPort(baseAddr + 1);
	while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
		tmp = ReadByteFromHwPort(baseAddr + 1);
		timeout++;
	}
	enc.bits.sb4 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
	WriteByteToHwPort(baseAddr, ppcmd + 0x4);
	timeout = 0;
	tmp = ReadByteFromHwPort(baseAddr + 1);
	while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
		tmp = ReadByteFromHwPort(baseAddr + 1);
		timeout++;
	}
	enc.bits.sb5 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
	WriteByteToHwPort(baseAddr, ppcmd);
	timeout = 0;
	tmp = ReadByteFromHwPort(baseAddr + 1);
	while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
		tmp = ReadByteFromHwPort(baseAddr + 1);
		timeout++;
	}
	enc.bits.sb6 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
	WriteByteToHwPort(baseAddr, ppcmd + 0x4);
	timeout = 0;
	tmp = ReadByteFromHwPort(baseAddr + 1);
	while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
		tmp = ReadByteFromHwPort(baseAddr + 1);
		timeout++;
	}
	enc.bits.sb7 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
	WriteByteToHwPort(baseAddr, ppcmd);
	timeout = 0;
	tmp = ReadByteFromHwPort(baseAddr + 1);
	while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
		tmp = ReadByteFromHwPort(baseAddr + 1);
		timeout++;
	}

	WriteByteToHwPort(baseAddr, 0x0);
	timeout = 0;
	tmp = ReadByteFromHwPort(baseAddr + 1);
	while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
		tmp = ReadByteFromHwPort(baseAddr + 1);
		timeout++;
	}

	return (enc.value ^ 0x800000);
}

static void C6X_encResetAll(unsigned long baseAddr)
{
	unsigned timeout = 0;

//printk("Inside C6X_encResetAll\n");

	WriteByteToHwPort(baseAddr, 0x68);
	while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
		&& (timeout < C6XDIGIO_TIME_OUT)) {
		timeout++;
	}
	WriteByteToHwPort(baseAddr, 0x6C);
	timeout = 0;
	while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
		&& (timeout < C6XDIGIO_TIME_OUT)) {
		timeout++;
	}
	WriteByteToHwPort(baseAddr, 0x68);
	timeout = 0;
	while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
		&& (timeout < C6XDIGIO_TIME_OUT)) {
		timeout++;
	}
	WriteByteToHwPort(baseAddr, 0x0);
	timeout = 0;
	while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
		&& (timeout < C6XDIGIO_TIME_OUT)) {
		timeout++;
	}
}

static int c6xdigio_pwmo_insn_read(comedi_device * dev,
	comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
{
	printk("c6xdigio_pwmo_insn_read %x\n", insn->n);
	return insn->n;
}

static int c6xdigio_pwmo_insn_write(comedi_device * dev,
	comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
{
	int i;
	int chan = CR_CHAN(insn->chanspec);

	//  printk("c6xdigio_pwmo_insn_write %x\n", insn->n);
	for (i = 0; i < insn->n; i++) {
		C6X_pwmOutput(dev->iobase, chan, data[i]);
		/*    devpriv->ao_readback[chan] = data[i]; */
	}
	return i;
}

//static int c6xdigio_ei_init_insn_read(comedi_device *dev,
//                                 comedi_subdevice *s,
//                                 comedi_insn *insn,
//                                 lsampl_t *data)
//{
//  printk("c6xdigio_ei_init_insn_read %x\n", insn->n);
//  return insn->n;
//}

//static int c6xdigio_ei_init_insn_write(comedi_device *dev,
//                                 comedi_subdevice *s,
//                                 comedi_insn *insn,
//                                 lsampl_t *data)
//{
//  int i;
//  int chan = CR_CHAN(insn->chanspec);
//
//  C6X_encResetAll( dev->iobase );
//
//  return insn->n;
//}

static int c6xdigio_ei_insn_read(comedi_device * dev,
	comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
{
	//  printk("c6xdigio_ei__insn_read %x\n", insn->n);
	int n;
	int chan = CR_CHAN(insn->chanspec);

	for (n = 0; n < insn->n; n++) {
		data[n] = (C6X_encInput(dev->iobase, chan) & 0xffffff);
	}

	return n;
}

static void board_init(comedi_device * dev)
{

	//printk("Inside board_init\n");

	C6X_pwmInit(dev->iobase);
	C6X_encResetAll(dev->iobase);

}

//static void board_halt(comedi_device *dev) {
//  C6X_pwmInit(dev->iobase);
//}

/*
   options[0] - I/O port
   options[1] - irq
   options[2] - number of encoder chips installed
 */

static const struct pnp_device_id c6xdigio_pnp_tbl[] = {
	/* Standard LPT Printer Port */
	{.id = "PNP0400",.driver_data = 0},
	/* ECP Printer Port */
	{.id = "PNP0401",.driver_data = 0},
	{}
};

static struct pnp_driver c6xdigio_pnp_driver = {
	.name = "c6xdigio",
	.id_table = c6xdigio_pnp_tbl,
};

static int c6xdigio_attach(comedi_device * dev, comedi_devconfig * it)
{
	int result = 0;
	unsigned long iobase;
	unsigned int irq;
	comedi_subdevice *s;

	iobase = it->options[0];
	printk("comedi%d: c6xdigio: 0x%04lx\n", dev->minor, iobase);
	if (!request_region(iobase, C6XDIGIO_SIZE, "c6xdigio")) {
		printk("comedi%d: I/O port conflict\n", dev->minor);
		return -EIO;
	}
	dev->iobase = iobase;
	dev->board_name = "c6xdigio";

	result = alloc_subdevices(dev, 2);	// 3 with encoder_init write
	if (result < 0)
		return result;

	// Make sure that PnP ports gets activated
	pnp_register_driver(&c6xdigio_pnp_driver);

	irq = it->options[1];
	if (irq > 0) {
		printk("comedi%d: irq = %u ignored\n", dev->minor, irq);
	} else if (irq == 0) {
		printk("comedi%d: no irq\n", dev->minor);
	}

	s = dev->subdevices + 0;
	/* pwm output subdevice */
	s->type = COMEDI_SUBD_AO;	// Not sure what to put here
	s->subdev_flags = SDF_WRITEABLE;
	s->n_chan = 2;
	/*      s->trig[0] = c6xdigio_pwmo; */
	s->insn_read = c6xdigio_pwmo_insn_read;
	s->insn_write = c6xdigio_pwmo_insn_write;
	s->maxdata = 500;
	s->range_table = &range_bipolar10;	// A suitable lie

	s = dev->subdevices + 1;
	/* encoder (counter) subdevice */
	s->type = COMEDI_SUBD_COUNTER;
	s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
	s->n_chan = 2;
	/* s->trig[0] = c6xdigio_ei; */
	s->insn_read = c6xdigio_ei_insn_read;
	s->maxdata = 0xffffff;
	s->range_table = &range_unknown;

	//          s = dev->subdevices + 2;
	//      /* pwm output subdevice */
	//      s->type = COMEDI_SUBD_COUNTER;  // Not sure what to put here
	//      s->subdev_flags = SDF_WRITEABLE;
	//      s->n_chan = 1;
	//      /* s->trig[0] = c6xdigio_ei_init; */
	//      s->insn_read = c6xdigio_ei_init_insn_read;
	//      s->insn_write = c6xdigio_ei_init_insn_write;
	//      s->maxdata = 0xFFFF;  // Really just a don't care
	//      s->range_table = &range_unknown; // Not sure what to put here

	// I will call this init anyway but more than likely the DSP board will not be connect
	// when device driver is loaded.
	board_init(dev);

	return 0;
}

static int c6xdigio_detach(comedi_device * dev)
{
//  board_halt(dev);  // may not need this

	printk("comedi%d: c6xdigio: remove\n", dev->minor);

	if (dev->iobase) {
		release_region(dev->iobase, C6XDIGIO_SIZE);
	}
	if (dev->irq) {
		free_irq(dev->irq, dev);
	}			// Not using IRQ so I am not sure if I need this
	pnp_unregister_driver(&c6xdigio_pnp_driver);

	return 0;
}

COMEDI_INITCLEANUP(driver_c6xdigio);