2010-03-29 20:58:51 +04:00
/*
* Janz MODULbus VMOD - ICAN3 CAN Interface Driver
*
* Copyright ( c ) 2010 Ira W . Snyder < iws @ ovro . caltech . edu >
*
* 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/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/platform_device.h>
# include <linux/netdevice.h>
# include <linux/can.h>
# include <linux/can/dev.h>
# include <linux/can/error.h>
# include <linux/mfd/janz.h>
/* the DPM has 64k of memory, organized into 256x 256 byte pages */
# define DPM_NUM_PAGES 256
# define DPM_PAGE_SIZE 256
# define DPM_PAGE_ADDR(p) ((p) * DPM_PAGE_SIZE)
/* JANZ ICAN3 "old-style" host interface queue page numbers */
# define QUEUE_OLD_CONTROL 0
# define QUEUE_OLD_RB0 1
# define QUEUE_OLD_RB1 2
# define QUEUE_OLD_WB0 3
# define QUEUE_OLD_WB1 4
/* Janz ICAN3 "old-style" host interface control registers */
# define MSYNC_PEER 0x00 /* ICAN only */
# define MSYNC_LOCL 0x01 /* host only */
# define TARGET_RUNNING 0x02
# define MSYNC_RB0 0x01
# define MSYNC_RB1 0x02
# define MSYNC_RBLW 0x04
# define MSYNC_RB_MASK (MSYNC_RB0 | MSYNC_RB1)
# define MSYNC_WB0 0x10
# define MSYNC_WB1 0x20
# define MSYNC_WBLW 0x40
# define MSYNC_WB_MASK (MSYNC_WB0 | MSYNC_WB1)
/* Janz ICAN3 "new-style" host interface queue page numbers */
# define QUEUE_TOHOST 5
# define QUEUE_FROMHOST_MID 6
# define QUEUE_FROMHOST_HIGH 7
# define QUEUE_FROMHOST_LOW 8
/* The first free page in the DPM is #9 */
# define DPM_FREE_START 9
/* Janz ICAN3 "new-style" and "fast" host interface descriptor flags */
# define DESC_VALID 0x80
# define DESC_WRAP 0x40
# define DESC_INTERRUPT 0x20
# define DESC_IVALID 0x10
# define DESC_LEN(len) (len)
/* Janz ICAN3 Firmware Messages */
# define MSG_CONNECTI 0x02
# define MSG_DISCONNECT 0x03
# define MSG_IDVERS 0x04
# define MSG_MSGLOST 0x05
# define MSG_NEWHOSTIF 0x08
# define MSG_INQUIRY 0x0a
# define MSG_SETAFILMASK 0x10
# define MSG_INITFDPMQUEUE 0x11
# define MSG_HWCONF 0x12
# define MSG_FMSGLOST 0x15
# define MSG_CEVTIND 0x37
# define MSG_CBTRREQ 0x41
# define MSG_COFFREQ 0x42
# define MSG_CONREQ 0x43
# define MSG_CCONFREQ 0x47
/*
* Janz ICAN3 CAN Inquiry Message Types
*
* NOTE : there appears to be a firmware bug here . You must send
* NOTE : INQUIRY_STATUS and expect to receive an INQUIRY_EXTENDED
* NOTE : response . The controller never responds to a message with
* NOTE : the INQUIRY_EXTENDED subspec : (
*/
# define INQUIRY_STATUS 0x00
# define INQUIRY_TERMINATION 0x01
# define INQUIRY_EXTENDED 0x04
/* Janz ICAN3 CAN Set Acceptance Filter Mask Message Types */
# define SETAFILMASK_REJECT 0x00
# define SETAFILMASK_FASTIF 0x02
/* Janz ICAN3 CAN Hardware Configuration Message Types */
# define HWCONF_TERMINATE_ON 0x01
# define HWCONF_TERMINATE_OFF 0x00
/* Janz ICAN3 CAN Event Indication Message Types */
# define CEVTIND_EI 0x01
# define CEVTIND_DOI 0x02
# define CEVTIND_LOST 0x04
# define CEVTIND_FULL 0x08
# define CEVTIND_BEI 0x10
# define CEVTIND_CHIP_SJA1000 0x02
# define ICAN3_BUSERR_QUOTA_MAX 255
/* Janz ICAN3 CAN Frame Conversion */
# define ICAN3_ECHO 0x10
# define ICAN3_EFF_RTR 0x40
# define ICAN3_SFF_RTR 0x10
# define ICAN3_EFF 0x80
# define ICAN3_CAN_TYPE_MASK 0x0f
# define ICAN3_CAN_TYPE_SFF 0x00
# define ICAN3_CAN_TYPE_EFF 0x01
# define ICAN3_CAN_DLC_MASK 0x0f
/*
* SJA1000 Status and Error Register Definitions
*
* Copied from drivers / net / can / sja1000 / sja1000 . h
*/
/* status register content */
# define SR_BS 0x80
# define SR_ES 0x40
# define SR_TS 0x20
# define SR_RS 0x10
# define SR_TCS 0x08
# define SR_TBS 0x04
# define SR_DOS 0x02
# define SR_RBS 0x01
# define SR_CRIT (SR_BS|SR_ES)
/* ECC register */
# define ECC_SEG 0x1F
# define ECC_DIR 0x20
# define ECC_ERR 6
# define ECC_BIT 0x00
# define ECC_FORM 0x40
# define ECC_STUFF 0x80
# define ECC_MASK 0xc0
/* Number of buffers for use in the "new-style" host interface */
# define ICAN3_NEW_BUFFERS 16
/* Number of buffers for use in the "fast" host interface */
# define ICAN3_TX_BUFFERS 512
# define ICAN3_RX_BUFFERS 1024
/* SJA1000 Clock Input */
# define ICAN3_CAN_CLOCK 8000000
/* Driver Name */
# define DRV_NAME "janz-ican3"
/* DPM Control Registers -- starts at offset 0x100 in the MODULbus registers */
struct ican3_dpm_control {
/* window address register */
u8 window_address ;
u8 unused1 ;
/*
* Read access : clear interrupt from microcontroller
* Write access : send interrupt to microcontroller
*/
u8 interrupt ;
u8 unused2 ;
/* write-only: reset all hardware on the module */
u8 hwreset ;
u8 unused3 ;
/* write-only: generate an interrupt to the TPU */
u8 tpuinterrupt ;
} ;
struct ican3_dev {
/* must be the first member */
struct can_priv can ;
/* CAN network device */
struct net_device * ndev ;
struct napi_struct napi ;
/* Device for printing */
struct device * dev ;
/* module number */
unsigned int num ;
/* base address of registers and IRQ */
struct janz_cmodio_onboard_regs __iomem * ctrl ;
struct ican3_dpm_control __iomem * dpmctrl ;
void __iomem * dpm ;
int irq ;
/* CAN bus termination status */
struct completion termination_comp ;
bool termination_enabled ;
/* CAN bus error status registers */
struct completion buserror_comp ;
struct can_berr_counter bec ;
/* old and new style host interface */
unsigned int iftype ;
/*
* Any function which changes the current DPM page must hold this
* lock while it is performing data accesses . This ensures that the
* function will not be preempted and end up reading data from a
* different DPM page than it expects .
*/
spinlock_t lock ;
/* new host interface */
unsigned int rx_int ;
unsigned int rx_num ;
unsigned int tx_num ;
/* fast host interface */
unsigned int fastrx_start ;
unsigned int fastrx_int ;
unsigned int fastrx_num ;
unsigned int fasttx_start ;
unsigned int fasttx_num ;
/* first free DPM page */
unsigned int free_page ;
} ;
struct ican3_msg {
u8 control ;
u8 spec ;
__le16 len ;
u8 data [ 252 ] ;
} ;
struct ican3_new_desc {
u8 control ;
u8 pointer ;
} ;
struct ican3_fast_desc {
u8 control ;
u8 command ;
u8 data [ 14 ] ;
} ;
/* write to the window basic address register */
static inline void ican3_set_page ( struct ican3_dev * mod , unsigned int page )
{
BUG_ON ( page > = DPM_NUM_PAGES ) ;
iowrite8 ( page , & mod - > dpmctrl - > window_address ) ;
}
/*
* ICAN3 " old-style " host interface
*/
/*
2011-03-31 05:57:33 +04:00
* Receive a message from the ICAN3 " old-style " firmware interface
2010-03-29 20:58:51 +04:00
*
* LOCKING : must hold mod - > lock
*
* returns 0 on success , - ENOMEM when no message exists
*/
static int ican3_old_recv_msg ( struct ican3_dev * mod , struct ican3_msg * msg )
{
unsigned int mbox , mbox_page ;
u8 locl , peer , xord ;
/* get the MSYNC registers */
ican3_set_page ( mod , QUEUE_OLD_CONTROL ) ;
peer = ioread8 ( mod - > dpm + MSYNC_PEER ) ;
locl = ioread8 ( mod - > dpm + MSYNC_LOCL ) ;
xord = locl ^ peer ;
if ( ( xord & MSYNC_RB_MASK ) = = 0x00 ) {
dev_dbg ( mod - > dev , " no mbox for reading \n " ) ;
return - ENOMEM ;
}
/* find the first free mbox to read */
if ( ( xord & MSYNC_RB_MASK ) = = MSYNC_RB_MASK )
mbox = ( xord & MSYNC_RBLW ) ? MSYNC_RB0 : MSYNC_RB1 ;
else
mbox = ( xord & MSYNC_RB0 ) ? MSYNC_RB0 : MSYNC_RB1 ;
/* copy the message */
mbox_page = ( mbox = = MSYNC_RB0 ) ? QUEUE_OLD_RB0 : QUEUE_OLD_RB1 ;
ican3_set_page ( mod , mbox_page ) ;
memcpy_fromio ( msg , mod - > dpm , sizeof ( * msg ) ) ;
/*
* notify the firmware that the read buffer is available
* for it to fill again
*/
locl ^ = mbox ;
ican3_set_page ( mod , QUEUE_OLD_CONTROL ) ;
iowrite8 ( locl , mod - > dpm + MSYNC_LOCL ) ;
return 0 ;
}
/*
* Send a message through the " old-style " firmware interface
*
* LOCKING : must hold mod - > lock
*
* returns 0 on success , - ENOMEM when no free space exists
*/
static int ican3_old_send_msg ( struct ican3_dev * mod , struct ican3_msg * msg )
{
unsigned int mbox , mbox_page ;
u8 locl , peer , xord ;
/* get the MSYNC registers */
ican3_set_page ( mod , QUEUE_OLD_CONTROL ) ;
peer = ioread8 ( mod - > dpm + MSYNC_PEER ) ;
locl = ioread8 ( mod - > dpm + MSYNC_LOCL ) ;
xord = locl ^ peer ;
if ( ( xord & MSYNC_WB_MASK ) = = MSYNC_WB_MASK ) {
dev_err ( mod - > dev , " no mbox for writing \n " ) ;
return - ENOMEM ;
}
/* calculate a free mbox to use */
mbox = ( xord & MSYNC_WB0 ) ? MSYNC_WB1 : MSYNC_WB0 ;
/* copy the message to the DPM */
mbox_page = ( mbox = = MSYNC_WB0 ) ? QUEUE_OLD_WB0 : QUEUE_OLD_WB1 ;
ican3_set_page ( mod , mbox_page ) ;
memcpy_toio ( mod - > dpm , msg , sizeof ( * msg ) ) ;
locl ^ = mbox ;
if ( mbox = = MSYNC_WB1 )
locl | = MSYNC_WBLW ;
ican3_set_page ( mod , QUEUE_OLD_CONTROL ) ;
iowrite8 ( locl , mod - > dpm + MSYNC_LOCL ) ;
return 0 ;
}
/*
* ICAN3 " new-style " Host Interface Setup
*/
static void __devinit ican3_init_new_host_interface ( struct ican3_dev * mod )
{
struct ican3_new_desc desc ;
unsigned long flags ;
void __iomem * dst ;
int i ;
spin_lock_irqsave ( & mod - > lock , flags ) ;
/* setup the internal datastructures for RX */
mod - > rx_num = 0 ;
mod - > rx_int = 0 ;
/* tohost queue descriptors are in page 5 */
ican3_set_page ( mod , QUEUE_TOHOST ) ;
dst = mod - > dpm ;
/* initialize the tohost (rx) queue descriptors: pages 9-24 */
for ( i = 0 ; i < ICAN3_NEW_BUFFERS ; i + + ) {
desc . control = DESC_INTERRUPT | DESC_LEN ( 1 ) ; /* I L=1 */
desc . pointer = mod - > free_page ;
/* set wrap flag on last buffer */
if ( i = = ICAN3_NEW_BUFFERS - 1 )
desc . control | = DESC_WRAP ;
memcpy_toio ( dst , & desc , sizeof ( desc ) ) ;
dst + = sizeof ( desc ) ;
mod - > free_page + + ;
}
/* fromhost (tx) mid queue descriptors are in page 6 */
ican3_set_page ( mod , QUEUE_FROMHOST_MID ) ;
dst = mod - > dpm ;
/* setup the internal datastructures for TX */
mod - > tx_num = 0 ;
/* initialize the fromhost mid queue descriptors: pages 25-40 */
for ( i = 0 ; i < ICAN3_NEW_BUFFERS ; i + + ) {
desc . control = DESC_VALID | DESC_LEN ( 1 ) ; /* V L=1 */
desc . pointer = mod - > free_page ;
/* set wrap flag on last buffer */
if ( i = = ICAN3_NEW_BUFFERS - 1 )
desc . control | = DESC_WRAP ;
memcpy_toio ( dst , & desc , sizeof ( desc ) ) ;
dst + = sizeof ( desc ) ;
mod - > free_page + + ;
}
/* fromhost hi queue descriptors are in page 7 */
ican3_set_page ( mod , QUEUE_FROMHOST_HIGH ) ;
dst = mod - > dpm ;
/* initialize only a single buffer in the fromhost hi queue (unused) */
desc . control = DESC_VALID | DESC_WRAP | DESC_LEN ( 1 ) ; /* VW L=1 */
desc . pointer = mod - > free_page ;
memcpy_toio ( dst , & desc , sizeof ( desc ) ) ;
mod - > free_page + + ;
/* fromhost low queue descriptors are in page 8 */
ican3_set_page ( mod , QUEUE_FROMHOST_LOW ) ;
dst = mod - > dpm ;
/* initialize only a single buffer in the fromhost low queue (unused) */
desc . control = DESC_VALID | DESC_WRAP | DESC_LEN ( 1 ) ; /* VW L=1 */
desc . pointer = mod - > free_page ;
memcpy_toio ( dst , & desc , sizeof ( desc ) ) ;
mod - > free_page + + ;
spin_unlock_irqrestore ( & mod - > lock , flags ) ;
}
/*
* ICAN3 Fast Host Interface Setup
*/
static void __devinit ican3_init_fast_host_interface ( struct ican3_dev * mod )
{
struct ican3_fast_desc desc ;
unsigned long flags ;
unsigned int addr ;
void __iomem * dst ;
int i ;
spin_lock_irqsave ( & mod - > lock , flags ) ;
/* save the start recv page */
mod - > fastrx_start = mod - > free_page ;
mod - > fastrx_num = 0 ;
mod - > fastrx_int = 0 ;
/* build a single fast tohost queue descriptor */
memset ( & desc , 0 , sizeof ( desc ) ) ;
desc . control = 0x00 ;
desc . command = 1 ;
/* build the tohost queue descriptor ring in memory */
addr = 0 ;
for ( i = 0 ; i < ICAN3_RX_BUFFERS ; i + + ) {
/* set the wrap bit on the last buffer */
if ( i = = ICAN3_RX_BUFFERS - 1 )
desc . control | = DESC_WRAP ;
/* switch to the correct page */
ican3_set_page ( mod , mod - > free_page ) ;
/* copy the descriptor to the DPM */
dst = mod - > dpm + addr ;
memcpy_toio ( dst , & desc , sizeof ( desc ) ) ;
addr + = sizeof ( desc ) ;
/* move to the next page if necessary */
if ( addr > = DPM_PAGE_SIZE ) {
addr = 0 ;
mod - > free_page + + ;
}
}
/* make sure we page-align the next queue */
if ( addr ! = 0 )
mod - > free_page + + ;
/* save the start xmit page */
mod - > fasttx_start = mod - > free_page ;
mod - > fasttx_num = 0 ;
/* build a single fast fromhost queue descriptor */
memset ( & desc , 0 , sizeof ( desc ) ) ;
desc . control = DESC_VALID ;
desc . command = 1 ;
/* build the fromhost queue descriptor ring in memory */
addr = 0 ;
for ( i = 0 ; i < ICAN3_TX_BUFFERS ; i + + ) {
/* set the wrap bit on the last buffer */
if ( i = = ICAN3_TX_BUFFERS - 1 )
desc . control | = DESC_WRAP ;
/* switch to the correct page */
ican3_set_page ( mod , mod - > free_page ) ;
/* copy the descriptor to the DPM */
dst = mod - > dpm + addr ;
memcpy_toio ( dst , & desc , sizeof ( desc ) ) ;
addr + = sizeof ( desc ) ;
/* move to the next page if necessary */
if ( addr > = DPM_PAGE_SIZE ) {
addr = 0 ;
mod - > free_page + + ;
}
}
spin_unlock_irqrestore ( & mod - > lock , flags ) ;
}
/*
* ICAN3 " new-style " Host Interface Message Helpers
*/
/*
* LOCKING : must hold mod - > lock
*/
static int ican3_new_send_msg ( struct ican3_dev * mod , struct ican3_msg * msg )
{
struct ican3_new_desc desc ;
void __iomem * desc_addr = mod - > dpm + ( mod - > tx_num * sizeof ( desc ) ) ;
/* switch to the fromhost mid queue, and read the buffer descriptor */
ican3_set_page ( mod , QUEUE_FROMHOST_MID ) ;
memcpy_fromio ( & desc , desc_addr , sizeof ( desc ) ) ;
if ( ! ( desc . control & DESC_VALID ) ) {
dev_dbg ( mod - > dev , " %s: no free buffers \n " , __func__ ) ;
return - ENOMEM ;
}
/* switch to the data page, copy the data */
ican3_set_page ( mod , desc . pointer ) ;
memcpy_toio ( mod - > dpm , msg , sizeof ( * msg ) ) ;
/* switch back to the descriptor, set the valid bit, write it back */
ican3_set_page ( mod , QUEUE_FROMHOST_MID ) ;
desc . control ^ = DESC_VALID ;
memcpy_toio ( desc_addr , & desc , sizeof ( desc ) ) ;
/* update the tx number */
mod - > tx_num = ( desc . control & DESC_WRAP ) ? 0 : ( mod - > tx_num + 1 ) ;
return 0 ;
}
/*
* LOCKING : must hold mod - > lock
*/
static int ican3_new_recv_msg ( struct ican3_dev * mod , struct ican3_msg * msg )
{
struct ican3_new_desc desc ;
void __iomem * desc_addr = mod - > dpm + ( mod - > rx_num * sizeof ( desc ) ) ;
/* switch to the tohost queue, and read the buffer descriptor */
ican3_set_page ( mod , QUEUE_TOHOST ) ;
memcpy_fromio ( & desc , desc_addr , sizeof ( desc ) ) ;
if ( ! ( desc . control & DESC_VALID ) ) {
dev_dbg ( mod - > dev , " %s: no buffers to recv \n " , __func__ ) ;
return - ENOMEM ;
}
/* switch to the data page, copy the data */
ican3_set_page ( mod , desc . pointer ) ;
memcpy_fromio ( msg , mod - > dpm , sizeof ( * msg ) ) ;
/* switch back to the descriptor, toggle the valid bit, write it back */
ican3_set_page ( mod , QUEUE_TOHOST ) ;
desc . control ^ = DESC_VALID ;
memcpy_toio ( desc_addr , & desc , sizeof ( desc ) ) ;
/* update the rx number */
mod - > rx_num = ( desc . control & DESC_WRAP ) ? 0 : ( mod - > rx_num + 1 ) ;
return 0 ;
}
/*
* Message Send / Recv Helpers
*/
static int ican3_send_msg ( struct ican3_dev * mod , struct ican3_msg * msg )
{
unsigned long flags ;
int ret ;
spin_lock_irqsave ( & mod - > lock , flags ) ;
if ( mod - > iftype = = 0 )
ret = ican3_old_send_msg ( mod , msg ) ;
else
ret = ican3_new_send_msg ( mod , msg ) ;
spin_unlock_irqrestore ( & mod - > lock , flags ) ;
return ret ;
}
static int ican3_recv_msg ( struct ican3_dev * mod , struct ican3_msg * msg )
{
unsigned long flags ;
int ret ;
spin_lock_irqsave ( & mod - > lock , flags ) ;
if ( mod - > iftype = = 0 )
ret = ican3_old_recv_msg ( mod , msg ) ;
else
ret = ican3_new_recv_msg ( mod , msg ) ;
spin_unlock_irqrestore ( & mod - > lock , flags ) ;
return ret ;
}
/*
* Quick Pre - constructed Messages
*/
static int __devinit ican3_msg_connect ( struct ican3_dev * mod )
{
struct ican3_msg msg ;
memset ( & msg , 0 , sizeof ( msg ) ) ;
msg . spec = MSG_CONNECTI ;
msg . len = cpu_to_le16 ( 0 ) ;
return ican3_send_msg ( mod , & msg ) ;
}
static int __devexit ican3_msg_disconnect ( struct ican3_dev * mod )
{
struct ican3_msg msg ;
memset ( & msg , 0 , sizeof ( msg ) ) ;
msg . spec = MSG_DISCONNECT ;
msg . len = cpu_to_le16 ( 0 ) ;
return ican3_send_msg ( mod , & msg ) ;
}
static int __devinit ican3_msg_newhostif ( struct ican3_dev * mod )
{
struct ican3_msg msg ;
int ret ;
memset ( & msg , 0 , sizeof ( msg ) ) ;
msg . spec = MSG_NEWHOSTIF ;
msg . len = cpu_to_le16 ( 0 ) ;
/* If we're not using the old interface, switching seems bogus */
WARN_ON ( mod - > iftype ! = 0 ) ;
ret = ican3_send_msg ( mod , & msg ) ;
if ( ret )
return ret ;
/* mark the module as using the new host interface */
mod - > iftype = 1 ;
return 0 ;
}
static int __devinit ican3_msg_fasthostif ( struct ican3_dev * mod )
{
struct ican3_msg msg ;
unsigned int addr ;
memset ( & msg , 0 , sizeof ( msg ) ) ;
msg . spec = MSG_INITFDPMQUEUE ;
msg . len = cpu_to_le16 ( 8 ) ;
/* write the tohost queue start address */
addr = DPM_PAGE_ADDR ( mod - > fastrx_start ) ;
msg . data [ 0 ] = addr & 0xff ;
msg . data [ 1 ] = ( addr > > 8 ) & 0xff ;
msg . data [ 2 ] = ( addr > > 16 ) & 0xff ;
msg . data [ 3 ] = ( addr > > 24 ) & 0xff ;
/* write the fromhost queue start address */
addr = DPM_PAGE_ADDR ( mod - > fasttx_start ) ;
msg . data [ 4 ] = addr & 0xff ;
msg . data [ 5 ] = ( addr > > 8 ) & 0xff ;
msg . data [ 6 ] = ( addr > > 16 ) & 0xff ;
msg . data [ 7 ] = ( addr > > 24 ) & 0xff ;
/* If we're not using the new interface yet, we cannot do this */
WARN_ON ( mod - > iftype ! = 1 ) ;
return ican3_send_msg ( mod , & msg ) ;
}
/*
* Setup the CAN filter to either accept or reject all
* messages from the CAN bus .
*/
static int __devinit ican3_set_id_filter ( struct ican3_dev * mod , bool accept )
{
struct ican3_msg msg ;
int ret ;
/* Standard Frame Format */
memset ( & msg , 0 , sizeof ( msg ) ) ;
msg . spec = MSG_SETAFILMASK ;
msg . len = cpu_to_le16 ( 5 ) ;
msg . data [ 0 ] = 0x00 ; /* IDLo LSB */
msg . data [ 1 ] = 0x00 ; /* IDLo MSB */
msg . data [ 2 ] = 0xff ; /* IDHi LSB */
msg . data [ 3 ] = 0x07 ; /* IDHi MSB */
/* accept all frames for fast host if, or reject all frames */
msg . data [ 4 ] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT ;
ret = ican3_send_msg ( mod , & msg ) ;
if ( ret )
return ret ;
/* Extended Frame Format */
memset ( & msg , 0 , sizeof ( msg ) ) ;
msg . spec = MSG_SETAFILMASK ;
msg . len = cpu_to_le16 ( 13 ) ;
msg . data [ 0 ] = 0 ; /* MUX = 0 */
msg . data [ 1 ] = 0x00 ; /* IDLo LSB */
msg . data [ 2 ] = 0x00 ;
msg . data [ 3 ] = 0x00 ;
msg . data [ 4 ] = 0x20 ; /* IDLo MSB */
msg . data [ 5 ] = 0xff ; /* IDHi LSB */
msg . data [ 6 ] = 0xff ;
msg . data [ 7 ] = 0xff ;
msg . data [ 8 ] = 0x3f ; /* IDHi MSB */
/* accept all frames for fast host if, or reject all frames */
msg . data [ 9 ] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT ;
return ican3_send_msg ( mod , & msg ) ;
}
/*
* Bring the CAN bus online or offline
*/
static int ican3_set_bus_state ( struct ican3_dev * mod , bool on )
{
struct ican3_msg msg ;
memset ( & msg , 0 , sizeof ( msg ) ) ;
msg . spec = on ? MSG_CONREQ : MSG_COFFREQ ;
msg . len = cpu_to_le16 ( 0 ) ;
return ican3_send_msg ( mod , & msg ) ;
}
static int ican3_set_termination ( struct ican3_dev * mod , bool on )
{
struct ican3_msg msg ;
memset ( & msg , 0 , sizeof ( msg ) ) ;
msg . spec = MSG_HWCONF ;
msg . len = cpu_to_le16 ( 2 ) ;
msg . data [ 0 ] = 0x00 ;
msg . data [ 1 ] = on ? HWCONF_TERMINATE_ON : HWCONF_TERMINATE_OFF ;
return ican3_send_msg ( mod , & msg ) ;
}
static int ican3_send_inquiry ( struct ican3_dev * mod , u8 subspec )
{
struct ican3_msg msg ;
memset ( & msg , 0 , sizeof ( msg ) ) ;
msg . spec = MSG_INQUIRY ;
msg . len = cpu_to_le16 ( 2 ) ;
msg . data [ 0 ] = subspec ;
msg . data [ 1 ] = 0x00 ;
return ican3_send_msg ( mod , & msg ) ;
}
static int ican3_set_buserror ( struct ican3_dev * mod , u8 quota )
{
struct ican3_msg msg ;
memset ( & msg , 0 , sizeof ( msg ) ) ;
msg . spec = MSG_CCONFREQ ;
msg . len = cpu_to_le16 ( 2 ) ;
msg . data [ 0 ] = 0x00 ;
msg . data [ 1 ] = quota ;
return ican3_send_msg ( mod , & msg ) ;
}
/*
* ICAN3 to Linux CAN Frame Conversion
*/
static void ican3_to_can_frame ( struct ican3_dev * mod ,
struct ican3_fast_desc * desc ,
struct can_frame * cf )
{
if ( ( desc - > command & ICAN3_CAN_TYPE_MASK ) = = ICAN3_CAN_TYPE_SFF ) {
if ( desc - > data [ 1 ] & ICAN3_SFF_RTR )
cf - > can_id | = CAN_RTR_FLAG ;
cf - > can_id | = desc - > data [ 0 ] < < 3 ;
cf - > can_id | = ( desc - > data [ 1 ] & 0xe0 ) > > 5 ;
cf - > can_dlc = desc - > data [ 1 ] & ICAN3_CAN_DLC_MASK ;
memcpy ( cf - > data , & desc - > data [ 2 ] , sizeof ( cf - > data ) ) ;
} else {
cf - > can_dlc = desc - > data [ 0 ] & ICAN3_CAN_DLC_MASK ;
if ( desc - > data [ 0 ] & ICAN3_EFF_RTR )
cf - > can_id | = CAN_RTR_FLAG ;
if ( desc - > data [ 0 ] & ICAN3_EFF ) {
cf - > can_id | = CAN_EFF_FLAG ;
cf - > can_id | = desc - > data [ 2 ] < < 21 ; /* 28-21 */
cf - > can_id | = desc - > data [ 3 ] < < 13 ; /* 20-13 */
cf - > can_id | = desc - > data [ 4 ] < < 5 ; /* 12-5 */
cf - > can_id | = ( desc - > data [ 5 ] & 0xf8 ) > > 3 ;
} else {
cf - > can_id | = desc - > data [ 2 ] < < 3 ; /* 10-3 */
cf - > can_id | = desc - > data [ 3 ] > > 5 ; /* 2-0 */
}
memcpy ( cf - > data , & desc - > data [ 6 ] , sizeof ( cf - > data ) ) ;
}
}
static void can_frame_to_ican3 ( struct ican3_dev * mod ,
struct can_frame * cf ,
struct ican3_fast_desc * desc )
{
/* clear out any stale data in the descriptor */
memset ( desc - > data , 0 , sizeof ( desc - > data ) ) ;
/* we always use the extended format, with the ECHO flag set */
desc - > command = ICAN3_CAN_TYPE_EFF ;
desc - > data [ 0 ] | = cf - > can_dlc ;
desc - > data [ 1 ] | = ICAN3_ECHO ;
if ( cf - > can_id & CAN_RTR_FLAG )
desc - > data [ 0 ] | = ICAN3_EFF_RTR ;
/* pack the id into the correct places */
if ( cf - > can_id & CAN_EFF_FLAG ) {
desc - > data [ 0 ] | = ICAN3_EFF ;
desc - > data [ 2 ] = ( cf - > can_id & 0x1fe00000 ) > > 21 ; /* 28-21 */
desc - > data [ 3 ] = ( cf - > can_id & 0x001fe000 ) > > 13 ; /* 20-13 */
desc - > data [ 4 ] = ( cf - > can_id & 0x00001fe0 ) > > 5 ; /* 12-5 */
desc - > data [ 5 ] = ( cf - > can_id & 0x0000001f ) < < 3 ; /* 4-0 */
} else {
desc - > data [ 2 ] = ( cf - > can_id & 0x7F8 ) > > 3 ; /* bits 10-3 */
desc - > data [ 3 ] = ( cf - > can_id & 0x007 ) < < 5 ; /* bits 2-0 */
}
/* copy the data bits into the descriptor */
memcpy ( & desc - > data [ 6 ] , cf - > data , sizeof ( cf - > data ) ) ;
}
/*
* Interrupt Handling
*/
/*
* Handle an ID + Version message response from the firmware . We never generate
* this message in production code , but it is very useful when debugging to be
* able to display this message .
*/
static void ican3_handle_idvers ( struct ican3_dev * mod , struct ican3_msg * msg )
{
dev_dbg ( mod - > dev , " IDVERS response: %s \n " , msg - > data ) ;
}
static void ican3_handle_msglost ( struct ican3_dev * mod , struct ican3_msg * msg )
{
struct net_device * dev = mod - > ndev ;
struct net_device_stats * stats = & dev - > stats ;
struct can_frame * cf ;
struct sk_buff * skb ;
/*
* Report that communication messages with the microcontroller firmware
* are being lost . These are never CAN frames , so we do not generate an
* error frame for userspace
*/
if ( msg - > spec = = MSG_MSGLOST ) {
dev_err ( mod - > dev , " lost %d control messages \n " , msg - > data [ 0 ] ) ;
return ;
}
/*
* Oops , this indicates that we have lost messages in the fast queue ,
* which are exclusively CAN messages . Our driver isn ' t reading CAN
* frames fast enough .
*
* We ' ll pretend that the SJA1000 told us that it ran out of buffer
* space , because there is not a better message for this .
*/
skb = alloc_can_err_skb ( dev , & cf ) ;
if ( skb ) {
cf - > can_id | = CAN_ERR_CRTL ;
cf - > data [ 1 ] = CAN_ERR_CRTL_RX_OVERFLOW ;
stats - > rx_errors + + ;
stats - > rx_bytes + = cf - > can_dlc ;
netif_rx ( skb ) ;
}
}
/*
* Handle CAN Event Indication Messages from the firmware
*
* The ICAN3 firmware provides the values of some SJA1000 registers when it
* generates this message . The code below is largely copied from the
* drivers / net / can / sja1000 / sja1000 . c file , and adapted as necessary
*/
static int ican3_handle_cevtind ( struct ican3_dev * mod , struct ican3_msg * msg )
{
struct net_device * dev = mod - > ndev ;
struct net_device_stats * stats = & dev - > stats ;
enum can_state state = mod - > can . state ;
u8 status , isrc , rxerr , txerr ;
struct can_frame * cf ;
struct sk_buff * skb ;
/* we can only handle the SJA1000 part */
if ( msg - > data [ 1 ] ! = CEVTIND_CHIP_SJA1000 ) {
dev_err ( mod - > dev , " unable to handle errors on non-SJA1000 \n " ) ;
return - ENODEV ;
}
/* check the message length for sanity */
if ( le16_to_cpu ( msg - > len ) < 6 ) {
dev_err ( mod - > dev , " error message too short \n " ) ;
return - EINVAL ;
}
skb = alloc_can_err_skb ( dev , & cf ) ;
if ( skb = = NULL )
return - ENOMEM ;
isrc = msg - > data [ 0 ] ;
status = msg - > data [ 3 ] ;
rxerr = msg - > data [ 4 ] ;
txerr = msg - > data [ 5 ] ;
/* data overrun interrupt */
if ( isrc = = CEVTIND_DOI | | isrc = = CEVTIND_LOST ) {
dev_dbg ( mod - > dev , " data overrun interrupt \n " ) ;
cf - > can_id | = CAN_ERR_CRTL ;
cf - > data [ 1 ] = CAN_ERR_CRTL_RX_OVERFLOW ;
stats - > rx_over_errors + + ;
stats - > rx_errors + + ;
}
/* error warning + passive interrupt */
if ( isrc = = CEVTIND_EI ) {
dev_dbg ( mod - > dev , " error warning + passive interrupt \n " ) ;
if ( status & SR_BS ) {
state = CAN_STATE_BUS_OFF ;
cf - > can_id | = CAN_ERR_BUSOFF ;
can_bus_off ( dev ) ;
} else if ( status & SR_ES ) {
if ( rxerr > = 128 | | txerr > = 128 )
state = CAN_STATE_ERROR_PASSIVE ;
else
state = CAN_STATE_ERROR_WARNING ;
} else {
state = CAN_STATE_ERROR_ACTIVE ;
}
}
/* bus error interrupt */
if ( isrc = = CEVTIND_BEI ) {
u8 ecc = msg - > data [ 2 ] ;
dev_dbg ( mod - > dev , " bus error interrupt \n " ) ;
mod - > can . can_stats . bus_error + + ;
stats - > rx_errors + + ;
cf - > can_id | = CAN_ERR_PROT | CAN_ERR_BUSERROR ;
switch ( ecc & ECC_MASK ) {
case ECC_BIT :
cf - > data [ 2 ] | = CAN_ERR_PROT_BIT ;
break ;
case ECC_FORM :
cf - > data [ 2 ] | = CAN_ERR_PROT_FORM ;
break ;
case ECC_STUFF :
cf - > data [ 2 ] | = CAN_ERR_PROT_STUFF ;
break ;
default :
cf - > data [ 2 ] | = CAN_ERR_PROT_UNSPEC ;
cf - > data [ 3 ] = ecc & ECC_SEG ;
break ;
}
if ( ( ecc & ECC_DIR ) = = 0 )
cf - > data [ 2 ] | = CAN_ERR_PROT_TX ;
cf - > data [ 6 ] = txerr ;
cf - > data [ 7 ] = rxerr ;
}
if ( state ! = mod - > can . state & & ( state = = CAN_STATE_ERROR_WARNING | |
state = = CAN_STATE_ERROR_PASSIVE ) ) {
cf - > can_id | = CAN_ERR_CRTL ;
if ( state = = CAN_STATE_ERROR_WARNING ) {
mod - > can . can_stats . error_warning + + ;
cf - > data [ 1 ] = ( txerr > rxerr ) ?
CAN_ERR_CRTL_TX_WARNING :
CAN_ERR_CRTL_RX_WARNING ;
} else {
mod - > can . can_stats . error_passive + + ;
cf - > data [ 1 ] = ( txerr > rxerr ) ?
CAN_ERR_CRTL_TX_PASSIVE :
CAN_ERR_CRTL_RX_PASSIVE ;
}
cf - > data [ 6 ] = txerr ;
cf - > data [ 7 ] = rxerr ;
}
mod - > can . state = state ;
stats - > rx_errors + + ;
stats - > rx_bytes + = cf - > can_dlc ;
netif_rx ( skb ) ;
return 0 ;
}
static void ican3_handle_inquiry ( struct ican3_dev * mod , struct ican3_msg * msg )
{
switch ( msg - > data [ 0 ] ) {
case INQUIRY_STATUS :
case INQUIRY_EXTENDED :
mod - > bec . rxerr = msg - > data [ 5 ] ;
mod - > bec . txerr = msg - > data [ 6 ] ;
complete ( & mod - > buserror_comp ) ;
break ;
case INQUIRY_TERMINATION :
mod - > termination_enabled = msg - > data [ 6 ] & HWCONF_TERMINATE_ON ;
complete ( & mod - > termination_comp ) ;
break ;
default :
2011-03-31 05:57:33 +04:00
dev_err ( mod - > dev , " received an unknown inquiry response \n " ) ;
2010-03-29 20:58:51 +04:00
break ;
}
}
static void ican3_handle_unknown_message ( struct ican3_dev * mod ,
struct ican3_msg * msg )
{
2011-03-31 05:57:33 +04:00
dev_warn ( mod - > dev , " received unknown message: spec 0x%.2x length %d \n " ,
2010-03-29 20:58:51 +04:00
msg - > spec , le16_to_cpu ( msg - > len ) ) ;
}
/*
* Handle a control message from the firmware
*/
static void ican3_handle_message ( struct ican3_dev * mod , struct ican3_msg * msg )
{
dev_dbg ( mod - > dev , " %s: modno %d spec 0x%.2x len %d bytes \n " , __func__ ,
mod - > num , msg - > spec , le16_to_cpu ( msg - > len ) ) ;
switch ( msg - > spec ) {
case MSG_IDVERS :
ican3_handle_idvers ( mod , msg ) ;
break ;
case MSG_MSGLOST :
case MSG_FMSGLOST :
ican3_handle_msglost ( mod , msg ) ;
break ;
case MSG_CEVTIND :
ican3_handle_cevtind ( mod , msg ) ;
break ;
case MSG_INQUIRY :
ican3_handle_inquiry ( mod , msg ) ;
break ;
default :
ican3_handle_unknown_message ( mod , msg ) ;
break ;
}
}
/*
* Check that there is room in the TX ring to transmit another skb
*
* LOCKING : must hold mod - > lock
*/
static bool ican3_txok ( struct ican3_dev * mod )
{
struct ican3_fast_desc __iomem * desc ;
u8 control ;
/* copy the control bits of the descriptor */
ican3_set_page ( mod , mod - > fasttx_start + ( mod - > fasttx_num / 16 ) ) ;
desc = mod - > dpm + ( ( mod - > fasttx_num % 16 ) * sizeof ( * desc ) ) ;
control = ioread8 ( & desc - > control ) ;
/* if the control bits are not valid, then we have no more space */
if ( ! ( control & DESC_VALID ) )
return false ;
return true ;
}
/*
2011-03-31 05:57:33 +04:00
* Receive one CAN frame from the hardware
2010-03-29 20:58:51 +04:00
*
* CONTEXT : must be called from user context
*/
static int ican3_recv_skb ( struct ican3_dev * mod )
{
struct net_device * ndev = mod - > ndev ;
struct net_device_stats * stats = & ndev - > stats ;
struct ican3_fast_desc desc ;
void __iomem * desc_addr ;
struct can_frame * cf ;
struct sk_buff * skb ;
unsigned long flags ;
spin_lock_irqsave ( & mod - > lock , flags ) ;
/* copy the whole descriptor */
ican3_set_page ( mod , mod - > fastrx_start + ( mod - > fastrx_num / 16 ) ) ;
desc_addr = mod - > dpm + ( ( mod - > fastrx_num % 16 ) * sizeof ( desc ) ) ;
memcpy_fromio ( & desc , desc_addr , sizeof ( desc ) ) ;
spin_unlock_irqrestore ( & mod - > lock , flags ) ;
/* check that we actually have a CAN frame */
if ( ! ( desc . control & DESC_VALID ) )
return - ENOBUFS ;
/* allocate an skb */
skb = alloc_can_skb ( ndev , & cf ) ;
if ( unlikely ( skb = = NULL ) ) {
stats - > rx_dropped + + ;
goto err_noalloc ;
}
/* convert the ICAN3 frame into Linux CAN format */
ican3_to_can_frame ( mod , & desc , cf ) ;
/* receive the skb, update statistics */
netif_receive_skb ( skb ) ;
stats - > rx_packets + + ;
stats - > rx_bytes + = cf - > can_dlc ;
err_noalloc :
/* toggle the valid bit and return the descriptor to the ring */
desc . control ^ = DESC_VALID ;
spin_lock_irqsave ( & mod - > lock , flags ) ;
ican3_set_page ( mod , mod - > fastrx_start + ( mod - > fastrx_num / 16 ) ) ;
memcpy_toio ( desc_addr , & desc , 1 ) ;
/* update the next buffer pointer */
mod - > fastrx_num = ( desc . control & DESC_WRAP ) ? 0
: ( mod - > fastrx_num + 1 ) ;
/* there are still more buffers to process */
spin_unlock_irqrestore ( & mod - > lock , flags ) ;
return 0 ;
}
static int ican3_napi ( struct napi_struct * napi , int budget )
{
struct ican3_dev * mod = container_of ( napi , struct ican3_dev , napi ) ;
struct ican3_msg msg ;
unsigned long flags ;
int received = 0 ;
int ret ;
/* process all communication messages */
while ( true ) {
ret = ican3_recv_msg ( mod , & msg ) ;
if ( ret )
break ;
ican3_handle_message ( mod , & msg ) ;
}
/* process all CAN frames from the fast interface */
while ( received < budget ) {
ret = ican3_recv_skb ( mod ) ;
if ( ret )
break ;
received + + ;
}
/* We have processed all packets that the adapter had, but it
* was less than our budget , stop polling */
if ( received < budget )
napi_complete ( napi ) ;
spin_lock_irqsave ( & mod - > lock , flags ) ;
/* Wake up the transmit queue if necessary */
if ( netif_queue_stopped ( mod - > ndev ) & & ican3_txok ( mod ) )
netif_wake_queue ( mod - > ndev ) ;
spin_unlock_irqrestore ( & mod - > lock , flags ) ;
/* re-enable interrupt generation */
iowrite8 ( 1 < < mod - > num , & mod - > ctrl - > int_enable ) ;
return received ;
}
static irqreturn_t ican3_irq ( int irq , void * dev_id )
{
struct ican3_dev * mod = dev_id ;
u8 stat ;
/*
* The interrupt status register on this device reports interrupts
* as zeroes instead of using ones like most other devices
*/
stat = ioread8 ( & mod - > ctrl - > int_disable ) & ( 1 < < mod - > num ) ;
if ( stat = = ( 1 < < mod - > num ) )
return IRQ_NONE ;
/* clear the MODULbus interrupt from the microcontroller */
ioread8 ( & mod - > dpmctrl - > interrupt ) ;
/* disable interrupt generation, schedule the NAPI poller */
iowrite8 ( 1 < < mod - > num , & mod - > ctrl - > int_disable ) ;
napi_schedule ( & mod - > napi ) ;
return IRQ_HANDLED ;
}
/*
* Firmware reset , startup , and shutdown
*/
/*
* Reset an ICAN module to its power - on state
*
* CONTEXT : no network device registered
*/
static int ican3_reset_module ( struct ican3_dev * mod )
{
u8 val = 1 < < mod - > num ;
unsigned long start ;
u8 runold , runnew ;
/* disable interrupts so no more work is scheduled */
iowrite8 ( 1 < < mod - > num , & mod - > ctrl - > int_disable ) ;
/* the first unallocated page in the DPM is #9 */
mod - > free_page = DPM_FREE_START ;
ican3_set_page ( mod , QUEUE_OLD_CONTROL ) ;
runold = ioread8 ( mod - > dpm + TARGET_RUNNING ) ;
/* reset the module */
iowrite8 ( val , & mod - > ctrl - > reset_assert ) ;
iowrite8 ( val , & mod - > ctrl - > reset_deassert ) ;
/* wait until the module has finished resetting and is running */
start = jiffies ;
do {
ican3_set_page ( mod , QUEUE_OLD_CONTROL ) ;
runnew = ioread8 ( mod - > dpm + TARGET_RUNNING ) ;
if ( runnew = = ( runold ^ 0xff ) )
return 0 ;
msleep ( 10 ) ;
} while ( time_before ( jiffies , start + HZ / 4 ) ) ;
dev_err ( mod - > dev , " failed to reset CAN module \n " ) ;
return - ETIMEDOUT ;
}
static void __devexit ican3_shutdown_module ( struct ican3_dev * mod )
{
ican3_msg_disconnect ( mod ) ;
ican3_reset_module ( mod ) ;
}
/*
* Startup an ICAN module , bringing it into fast mode
*/
static int __devinit ican3_startup_module ( struct ican3_dev * mod )
{
int ret ;
ret = ican3_reset_module ( mod ) ;
if ( ret ) {
dev_err ( mod - > dev , " unable to reset module \n " ) ;
return ret ;
}
/* re-enable interrupts so we can send messages */
iowrite8 ( 1 < < mod - > num , & mod - > ctrl - > int_enable ) ;
ret = ican3_msg_connect ( mod ) ;
if ( ret ) {
dev_err ( mod - > dev , " unable to connect to module \n " ) ;
return ret ;
}
ican3_init_new_host_interface ( mod ) ;
ret = ican3_msg_newhostif ( mod ) ;
if ( ret ) {
dev_err ( mod - > dev , " unable to switch to new-style interface \n " ) ;
return ret ;
}
/* default to "termination on" */
ret = ican3_set_termination ( mod , true ) ;
if ( ret ) {
dev_err ( mod - > dev , " unable to enable termination \n " ) ;
return ret ;
}
/* default to "bus errors enabled" */
ret = ican3_set_buserror ( mod , ICAN3_BUSERR_QUOTA_MAX ) ;
if ( ret ) {
dev_err ( mod - > dev , " unable to set bus-error \n " ) ;
return ret ;
}
ican3_init_fast_host_interface ( mod ) ;
ret = ican3_msg_fasthostif ( mod ) ;
if ( ret ) {
dev_err ( mod - > dev , " unable to switch to fast host interface \n " ) ;
return ret ;
}
ret = ican3_set_id_filter ( mod , true ) ;
if ( ret ) {
dev_err ( mod - > dev , " unable to set acceptance filter \n " ) ;
return ret ;
}
return 0 ;
}
/*
* CAN Network Device
*/
static int ican3_open ( struct net_device * ndev )
{
struct ican3_dev * mod = netdev_priv ( ndev ) ;
u8 quota ;
int ret ;
/* open the CAN layer */
ret = open_candev ( ndev ) ;
if ( ret ) {
dev_err ( mod - > dev , " unable to start CAN layer \n " ) ;
return ret ;
}
/* set the bus error generation state appropriately */
if ( mod - > can . ctrlmode & CAN_CTRLMODE_BERR_REPORTING )
quota = ICAN3_BUSERR_QUOTA_MAX ;
else
quota = 0 ;
ret = ican3_set_buserror ( mod , quota ) ;
if ( ret ) {
dev_err ( mod - > dev , " unable to set bus-error \n " ) ;
close_candev ( ndev ) ;
return ret ;
}
/* bring the bus online */
ret = ican3_set_bus_state ( mod , true ) ;
if ( ret ) {
dev_err ( mod - > dev , " unable to set bus-on \n " ) ;
close_candev ( ndev ) ;
return ret ;
}
/* start up the network device */
mod - > can . state = CAN_STATE_ERROR_ACTIVE ;
netif_start_queue ( ndev ) ;
return 0 ;
}
static int ican3_stop ( struct net_device * ndev )
{
struct ican3_dev * mod = netdev_priv ( ndev ) ;
int ret ;
/* stop the network device xmit routine */
netif_stop_queue ( ndev ) ;
mod - > can . state = CAN_STATE_STOPPED ;
/* bring the bus offline, stop receiving packets */
ret = ican3_set_bus_state ( mod , false ) ;
if ( ret ) {
dev_err ( mod - > dev , " unable to set bus-off \n " ) ;
return ret ;
}
/* close the CAN layer */
close_candev ( ndev ) ;
return 0 ;
}
static int ican3_xmit ( struct sk_buff * skb , struct net_device * ndev )
{
struct ican3_dev * mod = netdev_priv ( ndev ) ;
struct net_device_stats * stats = & ndev - > stats ;
struct can_frame * cf = ( struct can_frame * ) skb - > data ;
struct ican3_fast_desc desc ;
void __iomem * desc_addr ;
unsigned long flags ;
spin_lock_irqsave ( & mod - > lock , flags ) ;
/* check that we can actually transmit */
if ( ! ican3_txok ( mod ) ) {
dev_err ( mod - > dev , " no free descriptors, stopping queue \n " ) ;
netif_stop_queue ( ndev ) ;
spin_unlock_irqrestore ( & mod - > lock , flags ) ;
return NETDEV_TX_BUSY ;
}
/* copy the control bits of the descriptor */
ican3_set_page ( mod , mod - > fasttx_start + ( mod - > fasttx_num / 16 ) ) ;
desc_addr = mod - > dpm + ( ( mod - > fasttx_num % 16 ) * sizeof ( desc ) ) ;
memset ( & desc , 0 , sizeof ( desc ) ) ;
memcpy_fromio ( & desc , desc_addr , 1 ) ;
/* convert the Linux CAN frame into ICAN3 format */
can_frame_to_ican3 ( mod , cf , & desc ) ;
/*
* the programming manual says that you must set the IVALID bit , then
* interrupt , then set the valid bit . Quite weird , but it seems to be
* required for this to work
*/
desc . control | = DESC_IVALID ;
memcpy_toio ( desc_addr , & desc , sizeof ( desc ) ) ;
/* generate a MODULbus interrupt to the microcontroller */
iowrite8 ( 0x01 , & mod - > dpmctrl - > interrupt ) ;
desc . control ^ = DESC_VALID ;
memcpy_toio ( desc_addr , & desc , sizeof ( desc ) ) ;
/* update the next buffer pointer */
mod - > fasttx_num = ( desc . control & DESC_WRAP ) ? 0
: ( mod - > fasttx_num + 1 ) ;
/* update statistics */
stats - > tx_packets + + ;
stats - > tx_bytes + = cf - > can_dlc ;
kfree_skb ( skb ) ;
/*
* This hardware doesn ' t have TX - done notifications , so we ' ll try and
* emulate it the best we can using ECHO skbs . Get the next TX
* descriptor , and see if we have room to send . If not , stop the queue .
* It will be woken when the ECHO skb for the current packet is recv ' d .
*/
/* copy the control bits of the descriptor */
if ( ! ican3_txok ( mod ) )
netif_stop_queue ( ndev ) ;
spin_unlock_irqrestore ( & mod - > lock , flags ) ;
return NETDEV_TX_OK ;
}
static const struct net_device_ops ican3_netdev_ops = {
. ndo_open = ican3_open ,
. ndo_stop = ican3_stop ,
. ndo_start_xmit = ican3_xmit ,
} ;
/*
* Low - level CAN Device
*/
/* This structure was stolen from drivers/net/can/sja1000/sja1000.c */
static struct can_bittiming_const ican3_bittiming_const = {
. name = DRV_NAME ,
. tseg1_min = 1 ,
. tseg1_max = 16 ,
. tseg2_min = 1 ,
. tseg2_max = 8 ,
. sjw_max = 4 ,
. brp_min = 1 ,
. brp_max = 64 ,
. brp_inc = 1 ,
} ;
/*
* This routine was stolen from drivers / net / can / sja1000 / sja1000 . c
*
* The bittiming register command for the ICAN3 just sets the bit timing
* registers on the SJA1000 chip directly
*/
static int ican3_set_bittiming ( struct net_device * ndev )
{
struct ican3_dev * mod = netdev_priv ( ndev ) ;
struct can_bittiming * bt = & mod - > can . bittiming ;
struct ican3_msg msg ;
u8 btr0 , btr1 ;
btr0 = ( ( bt - > brp - 1 ) & 0x3f ) | ( ( ( bt - > sjw - 1 ) & 0x3 ) < < 6 ) ;
btr1 = ( ( bt - > prop_seg + bt - > phase_seg1 - 1 ) & 0xf ) |
( ( ( bt - > phase_seg2 - 1 ) & 0x7 ) < < 4 ) ;
if ( mod - > can . ctrlmode & CAN_CTRLMODE_3_SAMPLES )
btr1 | = 0x80 ;
memset ( & msg , 0 , sizeof ( msg ) ) ;
msg . spec = MSG_CBTRREQ ;
msg . len = cpu_to_le16 ( 4 ) ;
msg . data [ 0 ] = 0x00 ;
msg . data [ 1 ] = 0x00 ;
msg . data [ 2 ] = btr0 ;
msg . data [ 3 ] = btr1 ;
return ican3_send_msg ( mod , & msg ) ;
}
static int ican3_set_mode ( struct net_device * ndev , enum can_mode mode )
{
struct ican3_dev * mod = netdev_priv ( ndev ) ;
int ret ;
if ( mode ! = CAN_MODE_START )
return - ENOTSUPP ;
/* bring the bus online */
ret = ican3_set_bus_state ( mod , true ) ;
if ( ret ) {
dev_err ( mod - > dev , " unable to set bus-on \n " ) ;
return ret ;
}
/* start up the network device */
mod - > can . state = CAN_STATE_ERROR_ACTIVE ;
if ( netif_queue_stopped ( ndev ) )
netif_wake_queue ( ndev ) ;
return 0 ;
}
static int ican3_get_berr_counter ( const struct net_device * ndev ,
struct can_berr_counter * bec )
{
struct ican3_dev * mod = netdev_priv ( ndev ) ;
int ret ;
ret = ican3_send_inquiry ( mod , INQUIRY_STATUS ) ;
if ( ret )
return ret ;
ret = wait_for_completion_timeout ( & mod - > buserror_comp , HZ ) ;
if ( ret < = 0 ) {
dev_info ( mod - > dev , " %s timed out \n " , __func__ ) ;
return - ETIMEDOUT ;
}
bec - > rxerr = mod - > bec . rxerr ;
bec - > txerr = mod - > bec . txerr ;
return 0 ;
}
/*
* Sysfs Attributes
*/
static ssize_t ican3_sysfs_show_term ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct ican3_dev * mod = netdev_priv ( to_net_dev ( dev ) ) ;
int ret ;
ret = ican3_send_inquiry ( mod , INQUIRY_TERMINATION ) ;
if ( ret )
return ret ;
ret = wait_for_completion_timeout ( & mod - > termination_comp , HZ ) ;
if ( ret < = 0 ) {
dev_info ( mod - > dev , " %s timed out \n " , __func__ ) ;
return - ETIMEDOUT ;
}
return snprintf ( buf , PAGE_SIZE , " %u \n " , mod - > termination_enabled ) ;
}
static ssize_t ican3_sysfs_set_term ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct ican3_dev * mod = netdev_priv ( to_net_dev ( dev ) ) ;
unsigned long enable ;
int ret ;
if ( strict_strtoul ( buf , 0 , & enable ) )
return - EINVAL ;
ret = ican3_set_termination ( mod , enable ) ;
if ( ret )
return ret ;
return count ;
}
2011-02-04 05:23:53 +03:00
static DEVICE_ATTR ( termination , S_IWUSR | S_IRUGO , ican3_sysfs_show_term ,
2010-03-29 20:58:51 +04:00
ican3_sysfs_set_term ) ;
static struct attribute * ican3_sysfs_attrs [ ] = {
& dev_attr_termination . attr ,
NULL ,
} ;
static struct attribute_group ican3_sysfs_attr_group = {
. attrs = ican3_sysfs_attrs ,
} ;
/*
* PCI Subsystem
*/
static int __devinit ican3_probe ( struct platform_device * pdev )
{
struct janz_platform_data * pdata ;
struct net_device * ndev ;
struct ican3_dev * mod ;
struct resource * res ;
struct device * dev ;
int ret ;
2011-04-06 18:02:25 +04:00
pdata = pdev - > dev . platform_data ;
2010-03-29 20:58:51 +04:00
if ( ! pdata )
return - ENXIO ;
dev_dbg ( & pdev - > dev , " probe: module number %d \n " , pdata - > modno ) ;
/* save the struct device for printing */
dev = & pdev - > dev ;
/* allocate the CAN device and private data */
ndev = alloc_candev ( sizeof ( * mod ) , 0 ) ;
if ( ! ndev ) {
dev_err ( dev , " unable to allocate CANdev \n " ) ;
ret = - ENOMEM ;
goto out_return ;
}
platform_set_drvdata ( pdev , ndev ) ;
mod = netdev_priv ( ndev ) ;
mod - > ndev = ndev ;
mod - > dev = & pdev - > dev ;
mod - > num = pdata - > modno ;
netif_napi_add ( ndev , & mod - > napi , ican3_napi , ICAN3_RX_BUFFERS ) ;
spin_lock_init ( & mod - > lock ) ;
init_completion ( & mod - > termination_comp ) ;
init_completion ( & mod - > buserror_comp ) ;
/* setup device-specific sysfs attributes */
ndev - > sysfs_groups [ 0 ] = & ican3_sysfs_attr_group ;
/* the first unallocated page in the DPM is 9 */
mod - > free_page = DPM_FREE_START ;
ndev - > netdev_ops = & ican3_netdev_ops ;
ndev - > flags | = IFF_ECHO ;
SET_NETDEV_DEV ( ndev , & pdev - > dev ) ;
mod - > can . clock . freq = ICAN3_CAN_CLOCK ;
mod - > can . bittiming_const = & ican3_bittiming_const ;
mod - > can . do_set_bittiming = ican3_set_bittiming ;
mod - > can . do_set_mode = ican3_set_mode ;
mod - > can . do_get_berr_counter = ican3_get_berr_counter ;
mod - > can . ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES
| CAN_CTRLMODE_BERR_REPORTING ;
/* find our IRQ number */
mod - > irq = platform_get_irq ( pdev , 0 ) ;
if ( mod - > irq < 0 ) {
dev_err ( dev , " IRQ line not found \n " ) ;
ret = - ENODEV ;
goto out_free_ndev ;
}
ndev - > irq = mod - > irq ;
/* get access to the MODULbus registers for this module */
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
dev_err ( dev , " MODULbus registers not found \n " ) ;
ret = - ENODEV ;
goto out_free_ndev ;
}
mod - > dpm = ioremap ( res - > start , resource_size ( res ) ) ;
if ( ! mod - > dpm ) {
dev_err ( dev , " MODULbus registers not ioremap \n " ) ;
ret = - ENOMEM ;
goto out_free_ndev ;
}
mod - > dpmctrl = mod - > dpm + DPM_PAGE_SIZE ;
/* get access to the control registers for this module */
res = platform_get_resource ( pdev , IORESOURCE_MEM , 1 ) ;
if ( ! res ) {
dev_err ( dev , " CONTROL registers not found \n " ) ;
ret = - ENODEV ;
goto out_iounmap_dpm ;
}
mod - > ctrl = ioremap ( res - > start , resource_size ( res ) ) ;
if ( ! mod - > ctrl ) {
dev_err ( dev , " CONTROL registers not ioremap \n " ) ;
ret = - ENOMEM ;
goto out_iounmap_dpm ;
}
/* disable our IRQ, then hookup the IRQ handler */
iowrite8 ( 1 < < mod - > num , & mod - > ctrl - > int_disable ) ;
ret = request_irq ( mod - > irq , ican3_irq , IRQF_SHARED , DRV_NAME , mod ) ;
if ( ret ) {
dev_err ( dev , " unable to request IRQ \n " ) ;
goto out_iounmap_ctrl ;
}
/* reset and initialize the CAN controller into fast mode */
napi_enable ( & mod - > napi ) ;
ret = ican3_startup_module ( mod ) ;
if ( ret ) {
dev_err ( dev , " %s: unable to start CANdev \n " , __func__ ) ;
goto out_free_irq ;
}
/* register with the Linux CAN layer */
ret = register_candev ( ndev ) ;
if ( ret ) {
dev_err ( dev , " %s: unable to register CANdev \n " , __func__ ) ;
goto out_free_irq ;
}
dev_info ( dev , " module %d: registered CAN device \n " , pdata - > modno ) ;
return 0 ;
out_free_irq :
napi_disable ( & mod - > napi ) ;
iowrite8 ( 1 < < mod - > num , & mod - > ctrl - > int_disable ) ;
free_irq ( mod - > irq , mod ) ;
out_iounmap_ctrl :
iounmap ( mod - > ctrl ) ;
out_iounmap_dpm :
iounmap ( mod - > dpm ) ;
out_free_ndev :
free_candev ( ndev ) ;
out_return :
return ret ;
}
static int __devexit ican3_remove ( struct platform_device * pdev )
{
struct net_device * ndev = platform_get_drvdata ( pdev ) ;
struct ican3_dev * mod = netdev_priv ( ndev ) ;
/* unregister the netdevice, stop interrupts */
unregister_netdev ( ndev ) ;
napi_disable ( & mod - > napi ) ;
iowrite8 ( 1 < < mod - > num , & mod - > ctrl - > int_disable ) ;
free_irq ( mod - > irq , mod ) ;
/* put the module into reset */
ican3_shutdown_module ( mod ) ;
/* unmap all registers */
iounmap ( mod - > ctrl ) ;
iounmap ( mod - > dpm ) ;
free_candev ( ndev ) ;
return 0 ;
}
static struct platform_driver ican3_driver = {
. driver = {
. name = DRV_NAME ,
. owner = THIS_MODULE ,
} ,
. probe = ican3_probe ,
. remove = __devexit_p ( ican3_remove ) ,
} ;
static int __init ican3_init ( void )
{
return platform_driver_register ( & ican3_driver ) ;
}
static void __exit ican3_exit ( void )
{
platform_driver_unregister ( & ican3_driver ) ;
}
MODULE_AUTHOR ( " Ira W. Snyder <iws@ovro.caltech.edu> " ) ;
MODULE_DESCRIPTION ( " Janz MODULbus VMOD-ICAN3 Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:janz-ican3 " ) ;
module_init ( ican3_init ) ;
module_exit ( ican3_exit ) ;