// SPDX-License-Identifier: GPL-2.0-or-later
/*******************************************************************************
*
* CTU CAN FD IP Core
*
* Copyright ( C ) 2015 - 2018 Ondrej Ille < ondrej . ille @ gmail . com > FEE CTU
* Copyright ( C ) 2018 - 2021 Ondrej Ille < ondrej . ille @ gmail . com > self - funded
* Copyright ( C ) 2018 - 2019 Martin Jerabek < martin . jerabek01 @ gmail . com > FEE CTU
* Copyright ( C ) 2018 - 2022 Pavel Pisa < pisa @ cmp . felk . cvut . cz > FEE CTU / self - funded
*
* Project advisors :
* Jiri Novak < jnovak @ fel . cvut . cz >
* Pavel Pisa < pisa @ cmp . felk . cvut . cz >
*
* Department of Measurement ( http : //meas.fel.cvut.cz/)
* Faculty of Electrical Engineering ( http : //www.fel.cvut.cz)
* Czech Technical University ( http : //www.cvut.cz/)
******************************************************************************/
#include <linux/module.h>
#include <linux/pci.h>
#include "ctucanfd.h"
#ifndef PCI_DEVICE_DATA
#define PCI_DEVICE_DATA(vend, dev, data) \
.vendor = PCI_VENDOR_ID_## vend, \
.device = PCI_DEVICE_ID_## vend## _## dev, \
.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 0 , 0 , \
.driver_data = (kernel_ulong_t)(data)
#endif
#ifndef PCI_VENDOR_ID_TEDIA
#define PCI_VENDOR_ID_TEDIA 0 x1760
#endif
#ifndef PCI_DEVICE_ID_TEDIA_CTUCAN_VER21
#define PCI_DEVICE_ID_TEDIA_CTUCAN_VER21 0 xff00
#endif
#define CTUCAN_BAR0_CTUCAN_ID 0 x0000
#define CTUCAN_BAR0_CRA_BASE 0 x4000
#define CYCLONE_IV_CRA_A2P_IE (0 x0050)
#define CTUCAN_WITHOUT_CTUCAN_ID 0
#define CTUCAN_WITH_CTUCAN_ID 1
struct ctucan_pci_board_data {
void __iomem *bar0_base;
void __iomem *cra_base;
void __iomem *bar1_base;
struct list_head ndev_list_head;
int use_msi;
};
static struct ctucan_pci_board_data *ctucan_pci_get_bdata(struct pci_dev *pdev)
{
return (struct ctucan_pci_board_data *)pci_get_drvdata(pdev);
}
static void ctucan_pci_set_drvdata(struct device *dev,
struct net_device *ndev)
{
struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
struct ctucan_priv *priv = netdev_priv(ndev);
struct ctucan_pci_board_data *bdata = ctucan_pci_get_bdata(pdev);
list_add(&priv->peers_on_pdev, &bdata->ndev_list_head);
priv->irq_flags = IRQF_SHARED;
}
/**
* ctucan_pci_probe - PCI registration call
* @ pdev : Handle to the pci device structure
* @ ent : Pointer to the entry from ctucan_pci_tbl
*
* This function does all the memory allocation and registration for the CAN
* device .
*
* Return : 0 on success and failure value on error
*/
static int ctucan_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct device *dev = &pdev->dev;
unsigned long driver_data = ent->driver_data;
struct ctucan_pci_board_data *bdata;
void __iomem *addr;
void __iomem *cra_addr;
void __iomem *bar0_base;
u32 cra_a2p_ie;
u32 ctucan_id = 0 ;
int ret;
unsigned int ntxbufs;
unsigned int num_cores = 1 ;
unsigned int core_i = 0 ;
int irq;
int msi_ok = 0 ;
ret = pci_enable_device(pdev);
if (ret) {
dev_err(dev, "pci_enable_device FAILED\n" );
goto err;
}
ret = pci_request_regions(pdev, KBUILD_MODNAME);
if (ret) {
dev_err(dev, "pci_request_regions FAILED\n" );
goto err_disable_device;
}
ret = pci_enable_msi(pdev);
if (!ret) {
dev_info(dev, "MSI enabled\n" );
pci_set_master(pdev);
msi_ok = 1 ;
}
dev_info(dev, "ctucan BAR0 0x%08llx 0x%08llx\n" ,
(long long )pci_resource_start(pdev, 0 ),
(long long )pci_resource_len(pdev, 0 ));
dev_info(dev, "ctucan BAR1 0x%08llx 0x%08llx\n" ,
(long long )pci_resource_start(pdev, 1 ),
(long long )pci_resource_len(pdev, 1 ));
addr = pci_iomap(pdev, 1 , pci_resource_len(pdev, 1 ));
if (!addr) {
dev_err(dev, "PCI BAR 1 cannot be mapped\n" );
ret = -ENOMEM;
goto err_release_regions;
}
/* Cyclone IV PCI Express Control Registers Area */
bar0_base = pci_iomap(pdev, 0 , pci_resource_len(pdev, 0 ));
if (!bar0_base) {
dev_err(dev, "PCI BAR 0 cannot be mapped\n" );
ret = -EIO;
goto err_pci_iounmap_bar1;
}
if (driver_data == CTUCAN_WITHOUT_CTUCAN_ID) {
cra_addr = bar0_base;
num_cores = 2 ;
} else {
cra_addr = bar0_base + CTUCAN_BAR0_CRA_BASE;
ctucan_id = ioread32(bar0_base + CTUCAN_BAR0_CTUCAN_ID);
dev_info(dev, "ctucan_id 0x%08lx\n" , (unsigned long )ctucan_id);
num_cores = ctucan_id & 0 xf;
}
irq = pdev->irq;
ntxbufs = 4 ;
bdata = kzalloc(sizeof (*bdata), GFP_KERNEL);
if (!bdata) {
ret = -ENOMEM;
goto err_pci_iounmap_bar0;
}
INIT_LIST_HEAD(&bdata->ndev_list_head);
bdata->bar0_base = bar0_base;
bdata->cra_base = cra_addr;
bdata->bar1_base = addr;
bdata->use_msi = msi_ok;
pci_set_drvdata(pdev, bdata);
ret = ctucan_probe_common(dev, addr, irq, ntxbufs, 100000000 ,
0 , ctucan_pci_set_drvdata);
if (ret < 0 )
goto err_free_board;
core_i++;
while (core_i < num_cores) {
addr += 0 x4000;
ret = ctucan_probe_common(dev, addr, irq, ntxbufs, 100000000 ,
0 , ctucan_pci_set_drvdata);
if (ret < 0 ) {
dev_info(dev, "CTU CAN FD core %d initialization failed\n" ,
core_i);
break ;
}
core_i++;
}
/* enable interrupt in
* Avalon - MM to PCI Express Interrupt Enable Register
*/
cra_a2p_ie = ioread32(cra_addr + CYCLONE_IV_CRA_A2P_IE);
dev_info(dev, "cra_a2p_ie 0x%08x\n" , cra_a2p_ie);
cra_a2p_ie |= 1 ;
iowrite32(cra_a2p_ie, cra_addr + CYCLONE_IV_CRA_A2P_IE);
cra_a2p_ie = ioread32(cra_addr + CYCLONE_IV_CRA_A2P_IE);
dev_info(dev, "cra_a2p_ie 0x%08x\n" , cra_a2p_ie);
return 0 ;
err_free_board:
pci_set_drvdata(pdev, NULL);
kfree(bdata);
err_pci_iounmap_bar0:
pci_iounmap(pdev, cra_addr);
err_pci_iounmap_bar1:
pci_iounmap(pdev, addr);
err_release_regions:
if (msi_ok)
pci_disable_msi(pdev);
pci_release_regions(pdev);
err_disable_device:
pci_disable_device(pdev);
err:
return ret;
}
/**
* ctucan_pci_remove - Unregister the device after releasing the resources
* @ pdev : Handle to the pci device structure
*
* This function frees all the resources allocated to the device .
* Return : 0 always
*/
static void ctucan_pci_remove(struct pci_dev *pdev)
{
struct net_device *ndev;
struct ctucan_priv *priv = NULL;
struct ctucan_pci_board_data *bdata = ctucan_pci_get_bdata(pdev);
dev_dbg(&pdev->dev, "ctucan_remove" );
if (!bdata) {
dev_err(&pdev->dev, "%s: no list of devices\n" , __func__);
return ;
}
/* disable interrupt in
* Avalon - MM to PCI Express Interrupt Enable Register
*/
if (bdata->cra_base)
iowrite32(0 , bdata->cra_base + CYCLONE_IV_CRA_A2P_IE);
while ((priv = list_first_entry_or_null(&bdata->ndev_list_head, struct ctucan_priv,
peers_on_pdev)) != NULL) {
ndev = priv->can.dev;
unregister_candev(ndev);
netif_napi_del(&priv->napi);
list_del_init(&priv->peers_on_pdev);
free_candev(ndev);
}
pci_iounmap(pdev, bdata->bar1_base);
if (bdata->use_msi)
pci_disable_msi(pdev);
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_iounmap(pdev, bdata->bar0_base);
pci_set_drvdata(pdev, NULL);
kfree(bdata);
}
static SIMPLE_DEV_PM_OPS(ctucan_pci_pm_ops, ctucan_suspend, ctucan_resume);
static const struct pci_device_id ctucan_pci_tbl[] = {
{PCI_DEVICE_DATA(TEDIA, CTUCAN_VER21,
CTUCAN_WITH_CTUCAN_ID)},
{},
};
static struct pci_driver ctucan_pci_driver = {
.name = KBUILD_MODNAME,
.id_table = ctucan_pci_tbl,
.probe = ctucan_pci_probe,
.remove = ctucan_pci_remove,
.driver.pm = &ctucan_pci_pm_ops,
};
module_pci_driver(ctucan_pci_driver);
MODULE_LICENSE("GPL" );
MODULE_AUTHOR("Pavel Pisa <pisa@cmp.felk.cvut.cz>" );
MODULE_DESCRIPTION("CTU CAN FD for PCI bus" );
Messung V0.5 in Prozent C=99 H=83 G=91
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland