Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/can/esd/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 12 kB image not shown  

Quelle  esd_402_pci-core.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2015 - 2016 Thomas Körper, esd electronic system design gmbh
 * Copyright (C) 2017 - 2023 Stefan Mätje, esd electronics gmbh
 */


#include <linux/can/dev.h>
#include <linux/can.h>
#include <linux/can/netlink.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/ethtool.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/pci.h>

#include "esdacc.h"

#define ESD_PCI_DEVICE_ID_PCIE402 0x0402

#define PCI402_FPGA_VER_MIN 0x003d
#define PCI402_MAX_CORES 6
#define PCI402_BAR 0
#define PCI402_IO_OV_OFFS 0
#define PCI402_IO_PCIEP_OFFS 0x10000
#define PCI402_IO_LEN_TOTAL 0x20000
#define PCI402_IO_LEN_CORE 0x2000
#define PCI402_PCICFG_MSICAP 0x50

#define PCI402_DMA_MASK DMA_BIT_MASK(32)
#define PCI402_DMA_SIZE ALIGN(0x10000, PAGE_SIZE)

#define PCI402_PCIEP_OF_INT_ENABLE 0x0050
#define PCI402_PCIEP_OF_BM_ADDR_LO 0x1000
#define PCI402_PCIEP_OF_BM_ADDR_HI 0x1004
#define PCI402_PCIEP_OF_MSI_ADDR_LO 0x1008
#define PCI402_PCIEP_OF_MSI_ADDR_HI 0x100c

struct pci402_card {
 /* Actually mapped io space, all other iomem derived from this */
 void __iomem *addr;
 void __iomem *addr_pciep;

 void *dma_buf;
 dma_addr_t dma_hnd;

 struct acc_ov ov;
 struct acc_core *cores;

 bool msi_enabled;
};

/* The BTR register capabilities described by the can_bittiming_const structures
 * below are valid since esdACC version 0x0032.
 */


/* Used if the esdACC FPGA is built as CAN-Classic version. */
static const struct can_bittiming_const pci402_bittiming_const = {
 .name = "esd_402",
 .tseg1_min = 1,
 .tseg1_max = 16,
 .tseg2_min = 1,
 .tseg2_max = 8,
 .sjw_max = 4,
 .brp_min = 1,
 .brp_max = 512,
 .brp_inc = 1,
};

/* Used if the esdACC FPGA is built as CAN-FD version. */
static const struct can_bittiming_const pci402_bittiming_const_canfd = {
 .name = "esd_402fd",
 .tseg1_min = 1,
 .tseg1_max = 256,
 .tseg2_min = 1,
 .tseg2_max = 128,
 .sjw_max = 128,
 .brp_min = 1,
 .brp_max = 256,
 .brp_inc = 1,
};

static const struct net_device_ops pci402_acc_netdev_ops = {
 .ndo_open = acc_open,
 .ndo_stop = acc_close,
 .ndo_start_xmit = acc_start_xmit,
 .ndo_change_mtu = can_change_mtu,
 .ndo_eth_ioctl = can_eth_ioctl_hwts,
};

static const struct ethtool_ops pci402_acc_ethtool_ops = {
 .get_ts_info = can_ethtool_op_get_ts_info_hwts,
};

static irqreturn_t pci402_interrupt(int irq, void *dev_id)
{
 struct pci_dev *pdev = dev_id;
 struct pci402_card *card = pci_get_drvdata(pdev);
 irqreturn_t irq_status;

 irq_status = acc_card_interrupt(&card->ov, card->cores);

 return irq_status;
}

static int pci402_set_msiconfig(struct pci_dev *pdev)
{
 struct pci402_card *card = pci_get_drvdata(pdev);
 u32 addr_lo_offs = 0;
 u32 addr_lo = 0;
 u32 addr_hi = 0;
 u32 data = 0;
 u16 csr = 0;
 int err;

 /* The FPGA hard IP PCIe core implements a 64-bit MSI Capability
 * Register Format
 */

 err = pci_read_config_word(pdev, PCI402_PCICFG_MSICAP + PCI_MSI_FLAGS, &csr);
 if (err)
  goto failed;

 err = pci_read_config_dword(pdev, PCI402_PCICFG_MSICAP + PCI_MSI_ADDRESS_LO,
        &addr_lo);
 if (err)
  goto failed;
 err = pci_read_config_dword(pdev, PCI402_PCICFG_MSICAP + PCI_MSI_ADDRESS_HI,
        &addr_hi);
 if (err)
  goto failed;

 err = pci_read_config_dword(pdev, PCI402_PCICFG_MSICAP + PCI_MSI_DATA_64,
        &data);
 if (err)
  goto failed;

 addr_lo_offs = addr_lo & 0x0000ffff;
 addr_lo &= 0xffff0000;

 if (addr_hi)
  addr_lo |= 1; /* To enable 64-Bit addressing in PCIe endpoint */

 if (!(csr & PCI_MSI_FLAGS_ENABLE)) {
  err = -EINVAL;
  goto failed;
 }

 iowrite32(addr_lo, card->addr_pciep + PCI402_PCIEP_OF_MSI_ADDR_LO);
 iowrite32(addr_hi, card->addr_pciep + PCI402_PCIEP_OF_MSI_ADDR_HI);
 acc_ov_write32(&card->ov, ACC_OV_OF_MSI_ADDRESSOFFSET, addr_lo_offs);
 acc_ov_write32(&card->ov, ACC_OV_OF_MSI_DATA, data);

 return 0;

failed:
 pci_warn(pdev, "Error while setting MSI configuration:\n"
   "CSR: 0x%.4x, addr: 0x%.8x%.8x, offs: 0x%.4x, data: 0x%.8x\n",
   csr, addr_hi, addr_lo, addr_lo_offs, data);

 return err;
}

static int pci402_init_card(struct pci_dev *pdev)
{
 struct pci402_card *card = pci_get_drvdata(pdev);

 card->ov.addr = card->addr + PCI402_IO_OV_OFFS;
 card->addr_pciep = card->addr + PCI402_IO_PCIEP_OFFS;

 acc_reset_fpga(&card->ov);
 acc_init_ov(&card->ov, &pdev->dev);

 if (card->ov.version < PCI402_FPGA_VER_MIN) {
  pci_err(pdev,
   "esdACC version (0x%.4x) outdated, please update\n",
   card->ov.version);
  return -EINVAL;
 }

 if (card->ov.timestamp_frequency != ACC_TS_FREQ_80MHZ) {
  pci_err(pdev,
   "esdACC timestamp frequency of %uHz not supported by driver. Aborted.\n",
   card->ov.timestamp_frequency);
  return -EINVAL;
 }

 if (card->ov.active_cores > PCI402_MAX_CORES) {
  pci_err(pdev,
   "Card with %u active cores not supported by driver. Aborted.\n",
   card->ov.active_cores);
  return -EINVAL;
 }
 card->cores = devm_kcalloc(&pdev->dev, card->ov.active_cores,
       sizeof(struct acc_core), GFP_KERNEL);
 if (!card->cores)
  return -ENOMEM;

 if (card->ov.features & ACC_OV_REG_FEAT_MASK_CANFD) {
  pci_warn(pdev,
    "esdACC with CAN-FD feature detected. This driver doesn't support CAN-FD yet.\n");
 }

#ifdef __LITTLE_ENDIAN
 /* So card converts all busmastered data to LE for us: */
 acc_ov_set_bits(&card->ov, ACC_OV_OF_MODE,
   ACC_OV_REG_MODE_MASK_ENDIAN_LITTLE);
#endif

 return 0;
}

static int pci402_init_interrupt(struct pci_dev *pdev)
{
 struct pci402_card *card = pci_get_drvdata(pdev);
 int err;

 err = pci_enable_msi(pdev);
 if (!err) {
  err = pci402_set_msiconfig(pdev);
  if (!err) {
   card->msi_enabled = true;
   acc_ov_set_bits(&card->ov, ACC_OV_OF_MODE,
     ACC_OV_REG_MODE_MASK_MSI_ENABLE);
   pci_dbg(pdev, "MSI preparation done\n");
  }
 }

 err = devm_request_irq(&pdev->dev, pdev->irq, pci402_interrupt,
          IRQF_SHARED, dev_name(&pdev->dev), pdev);
 if (err)
  goto failure_msidis;

 iowrite32(1, card->addr_pciep + PCI402_PCIEP_OF_INT_ENABLE);

 return 0;

failure_msidis:
 if (card->msi_enabled) {
  acc_ov_clear_bits(&card->ov, ACC_OV_OF_MODE,
      ACC_OV_REG_MODE_MASK_MSI_ENABLE);
  pci_disable_msi(pdev);
  card->msi_enabled = false;
 }

 return err;
}

static void pci402_finish_interrupt(struct pci_dev *pdev)
{
 struct pci402_card *card = pci_get_drvdata(pdev);

 iowrite32(0, card->addr_pciep + PCI402_PCIEP_OF_INT_ENABLE);
 devm_free_irq(&pdev->dev, pdev->irq, pdev);

 if (card->msi_enabled) {
  acc_ov_clear_bits(&card->ov, ACC_OV_OF_MODE,
      ACC_OV_REG_MODE_MASK_MSI_ENABLE);
  pci_disable_msi(pdev);
  card->msi_enabled = false;
 }
}

static int pci402_init_dma(struct pci_dev *pdev)
{
 struct pci402_card *card = pci_get_drvdata(pdev);
 int err;

 err = dma_set_coherent_mask(&pdev->dev, PCI402_DMA_MASK);
 if (err) {
  pci_err(pdev, "DMA set mask failed!\n");
  return err;
 }

 /* The esdACC DMA engine needs the DMA buffer aligned to a 64k
 * boundary. The DMA API guarantees to align the returned buffer to the
 * smallest PAGE_SIZE order which is greater than or equal to the
 * requested size. With PCI402_DMA_SIZE == 64kB this suffices here.
 */

 card->dma_buf = dma_alloc_coherent(&pdev->dev, PCI402_DMA_SIZE,
        &card->dma_hnd, GFP_KERNEL);
 if (!card->dma_buf)
  return -ENOMEM;

 acc_init_bm_ptr(&card->ov, card->cores, card->dma_buf);

 iowrite32(card->dma_hnd,
    card->addr_pciep + PCI402_PCIEP_OF_BM_ADDR_LO);
 iowrite32(0, card->addr_pciep + PCI402_PCIEP_OF_BM_ADDR_HI);

 pci_set_master(pdev);

 acc_ov_set_bits(&card->ov, ACC_OV_OF_MODE,
   ACC_OV_REG_MODE_MASK_BM_ENABLE);

 return 0;
}

static void pci402_finish_dma(struct pci_dev *pdev)
{
 struct pci402_card *card = pci_get_drvdata(pdev);
 int i;

 acc_ov_clear_bits(&card->ov, ACC_OV_OF_MODE,
     ACC_OV_REG_MODE_MASK_BM_ENABLE);

 pci_clear_master(pdev);

 iowrite32(0, card->addr_pciep + PCI402_PCIEP_OF_BM_ADDR_LO);
 iowrite32(0, card->addr_pciep + PCI402_PCIEP_OF_BM_ADDR_HI);

 card->ov.bmfifo.messages = NULL;
 card->ov.bmfifo.irq_cnt = NULL;
 for (i = 0; i < card->ov.active_cores; i++) {
  struct acc_core *core = &card->cores[i];

  core->bmfifo.messages = NULL;
  core->bmfifo.irq_cnt = NULL;
 }

 dma_free_coherent(&pdev->dev, PCI402_DMA_SIZE, card->dma_buf,
     card->dma_hnd);
 card->dma_buf = NULL;
}

static void pci402_unregister_core(struct acc_core *core)
{
 netdev_info(core->netdev, "unregister\n");
 unregister_candev(core->netdev);

 free_candev(core->netdev);
 core->netdev = NULL;
}

static int pci402_init_cores(struct pci_dev *pdev)
{
 struct pci402_card *card = pci_get_drvdata(pdev);
 int err;
 int i;

 for (i = 0; i < card->ov.active_cores; i++) {
  struct acc_core *core = &card->cores[i];
  struct acc_net_priv *priv;
  struct net_device *netdev;
  u32 fifo_config;

  core->addr = card->ov.addr + (i + 1) * PCI402_IO_LEN_CORE;

  fifo_config = acc_read32(core, ACC_CORE_OF_TXFIFO_CONFIG);
  core->tx_fifo_size = (fifo_config >> 24);
  if (core->tx_fifo_size <= 1) {
   pci_err(pdev, "Invalid tx_fifo_size!\n");
   err = -EINVAL;
   goto failure;
  }

  netdev = alloc_candev(sizeof(*priv), core->tx_fifo_size);
  if (!netdev) {
   err = -ENOMEM;
   goto failure;
  }
  core->netdev = netdev;

  netdev->flags |= IFF_ECHO;
  netdev->dev_port = i;
  netdev->netdev_ops = &pci402_acc_netdev_ops;
  netdev->ethtool_ops = &pci402_acc_ethtool_ops;
  SET_NETDEV_DEV(netdev, &pdev->dev);

  priv = netdev_priv(netdev);
  priv->can.clock.freq = card->ov.core_frequency;
  priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
   CAN_CTRLMODE_LISTENONLY |
   CAN_CTRLMODE_BERR_REPORTING |
   CAN_CTRLMODE_CC_LEN8_DLC;
  if (card->ov.features & ACC_OV_REG_FEAT_MASK_DAR)
   priv->can.ctrlmode_supported |= CAN_CTRLMODE_ONE_SHOT;
  if (card->ov.features & ACC_OV_REG_FEAT_MASK_CANFD)
   priv->can.bittiming_const = &pci402_bittiming_const_canfd;
  else
   priv->can.bittiming_const = &pci402_bittiming_const;
  priv->can.do_set_bittiming = acc_set_bittiming;
  priv->can.do_set_mode = acc_set_mode;
  priv->can.do_get_berr_counter = acc_get_berr_counter;

  priv->core = core;
  priv->ov = &card->ov;

  err = register_candev(netdev);
  if (err) {
   free_candev(core->netdev);
   core->netdev = NULL;
   goto failure;
  }

  netdev_info(netdev, "registered\n");
 }

 return 0;

failure:
 for (i--; i >= 0; i--)
  pci402_unregister_core(&card->cores[i]);

 return err;
}

static void pci402_finish_cores(struct pci_dev *pdev)
{
 struct pci402_card *card = pci_get_drvdata(pdev);
 int i;

 for (i = 0; i < card->ov.active_cores; i++)
  pci402_unregister_core(&card->cores[i]);
}

static int pci402_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
 struct pci402_card *card = NULL;
 int err;

 err = pci_enable_device(pdev);
 if (err)
  return err;

 card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL);
 if (!card) {
  err = -ENOMEM;
  goto failure_disable_pci;
 }

 pci_set_drvdata(pdev, card);

 err = pci_request_regions(pdev, pci_name(pdev));
 if (err)
  goto failure_disable_pci;

 card->addr = pci_iomap(pdev, PCI402_BAR, PCI402_IO_LEN_TOTAL);
 if (!card->addr) {
  err = -ENOMEM;
  goto failure_release_regions;
 }

 err = pci402_init_card(pdev);
 if (err)
  goto failure_unmap;

 err = pci402_init_dma(pdev);
 if (err)
  goto failure_unmap;

 err = pci402_init_interrupt(pdev);
 if (err)
  goto failure_finish_dma;

 err = pci402_init_cores(pdev);
 if (err)
  goto failure_finish_interrupt;

 return 0;

failure_finish_interrupt:
 pci402_finish_interrupt(pdev);

failure_finish_dma:
 pci402_finish_dma(pdev);

failure_unmap:
 pci_iounmap(pdev, card->addr);

failure_release_regions:
 pci_release_regions(pdev);

failure_disable_pci:
 pci_disable_device(pdev);

 return err;
}

static void pci402_remove(struct pci_dev *pdev)
{
 struct pci402_card *card = pci_get_drvdata(pdev);

 pci402_finish_interrupt(pdev);
 pci402_finish_cores(pdev);
 pci402_finish_dma(pdev);
 pci_iounmap(pdev, card->addr);
 pci_release_regions(pdev);
 pci_disable_device(pdev);
}

static const struct pci_device_id pci402_tbl[] = {
 {
  .vendor = PCI_VENDOR_ID_ESDGMBH,
  .device = ESD_PCI_DEVICE_ID_PCIE402,
  .subvendor = PCI_VENDOR_ID_ESDGMBH,
  .subdevice = PCI_ANY_ID,
 },
 { 0, }
};
MODULE_DEVICE_TABLE(pci, pci402_tbl);

static struct pci_driver pci402_driver = {
 .name = KBUILD_MODNAME,
 .id_table = pci402_tbl,
 .probe = pci402_probe,
 .remove = pci402_remove,
};
module_pci_driver(pci402_driver);

MODULE_DESCRIPTION("Socket-CAN driver for esd CAN 402 card family with esdACC core on PCIe");
MODULE_AUTHOR("Thomas Körper ");
MODULE_AUTHOR("Stefan Mätje ");
MODULE_LICENSE("GPL");

Messung V0.5
C=94 H=90 G=91

¤ Dauer der Verarbeitung: 0.4 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.