2010-10-18 21:47:27 +00:00
/*
* tscan1 . c : driver for Technologic Systems TS - CAN1 PC104 boards
*
* Copyright 2010 Andre B . Oliveira
*
* 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 , see < http : //www.gnu.org/licenses/>.
*/
/*
* References :
* - Getting started with TS - CAN1 , Technologic Systems , Jun 2009
* http : //www.embeddedarm.com/documentation/ts-can1-manual.pdf
*/
# include <linux/init.h>
# include <linux/io.h>
# include <linux/ioport.h>
# include <linux/isa.h>
# include <linux/module.h>
# include <linux/netdevice.h>
# include "sja1000.h"
MODULE_DESCRIPTION ( " Driver for Technologic Systems TS-CAN1 PC104 boards " ) ;
MODULE_AUTHOR ( " Andre B. Oliveira <anbadeol@gmail.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;
/* Maximum number of boards (one in each JP1:JP2 setting of IO address) */
# define TSCAN1_MAXDEV 4
/* PLD registers address offsets */
# define TSCAN1_ID1 0
# define TSCAN1_ID2 1
# define TSCAN1_VERSION 2
# define TSCAN1_LED 3
# define TSCAN1_PAGE 4
# define TSCAN1_MODE 5
# define TSCAN1_JUMPERS 6
/* PLD board identifier registers magic values */
# define TSCAN1_ID1_VALUE 0xf6
# define TSCAN1_ID2_VALUE 0xb9
/* PLD mode register SJA1000 IO enable bit */
# define TSCAN1_MODE_ENABLE 0x40
/* PLD jumpers register bits */
# define TSCAN1_JP4 0x10
# define TSCAN1_JP5 0x20
/* PLD IO base addresses start */
# define TSCAN1_PLD_ADDRESS 0x150
/* PLD register space size */
# define TSCAN1_PLD_SIZE 8
/* SJA1000 register space size */
# define TSCAN1_SJA1000_SIZE 32
/* SJA1000 crystal frequency (16MHz) */
# define TSCAN1_SJA1000_XTAL 16000000
/* SJA1000 IO base addresses */
2012-12-03 09:22:44 -05:00
static const unsigned short tscan1_sja1000_addresses [ ] = {
2010-10-18 21:47:27 +00:00
0x100 , 0x120 , 0x180 , 0x1a0 , 0x200 , 0x240 , 0x280 , 0x320
} ;
/* Read SJA1000 register */
static u8 tscan1_read ( const struct sja1000_priv * priv , int reg )
{
return inb ( ( unsigned long ) priv - > reg_base + reg ) ;
}
/* Write SJA1000 register */
static void tscan1_write ( const struct sja1000_priv * priv , int reg , u8 val )
{
outb ( val , ( unsigned long ) priv - > reg_base + reg ) ;
}
/* Probe for a TS-CAN1 board with JP2:JP1 jumper setting ID */
2012-12-03 09:22:44 -05:00
static int tscan1_probe ( struct device * dev , unsigned id )
2010-10-18 21:47:27 +00:00
{
struct net_device * netdev ;
struct sja1000_priv * priv ;
unsigned long pld_base , sja1000_base ;
int irq , i ;
pld_base = TSCAN1_PLD_ADDRESS + id * TSCAN1_PLD_SIZE ;
if ( ! request_region ( pld_base , TSCAN1_PLD_SIZE , dev_name ( dev ) ) )
return - EBUSY ;
if ( inb ( pld_base + TSCAN1_ID1 ) ! = TSCAN1_ID1_VALUE | |
inb ( pld_base + TSCAN1_ID2 ) ! = TSCAN1_ID2_VALUE ) {
release_region ( pld_base , TSCAN1_PLD_SIZE ) ;
return - ENODEV ;
}
switch ( inb ( pld_base + TSCAN1_JUMPERS ) & ( TSCAN1_JP4 | TSCAN1_JP5 ) ) {
case TSCAN1_JP4 :
irq = 6 ;
break ;
case TSCAN1_JP5 :
irq = 7 ;
break ;
case TSCAN1_JP4 | TSCAN1_JP5 :
irq = 5 ;
break ;
default :
dev_err ( dev , " invalid JP4:JP5 setting (no IRQ) \n " ) ;
release_region ( pld_base , TSCAN1_PLD_SIZE ) ;
return - EINVAL ;
}
netdev = alloc_sja1000dev ( 0 ) ;
if ( ! netdev ) {
release_region ( pld_base , TSCAN1_PLD_SIZE ) ;
return - ENOMEM ;
}
dev_set_drvdata ( dev , netdev ) ;
SET_NETDEV_DEV ( netdev , dev ) ;
netdev - > base_addr = pld_base ;
netdev - > irq = irq ;
priv = netdev_priv ( netdev ) ;
priv - > read_reg = tscan1_read ;
priv - > write_reg = tscan1_write ;
priv - > can . clock . freq = TSCAN1_SJA1000_XTAL / 2 ;
priv - > cdr = CDR_CBP | CDR_CLK_OFF ;
priv - > ocr = OCR_TX0_PUSHPULL ;
/* Select the first SJA1000 IO address that is free and that works */
for ( i = 0 ; i < ARRAY_SIZE ( tscan1_sja1000_addresses ) ; i + + ) {
sja1000_base = tscan1_sja1000_addresses [ i ] ;
if ( ! request_region ( sja1000_base , TSCAN1_SJA1000_SIZE ,
dev_name ( dev ) ) )
continue ;
/* Set SJA1000 IO base address and enable it */
outb ( TSCAN1_MODE_ENABLE | i , pld_base + TSCAN1_MODE ) ;
priv - > reg_base = ( void __iomem * ) sja1000_base ;
if ( ! register_sja1000dev ( netdev ) ) {
/* SJA1000 probe succeeded; turn LED off and return */
outb ( 0 , pld_base + TSCAN1_LED ) ;
netdev_info ( netdev , " TS-CAN1 at 0x%lx 0x%lx irq %d \n " ,
pld_base , sja1000_base , irq ) ;
return 0 ;
}
/* SJA1000 probe failed; release and try next address */
outb ( 0 , pld_base + TSCAN1_MODE ) ;
release_region ( sja1000_base , TSCAN1_SJA1000_SIZE ) ;
}
dev_err ( dev , " failed to assign SJA1000 IO address \n " ) ;
dev_set_drvdata ( dev , NULL ) ;
free_sja1000dev ( netdev ) ;
release_region ( pld_base , TSCAN1_PLD_SIZE ) ;
return - ENXIO ;
}
2012-12-03 09:22:44 -05:00
static int tscan1_remove ( struct device * dev , unsigned id /*unused*/ )
2010-10-18 21:47:27 +00:00
{
struct net_device * netdev ;
struct sja1000_priv * priv ;
unsigned long pld_base , sja1000_base ;
netdev = dev_get_drvdata ( dev ) ;
unregister_sja1000dev ( netdev ) ;
dev_set_drvdata ( dev , NULL ) ;
priv = netdev_priv ( netdev ) ;
pld_base = netdev - > base_addr ;
sja1000_base = ( unsigned long ) priv - > reg_base ;
outb ( 0 , pld_base + TSCAN1_MODE ) ; /* disable SJA1000 IO space */
release_region ( sja1000_base , TSCAN1_SJA1000_SIZE ) ;
release_region ( pld_base , TSCAN1_PLD_SIZE ) ;
free_sja1000dev ( netdev ) ;
return 0 ;
}
static struct isa_driver tscan1_isa_driver = {
. probe = tscan1_probe ,
2012-12-03 09:22:44 -05:00
. remove = tscan1_remove ,
2010-10-18 21:47:27 +00:00
. driver = {
. name = " tscan1 " ,
} ,
} ;
static int __init tscan1_init ( void )
{
return isa_register_driver ( & tscan1_isa_driver , TSCAN1_MAXDEV ) ;
}
module_init ( tscan1_init ) ;
static void __exit tscan1_exit ( void )
{
isa_unregister_driver ( & tscan1_isa_driver ) ;
}
module_exit ( tscan1_exit ) ;