-/* unican.c
- * Linux CAN-bus device driver.
- * Written for new CAN driver version by Pavel Pisa - OCERA team member
- * email:pisa@cmp.felk.cvut.cz
- * This software is released under the GPL-License.
- * Version lincan-0.2 9 Jul 2003
- */
+/**************************************************************************/
+/* File: unican.c - Unicontrols PCAN,PCAN-PCI, VCAN boards support */
+/* */
+/* LinCAN - (Not only) Linux CAN bus driver */
+/* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://dce.felk.cvut.cz> */
+/* Copyright (C) 2002-2009 Pavel Pisa <pisa@cmp.felk.cvut.cz> */
+/* Funded by OCERA and FRESCOR IST projects */
+/* Based on CAN driver code by Arnaud Westenberg <arnaud@wanadoo.nl> */
+/* */
+/* LinCAN is free software; you can redistribute it and/or modify it */
+/* under terms of the GNU General Public License as published by the */
+/* Free Software Foundation; either version 2, or (at your option) any */
+/* later version. LinCAN 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 LinCAN; see file */
+/* COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, */
+/* Cambridge, MA 02139, USA. */
+/* */
+/* To allow use of LinCAN in the compact embedded systems firmware */
+/* and RT-executives (RTEMS for example), main authors agree with next */
+/* special exception: */
+/* */
+/* Including LinCAN header files in a file, instantiating LinCAN generics */
+/* or templates, or linking other files with LinCAN objects to produce */
+/* an application image/executable, does not by itself cause the */
+/* resulting application image/executable to be covered by */
+/* the GNU General Public License. */
+/* This exception does not however invalidate any other reasons */
+/* why the executable file might be covered by the GNU Public License. */
+/* Publication of enhanced or derived LinCAN files is required although. */
+/**************************************************************************/
#include "../include/can.h"
#include "../include/can_sysdep.h"
}
-
-long unican_bus_latency(struct msgobj_t *obj)
-{
- long latency;
- latency=obj->hostchip->baudrate;
- if(latency){
- latency=(long)HZ*1000/latency;
- }
- return latency;
-}
-
-
/* * * unican Chip Functionality * * */
-int unican_enable_configuration(struct chip_t *chip)
+int unican_enable_configuration(struct canchip_t *chip)
{
return 0;
}
-int unican_disable_configuration(struct chip_t *chip)
+int unican_disable_configuration(struct canchip_t *chip)
{
return 0;
}
* Return Value: negative value reports error.
* File: src/unican.c
*/
-int unican_chip_config(struct chip_t *chip)
+int unican_chip_config(struct canchip_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) {
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);
return -ENODEV;
}
unican_delay(1);
-
+
/* enable all card interrupts */
ret = cl2_int_mode(chipext, INT_MODE_ALL);
if(ret != CL2_OK) {
* Return Value: negative value reports error.
* File: src/unican.c
*/
-int unican_extended_mask(struct chip_t *chip, unsigned long code, unsigned long mask)
+int unican_extended_mask(struct canchip_t *chip, unsigned long code, unsigned long mask)
{
return 0;
}
* Return Value: negative value reports error.
* File: src/unican.c
*/
-int unican_baud_rate(struct chip_t *chip, int rate, int clock, int sjw,
+int unican_baud_rate(struct canchip_t *chip, int rate, int clock, int sjw,
int sampl_pt, int flags)
{
int ret;
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;
}
*
* File: src/unican.c
*/
-void unican_read(struct chip_t *chip, struct msgobj_t *obj) {
+void unican_read(struct canchip_t *chip, struct msgobj_t *obj) {
sCAN_CARD *chipext = (sCAN_CARD *)chip->chip_data;
__u16 *ptr16;
__u16 u;
obj->rx_msg.flags = 0;
}
- /*if ( !(u & (CL2_REMOTE_FRAME<<8)) )
+ /*if ( !(u & (CL2_REMOTE_FRAME<<8)) )
obj->rx_msg.flags |= MSG_RTR;*/
obj->rx_msg.length = ( (u >> 4) & 0x000F );
#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;
* Positive value indicates immediate reception of message.
* File: src/unican.c
*/
-int unican_pre_read_config(struct chip_t *chip, struct msgobj_t *obj)
+int unican_pre_read_config(struct canchip_t *chip, struct msgobj_t *obj)
{
return 0;
}
* Return Value: negative value reports error.
* File: src/unican.c
*/
-int unican_pre_write_config(struct chip_t *chip, struct msgobj_t *obj,
+int unican_pre_write_config(struct canchip_t *chip, struct msgobj_t *obj,
struct canmsg_t *msg)
{
return 0;
* Return Value: negative value reports error.
* File: src/unican.c
*/
-int unican_send_msg(struct chip_t *chip, struct msgobj_t *obj,
+int unican_send_msg(struct canchip_t *chip, struct msgobj_t *obj,
struct canmsg_t *msg)
{
return 0;
* Zero value indicates finishing of all issued transmission requests.
* File: src/unican.c
*/
-int unican_check_tx_stat(struct chip_t *chip)
+int unican_check_tx_stat(struct canchip_t *chip)
{
return 0;
}
* Return Value: negative value reports error.
* File: src/unican.c
*/
-int unican_set_btregs(struct chip_t *chip, unsigned short btr0,
+int unican_set_btregs(struct canchip_t *chip, unsigned short btr0,
unsigned short btr1)
{
int ret;
* Return Value: negative value reports error.
* File: src/unican.c
*/
-int unican_start_chip(struct chip_t *chip)
+int unican_start_chip(struct canchip_t *chip)
{
return 0;
}
* Return Value: negative value reports error.
* File: src/unican.c
*/
-int unican_stop_chip(struct chip_t *chip)
+int unican_stop_chip(struct canchip_t *chip)
+{
+ return 0;
+}
+
+/**
+ * unican_attach_to_chip: - attaches to the chip, setups registers and state
+ * @chip: pointer to chip state structure
+ *
+ * Return Value: negative value reports error.
+ * File: src/sja1000p.c
+ */
+int unican_attach_to_chip(struct canchip_t *chip)
{
return 0;
}
+/**
+ * unican_release_chip: - called before chip structure removal if %CHIP_ATTACHED is set
+ * @chip: pointer to chip state structure
+ *
+ * Return Value: negative value reports error.
+ * File: src/sja1000p.c
+ */
+int unican_release_chip(struct canchip_t *chip)
+{
+ sCAN_CARD *chipext = (sCAN_CARD *)chip->chip_data;
+
+ unican_stop_chip(chip);
+ cl2_clear_interrupt(chipext);
+
+ return 0;
+}
/**
* unican_remote_request: - configures message object and asks for RTR message
* Return Value: negative value reports error.
* File: src/unican.c
*/
-int unican_remote_request(struct chip_t *chip, struct msgobj_t *obj)
+int unican_remote_request(struct canchip_t *chip, struct msgobj_t *obj)
{
CANMSG("unican_remote_request not implemented\n");
return -ENOSYS;
* Return Value: negative value reports error.
* File: src/unican.c
*/
-int unican_standard_mask(struct chip_t *chip, unsigned short code,
+int unican_standard_mask(struct canchip_t *chip, unsigned short code,
unsigned short mask)
{
CANMSG("unican_standard_mask not implemented\n");
* Return Value: negative value reports error.
* File: src/unican.c
*/
-int unican_clear_objects(struct chip_t *chip)
+int unican_clear_objects(struct canchip_t *chip)
{
CANMSG("unican_clear_objects not implemented\n");
return -ENOSYS;
* Return Value: negative value reports error.
* File: src/unican.c
*/
-int unican_config_irqs(struct chip_t *chip, short irqs)
+int unican_config_irqs(struct canchip_t *chip, short irqs)
{
CANMSG("unican_config_irqs not implemented\n");
* unican_irq_write_handler() for transmit events.
* File: src/unican.c
*/
-void unican_irq_write_handler(struct chip_t *chip, struct msgobj_t *obj)
+void unican_irq_write_handler(struct canchip_t *chip, struct msgobj_t *obj)
{
int cmd;
sCAN_CARD *chipext = (sCAN_CARD *)chip->chip_data;
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 */
if(len > CAN_MSG_LENGTH)
len = CAN_MSG_LENGTH;
u = (len << 12) | (cobid & 0x00FF);
-
- if ( !(obj->tx_slot->msg.flags & MSG_RTR) )
+
+ if ( !(obj->tx_slot->msg.flags & MSG_RTR) )
u |= CL2_REMOTE_FRAME<<8;
- if ( obj->tx_slot->msg.flags & MSG_EXT )
+ if ( obj->tx_slot->msg.flags & MSG_EXT )
u |= CL2_EXT_FRAME<<8;
unican_writew(u,ptr16++);
u = ((cobid>>16) & 0xFF00) | CL2_MESSAGE_VALID;
unican_writew(u,(__u16*)chipext->asyncTxBufPtr);
- if ( (chipext->asyncTxBufBase + chipext->asyncTxBufSize*16) <=
+ if ( (chipext->asyncTxBufBase + chipext->asyncTxBufSize*16) <=
(chipext->asyncTxBufPtr += 16) ) {
chipext->asyncTxBufPtr = chipext->asyncTxBufBase;
}
/* 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)
+void unican_irq_sync_activities(struct canchip_t *chip, struct msgobj_t *obj)
{
while(!can_msgobj_test_and_set_fl(obj,TX_LOCK)) {
/**
* unican_irq_handler: - interrupt service routine
* @irq: interrupt vector number, this value is system specific
- * @dev_id: driver private pointer registered at time of request_irq() call.
- * The CAN driver uses this pointer to store relationship of interrupt
- * to chip state structure - @struct chip_t
- * @regs: system dependent value pointing to registers stored in exception frame
- *
+ * @chip: pointer to chip state structure
+ *
* Interrupt handler is activated when state of CAN controller chip changes,
* there is message to be read or there is more space for new messages or
* error occurs. The receive events results in reading of the message from
* message queues.
* File: src/unican.c
*/
-can_irqreturn_t unican_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+int unican_irq_handler(int irq, struct canchip_t *chip)
{
- 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;
if(!(chip->flags&CHIP_CONFIGURED)) {
CANMSG("unican_irq_handler: called for non-configured device\n");
- return CAN_IRQ_NONE;
+ return CANCHIP_IRQ_NONE;
}
- if (cl2_get_status(chipext, &status) == CL2_NO_REQUEST)
- return CAN_IRQ_NONE;
+ if (cl2_get_status(chipext, &status) == CL2_NO_REQUEST) {
+ /* Reenable interrupts generation, this has to be even there,
+ * because irq_accept disables interrupts
+ */
+ cl2_gen_interrupt(chipext);
+ return CANCHIP_IRQ_NONE;
+ }
cl2_clear_interrupt(chipext);
unican_read(chip, obj);
}
+ /* Reenable interrupts generation */
cl2_gen_interrupt(chipext);
- return CAN_IRQ_HANDLED;
+ return CANCHIP_IRQ_HANDLED;
}
+/**
+ * unican_irq_accept: - fast irq accept routine, blocks further interrupts
+ * @irq: interrupt vector number, this value is system specific
+ * @chip: pointer to chip state structure
+ *
+ * This routine only accepts interrupt reception and stops further
+ * incoming interrupts, but does not handle situation causing interrupt.
+ * File: src/unican.c
+ */
+int unican_irq_accept(int irq, struct canchip_t *chip)
+{
+ sCAN_CARD *chipext = (sCAN_CARD *)chip->chip_data;
+
+ cl2_clear_interrupt(chipext);
+
+ return CANCHIP_IRQ_ACCEPTED;
+}
+
/*void unican_do_tx_timeout(unsigned long data)
{
struct msgobj_t *obj=(struct msgobj_t *)data;
-
+
}*/
/**
* Return Value: negative value reports error.
* File: src/unican.c
*/
-int unican_wakeup_tx(struct chip_t *chip, struct msgobj_t *obj)
+int unican_wakeup_tx(struct canchip_t *chip, struct msgobj_t *obj)
{
can_preempt_disable();
*/
int unican_request_io(struct candevice_t *candev)
{
- unsigned long remap_addr;
+ can_ioptr_t 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 ) ) ) {
+ if ( !( remap_addr = 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);
*/
int unican_release_io(struct candevice_t *candev)
{
- iounmap((void*)candev->dev_base_addr);
+ iounmap(candev->dev_base_addr);
can_release_mem_region(candev->io_addr,IO_RANGE);
return 0;
}
{
int ret;
int i;
- struct chip_t *chip = candev->chip[0];
+ struct canchip_t *chip = candev->chip[0];
sCAN_CARD *chipext;
-
+
if(chip->chip_data == NULL) {
chip->chip_data = can_checked_malloc(sizeof(sCAN_CARD));
return -ENODEV;
}
}
-
+
chipext = (sCAN_CARD *)chip->chip_data;
-
+
i = 0;
/* reset and test whether the card is present */
do {
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;
* Return Value: The function always returns zero
* File: src/unican.c
*/
-int unican_init_hw_data(struct candevice_t *candev)
+int unican_init_hw_data(struct candevice_t *candev)
{
candev->res_addr=0;
candev->nr_82527_chips=0;
return 0;
}
-#define CHIP_TYPE "unican"
-
/**
* unican_init_chip_data - Initialize chips
* @candev: Pointer to candevice/board structure
*/
int unican_init_chip_data(struct candevice_t *candev, int chipnr)
{
- struct chip_t *chip = candev->chip[chipnr];
- chip->chip_type = CHIP_TYPE;
+ struct canchip_t *chip = candev->chip[chipnr];
+ chip->chip_type = "unican";
chip->chip_base_addr = 0;
chip->clock = 10000000;
chip->int_clk_reg = 0x0;
chip->int_bus_reg = 0x0;
chip->max_objects = 1;
- chip->chip_base_addr=candev->io_addr;
-
+ chip->chip_base_addr=candev->dev_base_addr;
+
CANMSG("initializing unican chip operations\n");
chip->chipspecops->chip_config=unican_chip_config;
chip->chipspecops->baud_rate=unican_baud_rate;
chip->chipspecops->enable_configuration=unican_enable_configuration;
chip->chipspecops->disable_configuration=unican_disable_configuration;
chip->chipspecops->set_btregs=unican_set_btregs;
+ chip->chipspecops->attach_to_chip=unican_attach_to_chip;
+ chip->chipspecops->release_chip=unican_release_chip;
chip->chipspecops->start_chip=unican_start_chip;
chip->chipspecops->stop_chip=unican_stop_chip;
chip->chipspecops->irq_handler=unican_irq_handler;
+ chip->chipspecops->irq_accept=unican_irq_accept;
return 0;
}
* Return Value: The function always returns zero
* File: src/unican.c
*/
-int unican_init_obj_data(struct chip_t *chip, int objnr)
+int unican_init_obj_data(struct canchip_t *chip, int objnr)
{
struct msgobj_t *obj=chip->msgobj[objnr];
obj->obj_base_addr=chip->chip_base_addr;
int unican_pci_request_io(struct candevice_t *candev)
{
+ can_ioptr_t remap_addr;
+
#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");
}
#endif /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/
+ candev->io_addr=pci_resource_start(candev->sysdevptr.pcidev,0);
+ candev->res_addr=candev->io_addr;
+
+ if ( !( remap_addr = ioremap( candev->io_addr, IO_RANGE ) ) ) {
+ CANMSG("Unable to access I/O memory at: 0x%lx\n", candev->io_addr);
+ #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 -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);
+ DEBUGMSG("VMA: dev_base_addr: 0x%lx chip_base_addr: 0x%lx\n",
+ can_ioptr2ulong(candev->dev_base_addr),
+ can_ioptr2ulong(candev->chip[0]->chip_base_addr));
+
return 0;
}
int unican_pci_release_io(struct candevice_t *candev)
{
+ iounmap(candev->dev_base_addr);
#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))*/
{
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));
-
+ pcidev = can_pci_get_next_untaken_device(UNICAN_PCI_VENDOR, UNICAN_PCI_ID);
+ if(pcidev == NULL)
+ return -ENODEV;
+
if (pci_enable_device (pcidev)){
printk(KERN_CRIT "Setup of Unican PCI failed\n");
+ can_pci_dev_put(pcidev);
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");
+ can_pci_dev_put(pcidev);
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->io_addr=pci_resource_start(pcidev,0);
+ candev->res_addr=candev->io_addr;
+ candev->dev_base_addr=NULL;
+
/*candev->flags |= CANDEV_PROGRAMMABLE_IRQ;*/
candev->nr_82527_chips=0;
return 0;
}
+void unican_pci_done_hw_data(struct candevice_t *candev)
+{
+ struct pci_dev *pcidev = candev->sysdevptr.pcidev;
+ can_pci_dev_put(pcidev);
+}
int unican_pci_init_chip_data(struct candevice_t *candev, int chipnr)
{
hwspecops->release_io = unican_pci_release_io;
hwspecops->reset = unican_reset;
hwspecops->init_hw_data = unican_pci_init_hw_data;
+ hwspecops->done_hw_data = unican_pci_done_hw_data;
hwspecops->init_chip_data = unican_pci_init_chip_data;
hwspecops->init_obj_data = unican_init_obj_data;
hwspecops->write_register = NULL;