Merge patch series "can: esd: add support for esd GmbH PCIe/402 CAN interface"
Stefan Mätje <stefan.maetje@esd.eu> says: The purpose of this patch is to introduce a new CAN driver to support the esd GmbH 402 family of CAN interface boards. The hardware design is based on a CAN controller implemented in a FPGA attached to a PCIe link. More information on these boards can be found following the links included in the commit message. This patch supports all boards but will operate the CAN-FD capable boards only in Classic-CAN mode. The CAN-FD support will be added when the initial patch has stabilized. The patch is reuses the previous work of my former colleague: Link: https://lore.kernel.org/linux-can/1426592308-23817-1-git-send-email-thomas.koerper@esd.eu The patch is based on the linux-can-next main branch. Changed in v11: No functional, only editorial changes due to feedback on v10. - Make lifetime of macros used for hardware timestamp calculation very short by #undef-ing them after use. - Fixed insertion order of new entry in MAINTAINERS file. Changed in v10: Most changes due to feedback by Vincent Mailhol https://lore.kernel.org/linux-can/CAMZ6RqLOAC930GNOU+pWuoi6FgYwFOuFrSyAzVjvE2fuVgy8oA@mail.gmail.com - Add support for ethtool operations by using default operations provided by the can_dev module for drivers with hardware time stamp support. - Factor out core unregistration into pci402_unregister_core(). - Factor out getting next TX fifo index into acc_tx_fifo_next(). - Stop counting alloc_can_err_skb() failures in rx_dropped statistic. - Add CAN_ERR_CNT flag in CAN error frames as needed. - Rework function acc_reset_fpga(). To clear I^2C bus enable bit is not necessary after FPGA reset. - Simplify struct acc_bmmsg_rxtxdone layout. - Additional non functional changes due to feedback by Vincent - Some spelling corrections: ESDACC -> esdACC Changes in v9: - Fix returning success error code in case of allocation failure in pci402_probe(). Changes in v8: - Rebased to 6.6-rc2 on linux-can-next branch main Changes in v7: - Numerous changes. Find the quoted with inline comments about changes below after the changes list. Stuff that I don't understand and where I have questions is marked with ????. Unfortunately I will be AFK till 28th of November. Changes in v6: - Fixed the statistic handling of RX overrun errors and increase net_device_stats::rx_errors instead of net_device_stats::rx_dropped. - Added a patch to not increase rx statistics when generating a CAN rx error message frame as suggested on the linux-can list. - Added a patch to not not increase rx_bytes statistics for RTR frames as suggested on the linux-can list. The last two patches change the statistics handling from the previous style used in other drivers to the newly suggested one. Changes in v5: - Added the initialization for netdev::dev_port as it is implemented for another CAN driver. See https://lore.kernel.org/linux-can/20211026180553.1953189-1-mailhol.vincent@wanadoo.fr Changes in v4: - Fixed the build failure on ARCH=arm64 that was found by the Intel kernel test robot. See https://lore.kernel.org/linux-can/202109120608.7ZbQXkRh-lkp@intel.com Removed error monitoring code that used GCC's built-in compiler functions for atomic access (__sync_* functions). GCC versions after 9 (tested with "gcc-10 (Ubuntu 10.3.0-1ubuntu1~20.04)") don't implement the intrinsic atomic as in-line code but call "__aarch64_ldadd4_acq_rel" on arm64. This GCC support function is not exported by the kernel and therefore the module build post-processing fails. Removed that code because the error monitoring never showed a problem during the development this year. Changes in v3: - Rework the bus-off restart logic in acc_set_mode() and handle_core_msg_errstatechange() to call netif_wake_queue() from the error active event. - Changed pci402_init_card() to allocate a variable sized array of struct acc_core using devm_kcalloc() instead of using a fixed size array in struct pci402_card. - Changed handle_core_msg_txabort() to release aborted TX frames in TX FIFO order. - Fixed the acc_close() function to abort all pending TX request in esdACC controller. - Fixed counting of transmit aborts in handle_core_msg_txabort(). It is now done like in can_flush_echo_skb(). - Fixed handle_core_msg_buserr() to create error frames including the CAN RX and TX error counters that were missing. - Fixed acc_set_bittiming() neither to touch LOM mode setting of esdACC controller nor to enter or leave RESET mode. The esdACC controller is going active on the CAN bus in acc_open() and is going inactive (RESET mode) again in acc_close(). - Rely on the automatic release of memory fetched by devm_kzalloc(). But still use devm_irq_free() explicitely to make sure that the interrupt handler is disconnected at that point. This avoids a possible crash in non-MSI mode due to the IRQ triggered by another device on the same PCI IRQ line. - Changed to use DMA map API instead of pci_*_consistent compatibility wrappers. - Fixed stale email references and updated copyright information. - Removed any traces of future CAN-FD support. Changes in v2: - Avoid warning triggered by -Wshift-count-overflow on architectures with 32-bit dma_addr_t. - Fixed Makefile not to build the kernel module always. Doing this renamed esd402_pci.c to esd_402_pci-core.c as recommended by Marc. previous versions: v1 - https://lore.kernel.org/linux-can/20210728203647.15240-1-Stefan.Maetje@esd.eu v2 - https://lore.kernel.org/linux-can/20210730173805.3926-1-Stefan.Maetje@esd.eu v3 - https://lore.kernel.org/linux-can/20210908164640.23243-1-stefan.maetje@esd.eu v4 - https://lore.kernel.org/linux-can/20210916172152.5127-1-stefan.maetje@esd.eu v5 - https://lore.kernel.org/linux-can/20211109155326.2608822-1-stefan.maetje@esd.eu v6 - https://lore.kernel.org/linux-can/20211201220328.3079270-1-stefan.maetje@esd.eu v7 - https://lore.kernel.org/linux-can/20221106224156.3619334-1-stefan.maetje@esd.eu v8 - https://lore.kernel.org/linux-can/20231025141635.1459606-1-stefan.maetje@esd.eu v9 - https://lore.kernel.org/linux-can/20231107184103.2802678-1-stefan.maetje@esd.eu v10 - https://lore.kernel.org/linux-can/20231120175657.4070921-1-stefan.maetje@esd.eu Link: https://lore.kernel.org/all/20231122160211.2110448-1-stefan.maetje@esd.eu Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
commit
dbd86e47fd
@ -7887,6 +7887,13 @@ S: Maintained
|
||||
F: include/linux/errseq.h
|
||||
F: lib/errseq.c
|
||||
|
||||
ESD CAN NETWORK DRIVERS
|
||||
M: Stefan Mätje <stefan.maetje@esd.eu>
|
||||
R: socketcan@esd.eu
|
||||
L: linux-can@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/net/can/esd/
|
||||
|
||||
ESD CAN/USB DRIVERS
|
||||
M: Frank Jungclaus <frank.jungclaus@esd.eu>
|
||||
R: socketcan@esd.eu
|
||||
|
@ -218,6 +218,7 @@ config CAN_XILINXCAN
|
||||
source "drivers/net/can/c_can/Kconfig"
|
||||
source "drivers/net/can/cc770/Kconfig"
|
||||
source "drivers/net/can/ctucanfd/Kconfig"
|
||||
source "drivers/net/can/esd/Kconfig"
|
||||
source "drivers/net/can/ifi_canfd/Kconfig"
|
||||
source "drivers/net/can/m_can/Kconfig"
|
||||
source "drivers/net/can/mscan/Kconfig"
|
||||
|
@ -8,6 +8,7 @@ obj-$(CONFIG_CAN_VXCAN) += vxcan.o
|
||||
obj-$(CONFIG_CAN_SLCAN) += slcan/
|
||||
|
||||
obj-y += dev/
|
||||
obj-y += esd/
|
||||
obj-y += rcar/
|
||||
obj-y += spi/
|
||||
obj-y += usb/
|
||||
|
12
drivers/net/can/esd/Kconfig
Normal file
12
drivers/net/can/esd/Kconfig
Normal file
@ -0,0 +1,12 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config CAN_ESD_402_PCI
|
||||
tristate "esd electronics gmbh CAN-PCI(e)/402 family"
|
||||
depends on PCI && HAS_DMA
|
||||
help
|
||||
Support for C402 card family from esd electronics gmbh.
|
||||
This card family is based on the ESDACC CAN controller and
|
||||
available in several form factors: PCI, PCIe, PCIe Mini,
|
||||
M.2 PCIe, CPCIserial, PMC, XMC (see https://esd.eu/en)
|
||||
|
||||
This driver can also be built as a module. In this case the
|
||||
module will be called esd_402_pci.
|
7
drivers/net/can/esd/Makefile
Normal file
7
drivers/net/can/esd/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Makefile for esd gmbh ESDACC controller driver
|
||||
#
|
||||
esd_402_pci-objs := esdacc.o esd_402_pci-core.o
|
||||
|
||||
obj-$(CONFIG_CAN_ESD_402_PCI) += esd_402_pci.o
|
514
drivers/net/can/esd/esd_402_pci-core.c
Normal file
514
drivers/net/can/esd/esd_402_pci-core.c
Normal file
@ -0,0 +1,514 @@
|
||||
// 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.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
|
||||
CAN_CTRLMODE_LISTENONLY |
|
||||
CAN_CTRLMODE_BERR_REPORTING |
|
||||
CAN_CTRLMODE_CC_LEN8_DLC;
|
||||
|
||||
priv->can.clock.freq = card->ov.core_frequency;
|
||||
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 <socketcan@esd.eu>");
|
||||
MODULE_AUTHOR("Stefan Mätje <stefan.maetje@esd.eu>");
|
||||
MODULE_LICENSE("GPL");
|
764
drivers/net/can/esd/esdacc.c
Normal file
764
drivers/net/can/esd/esdacc.c
Normal file
@ -0,0 +1,764 @@
|
||||
// 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 "esdacc.h"
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ktime.h>
|
||||
|
||||
/* esdACC ID register layout */
|
||||
#define ACC_ID_ID_MASK GENMASK(28, 0)
|
||||
#define ACC_ID_EFF_FLAG BIT(29)
|
||||
|
||||
/* esdACC DLC register layout */
|
||||
#define ACC_DLC_DLC_MASK GENMASK(3, 0)
|
||||
#define ACC_DLC_RTR_FLAG BIT(4)
|
||||
#define ACC_DLC_TXD_FLAG BIT(5)
|
||||
|
||||
/* ecc value of esdACC equals SJA1000's ECC register */
|
||||
#define ACC_ECC_SEG 0x1f
|
||||
#define ACC_ECC_DIR 0x20
|
||||
#define ACC_ECC_BIT 0x00
|
||||
#define ACC_ECC_FORM 0x40
|
||||
#define ACC_ECC_STUFF 0x80
|
||||
#define ACC_ECC_MASK 0xc0
|
||||
|
||||
/* esdACC Status Register bits. Unused bits not documented. */
|
||||
#define ACC_REG_STATUS_MASK_STATUS_ES BIT(17)
|
||||
#define ACC_REG_STATUS_MASK_STATUS_EP BIT(18)
|
||||
#define ACC_REG_STATUS_MASK_STATUS_BS BIT(19)
|
||||
|
||||
/* esdACC Overview Module BM_IRQ_Mask register related defines */
|
||||
/* Two bit wide command masks to mask or unmask a single core IRQ */
|
||||
#define ACC_BM_IRQ_UNMASK BIT(0)
|
||||
#define ACC_BM_IRQ_MASK (ACC_BM_IRQ_UNMASK << 1)
|
||||
/* Command to unmask all IRQ sources. Created by shifting
|
||||
* and oring the two bit wide ACC_BM_IRQ_UNMASK 16 times.
|
||||
*/
|
||||
#define ACC_BM_IRQ_UNMASK_ALL 0x55555555U
|
||||
|
||||
static void acc_resetmode_enter(struct acc_core *core)
|
||||
{
|
||||
acc_set_bits(core, ACC_CORE_OF_CTRL_MODE,
|
||||
ACC_REG_CONTROL_MASK_MODE_RESETMODE);
|
||||
|
||||
/* Read back reset mode bit to flush PCI write posting */
|
||||
acc_resetmode_entered(core);
|
||||
}
|
||||
|
||||
static void acc_resetmode_leave(struct acc_core *core)
|
||||
{
|
||||
acc_clear_bits(core, ACC_CORE_OF_CTRL_MODE,
|
||||
ACC_REG_CONTROL_MASK_MODE_RESETMODE);
|
||||
|
||||
/* Read back reset mode bit to flush PCI write posting */
|
||||
acc_resetmode_entered(core);
|
||||
}
|
||||
|
||||
static void acc_txq_put(struct acc_core *core, u32 acc_id, u8 acc_dlc,
|
||||
const void *data)
|
||||
{
|
||||
acc_write32_noswap(core, ACC_CORE_OF_TXFIFO_DATA_1,
|
||||
*((const u32 *)(data + 4)));
|
||||
acc_write32_noswap(core, ACC_CORE_OF_TXFIFO_DATA_0,
|
||||
*((const u32 *)data));
|
||||
acc_write32(core, ACC_CORE_OF_TXFIFO_DLC, acc_dlc);
|
||||
/* CAN id must be written at last. This write starts TX. */
|
||||
acc_write32(core, ACC_CORE_OF_TXFIFO_ID, acc_id);
|
||||
}
|
||||
|
||||
static u8 acc_tx_fifo_next(struct acc_core *core, u8 tx_fifo_idx)
|
||||
{
|
||||
++tx_fifo_idx;
|
||||
if (tx_fifo_idx >= core->tx_fifo_size)
|
||||
tx_fifo_idx = 0U;
|
||||
return tx_fifo_idx;
|
||||
}
|
||||
|
||||
/* Convert timestamp from esdACC time stamp ticks to ns
|
||||
*
|
||||
* The conversion factor ts2ns from time stamp counts to ns is basically
|
||||
* ts2ns = NSEC_PER_SEC / timestamp_frequency
|
||||
*
|
||||
* We handle here only a fixed timestamp frequency of 80MHz. The
|
||||
* resulting ts2ns factor would be 12.5.
|
||||
*
|
||||
* At the end we multiply by 12 and add the half of the HW timestamp
|
||||
* to get a multiplication by 12.5. This way any overflow is
|
||||
* avoided until ktime_t itself overflows.
|
||||
*/
|
||||
#define ACC_TS_FACTOR (NSEC_PER_SEC / ACC_TS_FREQ_80MHZ)
|
||||
#define ACC_TS_80MHZ_SHIFT 1
|
||||
|
||||
static ktime_t acc_ts2ktime(struct acc_ov *ov, u64 ts)
|
||||
{
|
||||
u64 ns;
|
||||
|
||||
ns = (ts * ACC_TS_FACTOR) + (ts >> ACC_TS_80MHZ_SHIFT);
|
||||
|
||||
return ns_to_ktime(ns);
|
||||
}
|
||||
|
||||
#undef ACC_TS_FACTOR
|
||||
#undef ACC_TS_80MHZ_SHIFT
|
||||
|
||||
void acc_init_ov(struct acc_ov *ov, struct device *dev)
|
||||
{
|
||||
u32 temp;
|
||||
|
||||
temp = acc_ov_read32(ov, ACC_OV_OF_VERSION);
|
||||
ov->version = temp;
|
||||
ov->features = (temp >> 16);
|
||||
|
||||
temp = acc_ov_read32(ov, ACC_OV_OF_INFO);
|
||||
ov->total_cores = temp;
|
||||
ov->active_cores = (temp >> 8);
|
||||
|
||||
ov->core_frequency = acc_ov_read32(ov, ACC_OV_OF_CANCORE_FREQ);
|
||||
ov->timestamp_frequency = acc_ov_read32(ov, ACC_OV_OF_TS_FREQ_LO);
|
||||
|
||||
/* Depending on esdACC feature NEW_PSC enable the new prescaler
|
||||
* or adjust core_frequency according to the implicit division by 2.
|
||||
*/
|
||||
if (ov->features & ACC_OV_REG_FEAT_MASK_NEW_PSC) {
|
||||
acc_ov_set_bits(ov, ACC_OV_OF_MODE,
|
||||
ACC_OV_REG_MODE_MASK_NEW_PSC_ENABLE);
|
||||
} else {
|
||||
ov->core_frequency /= 2;
|
||||
}
|
||||
|
||||
dev_dbg(dev,
|
||||
"esdACC v%u, freq: %u/%u, feat/strap: 0x%x/0x%x, cores: %u/%u\n",
|
||||
ov->version, ov->core_frequency, ov->timestamp_frequency,
|
||||
ov->features, acc_ov_read32(ov, ACC_OV_OF_INFO) >> 16,
|
||||
ov->active_cores, ov->total_cores);
|
||||
}
|
||||
|
||||
void acc_init_bm_ptr(struct acc_ov *ov, struct acc_core *cores, const void *mem)
|
||||
{
|
||||
unsigned int u;
|
||||
|
||||
/* DMA buffer layout as follows where N is the number of CAN cores
|
||||
* implemented in the FPGA, i.e. N = ov->total_cores
|
||||
*
|
||||
* Section Layout Section size
|
||||
* ----------------------------------------------
|
||||
* FIFO Card/Overview ACC_CORE_DMABUF_SIZE
|
||||
* FIFO Core0 ACC_CORE_DMABUF_SIZE
|
||||
* ... ...
|
||||
* FIFO CoreN ACC_CORE_DMABUF_SIZE
|
||||
* irq_cnt Card/Overview sizeof(u32)
|
||||
* irq_cnt Core0 sizeof(u32)
|
||||
* ... ...
|
||||
* irq_cnt CoreN sizeof(u32)
|
||||
*/
|
||||
ov->bmfifo.messages = mem;
|
||||
ov->bmfifo.irq_cnt = mem + (ov->total_cores + 1U) * ACC_CORE_DMABUF_SIZE;
|
||||
|
||||
for (u = 0U; u < ov->active_cores; u++) {
|
||||
struct acc_core *core = &cores[u];
|
||||
|
||||
core->bmfifo.messages = mem + (u + 1U) * ACC_CORE_DMABUF_SIZE;
|
||||
core->bmfifo.irq_cnt = ov->bmfifo.irq_cnt + (u + 1U);
|
||||
}
|
||||
}
|
||||
|
||||
int acc_open(struct net_device *netdev)
|
||||
{
|
||||
struct acc_net_priv *priv = netdev_priv(netdev);
|
||||
struct acc_core *core = priv->core;
|
||||
u32 tx_fifo_status;
|
||||
u32 ctrl_mode;
|
||||
int err;
|
||||
|
||||
/* Retry to enter RESET mode if out of sync. */
|
||||
if (priv->can.state != CAN_STATE_STOPPED) {
|
||||
netdev_warn(netdev, "Entered %s() with bad can.state: %s\n",
|
||||
__func__, can_get_state_str(priv->can.state));
|
||||
acc_resetmode_enter(core);
|
||||
priv->can.state = CAN_STATE_STOPPED;
|
||||
}
|
||||
|
||||
err = open_candev(netdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ctrl_mode = ACC_REG_CONTROL_MASK_IE_RXTX |
|
||||
ACC_REG_CONTROL_MASK_IE_TXERROR |
|
||||
ACC_REG_CONTROL_MASK_IE_ERRWARN |
|
||||
ACC_REG_CONTROL_MASK_IE_OVERRUN |
|
||||
ACC_REG_CONTROL_MASK_IE_ERRPASS;
|
||||
|
||||
if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
|
||||
ctrl_mode |= ACC_REG_CONTROL_MASK_IE_BUSERR;
|
||||
|
||||
if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
|
||||
ctrl_mode |= ACC_REG_CONTROL_MASK_MODE_LOM;
|
||||
|
||||
acc_set_bits(core, ACC_CORE_OF_CTRL_MODE, ctrl_mode);
|
||||
|
||||
acc_resetmode_leave(core);
|
||||
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
|
||||
/* Resync TX FIFO indices to HW state after (re-)start. */
|
||||
tx_fifo_status = acc_read32(core, ACC_CORE_OF_TXFIFO_STATUS);
|
||||
core->tx_fifo_head = tx_fifo_status & 0xff;
|
||||
core->tx_fifo_tail = (tx_fifo_status >> 8) & 0xff;
|
||||
|
||||
netif_start_queue(netdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acc_close(struct net_device *netdev)
|
||||
{
|
||||
struct acc_net_priv *priv = netdev_priv(netdev);
|
||||
struct acc_core *core = priv->core;
|
||||
|
||||
acc_clear_bits(core, ACC_CORE_OF_CTRL_MODE,
|
||||
ACC_REG_CONTROL_MASK_IE_RXTX |
|
||||
ACC_REG_CONTROL_MASK_IE_TXERROR |
|
||||
ACC_REG_CONTROL_MASK_IE_ERRWARN |
|
||||
ACC_REG_CONTROL_MASK_IE_OVERRUN |
|
||||
ACC_REG_CONTROL_MASK_IE_ERRPASS |
|
||||
ACC_REG_CONTROL_MASK_IE_BUSERR);
|
||||
|
||||
netif_stop_queue(netdev);
|
||||
acc_resetmode_enter(core);
|
||||
priv->can.state = CAN_STATE_STOPPED;
|
||||
|
||||
/* Mark pending TX requests to be aborted after controller restart. */
|
||||
acc_write32(core, ACC_CORE_OF_TX_ABORT_MASK, 0xffff);
|
||||
|
||||
/* ACC_REG_CONTROL_MASK_MODE_LOM is only accessible in RESET mode */
|
||||
acc_clear_bits(core, ACC_CORE_OF_CTRL_MODE,
|
||||
ACC_REG_CONTROL_MASK_MODE_LOM);
|
||||
|
||||
close_candev(netdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
netdev_tx_t acc_start_xmit(struct sk_buff *skb, struct net_device *netdev)
|
||||
{
|
||||
struct acc_net_priv *priv = netdev_priv(netdev);
|
||||
struct acc_core *core = priv->core;
|
||||
struct can_frame *cf = (struct can_frame *)skb->data;
|
||||
u8 tx_fifo_head = core->tx_fifo_head;
|
||||
int fifo_usage;
|
||||
u32 acc_id;
|
||||
u8 acc_dlc;
|
||||
|
||||
if (can_dropped_invalid_skb(netdev, skb))
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
/* Access core->tx_fifo_tail only once because it may be changed
|
||||
* from the interrupt level.
|
||||
*/
|
||||
fifo_usage = tx_fifo_head - core->tx_fifo_tail;
|
||||
if (fifo_usage < 0)
|
||||
fifo_usage += core->tx_fifo_size;
|
||||
|
||||
if (fifo_usage >= core->tx_fifo_size - 1) {
|
||||
netdev_err(core->netdev,
|
||||
"BUG: TX ring full when queue awake!\n");
|
||||
netif_stop_queue(netdev);
|
||||
return NETDEV_TX_BUSY;
|
||||
}
|
||||
|
||||
if (fifo_usage == core->tx_fifo_size - 2)
|
||||
netif_stop_queue(netdev);
|
||||
|
||||
acc_dlc = can_get_cc_dlc(cf, priv->can.ctrlmode);
|
||||
if (cf->can_id & CAN_RTR_FLAG)
|
||||
acc_dlc |= ACC_DLC_RTR_FLAG;
|
||||
|
||||
if (cf->can_id & CAN_EFF_FLAG) {
|
||||
acc_id = cf->can_id & CAN_EFF_MASK;
|
||||
acc_id |= ACC_ID_EFF_FLAG;
|
||||
} else {
|
||||
acc_id = cf->can_id & CAN_SFF_MASK;
|
||||
}
|
||||
|
||||
can_put_echo_skb(skb, netdev, core->tx_fifo_head, 0);
|
||||
|
||||
core->tx_fifo_head = acc_tx_fifo_next(core, tx_fifo_head);
|
||||
|
||||
acc_txq_put(core, acc_id, acc_dlc, cf->data);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
int acc_get_berr_counter(const struct net_device *netdev,
|
||||
struct can_berr_counter *bec)
|
||||
{
|
||||
struct acc_net_priv *priv = netdev_priv(netdev);
|
||||
u32 core_status = acc_read32(priv->core, ACC_CORE_OF_STATUS);
|
||||
|
||||
bec->txerr = (core_status >> 8) & 0xff;
|
||||
bec->rxerr = core_status & 0xff;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acc_set_mode(struct net_device *netdev, enum can_mode mode)
|
||||
{
|
||||
struct acc_net_priv *priv = netdev_priv(netdev);
|
||||
|
||||
switch (mode) {
|
||||
case CAN_MODE_START:
|
||||
/* Paranoid FIFO index check. */
|
||||
{
|
||||
const u32 tx_fifo_status =
|
||||
acc_read32(priv->core, ACC_CORE_OF_TXFIFO_STATUS);
|
||||
const u8 hw_fifo_head = tx_fifo_status;
|
||||
|
||||
if (hw_fifo_head != priv->core->tx_fifo_head ||
|
||||
hw_fifo_head != priv->core->tx_fifo_tail) {
|
||||
netdev_warn(netdev,
|
||||
"TX FIFO mismatch: T %2u H %2u; TFHW %#08x\n",
|
||||
priv->core->tx_fifo_tail,
|
||||
priv->core->tx_fifo_head,
|
||||
tx_fifo_status);
|
||||
}
|
||||
}
|
||||
acc_resetmode_leave(priv->core);
|
||||
/* To leave the bus-off state the esdACC controller begins
|
||||
* here a grace period where it counts 128 "idle conditions" (each
|
||||
* of 11 consecutive recessive bits) on the bus as required
|
||||
* by the CAN spec.
|
||||
*
|
||||
* During this time the TX FIFO may still contain already
|
||||
* aborted "zombie" frames that are only drained from the FIFO
|
||||
* at the end of the grace period.
|
||||
*
|
||||
* To not to interfere with this drain process we don't
|
||||
* call netif_wake_queue() here. When the controller reaches
|
||||
* the error-active state again, it informs us about that
|
||||
* with an acc_bmmsg_errstatechange message. Then
|
||||
* netif_wake_queue() is called from
|
||||
* handle_core_msg_errstatechange() instead.
|
||||
*/
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acc_set_bittiming(struct net_device *netdev)
|
||||
{
|
||||
struct acc_net_priv *priv = netdev_priv(netdev);
|
||||
const struct can_bittiming *bt = &priv->can.bittiming;
|
||||
u32 brp;
|
||||
u32 btr;
|
||||
|
||||
if (priv->ov->features & ACC_OV_REG_FEAT_MASK_CANFD) {
|
||||
u32 fbtr = 0;
|
||||
|
||||
netdev_dbg(netdev, "bit timing: brp %u, prop %u, ph1 %u ph2 %u, sjw %u\n",
|
||||
bt->brp, bt->prop_seg,
|
||||
bt->phase_seg1, bt->phase_seg2, bt->sjw);
|
||||
|
||||
brp = FIELD_PREP(ACC_REG_BRP_FD_MASK_BRP, bt->brp - 1);
|
||||
|
||||
btr = FIELD_PREP(ACC_REG_BTR_FD_MASK_TSEG1, bt->phase_seg1 + bt->prop_seg - 1);
|
||||
btr |= FIELD_PREP(ACC_REG_BTR_FD_MASK_TSEG2, bt->phase_seg2 - 1);
|
||||
btr |= FIELD_PREP(ACC_REG_BTR_FD_MASK_SJW, bt->sjw - 1);
|
||||
|
||||
/* Keep order of accesses to ACC_CORE_OF_BRP and ACC_CORE_OF_BTR. */
|
||||
acc_write32(priv->core, ACC_CORE_OF_BRP, brp);
|
||||
acc_write32(priv->core, ACC_CORE_OF_BTR, btr);
|
||||
|
||||
netdev_dbg(netdev, "esdACC: BRP %u, NBTR 0x%08x, DBTR 0x%08x",
|
||||
brp, btr, fbtr);
|
||||
} else {
|
||||
netdev_dbg(netdev, "bit timing: brp %u, prop %u, ph1 %u ph2 %u, sjw %u\n",
|
||||
bt->brp, bt->prop_seg,
|
||||
bt->phase_seg1, bt->phase_seg2, bt->sjw);
|
||||
|
||||
brp = FIELD_PREP(ACC_REG_BRP_CL_MASK_BRP, bt->brp - 1);
|
||||
|
||||
btr = FIELD_PREP(ACC_REG_BTR_CL_MASK_TSEG1, bt->phase_seg1 + bt->prop_seg - 1);
|
||||
btr |= FIELD_PREP(ACC_REG_BTR_CL_MASK_TSEG2, bt->phase_seg2 - 1);
|
||||
btr |= FIELD_PREP(ACC_REG_BTR_CL_MASK_SJW, bt->sjw - 1);
|
||||
|
||||
/* Keep order of accesses to ACC_CORE_OF_BRP and ACC_CORE_OF_BTR. */
|
||||
acc_write32(priv->core, ACC_CORE_OF_BRP, brp);
|
||||
acc_write32(priv->core, ACC_CORE_OF_BTR, btr);
|
||||
|
||||
netdev_dbg(netdev, "esdACC: BRP %u, BTR 0x%08x", brp, btr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void handle_core_msg_rxtxdone(struct acc_core *core,
|
||||
const struct acc_bmmsg_rxtxdone *msg)
|
||||
{
|
||||
struct acc_net_priv *priv = netdev_priv(core->netdev);
|
||||
struct net_device_stats *stats = &core->netdev->stats;
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (msg->acc_dlc.len & ACC_DLC_TXD_FLAG) {
|
||||
u8 tx_fifo_tail = core->tx_fifo_tail;
|
||||
|
||||
if (core->tx_fifo_head == tx_fifo_tail) {
|
||||
netdev_warn(core->netdev,
|
||||
"TX interrupt, but queue is empty!?\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Direct access echo skb to attach HW time stamp. */
|
||||
skb = priv->can.echo_skb[tx_fifo_tail];
|
||||
if (skb) {
|
||||
skb_hwtstamps(skb)->hwtstamp =
|
||||
acc_ts2ktime(priv->ov, msg->ts);
|
||||
}
|
||||
|
||||
stats->tx_packets++;
|
||||
stats->tx_bytes += can_get_echo_skb(core->netdev, tx_fifo_tail,
|
||||
NULL);
|
||||
|
||||
core->tx_fifo_tail = acc_tx_fifo_next(core, tx_fifo_tail);
|
||||
|
||||
netif_wake_queue(core->netdev);
|
||||
|
||||
} else {
|
||||
struct can_frame *cf;
|
||||
|
||||
skb = alloc_can_skb(core->netdev, &cf);
|
||||
if (!skb) {
|
||||
stats->rx_dropped++;
|
||||
return;
|
||||
}
|
||||
|
||||
cf->can_id = msg->id & ACC_ID_ID_MASK;
|
||||
if (msg->id & ACC_ID_EFF_FLAG)
|
||||
cf->can_id |= CAN_EFF_FLAG;
|
||||
|
||||
can_frame_set_cc_len(cf, msg->acc_dlc.len & ACC_DLC_DLC_MASK,
|
||||
priv->can.ctrlmode);
|
||||
|
||||
if (msg->acc_dlc.len & ACC_DLC_RTR_FLAG) {
|
||||
cf->can_id |= CAN_RTR_FLAG;
|
||||
} else {
|
||||
memcpy(cf->data, msg->data, cf->len);
|
||||
stats->rx_bytes += cf->len;
|
||||
}
|
||||
stats->rx_packets++;
|
||||
|
||||
skb_hwtstamps(skb)->hwtstamp = acc_ts2ktime(priv->ov, msg->ts);
|
||||
|
||||
netif_rx(skb);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_core_msg_txabort(struct acc_core *core,
|
||||
const struct acc_bmmsg_txabort *msg)
|
||||
{
|
||||
struct net_device_stats *stats = &core->netdev->stats;
|
||||
u8 tx_fifo_tail = core->tx_fifo_tail;
|
||||
u32 abort_mask = msg->abort_mask; /* u32 extend to avoid warnings later */
|
||||
|
||||
/* The abort_mask shows which frames were aborted in esdACC's FIFO. */
|
||||
while (tx_fifo_tail != core->tx_fifo_head && (abort_mask)) {
|
||||
const u32 tail_mask = (1U << tx_fifo_tail);
|
||||
|
||||
if (!(abort_mask & tail_mask))
|
||||
break;
|
||||
abort_mask &= ~tail_mask;
|
||||
|
||||
can_free_echo_skb(core->netdev, tx_fifo_tail, NULL);
|
||||
stats->tx_dropped++;
|
||||
stats->tx_aborted_errors++;
|
||||
|
||||
tx_fifo_tail = acc_tx_fifo_next(core, tx_fifo_tail);
|
||||
}
|
||||
core->tx_fifo_tail = tx_fifo_tail;
|
||||
if (abort_mask)
|
||||
netdev_warn(core->netdev, "Unhandled aborted messages\n");
|
||||
|
||||
if (!acc_resetmode_entered(core))
|
||||
netif_wake_queue(core->netdev);
|
||||
}
|
||||
|
||||
static void handle_core_msg_overrun(struct acc_core *core,
|
||||
const struct acc_bmmsg_overrun *msg)
|
||||
{
|
||||
struct acc_net_priv *priv = netdev_priv(core->netdev);
|
||||
struct net_device_stats *stats = &core->netdev->stats;
|
||||
struct can_frame *cf;
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* lost_cnt may be 0 if not supported by esdACC version */
|
||||
if (msg->lost_cnt) {
|
||||
stats->rx_errors += msg->lost_cnt;
|
||||
stats->rx_over_errors += msg->lost_cnt;
|
||||
} else {
|
||||
stats->rx_errors++;
|
||||
stats->rx_over_errors++;
|
||||
}
|
||||
|
||||
skb = alloc_can_err_skb(core->netdev, &cf);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
cf->can_id |= CAN_ERR_CRTL;
|
||||
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
|
||||
|
||||
skb_hwtstamps(skb)->hwtstamp = acc_ts2ktime(priv->ov, msg->ts);
|
||||
|
||||
netif_rx(skb);
|
||||
}
|
||||
|
||||
static void handle_core_msg_buserr(struct acc_core *core,
|
||||
const struct acc_bmmsg_buserr *msg)
|
||||
{
|
||||
struct acc_net_priv *priv = netdev_priv(core->netdev);
|
||||
struct net_device_stats *stats = &core->netdev->stats;
|
||||
struct can_frame *cf;
|
||||
struct sk_buff *skb;
|
||||
const u32 reg_status = msg->reg_status;
|
||||
const u8 rxerr = reg_status;
|
||||
const u8 txerr = (reg_status >> 8);
|
||||
u8 can_err_prot_type = 0U;
|
||||
|
||||
priv->can.can_stats.bus_error++;
|
||||
|
||||
/* Error occurred during transmission? */
|
||||
if (msg->ecc & ACC_ECC_DIR) {
|
||||
stats->rx_errors++;
|
||||
} else {
|
||||
can_err_prot_type |= CAN_ERR_PROT_TX;
|
||||
stats->tx_errors++;
|
||||
}
|
||||
/* Determine error type */
|
||||
switch (msg->ecc & ACC_ECC_MASK) {
|
||||
case ACC_ECC_BIT:
|
||||
can_err_prot_type |= CAN_ERR_PROT_BIT;
|
||||
break;
|
||||
case ACC_ECC_FORM:
|
||||
can_err_prot_type |= CAN_ERR_PROT_FORM;
|
||||
break;
|
||||
case ACC_ECC_STUFF:
|
||||
can_err_prot_type |= CAN_ERR_PROT_STUFF;
|
||||
break;
|
||||
default:
|
||||
can_err_prot_type |= CAN_ERR_PROT_UNSPEC;
|
||||
break;
|
||||
}
|
||||
|
||||
skb = alloc_can_err_skb(core->netdev, &cf);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR | CAN_ERR_CNT;
|
||||
|
||||
/* Set protocol error type */
|
||||
cf->data[2] = can_err_prot_type;
|
||||
/* Set error location */
|
||||
cf->data[3] = msg->ecc & ACC_ECC_SEG;
|
||||
|
||||
/* Insert CAN TX and RX error counters. */
|
||||
cf->data[6] = txerr;
|
||||
cf->data[7] = rxerr;
|
||||
|
||||
skb_hwtstamps(skb)->hwtstamp = acc_ts2ktime(priv->ov, msg->ts);
|
||||
|
||||
netif_rx(skb);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_core_msg_errstatechange(struct acc_core *core,
|
||||
const struct acc_bmmsg_errstatechange *msg)
|
||||
{
|
||||
struct acc_net_priv *priv = netdev_priv(core->netdev);
|
||||
struct can_frame *cf = NULL;
|
||||
struct sk_buff *skb;
|
||||
const u32 reg_status = msg->reg_status;
|
||||
const u8 rxerr = reg_status;
|
||||
const u8 txerr = (reg_status >> 8);
|
||||
enum can_state new_state;
|
||||
|
||||
if (reg_status & ACC_REG_STATUS_MASK_STATUS_BS) {
|
||||
new_state = CAN_STATE_BUS_OFF;
|
||||
} else if (reg_status & ACC_REG_STATUS_MASK_STATUS_EP) {
|
||||
new_state = CAN_STATE_ERROR_PASSIVE;
|
||||
} else if (reg_status & ACC_REG_STATUS_MASK_STATUS_ES) {
|
||||
new_state = CAN_STATE_ERROR_WARNING;
|
||||
} else {
|
||||
new_state = CAN_STATE_ERROR_ACTIVE;
|
||||
if (priv->can.state == CAN_STATE_BUS_OFF) {
|
||||
/* See comment in acc_set_mode() for CAN_MODE_START */
|
||||
netif_wake_queue(core->netdev);
|
||||
}
|
||||
}
|
||||
|
||||
skb = alloc_can_err_skb(core->netdev, &cf);
|
||||
|
||||
if (new_state != priv->can.state) {
|
||||
enum can_state tx_state, rx_state;
|
||||
|
||||
tx_state = (txerr >= rxerr) ?
|
||||
new_state : CAN_STATE_ERROR_ACTIVE;
|
||||
rx_state = (rxerr >= txerr) ?
|
||||
new_state : CAN_STATE_ERROR_ACTIVE;
|
||||
|
||||
/* Always call can_change_state() to update the state
|
||||
* even if alloc_can_err_skb() may have failed.
|
||||
* can_change_state() can cope with a NULL cf pointer.
|
||||
*/
|
||||
can_change_state(core->netdev, cf, tx_state, rx_state);
|
||||
}
|
||||
|
||||
if (skb) {
|
||||
cf->can_id |= CAN_ERR_CNT;
|
||||
cf->data[6] = txerr;
|
||||
cf->data[7] = rxerr;
|
||||
|
||||
skb_hwtstamps(skb)->hwtstamp = acc_ts2ktime(priv->ov, msg->ts);
|
||||
|
||||
netif_rx(skb);
|
||||
}
|
||||
|
||||
if (new_state == CAN_STATE_BUS_OFF) {
|
||||
acc_write32(core, ACC_CORE_OF_TX_ABORT_MASK, 0xffff);
|
||||
can_bus_off(core->netdev);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_core_interrupt(struct acc_core *core)
|
||||
{
|
||||
u32 msg_fifo_head = core->bmfifo.local_irq_cnt & 0xff;
|
||||
|
||||
while (core->bmfifo.msg_fifo_tail != msg_fifo_head) {
|
||||
const union acc_bmmsg *msg =
|
||||
&core->bmfifo.messages[core->bmfifo.msg_fifo_tail];
|
||||
|
||||
switch (msg->msg_id) {
|
||||
case BM_MSG_ID_RXTXDONE:
|
||||
handle_core_msg_rxtxdone(core, &msg->rxtxdone);
|
||||
break;
|
||||
|
||||
case BM_MSG_ID_TXABORT:
|
||||
handle_core_msg_txabort(core, &msg->txabort);
|
||||
break;
|
||||
|
||||
case BM_MSG_ID_OVERRUN:
|
||||
handle_core_msg_overrun(core, &msg->overrun);
|
||||
break;
|
||||
|
||||
case BM_MSG_ID_BUSERR:
|
||||
handle_core_msg_buserr(core, &msg->buserr);
|
||||
break;
|
||||
|
||||
case BM_MSG_ID_ERRPASSIVE:
|
||||
case BM_MSG_ID_ERRWARN:
|
||||
handle_core_msg_errstatechange(core,
|
||||
&msg->errstatechange);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Ignore all other BM messages (like the CAN-FD messages) */
|
||||
break;
|
||||
}
|
||||
|
||||
core->bmfifo.msg_fifo_tail =
|
||||
(core->bmfifo.msg_fifo_tail + 1) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* acc_card_interrupt() - handle the interrupts of an esdACC FPGA
|
||||
*
|
||||
* @ov: overview module structure
|
||||
* @cores: array of core structures
|
||||
*
|
||||
* This function handles all interrupts pending for the overview module and the
|
||||
* CAN cores of the esdACC FPGA.
|
||||
*
|
||||
* It examines for all cores (the overview module core and the CAN cores)
|
||||
* the bmfifo.irq_cnt and compares it with the previously saved
|
||||
* bmfifo.local_irq_cnt. An IRQ is pending if they differ. The esdACC FPGA
|
||||
* updates the bmfifo.irq_cnt values by DMA.
|
||||
*
|
||||
* The pending interrupts are masked by writing to the IRQ mask register at
|
||||
* ACC_OV_OF_BM_IRQ_MASK. This register has for each core a two bit command
|
||||
* field evaluated as follows:
|
||||
*
|
||||
* Define, bit pattern: meaning
|
||||
* 00: no action
|
||||
* ACC_BM_IRQ_UNMASK, 01: unmask interrupt
|
||||
* ACC_BM_IRQ_MASK, 10: mask interrupt
|
||||
* 11: no action
|
||||
*
|
||||
* For each CAN core with a pending IRQ handle_core_interrupt() handles all
|
||||
* busmaster messages from the message FIFO. The last handled message (FIFO
|
||||
* index) is written to the CAN core to acknowledge its handling.
|
||||
*
|
||||
* Last step is to unmask all interrupts in the FPGA using
|
||||
* ACC_BM_IRQ_UNMASK_ALL.
|
||||
*
|
||||
* Return:
|
||||
* IRQ_HANDLED, if card generated an interrupt that was handled
|
||||
* IRQ_NONE, if the interrupt is not ours
|
||||
*/
|
||||
irqreturn_t acc_card_interrupt(struct acc_ov *ov, struct acc_core *cores)
|
||||
{
|
||||
u32 irqmask;
|
||||
int i;
|
||||
|
||||
/* First we look for whom interrupts are pending, card/overview
|
||||
* or any of the cores. Two bits in irqmask are used for each;
|
||||
* Each two bit field is set to ACC_BM_IRQ_MASK if an IRQ is
|
||||
* pending.
|
||||
*/
|
||||
irqmask = 0U;
|
||||
if (READ_ONCE(*ov->bmfifo.irq_cnt) != ov->bmfifo.local_irq_cnt) {
|
||||
irqmask |= ACC_BM_IRQ_MASK;
|
||||
ov->bmfifo.local_irq_cnt = READ_ONCE(*ov->bmfifo.irq_cnt);
|
||||
}
|
||||
|
||||
for (i = 0; i < ov->active_cores; i++) {
|
||||
struct acc_core *core = &cores[i];
|
||||
|
||||
if (READ_ONCE(*core->bmfifo.irq_cnt) != core->bmfifo.local_irq_cnt) {
|
||||
irqmask |= (ACC_BM_IRQ_MASK << (2 * (i + 1)));
|
||||
core->bmfifo.local_irq_cnt = READ_ONCE(*core->bmfifo.irq_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
if (!irqmask)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* At second we tell the card we're working on them by writing irqmask,
|
||||
* call handle_{ov|core}_interrupt and then acknowledge the
|
||||
* interrupts by writing irq_cnt:
|
||||
*/
|
||||
acc_ov_write32(ov, ACC_OV_OF_BM_IRQ_MASK, irqmask);
|
||||
|
||||
if (irqmask & ACC_BM_IRQ_MASK) {
|
||||
/* handle_ov_interrupt(); - no use yet. */
|
||||
acc_ov_write32(ov, ACC_OV_OF_BM_IRQ_COUNTER,
|
||||
ov->bmfifo.local_irq_cnt);
|
||||
}
|
||||
|
||||
for (i = 0; i < ov->active_cores; i++) {
|
||||
struct acc_core *core = &cores[i];
|
||||
|
||||
if (irqmask & (ACC_BM_IRQ_MASK << (2 * (i + 1)))) {
|
||||
handle_core_interrupt(core);
|
||||
acc_write32(core, ACC_OV_OF_BM_IRQ_COUNTER,
|
||||
core->bmfifo.local_irq_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
acc_ov_write32(ov, ACC_OV_OF_BM_IRQ_MASK, ACC_BM_IRQ_UNMASK_ALL);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
356
drivers/net/can/esd/esdacc.h
Normal file
356
drivers/net/can/esd/esdacc.h
Normal file
@ -0,0 +1,356 @@
|
||||
/* 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/bits.h>
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#define ACC_TS_FREQ_80MHZ (80 * HZ_PER_MHZ)
|
||||
#define ACC_I2C_ADDON_DETECT_DELAY_MS 10
|
||||
|
||||
/* esdACC Overview Module */
|
||||
#define ACC_OV_OF_PROBE 0x0000
|
||||
#define ACC_OV_OF_VERSION 0x0004
|
||||
#define ACC_OV_OF_INFO 0x0008
|
||||
#define ACC_OV_OF_CANCORE_FREQ 0x000c
|
||||
#define ACC_OV_OF_TS_FREQ_LO 0x0010
|
||||
#define ACC_OV_OF_TS_FREQ_HI 0x0014
|
||||
#define ACC_OV_OF_IRQ_STATUS_CORES 0x0018
|
||||
#define ACC_OV_OF_TS_CURR_LO 0x001c
|
||||
#define ACC_OV_OF_TS_CURR_HI 0x0020
|
||||
#define ACC_OV_OF_IRQ_STATUS 0x0028
|
||||
#define ACC_OV_OF_MODE 0x002c
|
||||
#define ACC_OV_OF_BM_IRQ_COUNTER 0x0070
|
||||
#define ACC_OV_OF_BM_IRQ_MASK 0x0074
|
||||
#define ACC_OV_OF_MSI_DATA 0x0080
|
||||
#define ACC_OV_OF_MSI_ADDRESSOFFSET 0x0084
|
||||
|
||||
/* Feature flags are contained in the upper 16 bit of the version
|
||||
* register at ACC_OV_OF_VERSION but only used with these masks after
|
||||
* extraction into an extra variable => (xx - 16).
|
||||
*/
|
||||
#define ACC_OV_REG_FEAT_MASK_CANFD BIT(27 - 16)
|
||||
#define ACC_OV_REG_FEAT_MASK_NEW_PSC BIT(28 - 16)
|
||||
|
||||
#define ACC_OV_REG_MODE_MASK_ENDIAN_LITTLE BIT(0)
|
||||
#define ACC_OV_REG_MODE_MASK_BM_ENABLE BIT(1)
|
||||
#define ACC_OV_REG_MODE_MASK_MODE_LED BIT(2)
|
||||
#define ACC_OV_REG_MODE_MASK_TIMER_ENABLE BIT(4)
|
||||
#define ACC_OV_REG_MODE_MASK_TIMER_ONE_SHOT BIT(5)
|
||||
#define ACC_OV_REG_MODE_MASK_TIMER_ABSOLUTE BIT(6)
|
||||
#define ACC_OV_REG_MODE_MASK_TIMER GENMASK(6, 4)
|
||||
#define ACC_OV_REG_MODE_MASK_TS_SRC GENMASK(8, 7)
|
||||
#define ACC_OV_REG_MODE_MASK_I2C_ENABLE BIT(11)
|
||||
#define ACC_OV_REG_MODE_MASK_MSI_ENABLE BIT(14)
|
||||
#define ACC_OV_REG_MODE_MASK_NEW_PSC_ENABLE BIT(15)
|
||||
#define ACC_OV_REG_MODE_MASK_FPGA_RESET BIT(31)
|
||||
|
||||
/* esdACC CAN Core Module */
|
||||
#define ACC_CORE_OF_CTRL_MODE 0x0000
|
||||
#define ACC_CORE_OF_STATUS_IRQ 0x0008
|
||||
#define ACC_CORE_OF_BRP 0x000c
|
||||
#define ACC_CORE_OF_BTR 0x0010
|
||||
#define ACC_CORE_OF_FBTR 0x0014
|
||||
#define ACC_CORE_OF_STATUS 0x0030
|
||||
#define ACC_CORE_OF_TXFIFO_CONFIG 0x0048
|
||||
#define ACC_CORE_OF_TXFIFO_STATUS 0x004c
|
||||
#define ACC_CORE_OF_TX_STATUS_IRQ 0x0050
|
||||
#define ACC_CORE_OF_TX_ABORT_MASK 0x0054
|
||||
#define ACC_CORE_OF_BM_IRQ_COUNTER 0x0070
|
||||
#define ACC_CORE_OF_TXFIFO_ID 0x00c0
|
||||
#define ACC_CORE_OF_TXFIFO_DLC 0x00c4
|
||||
#define ACC_CORE_OF_TXFIFO_DATA_0 0x00c8
|
||||
#define ACC_CORE_OF_TXFIFO_DATA_1 0x00cc
|
||||
|
||||
#define ACC_REG_CONTROL_MASK_MODE_RESETMODE BIT(0)
|
||||
#define ACC_REG_CONTROL_MASK_MODE_LOM BIT(1)
|
||||
#define ACC_REG_CONTROL_MASK_MODE_STM BIT(2)
|
||||
#define ACC_REG_CONTROL_MASK_MODE_TRANSEN BIT(5)
|
||||
#define ACC_REG_CONTROL_MASK_MODE_TS BIT(6)
|
||||
#define ACC_REG_CONTROL_MASK_MODE_SCHEDULE BIT(7)
|
||||
|
||||
#define ACC_REG_CONTROL_MASK_IE_RXTX BIT(8)
|
||||
#define ACC_REG_CONTROL_MASK_IE_TXERROR BIT(9)
|
||||
#define ACC_REG_CONTROL_MASK_IE_ERRWARN BIT(10)
|
||||
#define ACC_REG_CONTROL_MASK_IE_OVERRUN BIT(11)
|
||||
#define ACC_REG_CONTROL_MASK_IE_TSI BIT(12)
|
||||
#define ACC_REG_CONTROL_MASK_IE_ERRPASS BIT(13)
|
||||
#define ACC_REG_CONTROL_MASK_IE_ALI BIT(14)
|
||||
#define ACC_REG_CONTROL_MASK_IE_BUSERR BIT(15)
|
||||
|
||||
/* BRP and BTR register layout for CAN-Classic version */
|
||||
#define ACC_REG_BRP_CL_MASK_BRP GENMASK(8, 0)
|
||||
#define ACC_REG_BTR_CL_MASK_TSEG1 GENMASK(3, 0)
|
||||
#define ACC_REG_BTR_CL_MASK_TSEG2 GENMASK(18, 16)
|
||||
#define ACC_REG_BTR_CL_MASK_SJW GENMASK(25, 24)
|
||||
|
||||
/* BRP and BTR register layout for CAN-FD version */
|
||||
#define ACC_REG_BRP_FD_MASK_BRP GENMASK(7, 0)
|
||||
#define ACC_REG_BTR_FD_MASK_TSEG1 GENMASK(7, 0)
|
||||
#define ACC_REG_BTR_FD_MASK_TSEG2 GENMASK(22, 16)
|
||||
#define ACC_REG_BTR_FD_MASK_SJW GENMASK(30, 24)
|
||||
|
||||
/* 256 BM_MSGs of 32 byte size */
|
||||
#define ACC_CORE_DMAMSG_SIZE 32U
|
||||
#define ACC_CORE_DMABUF_SIZE (256U * ACC_CORE_DMAMSG_SIZE)
|
||||
|
||||
enum acc_bmmsg_id {
|
||||
BM_MSG_ID_RXTXDONE = 0x01,
|
||||
BM_MSG_ID_TXABORT = 0x02,
|
||||
BM_MSG_ID_OVERRUN = 0x03,
|
||||
BM_MSG_ID_BUSERR = 0x04,
|
||||
BM_MSG_ID_ERRPASSIVE = 0x05,
|
||||
BM_MSG_ID_ERRWARN = 0x06,
|
||||
BM_MSG_ID_TIMESLICE = 0x07,
|
||||
BM_MSG_ID_HWTIMER = 0x08,
|
||||
BM_MSG_ID_HOTPLUG = 0x09,
|
||||
};
|
||||
|
||||
/* The struct acc_bmmsg_* structure declarations that follow here provide
|
||||
* access to the ring buffer of bus master messages maintained by the FPGA
|
||||
* bus master engine. All bus master messages have the same size of
|
||||
* ACC_CORE_DMAMSG_SIZE and a minimum alignment of ACC_CORE_DMAMSG_SIZE in
|
||||
* memory.
|
||||
*
|
||||
* All structure members are natural aligned. Therefore we should not need
|
||||
* a __packed attribute. All struct acc_bmmsg_* declarations have at least
|
||||
* reserved* members to fill the structure to the full ACC_CORE_DMAMSG_SIZE.
|
||||
*
|
||||
* A failure of this property due padding will be detected at compile time
|
||||
* by static_assert(sizeof(union acc_bmmsg) == ACC_CORE_DMAMSG_SIZE).
|
||||
*/
|
||||
|
||||
struct acc_bmmsg_rxtxdone {
|
||||
u8 msg_id;
|
||||
u8 txfifo_level;
|
||||
u8 reserved1[2];
|
||||
u8 txtsfifo_level;
|
||||
u8 reserved2[3];
|
||||
u32 id;
|
||||
struct {
|
||||
u8 len;
|
||||
u8 txdfifo_idx;
|
||||
u8 zeroes8;
|
||||
u8 reserved;
|
||||
} acc_dlc;
|
||||
u8 data[CAN_MAX_DLEN];
|
||||
/* Time stamps in struct acc_ov::timestamp_frequency ticks. */
|
||||
u64 ts;
|
||||
};
|
||||
|
||||
struct acc_bmmsg_txabort {
|
||||
u8 msg_id;
|
||||
u8 txfifo_level;
|
||||
u16 abort_mask;
|
||||
u8 txtsfifo_level;
|
||||
u8 reserved2[1];
|
||||
u16 abort_mask_txts;
|
||||
u64 ts;
|
||||
u32 reserved3[4];
|
||||
};
|
||||
|
||||
struct acc_bmmsg_overrun {
|
||||
u8 msg_id;
|
||||
u8 txfifo_level;
|
||||
u8 lost_cnt;
|
||||
u8 reserved1;
|
||||
u8 txtsfifo_level;
|
||||
u8 reserved2[3];
|
||||
u64 ts;
|
||||
u32 reserved3[4];
|
||||
};
|
||||
|
||||
struct acc_bmmsg_buserr {
|
||||
u8 msg_id;
|
||||
u8 txfifo_level;
|
||||
u8 ecc;
|
||||
u8 reserved1;
|
||||
u8 txtsfifo_level;
|
||||
u8 reserved2[3];
|
||||
u64 ts;
|
||||
u32 reg_status;
|
||||
u32 reg_btr;
|
||||
u32 reserved3[2];
|
||||
};
|
||||
|
||||
struct acc_bmmsg_errstatechange {
|
||||
u8 msg_id;
|
||||
u8 txfifo_level;
|
||||
u8 reserved1[2];
|
||||
u8 txtsfifo_level;
|
||||
u8 reserved2[3];
|
||||
u64 ts;
|
||||
u32 reg_status;
|
||||
u32 reserved3[3];
|
||||
};
|
||||
|
||||
struct acc_bmmsg_timeslice {
|
||||
u8 msg_id;
|
||||
u8 txfifo_level;
|
||||
u8 reserved1[2];
|
||||
u8 txtsfifo_level;
|
||||
u8 reserved2[3];
|
||||
u64 ts;
|
||||
u32 reserved3[4];
|
||||
};
|
||||
|
||||
struct acc_bmmsg_hwtimer {
|
||||
u8 msg_id;
|
||||
u8 reserved1[3];
|
||||
u32 reserved2[1];
|
||||
u64 timer;
|
||||
u32 reserved3[4];
|
||||
};
|
||||
|
||||
struct acc_bmmsg_hotplug {
|
||||
u8 msg_id;
|
||||
u8 reserved1[3];
|
||||
u32 reserved2[7];
|
||||
};
|
||||
|
||||
union acc_bmmsg {
|
||||
u8 msg_id;
|
||||
struct acc_bmmsg_rxtxdone rxtxdone;
|
||||
struct acc_bmmsg_txabort txabort;
|
||||
struct acc_bmmsg_overrun overrun;
|
||||
struct acc_bmmsg_buserr buserr;
|
||||
struct acc_bmmsg_errstatechange errstatechange;
|
||||
struct acc_bmmsg_timeslice timeslice;
|
||||
struct acc_bmmsg_hwtimer hwtimer;
|
||||
};
|
||||
|
||||
/* Check size of union acc_bmmsg to be of expected size. */
|
||||
static_assert(sizeof(union acc_bmmsg) == ACC_CORE_DMAMSG_SIZE);
|
||||
|
||||
struct acc_bmfifo {
|
||||
const union acc_bmmsg *messages;
|
||||
/* irq_cnt points to an u32 value where the esdACC FPGA deposits
|
||||
* the bm_fifo head index in coherent DMA memory. Only bits 7..0
|
||||
* are valid. Use READ_ONCE() to access this memory location.
|
||||
*/
|
||||
const u32 *irq_cnt;
|
||||
u32 local_irq_cnt;
|
||||
u32 msg_fifo_tail;
|
||||
};
|
||||
|
||||
struct acc_core {
|
||||
void __iomem *addr;
|
||||
struct net_device *netdev;
|
||||
struct acc_bmfifo bmfifo;
|
||||
u8 tx_fifo_size;
|
||||
u8 tx_fifo_head;
|
||||
u8 tx_fifo_tail;
|
||||
};
|
||||
|
||||
struct acc_ov {
|
||||
void __iomem *addr;
|
||||
struct acc_bmfifo bmfifo;
|
||||
u32 timestamp_frequency;
|
||||
u32 core_frequency;
|
||||
u16 version;
|
||||
u16 features;
|
||||
u8 total_cores;
|
||||
u8 active_cores;
|
||||
};
|
||||
|
||||
struct acc_net_priv {
|
||||
struct can_priv can; /* must be the first member! */
|
||||
struct acc_core *core;
|
||||
struct acc_ov *ov;
|
||||
};
|
||||
|
||||
static inline u32 acc_read32(struct acc_core *core, unsigned short offs)
|
||||
{
|
||||
return ioread32be(core->addr + offs);
|
||||
}
|
||||
|
||||
static inline void acc_write32(struct acc_core *core,
|
||||
unsigned short offs, u32 v)
|
||||
{
|
||||
iowrite32be(v, core->addr + offs);
|
||||
}
|
||||
|
||||
static inline void acc_write32_noswap(struct acc_core *core,
|
||||
unsigned short offs, u32 v)
|
||||
{
|
||||
iowrite32(v, core->addr + offs);
|
||||
}
|
||||
|
||||
static inline void acc_set_bits(struct acc_core *core,
|
||||
unsigned short offs, u32 mask)
|
||||
{
|
||||
u32 v = acc_read32(core, offs);
|
||||
|
||||
v |= mask;
|
||||
acc_write32(core, offs, v);
|
||||
}
|
||||
|
||||
static inline void acc_clear_bits(struct acc_core *core,
|
||||
unsigned short offs, u32 mask)
|
||||
{
|
||||
u32 v = acc_read32(core, offs);
|
||||
|
||||
v &= ~mask;
|
||||
acc_write32(core, offs, v);
|
||||
}
|
||||
|
||||
static inline int acc_resetmode_entered(struct acc_core *core)
|
||||
{
|
||||
u32 ctrl = acc_read32(core, ACC_CORE_OF_CTRL_MODE);
|
||||
|
||||
return (ctrl & ACC_REG_CONTROL_MASK_MODE_RESETMODE) != 0;
|
||||
}
|
||||
|
||||
static inline u32 acc_ov_read32(struct acc_ov *ov, unsigned short offs)
|
||||
{
|
||||
return ioread32be(ov->addr + offs);
|
||||
}
|
||||
|
||||
static inline void acc_ov_write32(struct acc_ov *ov,
|
||||
unsigned short offs, u32 v)
|
||||
{
|
||||
iowrite32be(v, ov->addr + offs);
|
||||
}
|
||||
|
||||
static inline void acc_ov_set_bits(struct acc_ov *ov,
|
||||
unsigned short offs, u32 b)
|
||||
{
|
||||
u32 v = acc_ov_read32(ov, offs);
|
||||
|
||||
v |= b;
|
||||
acc_ov_write32(ov, offs, v);
|
||||
}
|
||||
|
||||
static inline void acc_ov_clear_bits(struct acc_ov *ov,
|
||||
unsigned short offs, u32 b)
|
||||
{
|
||||
u32 v = acc_ov_read32(ov, offs);
|
||||
|
||||
v &= ~b;
|
||||
acc_ov_write32(ov, offs, v);
|
||||
}
|
||||
|
||||
static inline void acc_reset_fpga(struct acc_ov *ov)
|
||||
{
|
||||
acc_ov_write32(ov, ACC_OV_OF_MODE, ACC_OV_REG_MODE_MASK_FPGA_RESET);
|
||||
|
||||
/* (Re-)start and wait for completion of addon detection on the I^2C bus */
|
||||
acc_ov_set_bits(ov, ACC_OV_OF_MODE, ACC_OV_REG_MODE_MASK_I2C_ENABLE);
|
||||
mdelay(ACC_I2C_ADDON_DETECT_DELAY_MS);
|
||||
}
|
||||
|
||||
void acc_init_ov(struct acc_ov *ov, struct device *dev);
|
||||
void acc_init_bm_ptr(struct acc_ov *ov, struct acc_core *cores,
|
||||
const void *mem);
|
||||
int acc_open(struct net_device *netdev);
|
||||
int acc_close(struct net_device *netdev);
|
||||
netdev_tx_t acc_start_xmit(struct sk_buff *skb, struct net_device *netdev);
|
||||
int acc_get_berr_counter(const struct net_device *netdev,
|
||||
struct can_berr_counter *bec);
|
||||
int acc_set_mode(struct net_device *netdev, enum can_mode mode);
|
||||
int acc_set_bittiming(struct net_device *netdev);
|
||||
irqreturn_t acc_card_interrupt(struct acc_ov *ov, struct acc_core *cores);
|
Loading…
x
Reference in New Issue
Block a user