#include "../include/can_sysdep.h"
#include "../include/main.h"
#include "../include/unican_cl2.h"
+#include "../include/setup.h"
+
+#define UNICAN_PCI_VENDOR 0xFA3C
+#define UNICAN_PCI_ID 0x0101
+
+static void unican_delay(long msdelay)
+{
+ #ifdef CAN_WITH_RTL
+ if(!rtl_rt_system_is_idle()) {
+ rtl_delay(1000000l*msdelay);
+ }else
+ #endif /*CAN_WITH_RTL*/
+ {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((msdelay*HZ)/1000+1);
+ }
+
+}
long unican_bus_latency(struct msgobj_t *obj)
*/
int unican_chip_config(struct chip_t *chip)
{
+ int ret;
+ sCAN_CARD *chipext = (sCAN_CARD *)chip->chip_data;
+
+ unican_delay(10);
+
+ /* disable all card interrupts */
+ ret = cl2_int_mode(chipext, INT_MODE_ALL*0);
+ if(ret != CL2_OK) {
+ CANMSG("disable interrupts by cl2_iit_mode returned %d\n",ret);
+ return -ENODEV;
+ }
+ unican_delay(1);
+
+ if (chip->baudrate == 0)
+ chip->baudrate=1000000;
+
+ ret = chip->chipspecops->baud_rate(chip,chip->baudrate,chip->clock,0,75,0);
+ if(ret < 0){
+ CANMSG("can not set baudrate\n");
+ return ret;
+ }
+
+ unican_delay(2);
+ /* set interrupt inhibit time to 1 ms */
+ ret = cl2_set_iit(chipext, 10);
+ if(ret != CL2_OK) {
+ CANMSG("cl2_set_iit returned %d\n",ret);
+ return -ENODEV;
+ }
+ unican_delay(1);
+
+ /* enable start interrupt inhibit time command */
+ ret = cl2_iit_mode(chipext, 1);
+ if(ret != CL2_OK) {
+ CANMSG("cl2_iit_mode returned %d\n",ret);
+ return -ENODEV;
+ }
+ unican_delay(1);
+
+ /* enable all card interrupts */
+ ret = cl2_int_mode(chipext, INT_MODE_ALL);
+ if(ret != CL2_OK) {
+ CANMSG("cl2_iit_mode returned %d\n",ret);
+ return -ENODEV;
+ }
+ unican_delay(1);
+
+ /* generate interrupt command */
+ cl2_gen_interrupt(chipext);
+
+
return 0;
}
int unican_baud_rate(struct chip_t *chip, int rate, int clock, int sjw,
int sampl_pt, int flags)
{
+ int ret;
+ sCAN_CARD *chipext = (sCAN_CARD *)chip->chip_data;
+ int bt_val;
+
+ switch (rate) {
+ case 5000: bt_val = CL2_BITRATE_5K; break;
+ case 10000: bt_val = CL2_BITRATE_10K; break;
+ case 20000: bt_val = CL2_BITRATE_20K; break;
+ case 50000: bt_val = CL2_BITRATE_50K; break;
+ case 100000: bt_val = CL2_BITRATE_100K; break;
+ case 125000: bt_val = CL2_BITRATE_125K; break;
+ case 200000: bt_val = CL2_BITRATE_200K; break;
+ case 250000: bt_val = CL2_BITRATE_250K; break;
+ case 500000: bt_val = CL2_BITRATE_500K; break;
+ case 800000: bt_val = CL2_BITRATE_800K; break;
+ case 1000000:bt_val = CL2_BITRATE_1M; break;
+ default: return -EINVAL;
+ }
+
+ ret=cl2_set_bitrate(chipext,bt_val);
+ if(ret == CL2_COMMAND_BUSY) return -EBUSY;
+ if(ret != CL2_OK) return -EINVAL;
+ unican_delay(2);
+
return 0;
}
* @chip: pointer to chip state structure
* @obj: pinter to CAN message queue information
*
+ * This is rewritten cl2_receive_data function. The direct use of CL2
+ * function would require one more message data copy to reformat message
+ * data into different structure layout. Other way is to rewrite CL2 sources.
+ * No of these solutions is perfect.
+ *
* File: src/unican.c
*/
void unican_read(struct chip_t *chip, struct msgobj_t *obj) {
+ sCAN_CARD *chipext = (sCAN_CARD *)chip->chip_data;
+ __u16 *ptr16;
+ __u16 u;
+ unsigned long timestamp;
+ int i;
+
+ do {
+ ptr16 = (__u16*)chipext->rxBufPtr;
+ u = unican_readw(ptr16++);
+ if ( !(u & CL2_MESSAGE_VALID) ) break; /* No more messages in the queue */
+
+ obj->rx_msg.id = ((__u32)(u & 0xFF00 )) << 16;
+ u = unican_readw(ptr16++);
+ obj->rx_msg.id |= ((__u32)( u & 0x00FF )) << 16;
+ obj->rx_msg.id |= (__u32)( u & 0xFF00 );
+ u = unican_readw(ptr16++);
+ obj->rx_msg.id |= (__u32)( u & 0x00FF );
+
+
+ u >>= 8;
+
+ if ( u & CL2_EXT_FRAME ) { /* 2.0B frame */
+ obj->rx_msg.id >>= 3;
+ obj->rx_msg.flags = MSG_EXT;
+ } else { /* 2.0A frame */
+ obj->rx_msg.id >>= 21;
+ obj->rx_msg.flags = 0;
+ }
+
+ /*if ( !(u & (CL2_REMOTE_FRAME<<8)) )
+ obj->rx_msg.flags |= MSG_RTR;*/
+
+ obj->rx_msg.length = ( (u >> 4) & 0x000F );
+ if(obj->rx_msg.length > CAN_MSG_LENGTH) obj->rx_msg.length = CAN_MSG_LENGTH;
+
+ for ( i = 0; i < obj->rx_msg.length; ) {
+ u = unican_readw(ptr16++);
+ obj->rx_msg.data[i++] = (__u8)( u );
+ obj->rx_msg.data[i++] = (__u8)( u >> 8 );
+ }
+ if ( obj->rx_msg.length & 0x01 ) { /* odd */
+ timestamp = ( (unican_readw(ptr16++) & 0x00FF) | (u & 0xFF00) );
+ } else { /* even */
+ u = unican_readw(ptr16++);
+ timestamp = (u << 8) | (u >> 8);
+ }
+ unican_writew(0x000,(__u16*)chipext->rxBufPtr);
+
+ #ifdef CAN_MSG_VERSION_2
+ obj->rx_msg.timestamp.tv_sec = 0;
+ obj->rx_msg.timestamp.tv_usec = timestamp;
+ #else /* CAN_MSG_VERSION_2 */
+ obj->rx_msg.timestamp = timestamp;
+ #endif /* CAN_MSG_VERSION_2 */
+
+ /* increment rx-buffer pointer */
+ if ( (chipext->rxBufBase + chipext->rxBufSize*16 ) <= (chipext->rxBufPtr += 16) ) {
+ chipext->rxBufPtr = chipext->rxBufBase;
+ }
+ canque_filter_msg2edges(obj->qends, &obj->rx_msg);
+
+ } while (1);
}
/**
int unican_set_btregs(struct chip_t *chip, unsigned short btr0,
unsigned short btr1)
{
+ int ret;
+ sCAN_CARD *chipext = (sCAN_CARD *)chip->chip_data;
+ int bt_val;
+
+ bt_val=btr0 | (btr1<<8);
+ ret=cl2_set_bitrate(chipext,bt_val);
+ if(ret == CL2_COMMAND_BUSY) return -EBUSY;
+ if(ret != CL2_OK) return -EINVAL;
+
return 0;
}
*/
int unican_config_irqs(struct chip_t *chip, short irqs)
{
+
CANMSG("unican_config_irqs not implemented\n");
return -ENOSYS;
}
*/
void unican_irq_write_handler(struct chip_t *chip, struct msgobj_t *obj)
{
+ int cmd;
+ sCAN_CARD *chipext = (sCAN_CARD *)chip->chip_data;
+ __u16 *ptr16 = (__u16*)chipext->rxBufPtr;
+ __u16 u;
+ unsigned long timestamp=0;
+ unsigned long cobid;
+ int i;
+ int len;
+
+ #if 0
+ if(obj->tx_slot){
+ /* Do local transmitted message distribution if enabled */
+ if (processlocal){
+ obj->tx_slot->msg.flags |= MSG_LOCAL;
+ canque_filter_msg2edges(obj->qends, &obj->tx_slot->msg);
+ }
+ /* Free transmitted slot */
+ canque_free_outslot(obj->qends, obj->tx_qedge, obj->tx_slot);
+ obj->tx_slot=NULL;
+ }
+ #endif
+
+ if ( chipext->asyncTxBufSize==0 ) {
+ canque_notify_inends(obj->tx_qedge, CANQUEUE_NOTIFY_ERRTX_PREP);
+ return; /* No asynchronous queue configured */
+ }
+
+ do {
+ ptr16 = (__u16*)chipext->asyncTxBufPtr;
+ if(unican_readw(ptr16) & CL2_MESSAGE_VALID)
+ return; /* No free space in asynchronous Tx queue */
+
+ cmd=canque_test_outslot(obj->qends, &obj->tx_qedge, &obj->tx_slot);
+ if(cmd<0)
+ return; /* No more messages to send */
+
+
+ cobid = obj->tx_slot->msg.id;
+
+ if ( (obj->tx_slot->msg.flags & MSG_EXT) ) { /* 2.0B frame */
+ cobid <<= 3;
+ } else { /* 2.0A frame */
+ cobid <<= 5+16;
+ }
+ ptr16++;
+ u = ((cobid>>16) & 0x00FF ) + (cobid & 0xFF00);
+ unican_writew(u,ptr16++);
+
+ len = obj->tx_slot->msg.length;
+ if(len > CAN_MSG_LENGTH)
+ len = CAN_MSG_LENGTH;
+ u = (len << 12) | (cobid & 0x00FF);
+
+ if ( !(obj->tx_slot->msg.flags & MSG_RTR) )
+ u |= CL2_REMOTE_FRAME<<8;
+ if ( obj->tx_slot->msg.flags & MSG_EXT )
+ u |= CL2_EXT_FRAME<<8;
+
+ unican_writew(u,ptr16++);
+
+ for ( i = 0; i < len-1; ) {
+ u = obj->tx_slot->msg.data[i++];
+ u |= ((__u16)obj->tx_slot->msg.data[i]<<8); i++;
+ unican_writew(u,ptr16++);
+ }
+ if(i == len) {
+ unican_writew(timestamp,ptr16);
+ } else {
+ u = obj->tx_slot->msg.data[i++];
+ u |= ((timestamp & 0x00FF)<<8);
+ unican_writew(u,ptr16++);
+ unican_writew(timestamp & 0x00FF, ptr16);
+ }
+
+ u = ((cobid>>16) & 0xFF00) | CL2_MESSAGE_VALID;
+ unican_writew(u,(__u16*)chipext->asyncTxBufPtr);
+
+ if ( (chipext->asyncTxBufBase + chipext->asyncTxBufSize*16) <=
+ (chipext->asyncTxBufPtr += 16) ) {
+ chipext->asyncTxBufPtr = chipext->asyncTxBufBase;
+ }
+
+
+ /* Do local transmitted message distribution if enabled. */
+ /* This code should not be called directly there, because it breaks strict
+ behavior of queues if O_SYNC is set. */
+ if (processlocal){
+ obj->tx_slot->msg.flags |= MSG_LOCAL;
+ canque_filter_msg2edges(obj->qends, &obj->tx_slot->msg);
+ }
+ /* Free transmitted slot */
+ canque_free_outslot(obj->qends, obj->tx_qedge, obj->tx_slot);
+ obj->tx_slot=NULL;
+
+ }while(1);
+
+ return;
}
+void unican_irq_sync_activities(struct chip_t *chip, struct msgobj_t *obj)
+{
+ while(!can_msgobj_test_and_set_fl(obj,TX_LOCK)) {
+
+ if(can_msgobj_test_and_clear_fl(obj,TX_REQUEST)) {
+ unican_irq_write_handler(chip, obj);
+ }
+
+ /*if(can_msgobj_test_and_clear_fl(obj,FILTCH_REQUEST)) {
+ unican_irq_update_filter(chip, obj);
+ }*/
+
+ can_msgobj_clear_fl(obj,TX_LOCK);
+ if(can_msgobj_test_fl(obj,TX_REQUEST))
+ continue;
+ if(can_msgobj_test_fl(obj,FILTCH_REQUEST) && !obj->tx_slot)
+ continue;
+ break;
+ }
+}
+
+
#define MAX_RETR 10
/**
*/
can_irqreturn_t unican_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
{
- return CAN_IRQ_HANDLED;
-}
+ struct chip_t *chip=(struct chip_t *)dev_id;
+ sCAN_CARD *chipext = (sCAN_CARD *)chip->chip_data;
+ struct msgobj_t *obj=chip->msgobj[0];
+ __u16 status;
+ __u16 error;
+
+ if(!(chip->flags&CHIP_CONFIGURED)) {
+ CANMSG("unican_irq_handler: called for non-configured device\n");
+ return CAN_IRQ_NONE;
+ }
+ if (cl2_get_status(chipext, &status) == CL2_NO_REQUEST)
+ return CAN_IRQ_NONE;
-void unican_schedule_next(struct msgobj_t *obj)
-{
- int cmd;
+ cl2_clear_interrupt(chipext);
- can_preempt_disable();
- can_msgobj_set_fl(obj,TX_REQUEST);
-
- while(!can_msgobj_test_and_set_fl(obj,TX_LOCK)){
+ if(status & CL2_CARD_ERROR) {
+ cl2_get_error(chipext, &error);
+ CANMSG("unican_irq_handler: card status=0x%04x error=0x%04x \n",status,error);
+ }
+ if(status & CL2_ASYNC_QUEUE_EMPTY) {
- can_msgobj_clear_fl(obj,TX_REQUEST);
-
- cmd=canque_test_outslot(obj->qends, &obj->tx_qedge, &obj->tx_slot);
- if(cmd>=0) {
- mod_timer(&obj->tx_timeout,
- jiffies+unican_bus_latency(obj));
- DEBUGMSG("unican: scheduled delivery\n");
+ }
+ if(status & CL2_SYNC_QUEUE_EMPTY) {
+ can_msgobj_set_fl(obj,TX_REQUEST);
- } else
- can_msgobj_clear_fl(obj,TX_LOCK);
-
- if(!can_msgobj_test_fl(obj,TX_REQUEST)) break;
- DEBUGMSG("TX looping in unican_schedule_next\n");
+ /* calls unican_irq_write_handler synchronized with other invocations */
+ unican_irq_sync_activities(chip, obj);
+
+ }
+ if(status & CL2_DATA_IN_RBUF) {
+ unican_read(chip, obj);
}
- can_preempt_enable();
+ cl2_gen_interrupt(chipext);
+
+ return CAN_IRQ_HANDLED;
}
-void unican_do_tx_timeout(unsigned long data)
+/*void unican_do_tx_timeout(unsigned long data)
{
struct msgobj_t *obj=(struct msgobj_t *)data;
- if(obj->tx_slot) {
- /* Deliver message to edges */
- canque_filter_msg2edges(obj->qends, &obj->tx_slot->msg);
- /* Free transmitted slot */
- canque_free_outslot(obj->qends, obj->tx_qedge, obj->tx_slot);
- obj->tx_slot=NULL;
- DEBUGMSG("unican: delayed delivery\n");
- }
- can_msgobj_clear_fl(obj,TX_LOCK);
-
- unican_schedule_next(obj);
-}
+}*/
/**
* unican_wakeup_tx: - wakeups TX processing
*/
int unican_wakeup_tx(struct chip_t *chip, struct msgobj_t *obj)
{
- /* can_msgobj_set_fl(obj,TX_REQUEST); */
-
- struct canque_edge_t *qedge;
- struct canque_slot_t *slot;
- int cmd;
+ can_preempt_disable();
- can_msgobj_clear_fl(obj,TX_REQUEST);
+ can_msgobj_set_fl(obj,TX_REQUEST);
- #ifndef CAN_WITH_RTL
- if(!unican_bus_latency(obj)) {
- #endif /*CAN_WITH_RTL*/
- /* Ensure delivery of all ready slots */
- while((cmd=canque_test_outslot(obj->qends, &qedge, &slot)) >= 0){
- if(cmd==0) {
- canque_filter_msg2edges(obj->qends, &slot->msg);
- DEBUGMSG("unican: direct delivery\n");
- }
- canque_free_outslot(obj->qends, qedge, slot);
- }
- #ifndef CAN_WITH_RTL
- } else {
- unican_schedule_next(obj);
- }
- #endif /*CAN_WITH_RTL*/
+ /* calls unican_irq_write_handler synchronized with other invocations
+ from kernel and IRQ context */
+ unican_irq_sync_activities(chip, obj);
+
+ can_preempt_enable();
return 0;
}
/* * * unican Board Functionality * * */
+#define IO_RANGE 0x1000
+
/**
* unican_request_io: - reserve io or memory range for can board
* @candev: pointer to candevice/board which asks for io. Field @io_addr
*/
int unican_request_io(struct candevice_t *candev)
{
+ unsigned long remap_addr;
+ if (!can_request_mem_region(candev->io_addr,IO_RANGE,DEVICE_NAME " - unican")) {
+ CANMSG("Unable to request IO-memory: 0x%lx\n",candev->io_addr);
+ return -ENODEV;
+ }
+ if ( !( remap_addr = (long) ioremap( candev->io_addr, IO_RANGE ) ) ) {
+ CANMSG("Unable to access I/O memory at: 0x%lx\n", candev->io_addr);
+ can_release_mem_region(candev->io_addr,IO_RANGE);
+ return -ENODEV;
+
+ }
+ can_base_addr_fixup(candev, remap_addr);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", candev->io_addr, candev->io_addr + IO_RANGE - 1);
return 0;
}
*/
int unican_release_io(struct candevice_t *candev)
{
+ iounmap((void*)candev->dev_base_addr);
+ can_release_mem_region(candev->io_addr,IO_RANGE);
return 0;
}
*/
int unican_reset(struct candevice_t *candev)
{
+ int ret;
+ int i;
+ struct chip_t *chip = candev->chip[0];
+ sCAN_CARD *chipext;
+
+
+ if(chip->chip_data == NULL) {
+ chip->chip_data = can_checked_malloc(sizeof(sCAN_CARD));
+ if(!chip->chip_data) return -ENOMEM;
+ memset(chip->chip_data,0,sizeof(sCAN_CARD));
+ ret = cl2_init_card(chip->chip_data,(void*)chip->chip_base_addr,chip->chip_irq);
+ if(ret != CL2_OK){
+ CANMSG("cl2_init_card returned %d\n",ret);
+ return -ENODEV;
+ }
+ }
+
+ chipext = (sCAN_CARD *)chip->chip_data;
+
+ i = 0;
+ /* reset and test whether the card is present */
+ do {
+ cl2_reset_card(chipext);
+ unican_delay(10);
+ i++;
+ ret = cl2_test_card(chipext);
+ } while((ret != CL2_OK)&&(i<10));
+
+ if(ret != CL2_OK) {
+ CANMSG("card check failed %d\n",ret);
+ return -ENODEV;
+ }
+
+ /* start card firmware */
+ ret = cl2_start_firmware(chipext);
+ if(ret != CL2_OK){
+ CANMSG("cl2_start_firmware returned %d\n",ret);
+ return -ENODEV;
+ }
+
+ unican_delay(100);
+
return 0;
}
chip->int_clk_reg = 0x0;
chip->int_bus_reg = 0x0;
chip->max_objects = 1;
-
+ chip->chip_base_addr=candev->io_addr;
+
CANMSG("initializing unican chip operations\n");
chip->chipspecops->chip_config=unican_chip_config;
chip->chipspecops->baud_rate=unican_baud_rate;
chip->chipspecops->set_btregs=unican_set_btregs;
chip->chipspecops->start_chip=unican_start_chip;
chip->chipspecops->stop_chip=unican_stop_chip;
- chip->chipspecops->irq_handler=NULL;
+ chip->chipspecops->irq_handler=unican_irq_handler;
return 0;
}
{
struct msgobj_t *obj=chip->msgobj[objnr];
obj->obj_base_addr=chip->chip_base_addr;
- obj->tx_timeout.function=unican_do_tx_timeout;
- obj->tx_timeout.data=(unsigned long)obj;
+ /*obj->tx_timeout.function=unican_do_tx_timeout;
+ obj->tx_timeout.data=(unsigned long)obj;*/
return 0;
}
hwspecops->program_irq = unican_program_irq;
return 0;
}
+
+
+/* Unicontrols PCI board specific functions */
+
+#ifdef CAN_ENABLE_PCI_SUPPORT
+
+int unican_pci_request_io(struct candevice_t *candev)
+{
+ #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))
+ if(pci_request_region(candev->sysdevptr.pcidev, 0, "unican_pci") != 0){
+ CANMSG("Request of Unican PCI range failed\n");
+ return -ENODEV;
+ }
+ #else /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/
+ if(pci_request_regions(candev->sysdevptr.pcidev, "kv_pcican") != 0){
+ CANMSG("Request of Unican PCI range failed\n");
+ return -ENODEV;
+ }
+ #endif /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/
+
+ return 0;
+}
+
+
+int unican_pci_release_io(struct candevice_t *candev)
+{
+ #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))
+ pci_release_region(candev->sysdevptr.pcidev, 0);
+ #else /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/
+ pci_release_regions(candev->sysdevptr.pcidev);
+ #endif /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/
+ return 0;
+}
+
+
+int unican_pci_init_hw_data(struct candevice_t *candev)
+{
+ struct pci_dev *pcidev = NULL;
+
+ do {
+ pcidev = pci_find_device(UNICAN_PCI_VENDOR, UNICAN_PCI_ID, pcidev);
+ if(pcidev == NULL) return -ENODEV;
+ } while(can_check_dev_taken(pcidev));
+
+ if (pci_enable_device (pcidev)){
+ printk(KERN_CRIT "Setup of Unican PCI failed\n");
+ return -EIO;
+ }
+ candev->sysdevptr.pcidev=pcidev;
+
+ if(!(pci_resource_flags(pcidev,0)&IORESOURCE_MEM)){
+ printk(KERN_CRIT "Unican PCI region 0 is not MEM\n");
+ return -EIO;
+ }
+ candev->dev_base_addr=pci_resource_start(pcidev,0); /*S5920*/
+ candev->io_addr=candev->dev_base_addr;
+ candev->res_addr=candev->dev_base_addr;
+
+ /*candev->flags |= CANDEV_PROGRAMMABLE_IRQ;*/
+
+ candev->nr_82527_chips=0;
+ candev->nr_sja1000_chips=0;
+ candev->nr_all_chips=1;
+
+ return 0;
+}
+
+
+int unican_pci_init_chip_data(struct candevice_t *candev, int chipnr)
+{
+ int ret;
+ candev->chip[chipnr]->chip_irq=candev->sysdevptr.pcidev->irq;
+ ret = unican_pci_init_chip_data(candev, chipnr);
+ candev->chip[chipnr]->flags |= CHIP_IRQ_PCI;
+ return ret;
+}
+
+int unican_pci_register(struct hwspecops_t *hwspecops)
+{
+ hwspecops->request_io = unican_pci_request_io;
+ hwspecops->release_io = unican_pci_release_io;
+ hwspecops->reset = unican_reset;
+ hwspecops->init_hw_data = unican_pci_init_hw_data;
+ hwspecops->init_chip_data = unican_pci_init_chip_data;
+ hwspecops->init_obj_data = unican_init_obj_data;
+ hwspecops->write_register = NULL;
+ hwspecops->read_register = NULL;
+ hwspecops->program_irq = unican_program_irq;
+ return 0;
+}
+
+#endif /*CAN_ENABLE_PCI_SUPPORT*/
+
+#ifdef CAN_ENABLE_VME_SUPPORT
+
+int unican_vme_reset(struct candevice_t *candev)
+{
+ struct chip_t *chip = candev->chip[0];
+ unican_writew(chip->chip_irq,chip->chip_base_addr+CL2_VME_INT_VECTOR);
+ return unican_reset(candev);
+}
+
+
+int unican_vme_register(struct hwspecops_t *hwspecops)
+{
+ hwspecops->request_io = unican_request_io;
+ hwspecops->release_io = unican_release_io;
+ hwspecops->reset = unican_vme_reset;
+ hwspecops->init_hw_data = unican_init_hw_data;
+ hwspecops->init_chip_data = unican_init_chip_data;
+ hwspecops->init_obj_data = unican_init_obj_data;
+ hwspecops->write_register = NULL;
+ hwspecops->read_register = NULL;
+ hwspecops->program_irq = unican_program_irq;
+ return 0;
+}
+
+#endif /*CAN_ENABLE_VME_SUPPORT*/