From: "george cewe" <gcewe@copper.net>
To: <linux-kernel@vger.kernel.org>
Subject: submission of CAN driver for i82527 chip
Date: Thu, 20 Mar 2003 12:54:36 -0500 [thread overview]
Message-ID: <005501c2ef09$c6f93480$54491cd8@CEVE> (raw)
[-- Attachment #1: Type: text/plain, Size: 477 bytes --]
Dear Kernel Maintainer;
Please find enclosed 2 files of a driver for Intel 82527 CAN Controller,
This driver is a simplified version of 'ocan' driver by Allesandro Rubini.
'jcan' is a character driver for kernel 2.4 and up, I inserted it in
linux_root/drivers/char directory.
I tested it on embedded MPC823 platform, but it should be running on other
platforms as well.
This is first time I submit a Linux driver and I would appreciate your
comments.
regards George Cewe.
[-- Attachment #2: jcan.h --]
[-- Type: text/plain, Size: 4681 bytes --]
/*
* jcan.h -- header for the CAN driver
*
* Copyright (C) 2003 George Cewe <gcewe@copper.net>
* Copyright (C) 2003 Advanced Concepts Inc.
* Copyright (C) 2002 Alessandro Rubini <rubini@linux.it>
* Copyright (C) 2001 Ascensit S.p.A <support@ascensit.com>
* Author: Rodolfo Giometti <r.giometti@ascensit.com>
*
*
* 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.
*/
#ifndef __JCAN_H__
#define __JCAN_H__
#include <linux/types.h>
#define CAN_READ_BUFS 64 /* read buffer has 64 messages */
#define I527_XTD_XTD 0x04
#define I527_XTD_STD 0x00
/*
* data structures used in both kernel and user space (mostly for ioctl)
*/
typedef enum
{ B_100K
,B_125K
,B_250K
,B_500K
,B_1M
,BPS_MAX
} BPS;
typedef struct
{
__u8 reg;
__u8 val;
} CAN_REG;
#define CAN_MULTIREG_MAX 16
typedef struct
{
__u8 first, last;
__u8 regs[CAN_MULTIREG_MAX];
} CAN_MULTIREG;
typedef struct
{
__u8 data[256];
} CAN_ALLREGS;
typedef struct
{
__u32 m_msg15;
__u32 m_xtd;
__u16 m_std;
} CAN_MASKS;
/* This structure is both part of the device structure and arg of ioctl */
typedef struct
{
__u32 id;
__u8 msgobj; /* 1..15 */
__u8 flags;
__u8 config; /* same as config register */
__u8 dlc; /* data length counter */
__u8 data[8];
__u16 error; /* also for alignment */
} CAN_MSG;
/* And these are the message flags */
#define CAN_MSGFLAG_WRITE 0x01
#define CAN_MSGFLAG_READ 0x02 /* set either write read */
#define CAN_MSGFLAG_DOREMOTE 0x04 /* send a remote frame, for reading */
#define CAN_MSGFLAG_PEEKONLY 0x08 /* used by IOCPEEK */
#define CAN_ERROR_MASK 0x07 /* Tx error code */
#define CAN_ERROR_MSGLST 0x10 /* Message lost error */
#define CAN_ERROR_OVRFLW 0x80 /* Read buffer overflow */
/*
* ioctl() definitons
*/
/* Use 'z' as magic number */
#define CAN_IOC_MAGIC 'z' /* like other CAN drivers, per ioctl-number.txt */
#define CAN_IOCRESET _IO(CAN_IOC_MAGIC, 0)
/* individual registers */
#define CAN_IOCREADREG _IOWR(CAN_IOC_MAGIC, 1, CAN_REG)
#define CAN_IOCWRITEREG _IOW(CAN_IOC_MAGIC, 2, CAN_REG)
/* range of registers */
#define CAN_IOCREADMULTI _IOWR(CAN_IOC_MAGIC, 3, CAN_MULTIREG)
#define CAN_IOCWRITEMULTI _IOW(CAN_IOC_MAGIC, 4, CAN_MULTIREG)
/* one message */
#define CAN_IOCSETUPMSG _IOW(CAN_IOC_MAGIC, 5, CAN_MSG)
#define CAN_IOCWRITEMSG _IOW(CAN_IOC_MAGIC, 6, CAN_MSG)
#define CAN_IOCTXMSG _IOW(CAN_IOC_MAGIC, 7, unsigned long)
#define CAN_IOCRXMSG _IOWR(CAN_IOC_MAGIC, 8, CAN_MSG)
#define CAN_IOCRELEASEMSG _IOW(CAN_IOC_MAGIC, 9, unsigned long)
/* all registers */
#define CAN_IOCREADALL _IOR(CAN_IOC_MAGIC, 10, CAN_ALLREGS)
/* masks, times, bus configuration */
#define CAN_IOCGETMASKS _IOR(CAN_IOC_MAGIC, 11, CAN_MASKS)
#define CAN_IOCSETMASKS _IOW(CAN_IOC_MAGIC, 12, CAN_MASKS)
#define CAN_IOCGETBPS _IOR(CAN_IOC_MAGIC, 13, unsigned long)
#define CAN_IOCSETBPS _IOW(CAN_IOC_MAGIC, 14, unsigned long)
#define CAN_IOCGETBUSCONF _IOR(CAN_IOC_MAGIC, 15, CAN_REG)
#define CAN_IOCSETBUSCONF _IOW(CAN_IOC_MAGIC, 16, CAN_REG)
/* misc */
#define CAN_IOCSOFTRESET _IO(CAN_IOC_MAGIC, 17)
#define CAN_IOCWRITEQ _IOW(CAN_IOC_MAGIC, 18, CAN_MSG)
#define CAN_IOCGIRQCOUNT _IOR(CAN_IOC_MAGIC, 19, unsigned long)
#define CAN_IOCREQSIGNAL _IOW(CAN_IOC_MAGIC, 20, unsigned long)
/* I/O ports */
#define CAN_IOCINPUT _IOWR(CAN_IOC_MAGIC, 21, CAN_REG)
#define CAN_IOCOUTPUT _IOW(CAN_IOC_MAGIC, 22, CAN_REG)
#define CAN_IOCIOCFG _IOW(CAN_IOC_MAGIC, 23, CAN_REG)
/* peek (like rx) */
#define CAN_IOCPEEKMSG _IOWR(CAN_IOC_MAGIC, 24, CAN_MSG)
#define CAN_IOC_MAXNR 24
#endif /* __JCAN_H__ */
[-- Attachment #3: jcan.c --]
[-- Type: text/plain, Size: 49540 bytes --]
/*
* jcan.c -- CAN driver for i82527
*
* Copyright (C) 2003 George Cewe <gcewe@copper.net>
* Copyright (C) 2003 Advanced Concepts Inc.
* Copyright (C) 2002 Alessandro Rubini <rubini@linux.it>
* Copyright (C) 2002 System SpA <info.electronics@system-group.it>
*
* 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.
*
*
* This driver is a simplified version of "ocan" driver by Alessandro Rubini.
* It supports one memory mapped Intel i82527 CAN chip, running under J1939
* protocol (250kBps, extended message id).
*
*/
#define __NO_VERSION__
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <asm/irq.h>
#include <asm/io.h>
#ifdef __powerpc__
#include <asm-ppc/8xx_immap.h>
#endif
#include "jcan.h"
/*=============================================================================
* Messaging stuff
*/
#undef PDEBUG /* undef it, just in case */
#ifdef CAN_DEBUG
#ifdef __KERNEL__
/* This one if debugging is on, and kernel space */
#define PDEBUG(fmt, args...) printk(KERN_DEBUG CAN_TEXT fmt,##args)
#else
/* This one for user space */
#define PDEBUG(fmt, args...) fprintf(stdout, fmt, ##args)
#endif
#else
/* nothing */
#define PDEBUG(fmt, args...) /* nothing */
#endif /* CAN_DEBUG */
#ifdef CAN_DEBUG
#define debug 1
#else
#define debug 0
#endif
// base address and interrupt number
#define IO_ADDR 0xffff0000
#define IO_RANGE 0x100
#define IRQ_NUM SIU_IRQ3
#ifndef CAN_MAJOR
#define CAN_MAJOR 91
#endif
#define CAN_NAME "jcan"
#define CAN_TEXT "jcan: "
/*
* Sysctl constants
*/
#define DEV_CAN 2658 /* random for /proc/sys/dev/jcan/ */
/* inside /proc/sys/dev/jcan, each backend can use 100 keys */
/*
* minor allocation: each device has 1 general entry point, 15
* message-buffer entry points and a few information/error files.
* So it will need 5 bits. This accounts for up to eight devices
*/
#define CAN_DEV_TYPE(minor) ((minor)&0x1f)
#define CAN_DEV_TYPE_GENERIC 0 /* 1..15 are the message objects */
#define CAN_DEV_TYPE_ERROR 16
#define CAN_DEV_TYPE_IO1 17
#define CAN_DEV_TYPE_IO2 18
#define CAN_DEV_NUM(minor) ((minor)>>5)
/*
* Device structures
*/
#define CAN_BUFSIZE 64 /* 64 pointers for msg packets */
// i82527 related definitions =============================================
#define I527_MSG_OBJS 16 /* 0 is the whole device, 1..15 are objects */
#define I527_FIRST_OBJ 1
/* 82527 address map (referred from base address) & internal bits.
See the 82527 data-sheet page 9 and following */
#define I527_MAP_SIZE 0x100
#define I527_CTRL_REG 0x00
#define I527_CCE 0x40
#define I527_EIE 0x08
#define I527_SIE 0x04
#define I527_IE 0x02
#define I527_INIT 0x01
#define I527_STAT_REG 0x01
#define I527_STAT_REG_DEFAULT 0x07
#define I527_CPU_INT_REG 0x02
#define I527_RSTST 0x80
#define I527_DSC 0x40
#define I527_DMC 0x20
#define I527_PWD 0x10
#define I527_SLEEP 0x08
#define I527_MUX 0x04
#define I527_CEN 0x01
/* Reserved 0x03 */
#define I527_HI_SPEED_RD 0x04
#define I527_GMASK_STD 0x06
#define I527_GMASK_XTD 0x08
#define I527_MSG15_MASK 0x0c
/* Message 1 0x10 */
#define I527_MSG_OFF 0x10 /* No size definition here */
#define I527_MSG_CTRL 0x00
#define I527_MSGVAL_R 0xff7f /* *********************** */
#define I527_MSGVAL_S 0xffbf /* WARNING!!! */
#define I527_TXIE_R 0xffdf /* These masks must be */
#define I527_TXIE_S 0xffef /* &-ed and *NOT* |-ed */
#define I527_RXIE_R 0xfff7 /* */
#define I527_RXIE_S 0xfffb /* */
#define I527_INTPND_R 0xfffd /* */
#define I527_INTPND_S 0xfffe /* */
#define I527_RMTPND_R 0x7fff /* WARNING!!! */
#define I527_RMTPND_S 0xbfff /* These masks must be */
#define I527_TXRQST_R 0xdfff /* &-ed and *NOT* |-ed */
#define I527_TXRQST_S 0xefff /* */
#define I527_MSGLST_R 0xf7ff /* */
#define I527_MSGLST_S 0xfbff /* */
#define I527_CPUUPD_R 0xf7ff /* WARNING!!! */
#define I527_CPUUPD_S 0xfbff /* These masks must be */
#define I527_NEWDAT_R 0xfdff /* &-ed and *NOT* |-ed */
#define I527_NEWDAT_S 0xfeff /* *********************** */
#define I527_MSG_ARBIT 0x02
#define I527_MSG_CFG 0x06
#define I527_DLC_MASK 0xf0
#define I527_DLC_SHIFT 4
#define I527_DLC_MASK0 0x0f
#define I527_DIR 0x08
#define I527_DIR_TX 0x08
#define I527_DIR_RX 0x00
#define I527_XTD 0x04
#define I527_MSG_DATA 0x07 /* 8 bytes */
#define I527_CLKOUT_REG 0x1f
#define I527_SL_MASK 0x30
#define I527_SL_SHIFT 4
#define I527_SL_MASK0 0x03
#define I527_CLKDIV_MASK 0x0f
#define I527_CLKDIV_SHIFT 0
#define I527_CLKDIV_MASK0 0x0f
/* Message 2 0x20 */
#define I527_BUSCFG_REG 0x2f
#define I527_COBY 0x40
#define I527_POL 0x20
#define I527_DCT1 0x08
#define I527_DCR1 0x02
#define I527_DCR0 0x01
/* Message 3 0x30 */
#define I527_BITT0_REG 0x3f
#define I527_SJW_MASK 0xc0
#define I527_SJW_SHIFT 0x06
#define I527_SJW_MASK0 0x03
#define I527_BRP_MASK 0x3f
#define I527_BRP_SHIFT 0
#define I527_BRP_MASK0 0x3f
/* Message 4 0x40 */
#define I527_BITT1_REG 0x4f
#define I527_SPL_MASK 0x80
#define I527_SPL_SHIFT 7
#define I527_SPL_MASK0 0x01
#define I527_TSEG2_MASK 0x70
#define I527_TSEG2_SHIFT 4
#define I527_TSEG2_MASK0 0x07
#define I527_TSEG1_MASK 0x0f
#define I527_TSEG1_SHIFT 0
#define I527_TSEG1_MASK0 0x0f
/* Message 5 0x50 */
#define I527_INT_REG 0x5f
/* Message 6 0x60 */
/* Reserved 0x6f */
/* Message 7 0x70 */
/* Reserved 0x7f */
/* Message 8 0x80 */
/* Reserved 0x8f */
/* Message 9 0x90 */
#define I527_P1CONF 0x9f
/* Message 10 0xa0 */
#define I527_P2CONF 0xaf
/* Message 11 0xb0 */
#define I527_P1IN 0xbf
/* Message 12 0xc0 */
#define I527_P2IN 0xcf
/* Message 13 0xd0 */
#define I527_P1OUT 0xdf
/* Message 14 0xe0 */
#define I527_P2OUT 0xef
/* Message 15 0xf0 */
#define I527_SER_RST_ADD 0xff
typedef struct
{
__u8 cpuint;
__u8 clkout;
__u8 bittm0;
__u8 bittm1;
} CAN_BPS;
/*
* WARNING: can_times fields must all be __u8, because I sometimes
* iniitalize it from arrays of bytes (for example dev->hal->h_defaults)
*/
typedef struct
{
__u8 t_dsc;
__u8 t_dmc;
__u8 t_clkout_div;
__u8 t_clkout_slew;
__u8 t_sjw;
__u8 t_spl;
__u8 t_brp;
__u8 t_tseg1;
__u8 t_tseg2;
} CAN_TIMES;
/* This is the MSGOBJ structure */
typedef struct
{
u32 obj_id; /* current id */
struct file *owner; /* someone is waiting for a message */
volatile u16 obj_flags; /* object-specific flags */
u8 regbase; /* register offset */
volatile u8 error; /* error on message */
spinlock_t lock;
unsigned long flags;
wait_queue_head_t read_q; /* read queue */
struct fasync_struct *async_read_q; /* async read queue */
wait_queue_head_t write_q; /* write queue */
struct fasync_struct *async_write_q; /* async write queue*/
volatile int head;
volatile int tail;
CAN_MSG **msg; /* allocated using CAN_BUFSIZE */;
} CAN_MSGOBJ;
/* object flags and error flags, used internally */
#define CAN_OBJFLAG_WRITE 0x01 /* being used to write/read */
#define CAN_OBJFLAG_READ 0x02 /* (same values as MSGFLAG) */
#define CAN_OBJFLAG_REMOTE 0x04 /* remote flag on */
#define CAN_OBJFLAG_HWBUSY 0x40 /* message is being transmitted */
#define CAN_OBJFLAG_BUSY 0x80 /* message is in use */
/* error notification via signals */
typedef struct
{
struct file *file;
int signal;
pid_t pid;
} CAN_ERROR;
#define CAN_NR_ERROR_FILES 4 /* at most 4 processes can get a signal */
typedef struct
{
u32 base;
volatile u32 irq_count;
u8 usecount;
volatile u8 statusbyte;
u8 registered;
u8 gotirq;
CAN_MSGOBJ *objs[I527_MSG_OBJS];
CAN_ERROR errorinfo[CAN_NR_ERROR_FILES];
wait_queue_head_t error_q;
BPS eBps;
} CAN_DEV;
static kmem_cache_t *gpsCache; // The memory cache for msg packets
static CAN_DEV gsDev; // The device data
static const CAN_BPS gasBps[BPS_MAX] =
{ {0x40, 0x31, 0x03, 0x4d} // 100k
,{0x40, 0x31, 0x03, 0x49} // 125k
,{0x40, 0x31, 0x01, 0x49} // 250k
,{0x40, 0x31, 0x00, 0x49} // 500k
,{0x40, 0x31, 0x00, 0x14} // 1M
};
static spinlock_t can_lock = SPIN_LOCK_UNLOCKED;
/////////////////////////////////////////////////////////////////////////////
static int can_readb(CAN_DEV *dev, u_char reg)
{
return readb((volatile unsigned char *)(dev->base + reg));
}
//---------------------------------------------------------------------------
static int can_writeb(CAN_DEV *dev, u_char reg, unsigned char val)
{
writeb(val, (volatile unsigned char *)(dev->base + reg));
return 0;
}
/*===========================================================================
* basic functions use direct methods
*/
#define i527_read_reg(dev, reg) \
(can_readb((dev), (reg)))
#define i527_write_reg(dev, reg, value) \
(can_writeb((dev), (reg), (value)))
/*
* bit-setting operation and its use
*/
extern inline int i527_bits(CAN_DEV *dev, int reg,
int bits, int value)
{
int res = i527_read_reg(dev, reg);
if (res < 0) return res;
res = (res & ~bits) | (bits & value);
return i527_write_reg(dev, reg, res);
}
#define can_enable_irq(dev) i527_bits((dev), I527_CTRL_REG, \
I527_EIE|I527_IE, I527_EIE|I527_IE)
#define can_disable_irq(dev) i527_bits((dev), I527_CTRL_REG, \
I527_EIE|I527_IE, 0)
#define can_read_msg_dlc(dev, n) \
((i527_read_msgcfg(dev, n) & I527_DLC_MASK) \
>> I527_DLC_SHIFT)
#define can_write_msg_dlc(dev, n, dlc) \
(i527_bits((dev), n * I527_MSG_OFF + I527_MSG_CFG, \
I527_DLC_MASK, (dlc) << I527_DLC_SHIFT))
/*
* Enable/disable configuration changes
*/
/* enable and return previous value */
extern inline int can_enable_cfg(CAN_DEV *dev)
{
int res = i527_read_reg(dev, I527_CTRL_REG);
if (res < 0) return res;
if (res & I527_CCE) return I527_CCE;
res = i527_write_reg(dev, I527_CTRL_REG, res | I527_CCE);
if (res < 0) return res;
return 0;
}
/* disable and return previous value */
extern inline int can_disable_cfg(CAN_DEV *dev)
{
int res = i527_read_reg(dev, I527_CTRL_REG);
if (res < 0) return res;
if (!(res & I527_CCE)) return 0;
res = i527_write_reg(dev, I527_CTRL_REG, res & ~I527_CCE);
if (res < 0) return res;
return I527_CCE;
}
/* restore previous value */
extern inline int can_restore_cfg(CAN_DEV *dev, int prev)
{
return i527_bits(dev, I527_CTRL_REG, I527_CCE, prev);
}
/*
* higher level functions (can't be in i82527.h, for strange dependencies)
*/
extern inline int i527_read_msgcfg(CAN_DEV *dev,
CAN_MSGOBJ *obj)
{
return i527_read_reg(dev, obj->regbase + I527_MSG_CFG);
}
extern inline int i527_write_msgcfg(CAN_DEV *dev,
CAN_MSGOBJ *obj, int val)
{
if (val & ~0xff) return -EINVAL;
return i527_write_reg(dev, obj->regbase + I527_MSG_CFG, val);
}
extern inline u16 i527_read_std_mask(CAN_DEV *dev)
{
/* errors are ignored, hmm... */
u16 ret = i527_read_reg(dev, I527_GMASK_STD) << 8
| i527_read_reg(dev, I527_GMASK_STD + 1);
return ret;
}
extern inline void i527_write_std_mask(CAN_DEV *dev, u16 val)
{
/* errors are ignored, hmm... */
i527_write_reg(dev, I527_GMASK_STD, val >> 8);
i527_write_reg(dev, I527_GMASK_STD + 1, val & 0xff);
}
extern inline u32 i527_read_x_mask(CAN_DEV *dev)
{
/* errors are ignored, hmm... */
u32 ret = i527_read_reg(dev, I527_GMASK_XTD) << 24
| i527_read_reg(dev, I527_GMASK_XTD + 1) << 16
| i527_read_reg(dev, I527_GMASK_XTD + 2) << 8
| i527_read_reg(dev, I527_GMASK_XTD + 3);
return ret;
}
extern inline void i527_write_x_mask(CAN_DEV *dev, u32 val)
{
/* errors are ignored, hmm... */
i527_write_reg(dev, I527_GMASK_XTD, val >> 24);
i527_write_reg(dev, I527_GMASK_XTD + 1, (val >> 16) & 0xff);
i527_write_reg(dev, I527_GMASK_XTD + 2, (val >> 8) & 0xff);
i527_write_reg(dev, I527_GMASK_XTD + 3, (val >> 0) & 0xff);
}
extern inline u32 i527_read_15_mask(CAN_DEV *dev)
{
/* errors are ignored, hmm... */
u32 ret = i527_read_reg(dev, I527_MSG15_MASK) << 24
| i527_read_reg(dev, I527_MSG15_MASK + 1) << 16
| i527_read_reg(dev, I527_MSG15_MASK + 2) << 8
| i527_read_reg(dev, I527_MSG15_MASK + 3);
return ret;
}
extern inline void i527_write_15_mask(CAN_DEV *dev,
u32 val)
{
/* errors are ignored, hmm... */
i527_write_reg(dev, I527_MSG15_MASK, val >> 24);
i527_write_reg(dev, I527_MSG15_MASK + 1, (val >> 16) & 0xff);
i527_write_reg(dev, I527_MSG15_MASK + 2, (val >> 8) & 0xff);
i527_write_reg(dev, I527_MSG15_MASK + 3, (val >> 0) & 0xff);
}
extern inline u16 i527_read_msgctrl(CAN_DEV *dev,
CAN_MSGOBJ *obj)
{
/* FIXME: this is used little-endian, but doesn't need to be 16b */
/* errors are ignored, hmm... */
u16 ret = i527_read_reg(dev, obj->regbase + I527_MSG_CTRL)
| (i527_read_reg(dev, obj->regbase + I527_MSG_CTRL +1 )
<< 8);
return ret;
}
extern inline void i527_write_msgctrl(CAN_DEV *dev,
CAN_MSGOBJ *obj, u16 val)
{
/* FIXME: this is used little-endian, but doesn't need to be 16b */
/* errors are ignored, hmm... */
i527_write_reg(dev, obj->regbase + I527_MSG_CTRL, val & 0xff);
i527_write_reg(dev, obj->regbase + I527_MSG_CTRL + 1, val >> 8);
}
/* write a single byte of msgctrl, twice as fast as the function above */
extern inline void i527_msgflag(CAN_DEV *dev,
CAN_MSGOBJ *obj, u16 act)
{
if ((act & 0xff) == 0xff)
i527_write_reg(dev, obj->regbase + I527_MSG_CTRL + 1, act >> 8);
else
i527_write_reg(dev, obj->regbase + I527_MSG_CTRL, act & 0xff);
}
/* same, but for unallocated objects */
extern inline void i527_msgflag_noobj(CAN_DEV *dev,
int msgnum, u16 act)
{
if ((act & 0xff) == 0xff)
i527_write_reg(dev, msgnum*I527_MSG_OFF + I527_MSG_CTRL + 1,
act >> 8);
else
i527_write_reg(dev, msgnum*I527_MSG_OFF + I527_MSG_CTRL,
act & 0xff);
}
/* read a single byte of msgctrl (use the _S flag. Hm....) */
extern inline int i527_check_msgflag(CAN_DEV *dev,
CAN_MSGOBJ *obj, u16 act)
{
u16 reg;
if ((act & 0xff) == 0xff) {
reg = i527_read_reg(dev, obj->regbase + I527_MSG_CTRL + 1);
return !((reg << 8) & ~act);
}
reg = i527_read_reg(dev, obj->regbase + I527_MSG_CTRL);
return !(reg & ~act);
}
extern inline u32 i527_read_msgarb(CAN_DEV *dev,
CAN_MSGOBJ *obj)
{
int port = obj->regbase + I527_MSG_ARBIT;
/* errors are ignored, hmm... */
u32 ret = i527_read_reg(dev, port) << 24
| i527_read_reg(dev, port + 1) << 16
| i527_read_reg(dev, port + 2) << 8
| i527_read_reg(dev, port + 3);
return ret;
}
extern inline void i527_write_msgarb(CAN_DEV *dev,
CAN_MSGOBJ *obj, u32 val)
{
int port = obj->regbase + I527_MSG_ARBIT;
i527_write_reg(dev, port, val >> 24);
i527_write_reg(dev, port + 1, (val >> 16) & 0xff);
i527_write_reg(dev, port + 2, (val >> 8) & 0xff);
i527_write_reg(dev, port + 3, (val >> 0) & 0xff);
}
extern inline void i527_read_msgdata(CAN_DEV *dev,
CAN_MSGOBJ *obj,
int n, u8 *data)
{
int i;
u8 reg = obj->regbase + I527_MSG_DATA;
for (i=0; i<n; i++)
data[i] = i527_read_reg(dev, reg++);
}
extern inline void i527_write_msgdata(CAN_DEV *dev,
CAN_MSGOBJ *obj,
int n, u8 *data)
{
int i;
u8 reg = obj->regbase + I527_MSG_DATA;
if (!n) return;
i527_msgflag(dev, obj, I527_CPUUPD_S); /* CPU updating */
for (i=0; i<n && i<8; i++)
i527_write_reg(dev, reg++, data[i]);
i527_msgflag(dev, obj, I527_CPUUPD_R);
}
/*===========================================================================
* a collection of quick helper functions (all of them inline)
*/
/* objects can become allocated items, and allocation is hidden here */
extern inline CAN_MSGOBJ *lib_findobj(CAN_DEV *dev, int msgnum, int prio)
{
/* msgnum already checked */
return dev->objs[msgnum];
}
/* if/when allocation is there, release them here */
extern inline void lib_releaseobj(CAN_DEV *dev, int msgnum, CAN_MSGOBJ *obj)
{
int i;
if (!obj) return;
spin_lock(&obj->lock);
obj->owner = NULL;
obj->obj_flags &= ~(CAN_OBJFLAG_BUSY | CAN_OBJFLAG_HWBUSY);
i527_msgflag(dev, obj, I527_MSGVAL_R & I527_TXIE_R & I527_RXIE_R);
for (i=0; i<CAN_BUFSIZE; i++)
if (obj->msg[i]) {
PDEBUG("discarding packet in obj %i\n", msgnum);
kmem_cache_free(gpsCache, obj->msg[i]);
obj->msg[i] = NULL;
}
obj-> head = obj->tail = 0;
spin_unlock(&obj->lock);
}
/* returns 0 or -EINVAL */
extern inline int lib_checkmsgnum(int msgnum, int flags)
{
if (msgnum > 0 && msgnum < 15)
return 0;
if ( (msgnum == 15) && !(flags & CAN_MSGFLAG_WRITE) )
return 0;
printk("lib_checkmsgnum error, num= %d\n", msgnum);
return -EINVAL;
}
/* returns either 0 or -EBUSY, adjusts ownership */
extern inline int lib_setowner(CAN_MSGOBJ *obj,
struct file *filp, int rdwr)
{
spin_lock(&obj->lock);
if ( (obj->obj_flags & CAN_OBJFLAG_BUSY) && (obj->owner != filp) ) {
spin_unlock(&obj->lock);
return -EBUSY;
}
obj->owner = filp;
obj->obj_flags =
(obj->obj_flags & ~(CAN_MSGFLAG_WRITE | CAN_MSGFLAG_READ))
| CAN_OBJFLAG_BUSY | rdwr;
spin_unlock(&obj->lock);
return 0;
}
/* find this file in the errorfiles */
extern inline CAN_ERROR *lib_findfile(CAN_DEV *dev, struct file *file)
{
CAN_ERROR *info = dev->errorinfo;
int i;
for (i = 0; i<CAN_NR_ERROR_FILES; i++, info++)
if (info->file == file)
return info;
return NULL;
}
/*===========================================================================
* Normal management message objects functions
*/
//---------------------------------------------------------------------------
static void can_setbps(CAN_DEV *dev, BPS eBps )
{
int j;
__u8 nTmp;
const CAN_BPS *psBps = gasBps + eBps;
j = can_enable_cfg(dev);
nTmp = i527_read_reg(dev, I527_CPU_INT_REG) & ~(I527_DSC|I527_DMC);
nTmp |= psBps->cpuint;
i527_write_reg(dev, I527_CPU_INT_REG, nTmp);
i527_write_reg(dev, I527_CLKOUT_REG, psBps->clkout);
i527_write_reg(dev, I527_BITT0_REG, psBps->bittm0);
i527_write_reg(dev, I527_BITT1_REG, psBps->bittm1);
PDEBUG("libdrv: index %lx\n", eBps);
PDEBUG("libdrv: cpu_int_reg %x\n", nTmp);
PDEBUG("libdrv: cpu_clk_out %x\n", psBps->clkout);
PDEBUG("libdrv: cpu_bit0_reg %x\n", psBps->bittm0);
PDEBUG("libdrv: cpu_bit1_reg %x\n", psBps->bittm1);
can_restore_cfg(dev, j);
dev->eBps = eBps;
}
/*---------------------------------------------------------------------------
* full-reset function, called by deviceinit or by ioctl(RESET)
--------------------------------------------------------------------------*/
static void can_reset_i82527(CAN_DEV *dev)
{
int i;
PDEBUG("init/reset of i82527 at 0x%08lx\n", (unsigned long)dev->base);
i527_write_reg(dev,I527_CTRL_REG, I527_CCE|I527_INIT); //41
i527_write_reg(dev,I527_CLKOUT_REG, 3 << I527_SL_SHIFT); //30
i527_write_reg(dev, I527_STAT_REG, 0x00);
i527_write_reg(dev, I527_P1CONF, 0x00); /* default is input */
i527_write_reg(dev, I527_P2CONF, 0x00);
i527_write_15_mask(dev, 0xffffffff);
i527_write_std_mask(dev, 0xffff);
i527_write_x_mask(dev, 0xffffffff);
/* Turn off all message objects and clear message identifiers */
for (i = I527_FIRST_OBJ; i < I527_MSG_OBJS; i++)
{
if (dev->objs[i])
{
lib_releaseobj(dev, i, dev->objs[i]); /* useful for reset */
i527_write_msgctrl(dev, dev->objs[i],
I527_MSGVAL_R & I527_TXIE_R &
I527_RXIE_R & I527_INTPND_R &
I527_RMTPND_R & I527_TXRQST_R &
I527_MSGLST_R & I527_NEWDAT_R);
i527_write_msgarb(dev, dev->objs[i], 0);
}
}
/* set default values */
i527_write_reg(dev, I527_BUSCFG_REG, 0x4a);
can_setbps(dev, B_250K);
/* Initialization end */
i527_write_reg(dev, I527_STAT_REG, I527_STAT_REG_DEFAULT);
if (dev->usecount)
{
can_enable_irq(dev); /* warm reset */
}
else
{
i527_write_reg(dev, I527_CTRL_REG, 0); /* cold reset */
}
}
/*---------------------------------------------------------------------------
* This is called from ioctl or from the bottom half handler.
* It must configure and possibly transmit an object
* Everything checkable has already been checked if we get here.
-------------------------------------------------------------------------- */
static void can_config_and_transmit(CAN_DEV *dev,
CAN_MSGOBJ *obj,
CAN_MSG *msg, int flag)
{
int i;
/* it looks like message 15 fires an interrupt when re-enabled */
if (msg->msgobj != 15)
i527_msgflag(dev, obj, I527_MSGVAL_R); /* being updated */
if (msg->id != obj->obj_id) {
i527_write_msgarb(dev, obj, msg->id);
obj->obj_id = msg->id;
PDEBUG("message has new ID 0x%08lx\n", (unsigned long)obj->obj_id);
}
/* write DLC and X flag */
i = (msg->dlc << I527_DLC_SHIFT) | (msg->config & I527_XTD)
| (msg->flags & CAN_MSGFLAG_WRITE ? I527_DIR_TX : I527_DIR_RX);
i527_write_msgcfg(dev, obj, i);
/* write data bytes */
if (msg->flags & CAN_MSGFLAG_WRITE)
i527_write_msgdata(dev, obj, msg->dlc, msg->data);
/* now set flags: *XIE, MSGVAL; then clear CPUUpd and set newdat */
i527_msgflag(dev, obj,
I527_MSGVAL_S & I527_TXIE_S & I527_RXIE_S);
if (!flag)
{ /* only setup, no tx */
if (msg->flags & CAN_MSGFLAG_WRITE)
i527_msgflag(dev, obj, I527_CPUUPD_R & I527_NEWDAT_S);
else
i527_msgflag(dev, obj, I527_RMTPND_R & I527_MSGLST_R
& I527_NEWDAT_R);
return;
}
/* else, tx too */
i527_msgflag(dev, obj, I527_CPUUPD_R & I527_NEWDAT_S & I527_TXRQST_S);
}
//IRQ////////////////////////////////////////////////////////////////////////
/* this bottom half does the work of message handling and buffer popping */
//---------------------------------------------------------------------------
static void can_write_interrupt(CAN_DEV *dev,
CAN_MSGOBJ *obj, int objnum)
{
PDEBUG("can_wi: interrupt on objnum %d\n", objnum);
/* write-irq flushes the queue and then awakes writers */
i527_msgflag(dev, obj, I527_INTPND_R);
/* head and tail access is atomic with HWBUSY access */
spin_lock(&obj->lock);
if (obj->head == obj->tail)
{
obj->obj_flags &= ~CAN_OBJFLAG_HWBUSY;
spin_unlock(&obj->lock);
wake_up_interruptible(&obj->write_q);
if (obj->async_write_q)
kill_fasync(&obj->async_write_q, SIGIO, POLLOUT);
return;
}
spin_unlock(&obj->lock);
/* else push out another packet */
can_config_and_transmit(dev, obj, obj->msg[obj->tail], 1 /* tx */);
kmem_cache_free(gpsCache, obj->msg[obj->tail]);
obj->msg[obj->tail] = NULL;
obj->tail = (obj->tail + 1) % CAN_BUFSIZE;
/* possibly awake writers */
if ( ((obj->head + CAN_BUFSIZE - obj->tail) % CAN_BUFSIZE)
== CAN_BUFSIZE/2)
{
wake_up_interruptible(&obj->write_q);
if (obj->async_write_q)
kill_fasync(&obj->async_write_q, SIGIO, POLLOUT);
}
}
//---------------------------------------------------------------------------
static void can_read_interrupt(CAN_DEV *dev,
CAN_MSGOBJ *obj, int objnum)
{
CAN_MSG *msg;
int newhead;
PDEBUG("can_ri: interrupt on objnum %d\n", objnum);
/* Check for transmission errors */
obj->error = 0;
if (i527_check_msgflag(dev, obj, I527_MSGLST_S))
{
obj->error = CAN_ERROR_MSGLST;
i527_msgflag(dev, obj, I527_MSGLST_R);
}
if (objnum != 15 )
{ /* Argh!!! it wasn't clear *not* to do that for 15 */
/* "The CPU should clear the bit before reading data and check it" */
i527_msgflag(dev, obj, I527_NEWDAT_R);
}
newhead = (obj->head + 1) % CAN_BUFSIZE;
if (newhead == obj->tail)
{
/* overflow, discard this message and mark overlow in the previous */
// printk(KERN_ERR CAN_TEXT "overflow for msgobj %i\n", objnum);
printk("ov\n");
obj->msg[(obj->head+CAN_BUFSIZE-1)%CAN_BUFSIZE]->error
|= CAN_ERROR_OVRFLW;
goto ack_return;
}
msg = kmem_cache_alloc(gpsCache, GFP_ATOMIC);
if (!msg)
{
printk(KERN_ERR CAN_TEXT "out of memory for packet to msgobj %i\n",
objnum);
goto ack_return;
}
obj->msg[obj->head] = msg;
msg->id = i527_read_msgarb(dev, obj);
obj->obj_id = 0; /* invalidate, so tx will set it */
msg->msgobj = objnum;
msg->config = i527_read_msgcfg(dev, obj);
msg->dlc = (msg->config & I527_DLC_MASK) >> I527_DLC_SHIFT;
msg->flags = CAN_MSGFLAG_READ;
msg->error = obj->error;
i527_read_msgdata(dev, obj, msg->dlc, msg->data);
obj->head = newhead;
i527_msgflag(dev, obj, I527_INTPND_R);
/* FIXME: should re-check NewDat according to data sheet */
if (objnum == 15)
{
i527_msgflag(dev, obj, I527_NEWDAT_R & I527_RMTPND_R);
}
PDEBUG("%i bytes from 0x%08lx\n", msg->dlc, (long)msg->id);
/* Wake any reading process */
wake_up_interruptible(&obj->read_q);
/* Signal any asynchronous reader */
if (obj->async_read_q != NULL)
kill_fasync(&obj->async_read_q, SIGIO, POLLIN);
return;
ack_return:
i527_msgflag(dev, obj, I527_INTPND_R);
if (objnum == 15)
{
i527_msgflag(dev, obj, I527_NEWDAT_R & I527_RMTPND_R);
}
}
/* map the interrupt register value to the message object */
static const int irqtab[17] = {
0, 0, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
};
//-------------------------------------------------------------------------
static void can_bh(unsigned long nNum)
{
CAN_DEV *dev = &gsDev;
CAN_MSGOBJ *obj = NULL;
int objnum;
int status, irqreg;
unsigned long flags;
spin_lock_irqsave(&can_lock, flags);
/* Get the interrupt register -- even if we'd done it at hw irq */
irqreg = i527_read_reg(dev, I527_INT_REG);
status = i527_read_reg(dev, I527_STAT_REG);
PDEBUG("can_bh: interrupt on irq %d\n", irqreg);
dev->irq_count++;
#ifdef CAN_DEBUG /* needed to avoid "unused variable lec" */
/* print some stupid message */
if (1) {
PDEBUG("{{{ irq for device\n");
if (status & 0x80) PDEBUG("status: BusOff\n");
if (status & 0x40) PDEBUG("status: Warning\n");
if (status & 0x20) PDEBUG("status: Wake\n");
if (status & 0x10) PDEBUG("status: RXOK\n");
if (status & 0x08) PDEBUG("status: TXOK\n");
if ((status & 0x07) != 7) {
static char *lec[]={"No","Stuff","Form","Ack","B1","B0","CRC"};
PDEBUG("status: LEC: %s Error\n", lec[status&7]);
}
}
#endif /* CAN_DEBUG */
/* Reset the status register */
i527_write_reg(dev, I527_STAT_REG, I527_STAT_REG_DEFAULT);
do {
objnum = irqtab[irqreg];
if (!objnum)
{
/* awake sleeping readers and send signals */
CAN_ERROR *info = dev->errorinfo;
PDEBUG("Status Register IRQ (0x%02x)\n", status);
dev->statusbyte = status;
wake_up_interruptible(&dev->error_q);
for (objnum = 0; objnum < CAN_NR_ERROR_FILES; objnum++, info++)
if (info->file)
kill_proc(info->pid, info->signal, 1);
printk("jcan: irq objnum = 0\n");
continue;
}
obj = dev->objs[objnum];
if (!obj)
{
printk("jcan: irq obj = 0\n");
/* object not allocated, clear the interrupt anyways */
i527_msgflag_noobj(dev, objnum, I527_INTPND_R);
PDEBUG("resetted irq for unallocated device %i\n", objnum);
continue;
}
else
PDEBUG("jcan: irq objnum %d\n", objnum);
if (obj->obj_flags & CAN_OBJFLAG_WRITE)
can_write_interrupt(dev, obj, objnum);
else
can_read_interrupt(dev, obj, objnum);
udelay(10); /* 14 MCLK max, be safe */
}
while ( (irqreg = i527_read_reg(dev, I527_INT_REG)) );
spin_unlock_irqrestore(&can_lock, flags);
PDEBUG("end of irq}}}\n");
}
static DECLARE_TASKLET(can_task, can_bh, (unsigned long)0);
/* actual interrupt handling --------------------------------------------*/
static void can_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
CAN_DEV *dev = (CAN_DEV *) dev_id;
int irqreg;
/* Get the interrupt register's status */
irqreg = i527_read_reg(dev, I527_INT_REG);
if (!irqreg)
{
return; /* Not us, probably a shared handler */
}
if (irqreg > 16)
{
printk(KERN_ERR CAN_TEXT "irq register too high: %i\n", irqreg);
return;
}
/* everything else is done at bottom-half time */
PDEBUG("can_int dev %p int_reg %d\n", dev, irqreg);
PDEBUG("** HW irq for device %i:\n", irqtab[irqreg]);
tasklet_schedule(&can_task);
}
/*========================================================================
* File operations definitions
* ------------------------------------------------------- ioctl-------- */
static int can_ioctl (struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
#define IOC_BUFSIZE (CAN_READ_BUFS * sizeof(CAN_MSG))
static unsigned char localbuf[IOC_BUFSIZE];
CAN_DEV *dev = filp->private_data;
CAN_ERROR *info;
int size = _IOC_SIZE(cmd); /* the size bitfield in cmd */
int i, j, retval = 0; /* success by default */
/* I'm lazy, and I define "alias" names for the buffer */
void *karg = localbuf;
unsigned long *klong = karg;
CAN_REG *kreg = karg;
CAN_MSG *kmsg = karg;
CAN_MULTIREG *kmulti = karg;
CAN_MASKS *kmasks = karg;
CAN_MSGOBJ *obj;
unsigned char *area;
/*
* extract the type and number bitfields, and don't decode
* wrong cmds: return ENOTTY before verify_area()
*/
if (_IOC_TYPE(cmd) != CAN_IOC_MAGIC) return -ENOTTY;
if (_IOC_NR(cmd) > CAN_IOC_MAXNR) return -ENOTTY;
/*
* the direction is a bitmask, and VERIFY_WRITE catches R/W
* transfers. `Dir' is user-oriented, while
* verify_area is kernel-oriented, so the concept of "read" and
* "write" is reversed
*/
if (_IOC_DIR(cmd) & _IOC_READ)
{
if (!access_ok(VERIFY_WRITE, (void *)arg, size))
return -EFAULT;
}
else
if (_IOC_DIR(cmd) & _IOC_WRITE)
{
if (!access_ok(VERIFY_READ, (void *)arg, size))
return -EFAULT;
}
PDEBUG("ioctl: %08x %08lx: nr %i, size %i, dir %i\n",
cmd, arg, _IOC_NR(cmd), _IOC_SIZE(cmd), _IOC_DIR(cmd));
/* first, retrieve data from userspace */
if ((_IOC_DIR(cmd) & _IOC_WRITE) && (size <= IOC_BUFSIZE))
copy_from_user(karg, (void *)arg, size);
/* and now the big switch... */
switch(cmd)
{
case CAN_IOCRESET:
case CAN_IOCSOFTRESET:
can_disable_irq(dev);
free_irq(IRQ_NUM, (void *)dev);
can_reset_i82527(dev);
i527_write_reg(dev, I527_STAT_REG, I527_STAT_REG_DEFAULT);
#ifdef __powerpc__
request_8xxirq(IRQ_NUM, can_interrupt, 0, CAN_NAME, (void *)dev);
#else
request_irq(IRQ_NUM, can_interrupt, 0, CAN_NAME, (void *)dev);
#endif
can_enable_irq(dev);
break;
case CAN_IOCGIRQCOUNT:
*klong = dev->irq_count;
break;
case CAN_IOCREADREG:
kreg->val = i527_read_reg(dev, kreg->reg & 0xff);
break;
case CAN_IOCWRITEREG:
printk("jcan write reg %02x %02x\n", kreg->reg, kreg->val);
i527_write_reg(dev, kreg->reg & 0xff, kreg->val);
break;
case CAN_IOCREADMULTI:
if (kmulti->last < kmulti->first)
return -EINVAL;
if (kmulti->last - kmulti->first + 1 > CAN_MULTIREG_MAX)
return -EINVAL;
for (i=0, j=kmulti->first; j<=kmulti->last; i++, j++)
kmulti->regs[i] = i527_read_reg(dev, j);
break;
case CAN_IOCWRITEMULTI:
if (kmulti->last < kmulti->first)
return -EINVAL;
if (kmulti->last - kmulti->first + 1 > CAN_MULTIREG_MAX)
return -EINVAL;
i=0;
for (i=0, j=kmulti->first; j <= kmulti->last; j++)
i527_write_reg(dev, j, kmulti->regs[i++]);
break;
case CAN_IOCREADALL:
area = kmalloc(256, GFP_USER);
if (!area) return -ENOMEM;
for (i=0; i<256; i++)
((u8 *)area)[i] = i527_read_reg(dev, i);
copy_to_user((void *)arg, area, 256);
kfree(area);
return 0;
case CAN_IOCGETMASKS:
kmasks->m_msg15 = i527_read_15_mask(dev);
kmasks->m_xtd = i527_read_x_mask(dev);
kmasks->m_std = i527_read_std_mask(dev);
break;
case CAN_IOCSETMASKS:
i527_write_15_mask(dev, kmasks->m_msg15);
i527_write_x_mask(dev, kmasks->m_xtd);
i527_write_std_mask(dev, kmasks->m_std);
break;
case CAN_IOCGETBPS:
*klong = dev->eBps;
break;
case CAN_IOCSETBPS:
can_setbps(dev, (BPS)arg );
break;
case CAN_IOCGETBUSCONF:
kreg->val = i527_read_reg(dev, I527_BUSCFG_REG);
break;
case CAN_IOCSETBUSCONF:
j = can_enable_cfg(dev);
i527_write_reg(dev, I527_BUSCFG_REG, kreg->val);
can_restore_cfg(dev, j);
break;
//-----------------------------------------------------------------
case CAN_IOCWRITEMSG:
case CAN_IOCSETUPMSG: /* copy the msgobj struct to device registers */
if (lib_checkmsgnum(kmsg->msgobj, kmsg->flags))
return -EINVAL;
obj = lib_findobj(dev, kmsg->msgobj, GFP_USER);
if (!obj) return -ENOMEM;
if (lib_setowner(obj, filp, kmsg->flags &
(CAN_MSGFLAG_WRITE | CAN_MSGFLAG_READ) ))
return -EBUSY;
/* before we touch the message, ensure it's not transmitting */
if (obj->obj_flags & CAN_OBJFLAG_HWBUSY) {
if (filp->f_flags & O_NONBLOCK) return -EAGAIN;
wait_event_interruptible(obj->write_q,
!(obj->obj_flags &CAN_OBJFLAG_HWBUSY));
if (signal_pending(current)) return -ERESTARTSYS;
}
if (cmd == CAN_IOCWRITEMSG)
obj->obj_flags |= CAN_OBJFLAG_HWBUSY;
can_config_and_transmit(dev, obj, kmsg,
cmd == CAN_IOCWRITEMSG ? 1 : 0);
break;
case CAN_IOCWRITEQ: /* place it in the queue */
#if 0
if (lib_checkmsgnum(kmsg->msgobj, kmsg->flags))
return -EINVAL;
obj = lib_findobj(dev, kmsg->msgobj, GFP_USER);
if (!obj) return -ENOMEM;
if (lib_setowner(obj, filp, kmsg->flags &
(CAN_MSGFLAG_WRITE | CAN_MSGFLAG_READ) ))
return -EBUSY;
/* place the message in the queue */
i = (obj->head + 1) % CAN_BUFSIZE;
if (i == obj->tail) {
/* queue full: sleep -- no problem with concurrent access */
if (filp->f_flags & O_NONBLOCK) return -EAGAIN;
wait_event_interruptible(obj->write_q, (i != obj->tail));
if (signal_pending(current)) return -ERESTARTSYS;
}
newmsg = kmem_cache_alloc(gpsCache, GFP_USER);
if (!newmsg) {
printk(KERN_ERR "can't allocate outgoing packet for msg %i\n",
kmsg->msgobj);
return -ENOMEM;
}
spin_lock_bh(obj->lock);
/* if already tranmitting, just add this to the queue */
if ( obj->obj_flags & CAN_OBJFLAG_HWBUSY ) {
*newmsg = *kmsg;
obj->msg[obj->head] = newmsg;
obj->head = i;
spin_unlock_bh(obj->lock);
break;
}
spin_unlock_bh(obj->lock);
/* not transmitting, can safely begin transmission */
obj->obj_flags |= CAN_OBJFLAG_HWBUSY;
kmem_cache_free(gpsCache, newmsg);
can_config_and_transmit(dev, obj, kmsg, 1);
#endif
break;
//-------------------------------------------------------------------
case CAN_IOCTXMSG: /* tx only, no need to call config_and_transmit() */
if (lib_checkmsgnum(*klong, CAN_MSGFLAG_WRITE))
return -EINVAL;
obj = lib_findobj(dev, *klong, GFP_USER);
if (!obj) return -ENOMEM;
if (obj->owner != filp) return -EPERM;
if (obj->obj_flags & CAN_OBJFLAG_HWBUSY) {
if (filp->f_flags & O_NONBLOCK) return -EAGAIN;
wait_event_interruptible(obj->write_q,
!(obj->obj_flags &CAN_OBJFLAG_HWBUSY));
if (signal_pending(current)) return -ERESTARTSYS;
}
obj->obj_flags |= CAN_OBJFLAG_HWBUSY;
i527_msgflag(dev, obj, I527_CPUUPD_R & I527_NEWDAT_S
& I527_TXRQST_S);
break;
//--------------------------------------------------------------------
case CAN_IOCRXMSG:
/* the message must be already configured and owned */
if (lib_checkmsgnum(kmsg->msgobj, CAN_MSGFLAG_READ))
return -EINVAL;
obj = lib_findobj(dev, kmsg->msgobj, GFP_USER);
if (!obj) return -ENOMEM;
if (obj->owner != filp) return -EPERM;
if (obj->head == obj->tail) {
if (filp->f_flags & O_NONBLOCK) {
return -EAGAIN;
}
wait_event_interruptible(obj->read_q, obj->head != obj->tail);
if (signal_pending(current))
return -ERESTARTSYS;
if (obj->head == obj->tail) {
PDEBUG("Argh: no message\n");
return -EFAULT; /* Hmmm.... */
}
}
PDEBUG("jcan: received msg\n");
i=0;
while((i < CAN_READ_BUFS) && (obj->head != obj->tail))
{
/* ok, we have a message to pull from the buffer */
*kmsg++ = *(obj->msg[obj->tail]);
kmem_cache_free(gpsCache, obj->msg[obj->tail]);
obj->msg[obj->tail] = NULL;
obj->tail = (obj->tail+1) % CAN_BUFSIZE;
i++;
}
retval = i;
size = retval * sizeof(CAN_MSG);
break;
case CAN_IOCPEEKMSG: /* similar, but never sleep and use an exact ID */
#if 0
if (lib_checkmsgnum(kmsg->msgobj, CAN_MSGFLAG_READ))
return -EINVAL;
obj = lib_findobj(dev, kmsg->msgobj, GFP_USER);
if (!obj) return -ENOMEM;
if (obj->owner != filp) return -EPERM;
for (i = obj->tail; i != obj->head; i = i+1 % CAN_BUFSIZE) {
PDEBUG("peeking item %i (%x %x -- %x %x)\n", i,
obj->msg[i]->id, kmsg->id,
obj->msg[i]->config&I527_XTD,
kmsg->config&I527_XTD);
if ((obj->msg[i]->id == kmsg->id) &&
((obj->msg[i]->config&I527_XTD) == (kmsg->config&I527_XTD)))
break;
}
if (i == obj->head) return -EAGAIN;
j = kmsg->flags;
*kmsg = *(obj->msg[i]);
PDEBUG("peekonly: %i\n", j&CAN_MSGFLAG_PEEKONLY);
if (j & CAN_MSGFLAG_PEEKONLY)
break; /* return the data but leave it there */
kmem_cache_free(gpsCache, obj->msg[i]);
/*
* remove the packet by shifting pointers: don't touch head
* as it is the active end, modified at interrupt time
*/
while (i != obj->tail) {
j = (i-1 + CAN_BUFSIZE) % CAN_BUFSIZE;
PDEBUG("copy %i to %i\n", j, i);
obj->msg[i] = obj->msg[j];
i = j;
}
obj->tail = (i+1) % CAN_BUFSIZE;
#endif
break;
case CAN_IOCRELEASEMSG:
if (lib_checkmsgnum(*klong, CAN_MSGFLAG_READ))
return -EINVAL;
obj = lib_findobj(dev, *klong, GFP_USER);
if (!obj) return -ENOMEM;
if (obj->owner != filp) return -EPERM;
lib_releaseobj(dev, *klong, obj);
break;
case CAN_IOCREQSIGNAL:
if (*klong >= SIGRTMIN) return -EINVAL;
/* change an entry, if already there */
info = lib_findfile(dev, filp);
if (info) {
if (!*klong) info->file = NULL;
else info->signal = *klong;
break;
}
/* look for a free slot */
info = dev->errorinfo;
for (i = 0; i<CAN_NR_ERROR_FILES; i++, info++)
if (!info->file)
break;
if (i == CAN_NR_ERROR_FILES) return -EBUSY;
info->file = filp;
info->pid = current->pid;
info->signal = *klong;
break;
/*
* I/O ports
*/
case CAN_IOCINPUT:
if (kreg->reg != 1 && kreg->reg != 2) return -EINVAL;
kreg->val = i527_read_reg(dev, kreg->reg == 1
? I527_P1IN : I527_P2IN);
break;
case CAN_IOCOUTPUT:
if (kreg->reg != 1 && kreg->reg != 2) return -EINVAL;
i527_write_reg(dev, kreg->reg == 1
? I527_P1OUT : I527_P2OUT, kreg->val);
break;
case CAN_IOCIOCFG:
if (kreg->reg != 1 && kreg->reg != 2) return -EINVAL;
i527_write_reg(dev, kreg->reg == 1
? I527_P1CONF : I527_P2CONF, kreg->val);
break;
default:
PDEBUG("ioctl: no such command\n");
return -ENOTTY;
}
/* finally, copy data to user space and return */
if (retval < 0) return retval;
if ((_IOC_DIR(cmd) & _IOC_READ) && (size <= IOC_BUFSIZE))
copy_to_user((void *)arg, karg, size);
return retval; /* sometimes, positive is what I want */
}
//-------------------------------------------------------------------------
static ssize_t can_read(struct file *file, char *buf,
size_t count, loff_t *ppos)
{
// CAN_DEV *dev = (CAN_DEV *) file->private_data;
// int minor = MINOR(file->f_dentry->d_inode->i_rdev);
// int num = CAN_DEV_TYPE(minor);
// unsigned char status;
return -ENOSYS; /* FIXME: reimplement write */
}
//-------------------------------------------------------------------------
static ssize_t can_write(struct file *file, const char *buf,
size_t count, loff_t *ppos)
{
// CAN_DEV *dev = (CAN_DEV *) file->private_data;
// int minor = MINOR(file->f_dentry->d_inode->i_rdev);
// int num = CAN_DEV_TYPE(minor);
return -ENOSYS; /* FIXME: reimplement write */
}
//-------------------------------------------------------------------------
static int can_open(struct inode *inode, struct file *file)
{
int minor = MINOR(inode->i_rdev);
int num = CAN_DEV_TYPE(minor);
int accmode = file->f_flags & O_ACCMODE;
/* We cannot open Message Objects 1-15 for both reading and writing */
if ((num >= 1) && (num < 15) && accmode == O_RDWR)
return -EACCES;
/* We cannot open Message Object 15 for writing */
if ((num == 15) && (accmode != O_RDONLY))
return -EACCES;
/* Set the device's private data */
file->private_data = &gsDev;
/* Check for first open for 82527 */
if (++(gsDev.usecount) == 1)
{
PDEBUG("first open, enable interrupts\n");
i527_write_reg(&gsDev, I527_STAT_REG, I527_STAT_REG_DEFAULT);
can_enable_irq(&gsDev);
}
MOD_INC_USE_COUNT;
return 0;
}
//-------------------------------------------------------------------------
static int can_release(struct inode *inode, struct file *file)
{
CAN_DEV *dev = (CAN_DEV *) file->private_data;
CAN_MSGOBJ *obj;
CAN_ERROR *info;
int i;
/* independently of the minor number, look for any owned objects */
for (i = I527_FIRST_OBJ; i < I527_MSG_OBJS; i++)
{
obj = dev->objs[i];
if (obj && obj->owner == file)
lib_releaseobj(dev, i, obj);
}
/* similarly, release signal notification */
info = lib_findfile(dev, file);
if (info)
info->file = 0;
/* Check for last close for 82527 */
if (--(dev->usecount) == 0)
{
PDEBUG("last close, disable interrupts\n");
can_disable_irq(dev);
}
MOD_DEC_USE_COUNT;
return 0;
}
/*-------------------------------------------------------------------------
* Setup file operations
*/
static struct file_operations can_fops = {
ioctl: can_ioctl,
read: can_read,
write: can_write,
open: can_open,
release: can_release,
};
//-------------------------------------------------------------------------
static void cleanup_device(CAN_DEV *dev)
{
int i;
if (dev->gotirq)
free_irq(IRQ_NUM, (void *)dev);
for (i = 0; i<I527_MSG_OBJS; i++)
if ( dev->objs[i])
{
if (dev->objs[i]->msg)
kfree(dev->objs[i]->msg);
kfree(dev->objs[i]);
dev->objs[i] = NULL;
}
}
//-------------------------------------------------------------------------
int __init init_device(void)
{
int i;
int nRet = 0;
CAN_DEV *dev = &gsDev;
printk(KERN_INFO CAN_TEXT "setting up device, base %x, irq %i\n",
IO_ADDR, IRQ_NUM);
gpsCache = kmem_cache_create("jcan-packet", sizeof(CAN_MSG),
0, SLAB_HWCACHE_ALIGN, NULL, NULL);
if (!gpsCache)
{
printk(KERN_ERR CAN_TEXT "cannot allocate cache for packets\n");
return -ENOMEM;
}
memset(dev, 0, sizeof(*dev));
// Allocate all message objects
for (i = 1; i<I527_MSG_OBJS; i++)
{
CAN_MSGOBJ *obj;
if (!(obj = kmalloc(sizeof(CAN_MSGOBJ), GFP_KERNEL)))
break;
memset(obj, 0, sizeof(*obj));
obj->regbase = i * I527_MSG_OFF;
spin_lock_init(&obj->lock);
dev->objs[i] = obj;
obj->msg = kmalloc(CAN_BUFSIZE * sizeof(CAN_MSG *),
GFP_KERNEL);
if (!obj->msg) break;
memset(obj->msg, 0, CAN_BUFSIZE * sizeof(CAN_MSG *));
}
if (i < I527_MSG_OBJS)
{
nRet = -ENOMEM; goto fail;
}
// map device
dev->base = (long) ioremap(IO_ADDR, IO_RANGE );
if (! dev->base)
{
printk(KERN_ERR CAN_TEXT "I/O map at 0x%x busy\n", IO_ADDR);
goto fail;
}
else
printk("jcan: remapped to: 0x%lx\n", (long)dev->base);
/* Wait queue initialization */
init_waitqueue_head(&dev->error_q);
for (i=0; i<I527_MSG_OBJS; i++)
{
if (dev->objs[i])
{
init_waitqueue_head(&dev->objs[i]->read_q);
init_waitqueue_head(&dev->objs[i]->write_q);
}
}
#ifdef __powerpc__
// Make IRQ3 edge sensitive.
((immap_t *)IMAP_ADDR)->im_siu_conf.sc_siel |= 0x02000000;
nRet = request_8xxirq(IRQ_NUM, can_interrupt, 0, CAN_NAME, (void *)dev);
#else
nRet = request_irq(IRQ_NUM, can_interrupt, 0, CAN_NAME, (void *)dev);
#endif
if (nRet)
{
printk(KERN_ERR CAN_TEXT "dev: can't get irq %i\n", IRQ_NUM);
goto fail;
}
dev->gotirq = 1;
can_reset_i82527(dev); /* can't fail */
/* Register the char device */
nRet = register_chrdev(CAN_MAJOR, CAN_NAME, &can_fops);
if (nRet < 0)
{
printk(KERN_ERR CAN_TEXT "can't get major %d\n", CAN_MAJOR);
goto fail;
}
dev->registered = 1;
return 0; /* success */
fail:
cleanup_device(dev);
return nRet;
}
#ifdef MODULE
//MODULE_LICENSE("GPL");
MODULE_AUTHOR("George Cewe");
MODULE_DESCRIPTION("Driver for i82527 CAN controller");
//------------------------------------------------------------------------
static int can_init(void)
{
int nRet;
nRet = init_device();
if(!nRet)
printk(KERN_INFO CAN_TEXT "loaded (%sdebugging mode)\n",
debug ? "" : "non-");
return nRet;
}
//------------------------------------------------------------------------
static void can_cleanup(void)
{
cleanup_device(&gsDev);
if (gsDev.registered)
unregister_chrdev(CAN_MAJOR, CAN_NAME);
if (gpsCache)
kmem_cache_destroy(gpsCache);
printk(KERN_INFO CAN_TEXT "driver removed\n");
}
module_init(can_init);
module_exit(can_cleanup);
#endif
next reply other threads:[~2003-03-20 17:43 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2003-03-20 17:54 george cewe [this message]
2003-03-20 18:08 ` submission of CAN driver for i82527 chip Jeff Garzik
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to='005501c2ef09$c6f93480$54491cd8@CEVE' \
--to=gcewe@copper.net \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.