2009-05-16 03:39:32 +04:00
/*
* Copyright ( C ) 2007 Wolfgang Grandegger < wg @ grandegger . com >
* Copyright ( C ) 2008 Markus Plessing < plessing @ ems - wuensche . com >
* Copyright ( C ) 2008 Sebastian Haas < haas @ ems - wuensche . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the version 2 of the GNU General Public License
* as published by the Free Software Foundation
*
* 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 .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/netdevice.h>
# include <linux/delay.h>
# include <linux/pci.h>
# include <linux/can.h>
# include <linux/can/dev.h>
# include <linux/io.h>
# include "sja1000.h"
# define DRV_NAME "ems_pci"
MODULE_AUTHOR ( " Sebastian Haas <haas@ems-wuenche.com> " ) ;
2009-07-21 23:38:13 +04:00
MODULE_DESCRIPTION ( " Socket-CAN driver for EMS CPC-PCI/PCIe/104P CAN cards " ) ;
MODULE_SUPPORTED_DEVICE ( " EMS CPC-PCI/PCIe/104P CAN card " ) ;
2009-05-16 03:39:32 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;
2009-07-21 23:38:13 +04:00
# define EMS_PCI_V1_MAX_CHAN 2
# define EMS_PCI_V2_MAX_CHAN 4
# define EMS_PCI_MAX_CHAN EMS_PCI_V2_MAX_CHAN
2009-05-16 03:39:32 +04:00
struct ems_pci_card {
2009-07-21 23:38:13 +04:00
int version ;
2009-05-16 03:39:32 +04:00
int channels ;
struct pci_dev * pci_dev ;
struct net_device * net_dev [ EMS_PCI_MAX_CHAN ] ;
void __iomem * conf_addr ;
void __iomem * base_addr ;
} ;
# define EMS_PCI_CAN_CLOCK (16000000 / 2)
/*
* Register definitions and descriptions are from LinCAN 0.3 .3 .
*
* PSB4610 PITA - 2 bridge control registers
*/
# define PITA2_ICR 0x00 /* Interrupt Control Register */
# define PITA2_ICR_INT0 0x00000002 /* [RC] INT0 Active/Clear */
# define PITA2_ICR_INT0_EN 0x00020000 /* [RW] Enable INT0 */
# define PITA2_MISC 0x1c /* Miscellaneous Register */
# define PITA2_MISC_CONFIG 0x04000000 /* Multiplexed parallel interface */
2009-07-21 23:38:13 +04:00
/*
* Register definitions for the PLX 9030
*/
# define PLX_ICSR 0x4c /* Interrupt Control/Status register */
# define PLX_ICSR_LINTI1_ENA 0x0001 /* LINTi1 Enable */
# define PLX_ICSR_PCIINT_ENA 0x0040 /* PCI Interrupt Enable */
# define PLX_ICSR_LINTI1_CLR 0x0400 /* Local Edge Triggerable Interrupt Clear */
# define PLX_ICSR_ENA_CLR (PLX_ICSR_LINTI1_ENA | PLX_ICSR_PCIINT_ENA | \
PLX_ICSR_LINTI1_CLR )
2009-05-16 03:39:32 +04:00
/*
* The board configuration is probably following :
* RX1 is connected to ground .
* TX1 is not connected .
* CLKO is not connected .
* Setting the OCR register to 0xDA is a good idea .
2009-07-21 23:38:13 +04:00
* This means normal output mode , push - pull and the correct polarity .
2009-05-16 03:39:32 +04:00
*/
# define EMS_PCI_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL)
/*
* In the CDR register , you should set CBP to 1.
* You will probably also want to set the clock divider value to 7
* ( meaning direct oscillator output ) because the second SJA1000 chip
* is driven by the first one CLKOUT output .
*/
# define EMS_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK)
2009-07-21 23:38:13 +04:00
# define EMS_PCI_V1_BASE_BAR 1
2009-09-24 07:55:05 +04:00
# define EMS_PCI_V1_CONF_SIZE 4096 /* size of PITA control area */
2009-07-21 23:38:13 +04:00
# define EMS_PCI_V2_BASE_BAR 2
2009-09-24 07:55:05 +04:00
# define EMS_PCI_V2_CONF_SIZE 128 /* size of PLX control area */
2009-05-16 03:39:32 +04:00
# define EMS_PCI_CAN_BASE_OFFSET 0x400 /* offset where the controllers starts */
# define EMS_PCI_CAN_CTRL_SIZE 0x200 /* memory size for each controller */
2009-09-24 07:55:05 +04:00
# define EMS_PCI_BASE_SIZE 4096 /* size of controller area */
2010-01-07 14:58:11 +03:00
static DEFINE_PCI_DEVICE_TABLE ( ems_pci_tbl ) = {
2009-07-21 23:38:13 +04:00
/* CPC-PCI v1 */
{ PCI_VENDOR_ID_SIEMENS , 0x2104 , PCI_ANY_ID , PCI_ANY_ID , } ,
/* CPC-PCI v2 */
{ PCI_VENDOR_ID_PLX , PCI_DEVICE_ID_PLX_9030 , PCI_VENDOR_ID_PLX , 0x4000 } ,
/* CPC-104P v2 */
{ PCI_VENDOR_ID_PLX , PCI_DEVICE_ID_PLX_9030 , PCI_VENDOR_ID_PLX , 0x4002 } ,
2009-05-16 03:39:32 +04:00
{ 0 , }
} ;
MODULE_DEVICE_TABLE ( pci , ems_pci_tbl ) ;
/*
* Helper to read internal registers from card logic ( not CAN )
*/
2009-07-21 23:38:13 +04:00
static u8 ems_pci_v1_readb ( struct ems_pci_card * card , unsigned int port )
2009-05-16 03:39:32 +04:00
{
2009-07-21 23:38:13 +04:00
return readb ( card - > base_addr + ( port * 4 ) ) ;
2009-05-16 03:39:32 +04:00
}
2009-07-21 23:38:13 +04:00
static u8 ems_pci_v1_read_reg ( const struct sja1000_priv * priv , int port )
2009-05-16 03:39:32 +04:00
{
2009-07-21 23:38:13 +04:00
return readb ( priv - > reg_base + ( port * 4 ) ) ;
2009-05-16 03:39:32 +04:00
}
2009-07-21 23:38:13 +04:00
static void ems_pci_v1_write_reg ( const struct sja1000_priv * priv ,
int port , u8 val )
2009-05-16 03:39:32 +04:00
{
2009-07-21 23:38:13 +04:00
writeb ( val , priv - > reg_base + ( port * 4 ) ) ;
2009-05-16 03:39:32 +04:00
}
2009-07-21 23:38:13 +04:00
static void ems_pci_v1_post_irq ( const struct sja1000_priv * priv )
2009-05-16 03:39:32 +04:00
{
struct ems_pci_card * card = ( struct ems_pci_card * ) priv - > priv ;
/* reset int flag of pita */
2009-07-21 23:38:13 +04:00
writel ( PITA2_ICR_INT0_EN | PITA2_ICR_INT0 ,
card - > conf_addr + PITA2_ICR ) ;
}
static u8 ems_pci_v2_read_reg ( const struct sja1000_priv * priv , int port )
{
return readb ( priv - > reg_base + port ) ;
}
static void ems_pci_v2_write_reg ( const struct sja1000_priv * priv ,
int port , u8 val )
{
writeb ( val , priv - > reg_base + port ) ;
}
static void ems_pci_v2_post_irq ( const struct sja1000_priv * priv )
{
struct ems_pci_card * card = ( struct ems_pci_card * ) priv - > priv ;
writel ( PLX_ICSR_ENA_CLR , card - > conf_addr + PLX_ICSR ) ;
2009-05-16 03:39:32 +04:00
}
/*
* Check if a CAN controller is present at the specified location
* by trying to set ' em into the PeliCAN mode
*/
2009-05-30 11:55:49 +04:00
static inline int ems_pci_check_chan ( const struct sja1000_priv * priv )
2009-05-16 03:39:32 +04:00
{
unsigned char res ;
/* Make sure SJA1000 is in reset mode */
2009-07-21 23:38:13 +04:00
priv - > write_reg ( priv , REG_MOD , 1 ) ;
2009-05-16 03:39:32 +04:00
2009-07-21 23:38:13 +04:00
priv - > write_reg ( priv , REG_CDR , CDR_PELICAN ) ;
2009-05-16 03:39:32 +04:00
/* read reset-values */
2009-07-21 23:38:13 +04:00
res = priv - > read_reg ( priv , REG_CDR ) ;
2009-05-16 03:39:32 +04:00
if ( res = = CDR_PELICAN )
return 1 ;
return 0 ;
}
static void ems_pci_del_card ( struct pci_dev * pdev )
{
struct ems_pci_card * card = pci_get_drvdata ( pdev ) ;
struct net_device * dev ;
int i = 0 ;
for ( i = 0 ; i < card - > channels ; i + + ) {
dev = card - > net_dev [ i ] ;
if ( ! dev )
continue ;
dev_info ( & pdev - > dev , " Removing %s. \n " , dev - > name ) ;
unregister_sja1000dev ( dev ) ;
free_sja1000dev ( dev ) ;
}
if ( card - > base_addr ! = NULL )
pci_iounmap ( card - > pci_dev , card - > base_addr ) ;
if ( card - > conf_addr ! = NULL )
pci_iounmap ( card - > pci_dev , card - > conf_addr ) ;
kfree ( card ) ;
pci_disable_device ( pdev ) ;
pci_set_drvdata ( pdev , NULL ) ;
}
static void ems_pci_card_reset ( struct ems_pci_card * card )
{
/* Request board reset */
writeb ( 0 , card - > base_addr ) ;
}
/*
* Probe PCI device for EMS CAN signature and register each available
* CAN channel to SJA1000 Socket - CAN subsystem .
*/
static int __devinit ems_pci_add_card ( struct pci_dev * pdev ,
const struct pci_device_id * ent )
{
struct sja1000_priv * priv ;
struct net_device * dev ;
struct ems_pci_card * card ;
2009-09-24 07:55:05 +04:00
int max_chan , conf_size , base_bar ;
2009-05-16 03:39:32 +04:00
int err , i ;
/* Enabling PCI device */
if ( pci_enable_device ( pdev ) < 0 ) {
dev_err ( & pdev - > dev , " Enabling PCI device failed \n " ) ;
return - ENODEV ;
}
/* Allocating card structures to hold addresses, ... */
card = kzalloc ( sizeof ( struct ems_pci_card ) , GFP_KERNEL ) ;
if ( card = = NULL ) {
dev_err ( & pdev - > dev , " Unable to allocate memory \n " ) ;
pci_disable_device ( pdev ) ;
return - ENOMEM ;
}
pci_set_drvdata ( pdev , card ) ;
card - > pci_dev = pdev ;
card - > channels = 0 ;
2009-07-21 23:38:13 +04:00
if ( pdev - > vendor = = PCI_VENDOR_ID_PLX ) {
card - > version = 2 ; /* CPC-PCI v2 */
max_chan = EMS_PCI_V2_MAX_CHAN ;
base_bar = EMS_PCI_V2_BASE_BAR ;
2009-09-24 07:55:05 +04:00
conf_size = EMS_PCI_V2_CONF_SIZE ;
2009-07-21 23:38:13 +04:00
} else {
card - > version = 1 ; /* CPC-PCI v1 */
max_chan = EMS_PCI_V1_MAX_CHAN ;
base_bar = EMS_PCI_V1_BASE_BAR ;
2009-09-24 07:55:05 +04:00
conf_size = EMS_PCI_V1_CONF_SIZE ;
2009-07-21 23:38:13 +04:00
}
/* Remap configuration space and controller memory area */
2009-09-24 07:55:05 +04:00
card - > conf_addr = pci_iomap ( pdev , 0 , conf_size ) ;
2009-05-16 03:39:32 +04:00
if ( card - > conf_addr = = NULL ) {
err = - ENOMEM ;
goto failure_cleanup ;
}
2009-09-24 07:55:05 +04:00
card - > base_addr = pci_iomap ( pdev , base_bar , EMS_PCI_BASE_SIZE ) ;
2009-05-16 03:39:32 +04:00
if ( card - > base_addr = = NULL ) {
err = - ENOMEM ;
goto failure_cleanup ;
}
2009-07-21 23:38:13 +04:00
if ( card - > version = = 1 ) {
/* Configure PITA-2 parallel interface (enable MUX) */
writel ( PITA2_MISC_CONFIG , card - > conf_addr + PITA2_MISC ) ;
/* Check for unique EMS CAN signature */
if ( ems_pci_v1_readb ( card , 0 ) ! = 0x55 | |
ems_pci_v1_readb ( card , 1 ) ! = 0xAA | |
ems_pci_v1_readb ( card , 2 ) ! = 0x01 | |
ems_pci_v1_readb ( card , 3 ) ! = 0xCB | |
ems_pci_v1_readb ( card , 4 ) ! = 0x11 ) {
dev_err ( & pdev - > dev ,
" Not EMS Dr. Thomas Wuensche interface \n " ) ;
err = - ENODEV ;
goto failure_cleanup ;
}
2009-05-16 03:39:32 +04:00
}
ems_pci_card_reset ( card ) ;
/* Detect available channels */
2009-07-21 23:38:13 +04:00
for ( i = 0 ; i < max_chan ; i + + ) {
2009-05-16 03:39:32 +04:00
dev = alloc_sja1000dev ( 0 ) ;
if ( dev = = NULL ) {
err = - ENOMEM ;
goto failure_cleanup ;
}
card - > net_dev [ i ] = dev ;
priv = netdev_priv ( dev ) ;
priv - > priv = card ;
priv - > irq_flags = IRQF_SHARED ;
dev - > irq = pdev - > irq ;
2009-05-30 11:55:49 +04:00
priv - > reg_base = card - > base_addr + EMS_PCI_CAN_BASE_OFFSET
+ ( i * EMS_PCI_CAN_CTRL_SIZE ) ;
2009-07-21 23:38:13 +04:00
if ( card - > version = = 1 ) {
priv - > read_reg = ems_pci_v1_read_reg ;
priv - > write_reg = ems_pci_v1_write_reg ;
priv - > post_irq = ems_pci_v1_post_irq ;
} else {
priv - > read_reg = ems_pci_v2_read_reg ;
priv - > write_reg = ems_pci_v2_write_reg ;
priv - > post_irq = ems_pci_v2_post_irq ;
}
2009-05-16 03:39:32 +04:00
/* Check if channel is present */
2009-05-30 11:55:49 +04:00
if ( ems_pci_check_chan ( priv ) ) {
2009-05-16 03:39:32 +04:00
priv - > can . clock . freq = EMS_PCI_CAN_CLOCK ;
priv - > ocr = EMS_PCI_OCR ;
priv - > cdr = EMS_PCI_CDR ;
SET_NETDEV_DEV ( dev , & pdev - > dev ) ;
2009-07-21 23:38:13 +04:00
if ( card - > version = = 1 )
/* reset int flag of pita */
writel ( PITA2_ICR_INT0_EN | PITA2_ICR_INT0 ,
card - > conf_addr + PITA2_ICR ) ;
else
/* enable IRQ in PLX 9030 */
writel ( PLX_ICSR_ENA_CLR ,
card - > conf_addr + PLX_ICSR ) ;
2009-05-16 03:39:32 +04:00
/* Register SJA1000 device */
err = register_sja1000dev ( dev ) ;
if ( err ) {
dev_err ( & pdev - > dev , " Registering device failed "
" (err=%d) \n " , err ) ;
free_sja1000dev ( dev ) ;
goto failure_cleanup ;
}
card - > channels + + ;
2009-05-30 11:55:49 +04:00
dev_info ( & pdev - > dev , " Channel #%d at 0x%p, irq %d \n " ,
i + 1 , priv - > reg_base , dev - > irq ) ;
2009-05-16 03:39:32 +04:00
} else {
free_sja1000dev ( dev ) ;
}
}
return 0 ;
failure_cleanup :
dev_err ( & pdev - > dev , " Error: %d. Cleaning Up. \n " , err ) ;
ems_pci_del_card ( pdev ) ;
return err ;
}
static struct pci_driver ems_pci_driver = {
. name = DRV_NAME ,
. id_table = ems_pci_tbl ,
. probe = ems_pci_add_card ,
. remove = ems_pci_del_card ,
} ;
static int __init ems_pci_init ( void )
{
return pci_register_driver ( & ems_pci_driver ) ;
}
static void __exit ems_pci_exit ( void )
{
pci_unregister_driver ( & ems_pci_driver ) ;
}
module_init ( ems_pci_init ) ;
module_exit ( ems_pci_exit ) ;