18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2008 Per Dalen <per.dalen@cnw.se> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Parts of this software are based on (derived) the following: 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * - Kvaser linux driver, version 4.72 BETA 88c2ecf20Sopenharmony_ci * Copyright (C) 2002-2007 KVASER AB 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * - Lincan driver, version 0.3.3, OCERA project 118c2ecf20Sopenharmony_ci * Copyright (C) 2004 Pavel Pisa 128c2ecf20Sopenharmony_ci * Copyright (C) 2001 Arnaud Westenberg 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * - Socketcan SJA1000 drivers 158c2ecf20Sopenharmony_ci * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com> 168c2ecf20Sopenharmony_ci * Copyright (c) 2002-2007 Volkswagen Group Electronic Research 178c2ecf20Sopenharmony_ci * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33, 188c2ecf20Sopenharmony_ci * 38106 Braunschweig, GERMANY 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/kernel.h> 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 248c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 258c2ecf20Sopenharmony_ci#include <linux/delay.h> 268c2ecf20Sopenharmony_ci#include <linux/pci.h> 278c2ecf20Sopenharmony_ci#include <linux/can/dev.h> 288c2ecf20Sopenharmony_ci#include <linux/io.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include "sja1000.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define DRV_NAME "kvaser_pci" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ciMODULE_AUTHOR("Per Dalen <per.dalen@cnw.se>"); 358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Socket-CAN driver for KVASER PCAN PCI cards"); 368c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("KVASER PCAN PCI CAN card"); 378c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define MAX_NO_OF_CHANNELS 4 /* max no of channels on a single card */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistruct kvaser_pci { 428c2ecf20Sopenharmony_ci int channel; 438c2ecf20Sopenharmony_ci struct pci_dev *pci_dev; 448c2ecf20Sopenharmony_ci struct net_device *slave_dev[MAX_NO_OF_CHANNELS-1]; 458c2ecf20Sopenharmony_ci void __iomem *conf_addr; 468c2ecf20Sopenharmony_ci void __iomem *res_addr; 478c2ecf20Sopenharmony_ci int no_channels; 488c2ecf20Sopenharmony_ci u8 xilinx_ver; 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define KVASER_PCI_CAN_CLOCK (16000000 / 2) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* 548c2ecf20Sopenharmony_ci * The board configuration is probably following: 558c2ecf20Sopenharmony_ci * RX1 is connected to ground. 568c2ecf20Sopenharmony_ci * TX1 is not connected. 578c2ecf20Sopenharmony_ci * CLKO is not connected. 588c2ecf20Sopenharmony_ci * Setting the OCR register to 0xDA is a good idea. 598c2ecf20Sopenharmony_ci * This means normal output mode , push-pull and the correct polarity. 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_ci#define KVASER_PCI_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* 648c2ecf20Sopenharmony_ci * In the CDR register, you should set CBP to 1. 658c2ecf20Sopenharmony_ci * You will probably also want to set the clock divider value to 0 668c2ecf20Sopenharmony_ci * (meaning divide-by-2), the Pelican bit, and the clock-off bit 678c2ecf20Sopenharmony_ci * (you will have no need for CLKOUT anyway). 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ci#define KVASER_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* 728c2ecf20Sopenharmony_ci * These register values are valid for revision 14 of the Xilinx logic. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ci#define XILINX_VERINT 7 /* Lower nibble simulate interrupts, 758c2ecf20Sopenharmony_ci high nibble version number. */ 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define XILINX_PRESUMED_VERSION 14 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* 808c2ecf20Sopenharmony_ci * Important S5920 registers 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci#define S5920_INTCSR 0x38 838c2ecf20Sopenharmony_ci#define S5920_PTCR 0x60 848c2ecf20Sopenharmony_ci#define INTCSR_ADDON_INTENABLE_M 0x2000 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#define KVASER_PCI_PORT_BYTES 0x20 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define PCI_CONFIG_PORT_SIZE 0x80 /* size of the config io-memory */ 908c2ecf20Sopenharmony_ci#define PCI_PORT_SIZE 0x80 /* size of a channel io-memory */ 918c2ecf20Sopenharmony_ci#define PCI_PORT_XILINX_SIZE 0x08 /* size of a xilinx io-memory */ 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#define KVASER_PCI_VENDOR_ID1 0x10e8 /* the PCI device and vendor IDs */ 948c2ecf20Sopenharmony_ci#define KVASER_PCI_DEVICE_ID1 0x8406 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#define KVASER_PCI_VENDOR_ID2 0x1a07 /* the PCI device and vendor IDs */ 978c2ecf20Sopenharmony_ci#define KVASER_PCI_DEVICE_ID2 0x0008 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic const struct pci_device_id kvaser_pci_tbl[] = { 1008c2ecf20Sopenharmony_ci {KVASER_PCI_VENDOR_ID1, KVASER_PCI_DEVICE_ID1, PCI_ANY_ID, PCI_ANY_ID,}, 1018c2ecf20Sopenharmony_ci {KVASER_PCI_VENDOR_ID2, KVASER_PCI_DEVICE_ID2, PCI_ANY_ID, PCI_ANY_ID,}, 1028c2ecf20Sopenharmony_ci { 0,} 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, kvaser_pci_tbl); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic u8 kvaser_pci_read_reg(const struct sja1000_priv *priv, int port) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci return ioread8(priv->reg_base + port); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void kvaser_pci_write_reg(const struct sja1000_priv *priv, 1138c2ecf20Sopenharmony_ci int port, u8 val) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci iowrite8(val, priv->reg_base + port); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic void kvaser_pci_disable_irq(struct net_device *dev) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct sja1000_priv *priv = netdev_priv(dev); 1218c2ecf20Sopenharmony_ci struct kvaser_pci *board = priv->priv; 1228c2ecf20Sopenharmony_ci u32 intcsr; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* Disable interrupts from card */ 1258c2ecf20Sopenharmony_ci intcsr = ioread32(board->conf_addr + S5920_INTCSR); 1268c2ecf20Sopenharmony_ci intcsr &= ~INTCSR_ADDON_INTENABLE_M; 1278c2ecf20Sopenharmony_ci iowrite32(intcsr, board->conf_addr + S5920_INTCSR); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic void kvaser_pci_enable_irq(struct net_device *dev) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct sja1000_priv *priv = netdev_priv(dev); 1338c2ecf20Sopenharmony_ci struct kvaser_pci *board = priv->priv; 1348c2ecf20Sopenharmony_ci u32 tmp_en_io; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* Enable interrupts from card */ 1378c2ecf20Sopenharmony_ci tmp_en_io = ioread32(board->conf_addr + S5920_INTCSR); 1388c2ecf20Sopenharmony_ci tmp_en_io |= INTCSR_ADDON_INTENABLE_M; 1398c2ecf20Sopenharmony_ci iowrite32(tmp_en_io, board->conf_addr + S5920_INTCSR); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int number_of_sja1000_chip(void __iomem *base_addr) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci u8 status; 1458c2ecf20Sopenharmony_ci int i; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci for (i = 0; i < MAX_NO_OF_CHANNELS; i++) { 1488c2ecf20Sopenharmony_ci /* reset chip */ 1498c2ecf20Sopenharmony_ci iowrite8(MOD_RM, base_addr + 1508c2ecf20Sopenharmony_ci (i * KVASER_PCI_PORT_BYTES) + SJA1000_MOD); 1518c2ecf20Sopenharmony_ci status = ioread8(base_addr + 1528c2ecf20Sopenharmony_ci (i * KVASER_PCI_PORT_BYTES) + SJA1000_MOD); 1538c2ecf20Sopenharmony_ci /* check reset bit */ 1548c2ecf20Sopenharmony_ci if (!(status & MOD_RM)) 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return i; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic void kvaser_pci_del_chan(struct net_device *dev) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct sja1000_priv *priv; 1648c2ecf20Sopenharmony_ci struct kvaser_pci *board; 1658c2ecf20Sopenharmony_ci int i; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (!dev) 1688c2ecf20Sopenharmony_ci return; 1698c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 1708c2ecf20Sopenharmony_ci board = priv->priv; 1718c2ecf20Sopenharmony_ci if (!board) 1728c2ecf20Sopenharmony_ci return; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci dev_info(&board->pci_dev->dev, "Removing device %s\n", 1758c2ecf20Sopenharmony_ci dev->name); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* Disable PCI interrupts */ 1788c2ecf20Sopenharmony_ci kvaser_pci_disable_irq(dev); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci for (i = 0; i < board->no_channels - 1; i++) { 1818c2ecf20Sopenharmony_ci if (board->slave_dev[i]) { 1828c2ecf20Sopenharmony_ci dev_info(&board->pci_dev->dev, "Removing device %s\n", 1838c2ecf20Sopenharmony_ci board->slave_dev[i]->name); 1848c2ecf20Sopenharmony_ci unregister_sja1000dev(board->slave_dev[i]); 1858c2ecf20Sopenharmony_ci free_sja1000dev(board->slave_dev[i]); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci unregister_sja1000dev(dev); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci pci_iounmap(board->pci_dev, priv->reg_base); 1918c2ecf20Sopenharmony_ci pci_iounmap(board->pci_dev, board->conf_addr); 1928c2ecf20Sopenharmony_ci pci_iounmap(board->pci_dev, board->res_addr); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci free_sja1000dev(dev); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int kvaser_pci_add_chan(struct pci_dev *pdev, int channel, 1988c2ecf20Sopenharmony_ci struct net_device **master_dev, 1998c2ecf20Sopenharmony_ci void __iomem *conf_addr, 2008c2ecf20Sopenharmony_ci void __iomem *res_addr, 2018c2ecf20Sopenharmony_ci void __iomem *base_addr) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct net_device *dev; 2048c2ecf20Sopenharmony_ci struct sja1000_priv *priv; 2058c2ecf20Sopenharmony_ci struct kvaser_pci *board; 2068c2ecf20Sopenharmony_ci int err; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci dev = alloc_sja1000dev(sizeof(struct kvaser_pci)); 2098c2ecf20Sopenharmony_ci if (dev == NULL) 2108c2ecf20Sopenharmony_ci return -ENOMEM; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 2138c2ecf20Sopenharmony_ci board = priv->priv; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci board->pci_dev = pdev; 2168c2ecf20Sopenharmony_ci board->channel = channel; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* S5920 */ 2198c2ecf20Sopenharmony_ci board->conf_addr = conf_addr; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* XILINX board wide address */ 2228c2ecf20Sopenharmony_ci board->res_addr = res_addr; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (channel == 0) { 2258c2ecf20Sopenharmony_ci board->xilinx_ver = 2268c2ecf20Sopenharmony_ci ioread8(board->res_addr + XILINX_VERINT) >> 4; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* Assert PTADR# - we're in passive mode so the other bits are 2298c2ecf20Sopenharmony_ci not important */ 2308c2ecf20Sopenharmony_ci iowrite32(0x80808080UL, board->conf_addr + S5920_PTCR); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* Enable interrupts from card */ 2338c2ecf20Sopenharmony_ci kvaser_pci_enable_irq(dev); 2348c2ecf20Sopenharmony_ci } else { 2358c2ecf20Sopenharmony_ci struct sja1000_priv *master_priv = netdev_priv(*master_dev); 2368c2ecf20Sopenharmony_ci struct kvaser_pci *master_board = master_priv->priv; 2378c2ecf20Sopenharmony_ci master_board->slave_dev[channel - 1] = dev; 2388c2ecf20Sopenharmony_ci master_board->no_channels = channel + 1; 2398c2ecf20Sopenharmony_ci board->xilinx_ver = master_board->xilinx_ver; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci priv->reg_base = base_addr + channel * KVASER_PCI_PORT_BYTES; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci priv->read_reg = kvaser_pci_read_reg; 2458c2ecf20Sopenharmony_ci priv->write_reg = kvaser_pci_write_reg; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci priv->can.clock.freq = KVASER_PCI_CAN_CLOCK; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci priv->ocr = KVASER_PCI_OCR; 2508c2ecf20Sopenharmony_ci priv->cdr = KVASER_PCI_CDR; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci priv->irq_flags = IRQF_SHARED; 2538c2ecf20Sopenharmony_ci dev->irq = pdev->irq; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "reg_base=%p conf_addr=%p irq=%d\n", 2568c2ecf20Sopenharmony_ci priv->reg_base, board->conf_addr, dev->irq); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 2598c2ecf20Sopenharmony_ci dev->dev_id = channel; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* Register SJA1000 device */ 2628c2ecf20Sopenharmony_ci err = register_sja1000dev(dev); 2638c2ecf20Sopenharmony_ci if (err) { 2648c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Registering device failed (err=%d)\n", 2658c2ecf20Sopenharmony_ci err); 2668c2ecf20Sopenharmony_ci goto failure; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (channel == 0) 2708c2ecf20Sopenharmony_ci *master_dev = dev; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cifailure: 2758c2ecf20Sopenharmony_ci kvaser_pci_del_chan(dev); 2768c2ecf20Sopenharmony_ci return err; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic int kvaser_pci_init_one(struct pci_dev *pdev, 2808c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci int err; 2838c2ecf20Sopenharmony_ci struct net_device *master_dev = NULL; 2848c2ecf20Sopenharmony_ci struct sja1000_priv *priv; 2858c2ecf20Sopenharmony_ci struct kvaser_pci *board; 2868c2ecf20Sopenharmony_ci int no_channels; 2878c2ecf20Sopenharmony_ci void __iomem *base_addr = NULL; 2888c2ecf20Sopenharmony_ci void __iomem *conf_addr = NULL; 2898c2ecf20Sopenharmony_ci void __iomem *res_addr = NULL; 2908c2ecf20Sopenharmony_ci int i; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "initializing device %04x:%04x\n", 2938c2ecf20Sopenharmony_ci pdev->vendor, pdev->device); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci err = pci_enable_device(pdev); 2968c2ecf20Sopenharmony_ci if (err) 2978c2ecf20Sopenharmony_ci goto failure; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci err = pci_request_regions(pdev, DRV_NAME); 3008c2ecf20Sopenharmony_ci if (err) 3018c2ecf20Sopenharmony_ci goto failure_release_pci; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* S5920 */ 3048c2ecf20Sopenharmony_ci conf_addr = pci_iomap(pdev, 0, PCI_CONFIG_PORT_SIZE); 3058c2ecf20Sopenharmony_ci if (conf_addr == NULL) { 3068c2ecf20Sopenharmony_ci err = -ENODEV; 3078c2ecf20Sopenharmony_ci goto failure_release_regions; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* XILINX board wide address */ 3118c2ecf20Sopenharmony_ci res_addr = pci_iomap(pdev, 2, PCI_PORT_XILINX_SIZE); 3128c2ecf20Sopenharmony_ci if (res_addr == NULL) { 3138c2ecf20Sopenharmony_ci err = -ENOMEM; 3148c2ecf20Sopenharmony_ci goto failure_iounmap; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci base_addr = pci_iomap(pdev, 1, PCI_PORT_SIZE); 3188c2ecf20Sopenharmony_ci if (base_addr == NULL) { 3198c2ecf20Sopenharmony_ci err = -ENOMEM; 3208c2ecf20Sopenharmony_ci goto failure_iounmap; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci no_channels = number_of_sja1000_chip(base_addr); 3248c2ecf20Sopenharmony_ci if (no_channels == 0) { 3258c2ecf20Sopenharmony_ci err = -ENOMEM; 3268c2ecf20Sopenharmony_ci goto failure_iounmap; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci for (i = 0; i < no_channels; i++) { 3308c2ecf20Sopenharmony_ci err = kvaser_pci_add_chan(pdev, i, &master_dev, 3318c2ecf20Sopenharmony_ci conf_addr, res_addr, 3328c2ecf20Sopenharmony_ci base_addr); 3338c2ecf20Sopenharmony_ci if (err) 3348c2ecf20Sopenharmony_ci goto failure_cleanup; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci priv = netdev_priv(master_dev); 3388c2ecf20Sopenharmony_ci board = priv->priv; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "xilinx version=%d number of channels=%d\n", 3418c2ecf20Sopenharmony_ci board->xilinx_ver, board->no_channels); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, master_dev); 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cifailure_cleanup: 3478c2ecf20Sopenharmony_ci kvaser_pci_del_chan(master_dev); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cifailure_iounmap: 3508c2ecf20Sopenharmony_ci if (conf_addr != NULL) 3518c2ecf20Sopenharmony_ci pci_iounmap(pdev, conf_addr); 3528c2ecf20Sopenharmony_ci if (res_addr != NULL) 3538c2ecf20Sopenharmony_ci pci_iounmap(pdev, res_addr); 3548c2ecf20Sopenharmony_ci if (base_addr != NULL) 3558c2ecf20Sopenharmony_ci pci_iounmap(pdev, base_addr); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cifailure_release_regions: 3588c2ecf20Sopenharmony_ci pci_release_regions(pdev); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cifailure_release_pci: 3618c2ecf20Sopenharmony_ci pci_disable_device(pdev); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cifailure: 3648c2ecf20Sopenharmony_ci return err; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic void kvaser_pci_remove_one(struct pci_dev *pdev) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct net_device *dev = pci_get_drvdata(pdev); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci kvaser_pci_del_chan(dev); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci pci_release_regions(pdev); 3758c2ecf20Sopenharmony_ci pci_disable_device(pdev); 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic struct pci_driver kvaser_pci_driver = { 3798c2ecf20Sopenharmony_ci .name = DRV_NAME, 3808c2ecf20Sopenharmony_ci .id_table = kvaser_pci_tbl, 3818c2ecf20Sopenharmony_ci .probe = kvaser_pci_init_one, 3828c2ecf20Sopenharmony_ci .remove = kvaser_pci_remove_one, 3838c2ecf20Sopenharmony_ci}; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cimodule_pci_driver(kvaser_pci_driver); 386