www.pudn.com > canbus4linux.rar > isa_sja1000.c


/*
 * isa_sja1000.c
 * Copyright (c) 2003 Kirill Smelkov 
 *
 * ... BLURB ABOUT HARDWARE...
 *
 * 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.
 */

/*
 * two words about prefixes:
 * board_ prefix marks routines assotiated with the whole board
 * isa_sja1000_ prefix marks routines assotiated with some chip on the board
 */

#include 
#include 
#include 
#include 
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,67)
#include 
#endif
#include 

#include "trace.h"
#include "canbus4linux.h"
#include "isa_sja1000.h"

#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
#endif

struct io_info {
	unsigned long io_base;
	unsigned long io_len;
};

struct board_info {
	const char *name;
	int  io_irq;		// irq line
	long Fosc;			// oscillator frequency
	u8   OCR;			// OCR (output control register) value specific for board
	int  nchips;
	struct io_info io_info[MAXCHIPS];
};

static const struct board_info boards_list[] = {
//  name           irq   Fosc,		OCR,  nchips	{io_base, io_len} for each chip
{	"can-200pc",	5,	16000000,	0x1b,	2,		{ {0x100, 0x20}, {0x120, 0x20} }	},

// next two are for testing
{	"c200-1",		5,	16000000,	0x1b,	1,		{ {0x100, 0x20}                }	},	// first channel of can-200pc
{	"c200-2",		5,	16000000,	0x1b,	1,		{                {0x120, 0x20} }	},	// second channel of can-200pc

{	NULL	}
};

// find specified board in the list
static int find_board(const char * name)
{
	int i=0;

	while (boards_list[i].name) {
		if ( !strcmp(name, boards_list[i].name) )
			return i;

		++i;
	}

	return -1;
}

#define MAXBOARDS	2
static ISA_SJA1000_BOARD devices[MAXBOARDS];

// *******************
// * REGISTER ACCESS *
// *******************
static void isa_sja1000_writereg(void * data, u8 reg, u8 val)
{
	ISA_SJA1000_CHIP * self = (ISA_SJA1000_CHIP *) data;
	TRACE("writereg(0x%2.2x,0x%2.2x)", reg, val);

	outb(val, self->io_base + reg);
}

static u8 isa_sja1000_readreg(void * data, u8 reg)
{
	ISA_SJA1000_CHIP * self = (ISA_SJA1000_CHIP *) data;
	u8 val;

	val = inb(self->io_base + reg);
	TRACE("readreg(0x%2.2x) = 0x%2.2x", reg, val);

	return val;
}



// *** ?unneeded? _reset routines...
/*
static inline void isa_sja1000_reset_mode(ISA_SJA1000_CHIP * self)
{
	u8 val = inb(self->io_base + 0);
	u8 CR  = val | 0x01;
	int tries=100;

	while ( !(val & 0x01)) {
		outb_p(CR, self->io_base + 0);
		val = inb(self->io_base + 0);
		
		if (--tries == 0) {
			TRACE("can't reset sja1000... XXX: ?what can i do here?");
			break;
		}
	};
}

static inline void isa_sja1000_operating_mode(ISA_SJA1000_CHIP * self)
{
	u8 val = inb(self->io_base + 0);
	u8 CR  = val & 0x3e;	// ? 1e
	int tries=100;

	while (val & 0x01) {
		outb_p(CR, self->io_base + 0);
		val = inb(self->io_base + 0);

		if (--tries == 0) {
			TRACE("can't put sja1000 into operating mode... XXX: ?what can i do here?");
			break;
		}
	}
}


static void isa_sja1000_writereg_reset(void * data, u8 reg, u8 val)
{
	ISA_SJA1000_CHIP * self = (ISA_SJA1000_CHIP *) data;
	TRACE("DEPRECATED!");
	TRACE("writereg_reset(0x%2.2x,0x%2.2x)", reg, val);

	isa_sja1000_reset_mode(self);
	outb(val, self->io_base + reg);
	isa_sja1000_operating_mode(self);
}


static u8 isa_sja1000_readreg_reset(void * data, u8 reg)
{
	ISA_SJA1000_CHIP * self = (ISA_SJA1000_CHIP *) data;

	u8 ret;

	isa_sja1000_reset_mode(self);
	ret = inb(self->io_base + reg);
	isa_sja1000_operating_mode(self);

	TRACE("DEPRECATED!");
	TRACE("readreg_reset(0x%2.2x) = 0x%2.2x", reg, ret);
	return ret;
}
*/



// ****************
// * IRQ HANDLING *
// ****************
static int isa_sja1000_register_isr(void * data, sja1000_isr chip_isr, struct sja1000_admin * chip_isr_data)
{
	ISA_SJA1000_CHIP * self = (ISA_SJA1000_CHIP *) data;

	TRACE("register_isr()");

	if (!self)
		return -EINVAL;

	self->chip_isr		= chip_isr;
	self->chip_isr_data	= chip_isr_data;

	return 0;
}

static int isa_sja1000_unregister_isr(void * data)
{
	ISA_SJA1000_CHIP * self = (ISA_SJA1000_CHIP *) data;

	TRACE("unregister_isr()");

	if (!self)
		return -EINVAL;

	self->chip_isr		= 0;
	self->chip_isr_data	= 0;

	return 0;
}

// the whole board interrupt service routine
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,4)
static irqreturn_t board_isr(int irq, void * dev_id, struct pt_regs * regs)
#else
static void board_isr(int irq, void * dev_id, struct pt_regs * regs)
#endif
{
	ISA_SJA1000_BOARD * self = (ISA_SJA1000_BOARD *) dev_id;
	unsigned int i;

	TRACE("isr()");
	if (!self)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,4)
		return IRQ_NONE;
#else
		return;
#endif

	for (i=0;inchips;++i)
		if (self->chips[i].chip_isr)
			self->chips[i].chip_isr(&self->chips[i],self->chips[i].chip_isr_data);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,4)
		return IRQ_HANDLED;
#else
		return;
#endif
}

// *****************
// *  OPEN & CLOSE *
// *****************
static int isa_sja1000_open(void * data)
{
	ISA_SJA1000_CHIP * self = (ISA_SJA1000_CHIP *) data;
	int err;

	TRACE("open()");
//	spin_lock(&self->lock);
	do {
		if (self->open) {
			err = -EBUSY;
			break;
		}

		self->open++;

		// NOTE: this is currently specific for my card (the wierd thing)
		//       hardware reset is made via
		isa_sja1000_writereg(self, 0x1e, 0x00);		// reset

	} while (0);
//	spin_unlock(&self->lock);
	
	return 0;
}

static int isa_sja1000_close(void * data)
{
//	ISA_SJA1000_CHIP * self = (ISA_SJA1000_CHIP *) data;

	TRACE("close()");
	return 0;
}

char * cards[MAXBOARDS];
MODULE_PARM(cards,"1-" __MODULE_STRING(MAXBOARDS) "s");
MODULE_PARM_DESC(cards, "specify (comma separated) CAN board models");


// *************************
// * MODULE INIT & CLEANUP *
// *************************

void __exit isa_sja1000_cleanup(void);

int __init isa_sja1000_init(void)
{
	int err=-ENODEV;
	int num, i, minor;
	int cards_idx[MAXBOARDS];

	TRACE("init()");
	if (!cards[0]) {
		TRACE("you have to specify your card (cards parameter). giving up.");
		return -ENODEV;
	}

	for (num=0;numFosc,
			output_control_register:	idev->OCR
		};

		// io for all chips an the board
		TRACE("requesting io...");
		for (i=0;inchips;++i) {
			ISA_SJA1000_CHIP * chip = &dev->chips[i];

			unsigned long	io_base;
			unsigned long	io_len;

			snprintf(chip->name, MAX_DEVICE_NAME_LENGTH, "%s[%i]", idev->name, i);

			chip->num	= -1;
			chip->lock	= SPIN_LOCK_UNLOCKED;

			dev->nchips++;

			io_base = idev->io_info[i].io_base;
			io_len	= idev->io_info[i].io_len;

			if (!request_region(io_base, io_len, chip->name)) {
				TRACE("request_region(%lx,%lx) failed", io_base, io_len);
				err=-EBUSY;
				goto out;
			}

			chip->io_base = io_base;
			chip->io_len  = io_len;

			++minor;
		}


		// irq for the board
		io_irq  = idev->io_irq;

		TRACE("requesting irq...");
		err = request_irq(io_irq, board_isr, 0 /* flags */, "isacan", dev);
		if (err) {
			TRACE("request_irq(%i) failed", io_irq);
			goto out;
		}

		dev->io_irq = io_irq;


		// register every chip on the board in the canbus4linux subsystem
		for (i=0;inchips;++i) {
			ISA_SJA1000_CHIP * chip = &dev->chips[i];

			TRACE("%s: registering device no.: %i ...", idev->name, i);
			chip->num = sja1000_register_device(chip->name, CANBUS4LINUX_VERSION, chip, &access, 0, minor);
			if (chip->num == -1) {
				TRACE("sja1000_register_device() failed");
				err = -ENOMEM;
				goto out; // not enough memory to register this device
			}
		}

		err = 0;
	}

out:
	if (err)
		isa_sja1000_cleanup();

	return err;
}

void __exit isa_sja1000_cleanup(void)
{
	int num, i;

	TRACE("cleanup()");
	for (num=0;numnchips)
			continue;

		for (i=0; inchips; ++i) {
			ISA_SJA1000_CHIP * chip = &dev->chips[i];

			if (chip->num!=-1)
				chip->num = sja1000_unregister_device(chip->num);
		}
	
		if (dev->io_irq)
			free_irq(dev->io_irq, dev);

		for (i=0; inchips; ++i) {
			ISA_SJA1000_CHIP * chip = &dev->chips[i];

			if (chip->io_base)
				release_region(chip->io_base, chip->io_len);
		}
	}
}

module_init(isa_sja1000_init);
module_exit(isa_sja1000_cleanup);

MODULE_AUTHOR("Kirill Smelkov ");
MODULE_DESCRIPTION("CAN driver for generic SJA1000 based ISA card");

// vim: ts=4