On 09.03.21 10:43, Stéphane Grosjean wrote: > Hi, > > Thank you very much for your review. > >>> --- /dev/null >>> +++ b/kernel/drivers/can/peak_canfd/Kconfig >>> @@ -0,0 +1,7 @@ >>> +config XENO_DRIVERS_CAN_PEAK_CANFD >>> + depends on XENO_DRIVERS_CAN && PCI >>> + tristate "PEAK driver for PCAN-PCIe FD family" >>> + default n > >> Minor: default is "no" already, thus this line is not needed. > > Done. > >>> +#define DRV_NAME "xeno_peak_canfd" >>> + >>> +#define RTCAN_DEV_NAME "rtcan%d" >>> +#define RTCAN_CTRLR_NAME "peak_canfd" > >> Minor: inconsistent indention. > > Done (and a few others I had missed) > >>> + >>> +#ifndef CONFIG_XENO_DRIVERS_CAN_CALC_BITTIME_OLD >> >> (we should consider dropping this option, finally...) > > What do you mean? Should I remove that code now?
You could remove the code and make the driver depends on !XENO_DRIVERS_CAN_CALC_BITTIME_OLD, like FLEXCAN already does. Then the bug below is probably obsolet as well. In a separate commit, we could rip out that config options and all code paths that depend on it. It's declared "deprecated" for more than 8 years now (since ef036723abcf). Jan > > Finally, there's something wrong with the current version of the driver when > CONFIG_XENO_DRIVERS_CAN_DEBUG =y: > > $ cd build/utils/can > $ sudo ./rtcanconfig -b 500000 rtcan0 > $ sudo ./rtcanconfig -b 500000 rtcan1 > $ sudo ./rtcanconfig -b 500000 rtcan0 start > $ sudo ./rtcanconfig -b 500000 rtcan1 start > > $ sudo ./rtcansend -i 0x123 -l 10 -c rtcan0 > > $ sudo ./rtcanrecv -T -v rtcan1 > interface rtcan1 > s=3, ifr_name=rtcan1 > #0: (2) 1615281693237192690ns <0x123> [4] 00 00 00 00 > #1: (2) 1615281693238190749ns <0x123> [4] 01 00 00 00 > #2: (2) 1615281693239191263ns <0x123> [4] 02 00 00 00 > #3: (2) 1615281693240191832ns <0x123> [4] 03 00 00 00 > #4: (2) 1615281693241192490ns <0x123> [4] 04 00 00 00 > #5: (2) 1615281693242193009ns <0x123> [4] 05 00 00 00 > #6: (2) 1615281693243193633ns <0x123> [4] 06 00 00 00 > #7: (2) 1615281693244194237ns <0x123> [4] 07 00 00 00 > #8: (2) 1615281693245194827ns <0x123> [4] 08 00 00 00 > #9: (2) 1615281693246195385ns <0x123> [4] 09 00 00 00 > ^CSignal 2 received > Cleaning up... > > $ cat /proc/rtcan/rtcan0/registers > Killed > ... > $ dmesg > [ 772.385115] BUG: kernel NULL pointer dereference, address: 0000000000000081 > [ 772.385119] #PF: supervisor read access in kernel mode > [ 772.385120] #PF: error_code(0x0000) - not-present page > [ 772.385121] PGD 0 P4D 0 > [ 772.385123] Oops: 0000 [#1] SMP PTI > [ 772.385126] CPU: 3 PID: 3367 Comm: cat Not tainted 5.4.77-xenomai-3.x-dbg > #7 > [ 772.385127] Hardware name: ASUS All Series/X99-E WS, BIOS 4001 05/27/2019 > [ 772.385128] I-pipe domain: Linux > [ 772.385132] RIP: 0010:rtcan_peak_pciefd_proc_regs+0x11/0x70 > [xeno_can_peak_pciefd] > [ 772.385134] Code: c7 c6 00 f1 32 c0 48 89 c2 e8 3b 52 bc c9 41 5c 5d c3 0f > 1f 80 00 00 00 00 e8 db 2d 4d ca 55 48 89 e5 41 55 49 89 fd 41 54 53 <4c> 8b > a6 80 00 00 00 48 c7 c6 d0 12 33 c0 31 db 49 8b 54 24 58 e8 > [ 772.385135] RSP: 0018:ffffb3a884f77de0 EFLAGS: 00010246 > [ 772.385137] RAX: ffffffffc032f100 RBX: 0000000000000000 RCX: > 0000000000000000 > [ 772.385138] RDX: 0000000000001000 RSI: 0000000000000001 RDI: > ffff9ec9b9fe6c00 > [ 772.385139] RBP: ffffb3a884f77df8 R08: ffffd3a87f3c7c70 R09: > ffff9ec9b751e000 > [ 772.385140] R10: 0000000000000000 R11: ffff9ec9d8647d40 R12: > ffff9ec9b9fe6c00 > [ 772.385141] R13: ffff9ec9b9fe6c00 R14: ffff9ec9b9fe6c28 R15: > 0000000000000001 > [ 772.385143] FS: 00007fa6bf1ce580(0000) GS:ffff9eca0f780000(0000) > knlGS:0000000000000000 > [ 772.385144] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 > [ 772.385145] CR2: 0000000000000081 CR3: 00000004065a2001 CR4: > 00000000001606e0 > [ 772.385146] Call Trace: > [ 772.385152] seq_read+0xdc/0x470 > [ 772.385158] proc_reg_read+0x43/0x70 > [ 772.385161] __vfs_read+0x1b/0x40 > [ 772.385163] vfs_read+0xab/0x160 > [ 772.385165] ksys_read+0x67/0xe0 > [ 772.385167] __x64_sys_read+0x1a/0x20 > [ 772.385171] do_syscall_64+0x6f/0x1d0 > [ 772.385175] entry_SYSCALL_64_after_hwframe+0x44/0xa9 > [ 772.385176] RIP: 0033:0x7fa6bf0ebfb2 > [ 772.385177] Code: c0 e9 c2 fe ff ff 50 48 8d 3d ca cb 0a 00 e8 f5 19 02 00 > 0f 1f 44 00 00 f3 0f 1e fa 64 8b 04 25 18 00 00 00 85 c0 75 10 0f 05 <48> 3d > 00 f0 ff ff 77 56 c3 0f 1f 44 00 00 48 83 ec 28 48 89 54 24 > [ 772.385178] RSP: 002b:00007fffbd1a7308 EFLAGS: 00000246 ORIG_RAX: > 0000000000000000 > [ 772.385180] RAX: ffffffffffffffda RBX: 0000000000020000 RCX: > 00007fa6bf0ebfb2 > [ 772.385181] RDX: 0000000000020000 RSI: 00007fa6be1dc000 RDI: > 0000000000000003 > [ 772.385182] RBP: 00007fa6be1dc000 R08: 00007fa6be1db010 R09: > 0000000000000000 > [ 772.385183] R10: 0000000000000022 R11: 0000000000000246 R12: > 00005577bfb331f0 > [ 772.385184] R13: 0000000000000003 R14: 0000000000020000 R15: > 0000000000020000 > [ 772.385186] Modules linked in: md4 nls_utf8 cifs libarc4 fscache libdes > nls_iso8859_1 snd_hda_codec_hdmi snd_hda_codec_realtek snd_hda_codec_generic > ledtrig_audio intel_rapl_msr intel_rapl_common x86_pkg_temp_thermal > intel_powerclamp coretemp snd_hda_intel snd_intel_nhlt snd_hda_codec joydev > input_leds wmi_bmof mxm_wmi kvm_intel kvm snd_hda_core irqbypass nouveau > snd_hwdep snd_pcm video ttm snd_seq_midi snd_seq_midi_event snd_rawmidi > snd_seq drm_kms_helper fb_sys_fops syscopyarea sysfillrect sysimgblt > snd_seq_device snd_timer crct10dif_pclmul ghash_clmulni_intel aesni_intel snd > xeno_can_peak_pciefd xeno_can crypto_simd soundcore cryptd glue_helper mei_me > rapl intel_cstate mei wmi mac_hid sch_fq_codel parport_pc ppdev lp parport > drm drm_panel_orientation_quirks efivarfs ip_tables x_tables autofs4 > hid_generic uas usbhid usb_storage hid crc32_pclmul igb ahci i2c_algo_bit > i2c_i801 dca lpc_ich libahci e1000e > [ 772.385220] CR2: 0000000000000081 > [ 772.385222] ---[ end trace a489f33fb3e19ba2 ]--- > [ 772.385224] RIP: 0010:rtcan_peak_pciefd_proc_regs+0x11/0x70 > [xeno_can_peak_pciefd] > [ 772.385225] Code: c7 c6 00 f1 32 c0 48 89 c2 e8 3b 52 bc c9 41 5c 5d c3 0f > 1f 80 00 00 00 00 e8 db 2d 4d ca 55 48 89 e5 41 55 49 89 fd 41 54 53 <4c> 8b > a6 80 00 00 00 48 c7 c6 d0 12 33 c0 31 db 49 8b 54 24 58 e8 > [ 772.385226] RSP: 0018:ffffb3a884f77de0 EFLAGS: 00010246 > [ 772.385227] RAX: ffffffffc032f100 RBX: 0000000000000000 RCX: > 0000000000000000 > [ 772.385228] RDX: 0000000000001000 RSI: 0000000000000001 RDI: > ffff9ec9b9fe6c00 > [ 772.385229] RBP: ffffb3a884f77df8 R08: ffffd3a87f3c7c70 R09: > ffff9ec9b751e000 > [ 772.385230] R10: 0000000000000000 R11: ffff9ec9d8647d40 R12: > ffff9ec9b9fe6c00 > [ 772.385231] R13: ffff9ec9b9fe6c00 R14: ffff9ec9b9fe6c28 R15: > 0000000000000001 > [ 772.385232] FS: 00007fa6bf1ce580(0000) GS:ffff9eca0f780000(0000) > knlGS:0000000000000000 > [ 772.385233] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 > [ 772.385234] CR2: 0000000000000081 CR3: 00000004065a2001 CR4: > 00000000001606e0 > > Once fixed, you should receive v2 asap. > > Thank you agan for your work, > > --- > Stéphane Grosjean > PEAK-System France > 132, rue André Bisiaux > F-54320 MAXEVILLE > Tél : +(33) 9.72.54.51.97 > > > > From: Jan Kiszka <jan.kis...@siemens.com> > Sent: Monday, March 8, 2021 11:41 > To: Stéphane Grosjean <s.grosj...@peak-system.com>; Xenomai mailing list > <xenomai@xenomai.org> > Cc: Andy Tannenbaum <t...@bioniklabs.com>; Sebastian Smolorz > <sebastian.smol...@gmx.de> > Subject: Re: [PATCH] RT-socket-CAN: Add support of PEAK PCAN-PCIe FD cards > family > > On 04.03.21 14:45, Stephane Grosjean via Xenomai wrote: >> This patch includes the driver that supports the CANFD interfaces of >> the PCAN-PCIe FD family. This driver is largely inspired by the peak_pciefd >> driver included in the kernel since version 4.12. Except for the >> differences related to the RTDM model, this driver differs from the >> socket-CAN driver by the following points: >> >> . CAN 2.0 a/b support only (CAN-FD not available in rt-socket-can) >> . all interrupts are required (or released) when loading (or unloading) >> the module, not when activating (or deactivating) the CAN interface as in >> the socket-CAN model. Only hardware interruptions are enabled (or >> disabled) when the rtcan interface is set up (or down). >> . This way of operating allows the usage of the MSI mode: if CONFIG_PCI_MSI >> is defined while CONFIG_XENO_OPT_SHIRQ is not, then the driver activates >> the MSI mode by default. The legacy INTA mode is used otherwise. In case >> CONFIG_PCI_MSI is defined, the parameter module "usemsi" allows to change >> this default behaviour: usemsi=0 selects INTA mode while usemsi=1 selects >> MSI mode. >> . the Tx flow is managed more easily (no echo management in rt-socket-can) >> >> Signed-off-by: Stephane Grosjean <s.grosj...@peak-system.com> >> Tested-by: Andy Tannenbaum <t...@bioniklabs.com> > > Thanks a lot for this contribution! > > I've dropped Sebastian's and my historic addresses from CC - haven't > seen them in emails for way more than a decade by now. :) > >> --- >> kernel/drivers/can/Kconfig | 1 + >> kernel/drivers/can/Makefile | 2 +- >> kernel/drivers/can/peak_canfd/Kconfig | 7 + >> kernel/drivers/can/peak_canfd/Makefile | 6 + >> .../drivers/can/peak_canfd/rtcan_peak_canfd.c | 632 ++++++++++ >> .../can/peak_canfd/rtcan_peak_canfd_user.h | 51 + >> .../can/peak_canfd/rtcan_peak_pciefd.c | 1069 +++++++++++++++++ >> 7 files changed, 1767 insertions(+), 1 deletion(-) >> create mode 100644 kernel/drivers/can/peak_canfd/Kconfig >> create mode 100644 kernel/drivers/can/peak_canfd/Makefile >> create mode 100644 kernel/drivers/can/peak_canfd/rtcan_peak_canfd.c >> create mode 100644 kernel/drivers/can/peak_canfd/rtcan_peak_canfd_user.h >> create mode 100644 kernel/drivers/can/peak_canfd/rtcan_peak_pciefd.c >> >> diff --git a/kernel/drivers/can/Kconfig b/kernel/drivers/can/Kconfig >> index 0bbfcc9d1..1c055494d 100644 >> --- a/kernel/drivers/can/Kconfig >> +++ b/kernel/drivers/can/Kconfig >> @@ -86,6 +86,7 @@ config XENO_DRIVERS_CAN_FLEXCAN >> Say Y here if you want to support for Freescale FlexCAN. >> >> source "drivers/xenomai/can/mscan/Kconfig" >> +source "drivers/xenomai/can/peak_canfd/Kconfig" >> source "drivers/xenomai/can/sja1000/Kconfig" >> >> endmenu >> diff --git a/kernel/drivers/can/Makefile b/kernel/drivers/can/Makefile >> index 1c071b214..f78f6afdf 100644 >> --- a/kernel/drivers/can/Makefile >> +++ b/kernel/drivers/can/Makefile >> @@ -1,7 +1,7 @@ >> >> ccflags-y += -I$(srctree)/drivers/xenomai/can >> >> -obj-$(CONFIG_XENO_DRIVERS_CAN) += xeno_can.o mscan/ sja1000/ >> +obj-$(CONFIG_XENO_DRIVERS_CAN) += xeno_can.o mscan/ sja1000/ peak_canfd/ >> obj-$(CONFIG_XENO_DRIVERS_CAN_FLEXCAN) += xeno_can_flexcan.o >> obj-$(CONFIG_XENO_DRIVERS_CAN_VIRT) += xeno_can_virt.o >> >> diff --git a/kernel/drivers/can/peak_canfd/Kconfig >> b/kernel/drivers/can/peak_canfd/Kconfig >> new file mode 100644 >> index 000000000..8537d73b4 >> --- /dev/null >> +++ b/kernel/drivers/can/peak_canfd/Kconfig >> @@ -0,0 +1,7 @@ >> +config XENO_DRIVERS_CAN_PEAK_CANFD >> + depends on XENO_DRIVERS_CAN && PCI >> + tristate "PEAK driver for PCAN-PCIe FD family" >> + default n > > Minor: default is "no" already, thus this line is not needed. > >> + help >> + >> + This driver supports the PCAN-PCIe FD boards family from PEAK-System. >> diff --git a/kernel/drivers/can/peak_canfd/Makefile >> b/kernel/drivers/can/peak_canfd/Makefile >> new file mode 100644 >> index 000000000..22670a9c4 >> --- /dev/null >> +++ b/kernel/drivers/can/peak_canfd/Makefile >> @@ -0,0 +1,6 @@ >> +# >> +# Makefile for the PEAK-System CAN-FD IP module drivers >> +# >> +ccflags-y += -Idrivers/xenomai/can >> +obj-$(CONFIG_XENO_DRIVERS_CAN_PEAK_CANFD) += xeno_can_peak_pciefd.o >> +xeno_can_peak_pciefd-y := rtcan_peak_pciefd.o rtcan_peak_canfd.o >> diff --git a/kernel/drivers/can/peak_canfd/rtcan_peak_canfd.c >> b/kernel/drivers/can/peak_canfd/rtcan_peak_canfd.c >> new file mode 100644 >> index 000000000..bd20a5db6 >> --- /dev/null >> +++ b/kernel/drivers/can/peak_canfd/rtcan_peak_canfd.c >> @@ -0,0 +1,632 @@ >> +// SPDX-License-Identifier: GPL-2.0-only >> +/* >> + * CANFD firmware interface. >> + * >> + * Copyright (C) 2001-2021 PEAK System-Technik GmbH >> + * Copyright (C) 2019-2021 Stephane Grosjean <s.grosj...@peak-system.com> >> + */ >> +#include "rtcan_dev.h" >> +#include "rtcan_raw.h" >> +#include "rtcan_peak_canfd_user.h" >> + >> +#define DRV_NAME "xeno_peak_canfd" >> + >> +#define RTCAN_DEV_NAME "rtcan%d" >> +#define RTCAN_CTRLR_NAME "peak_canfd" > > Minor: inconsistent indention. > >> + >> +#ifndef CONFIG_XENO_DRIVERS_CAN_CALC_BITTIME_OLD > > (we should consider dropping this option, finally...) > >> +/* bittiming ranges of the PEAK-System PC CAN-FD interfaces */ >> +static const struct can_bittiming_const peak_canfd_nominal_const = { >> + .name = RTCAN_CTRLR_NAME, >> + .tseg1_min = 1, >> + .tseg1_max = (1 << PUCAN_TSLOW_TSGEG1_BITS), >> + .tseg2_min = 1, >> + .tseg2_max = (1 << PUCAN_TSLOW_TSGEG2_BITS), >> + .sjw_max = (1 << PUCAN_TSLOW_SJW_BITS), >> + .brp_min = 1, >> + .brp_max = (1 << PUCAN_TSLOW_BRP_BITS), >> + .brp_inc = 1, >> +}; >> +#endif >> + >> +/* initialize the command area */ >> +static struct peak_canfd_priv *pucan_init_cmd(struct peak_canfd_priv *priv) >> +{ >> + priv->cmd_len = 0; >> + return priv; >> +} >> + >> +/* add command 'cmd_op' to the command area */ >> +static void *pucan_add_cmd(struct peak_canfd_priv *priv, int cmd_op) >> +{ >> + struct pucan_command *cmd; >> + >> + if (priv->cmd_len + sizeof(*cmd) > priv->cmd_maxlen) >> + return NULL; >> + >> + cmd = priv->cmd_buffer + priv->cmd_len; >> + >> + /* reset all unused bit to default */ >> + memset(cmd, 0, sizeof(*cmd)); >> + >> + cmd->opcode_channel = pucan_cmd_opcode_channel(priv->index, cmd_op); >> + priv->cmd_len += sizeof(*cmd); >> + >> + return cmd; >> +} >> + >> +/* send the command(s) to the IP core through the host-device interface */ >> +static int pucan_write_cmd(struct peak_canfd_priv *priv) >> +{ >> + int err; >> + >> + /* prepare environment before writing the command */ >> + if (priv->pre_cmd) { >> + err = priv->pre_cmd(priv); >> + if (err) >> + return err; >> + } >> + >> + err = priv->write_cmd(priv); >> + if (err) >> + return err; >> + >> + /* update environment after writing the command */ >> + if (priv->post_cmd) >> + err = priv->post_cmd(priv); >> + >> + return err; >> +} >> + >> +/* set the device in RESET mode */ >> +static int pucan_set_reset_mode(struct peak_canfd_priv *priv) >> +{ >> + int err; >> + >> + pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_RESET_MODE); >> + err = pucan_write_cmd(priv); >> + if (!err) >> + priv->rdev->state = CAN_STATE_STOPPED; >> + >> + return err; >> +} >> + >> +/* set the device in NORMAL mode */ >> +static int pucan_set_normal_mode(struct peak_canfd_priv *priv) >> +{ >> + int err; >> + >> + pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_NORMAL_MODE); >> + err = pucan_write_cmd(priv); >> + if (!err) >> + priv->rdev->state = CAN_STATE_ERROR_ACTIVE; >> + >> + return err; >> +} >> + >> +/* set the device in LISTEN_ONLY mode */ >> +static int pucan_set_listen_only_mode(struct peak_canfd_priv *priv) >> +{ >> + int err; >> + >> + pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_LISTEN_ONLY_MODE); >> + err = pucan_write_cmd(priv); >> + if (!err) >> + priv->rdev->state = CAN_STATE_ERROR_ACTIVE; >> + >> + return err; >> +} >> + >> +/* set acceptance filters */ >> +static int pucan_set_std_filter(struct peak_canfd_priv *priv, u8 row, u32 >> mask) >> +{ >> + struct pucan_std_filter *cmd; >> + >> + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_SET_STD_FILTER); >> + >> + /* All the 11-bit CAN ID values are represented by one bit in a >> + * 64 rows array of 32 columns: the upper 6 bit of the CAN ID select >> + * the row while the lowest 5 bit select the column in that row. >> + * >> + * bit filter >> + * 1 passed >> + * 0 discarded >> + */ >> + >> + /* select the row */ >> + cmd->idx = row; >> + >> + /* set/unset bits in the row */ >> + cmd->mask = cpu_to_le32(mask); >> + >> + return pucan_write_cmd(priv); >> +} >> + >> +/* request the device to stop transmission */ >> +static int pucan_tx_abort(struct peak_canfd_priv *priv, u16 flags) >> +{ >> + struct pucan_tx_abort *cmd; >> + >> + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_TX_ABORT); >> + >> + cmd->flags = cpu_to_le16(flags); >> + >> + return pucan_write_cmd(priv); >> +} >> + >> +/* request the device to clear rx/tx error counters */ >> +static int pucan_clr_err_counters(struct peak_canfd_priv *priv) >> +{ >> + struct pucan_wr_err_cnt *cmd; >> + >> + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_WR_ERR_CNT); >> + >> + cmd->sel_mask = cpu_to_le16(PUCAN_WRERRCNT_TE | PUCAN_WRERRCNT_RE); >> + >> + /* write the counters new value */ >> + cmd->tx_counter = 0; >> + cmd->rx_counter = 0; >> + >> + return pucan_write_cmd(priv); >> +} >> + >> +/* set options to the device */ >> +static int pucan_set_options(struct peak_canfd_priv *priv, u16 opt_mask) >> +{ >> + struct pucan_options *cmd; >> + >> + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_SET_EN_OPTION); >> + >> + cmd->options = cpu_to_le16(opt_mask); >> + >> + return pucan_write_cmd(priv); >> +} >> + >> +/* request the device to notify the driver when Tx path is ready */ >> +static int pucan_setup_rx_barrier(struct peak_canfd_priv *priv) >> +{ >> + pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_RX_BARRIER); >> + >> + return pucan_write_cmd(priv); >> +} >> + >> +/* handle the reception of one CAN frame */ >> +static int pucan_handle_can_rx(struct peak_canfd_priv *priv, >> + struct pucan_rx_msg *msg) >> +{ >> + struct rtcan_skb skb = { .rb_frame_size = EMPTY_RB_FRAME_SIZE, }; >> + struct rtcan_rb_frame *cf = &skb.rb_frame; >> + struct rtcan_device *rdev = priv->rdev; >> + const u16 rx_msg_flags = le16_to_cpu(msg->flags); >> + >> + if (rx_msg_flags & PUCAN_MSG_EXT_DATA_LEN) { >> + /* CAN-FD frames are silently discarded */ >> + return 0; >> + } >> + >> + cf->can_id = le32_to_cpu(msg->can_id); >> + cf->can_dlc = get_can_dlc(pucan_msg_get_dlc(msg)); >> + >> + if (rx_msg_flags & PUCAN_MSG_EXT_ID) >> + cf->can_id |= CAN_EFF_FLAG; >> + >> + if (rx_msg_flags & PUCAN_MSG_RTR) >> + cf->can_id |= CAN_RTR_FLAG; >> + else { >> + memcpy(cf->data, msg->d, cf->can_dlc); >> + skb.rb_frame_size += cf->can_dlc; >> + } >> + >> + cf->can_ifindex = rdev->ifindex; >> + >> + /* Pass received frame out to the sockets */ >> + rtcan_rcv(rdev, &skb); >> + >> + return 0; >> +} >> + >> +/* handle rx/tx error counters notification */ >> +static int pucan_handle_error(struct peak_canfd_priv *priv, >> + struct pucan_error_msg *msg) >> +{ >> + priv->bec.txerr = msg->tx_err_cnt; >> + priv->bec.rxerr = msg->rx_err_cnt; >> + >> + return 0; >> +} >> + >> +/* handle status notification */ >> +static int pucan_handle_status(struct peak_canfd_priv *priv, >> + struct pucan_status_msg *msg) >> +{ >> + struct rtcan_skb skb = { .rb_frame_size = EMPTY_RB_FRAME_SIZE, }; >> + struct rtcan_rb_frame *cf = &skb.rb_frame; >> + struct rtcan_device *rdev = priv->rdev; >> + >> + /* this STATUS is the CNF of the RX_BARRIER: Tx path can be setup */ >> + if (pucan_status_is_rx_barrier(msg)) { >> + if (priv->enable_tx_path) { >> + int err = priv->enable_tx_path(priv); >> + >> + if (err) >> + return err; >> + } >> + >> + /* unlock senders */ >> + rtdm_sem_up(&rdev->tx_sem); >> + return 0; >> + } >> + >> + /* otherwise, it's a BUS status */ >> + cf->can_id = CAN_ERR_FLAG; >> + cf->can_dlc = CAN_ERR_DLC; >> + >> + /* test state error bits according to their priority */ >> + if (pucan_status_is_busoff(msg)) { >> + rtdm_printk(DRV_NAME " CAN%u: Bus-off entry status\n", >> + priv->index+1); >> + rdev->state = CAN_STATE_BUS_OFF; >> + cf->can_id |= CAN_ERR_BUSOFF; >> + >> + /* wakeup waiting senders */ >> + rtdm_sem_destroy(&rdev->tx_sem); >> + >> + } else if (pucan_status_is_passive(msg)) { >> + rtdm_printk(DRV_NAME " CAN%u: Error passive status\n", >> + priv->index+1); >> + rdev->state = CAN_STATE_ERROR_PASSIVE; >> + cf->can_id |= CAN_ERR_CRTL; >> + cf->data[1] = (priv->bec.txerr > priv->bec.rxerr) ? >> + CAN_ERR_CRTL_TX_PASSIVE : >> + CAN_ERR_CRTL_RX_PASSIVE; >> + cf->data[6] = priv->bec.txerr; >> + cf->data[7] = priv->bec.rxerr; >> + >> + } else if (pucan_status_is_warning(msg)) { >> + rtdm_printk(DRV_NAME " CAN%u: Error warning status\n", >> + priv->index+1); >> + rdev->state = CAN_STATE_ERROR_WARNING; >> + >> + cf->can_id |= CAN_ERR_CRTL; >> + cf->data[1] = (priv->bec.txerr > priv->bec.rxerr) ? >> + CAN_ERR_CRTL_TX_WARNING : >> + CAN_ERR_CRTL_RX_WARNING; >> + cf->data[6] = priv->bec.txerr; >> + cf->data[7] = priv->bec.rxerr; >> + >> + } else if (rdev->state != CAN_STATE_ERROR_ACTIVE) { >> + /* back to ERROR_ACTIVE */ >> + rtdm_printk(DRV_NAME " CAN%u: Error active status\n", >> + priv->index+1); >> + rdev->state = CAN_STATE_ERROR_ACTIVE; >> + } >> + >> + skb.rb_frame_size += cf->can_dlc; >> + cf->can_ifindex = rdev->ifindex; >> + >> + /* Pass received frame out to the sockets */ >> + rtcan_rcv(rdev, &skb); >> + >> + return 0; >> +} >> + >> +/* handle IP core Rx overflow notification */ >> +static int pucan_handle_cache_critical(struct peak_canfd_priv *priv) >> +{ >> + struct rtcan_skb skb = { .rb_frame_size = EMPTY_RB_FRAME_SIZE, }; >> + struct rtcan_rb_frame *cf = &skb.rb_frame; >> + struct rtcan_device *rdev = priv->rdev; >> + >> + cf->can_id = CAN_ERR_FLAG | CAN_ERR_CRTL; >> + cf->can_dlc = CAN_ERR_DLC; >> + >> + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; >> + >> + cf->data[6] = priv->bec.txerr; >> + cf->data[7] = priv->bec.rxerr; >> + >> + skb.rb_frame_size += cf->can_dlc; >> + cf->can_ifindex = rdev->ifindex; >> + >> + /* Pass received frame out to the sockets */ >> + rtcan_rcv(rdev, &skb); >> + >> + return 0; >> +} >> + >> +/* handle a single uCAN message */ >> +int peak_canfd_handle_msg(struct peak_canfd_priv *priv, >> + struct pucan_rx_msg *msg) >> +{ >> + u16 msg_type = le16_to_cpu(msg->type); >> + int msg_size = le16_to_cpu(msg->size); >> + int err; >> + >> + if (!msg_size || !msg_type) { >> + /* null packet found: end of list */ >> + goto exit; >> + } >> + >> + switch (msg_type) { >> + case PUCAN_MSG_CAN_RX: >> + err = pucan_handle_can_rx(priv, (struct pucan_rx_msg *)msg); >> + break; >> + case PUCAN_MSG_ERROR: >> + err = pucan_handle_error(priv, (struct pucan_error_msg *)msg); >> + break; >> + case PUCAN_MSG_STATUS: >> + err = pucan_handle_status(priv, >> + (struct pucan_status_msg *)msg); >> + break; >> + case PUCAN_MSG_CACHE_CRITICAL: >> + err = pucan_handle_cache_critical(priv); >> + break; >> + default: >> + err = 0; >> + } >> + >> + if (err < 0) >> + return err; >> + >> +exit: >> + return msg_size; >> +} >> + >> +/* handle a list of rx_count messages from rx_msg memory address */ >> +int peak_canfd_handle_msgs_list(struct peak_canfd_priv *priv, >> + struct pucan_rx_msg *msg_list, int msg_count) >> +{ >> + void *msg_ptr = msg_list; >> + int i, msg_size = 0; >> + >> + for (i = 0; i < msg_count; i++) { >> + msg_size = peak_canfd_handle_msg(priv, msg_ptr); >> + >> + /* a null packet can be found at the end of a list */ >> + if (msg_size <= 0) >> + break; >> + >> + msg_ptr += ALIGN(msg_size, 4); >> + } >> + >> + if (msg_size < 0) >> + return msg_size; >> + >> + return i; >> +} >> + >> +/* start the device (set the IP core in NORMAL or LISTEN-ONLY mode) */ >> +static int peak_canfd_start(struct rtcan_device *rdev, >> + rtdm_lockctx_t *lock_ctx) >> +{ >> + struct peak_canfd_priv *priv = rdev->priv; >> + int i, err = 0; >> + >> + switch (rdev->state) { >> + case CAN_STATE_BUS_OFF: >> + case CAN_STATE_STOPPED: >> + err = pucan_set_reset_mode(priv); >> + if (err) >> + break; >> + >> + /* set ineeded option: get rx/tx error counters */ >> + err = pucan_set_options(priv, PUCAN_OPTION_ERROR); >> + if (err) >> + break; >> + >> + /* accept all standard CAN ID */ >> + for (i = 0; i <= PUCAN_FLTSTD_ROW_IDX_MAX; i++) >> + pucan_set_std_filter(priv, i, 0xffffffff); >> + >> + /* clear device rx/tx error counters */ >> + err = pucan_clr_err_counters(priv); >> + if (err) >> + break; >> + >> + /* set resquested mode */ >> + if (priv->rdev->ctrl_mode & CAN_CTRLMODE_LISTENONLY) >> + err = pucan_set_listen_only_mode(priv); >> + else >> + err = pucan_set_normal_mode(priv); >> + >> + rtdm_sem_init(&rdev->tx_sem, 1); >> + >> + /* receiving the RB status says when Tx path is ready */ >> + err = pucan_setup_rx_barrier(priv); >> + break; >> + >> + default: >> + break; >> + } >> + >> + return err; >> +} >> + >> +/* stop the device (set the IP core in RESET mode) */ >> +static int peak_canfd_stop(struct rtcan_device *rdev, >> + rtdm_lockctx_t *lock_ctx) >> +{ >> + struct peak_canfd_priv *priv = rdev->priv; >> + int err = 0; >> + >> + switch (rdev->state) { >> + case CAN_STATE_BUS_OFF: >> + case CAN_STATE_STOPPED: >> + break; >> + >> + default: >> + /* go back to RESET mode */ >> + err = pucan_set_reset_mode(priv); >> + if (err) { >> + rtdm_printk(DRV_NAME " CAN%u: reset failed\n", >> + priv->index+1); >> + break; >> + } >> + >> + /* abort last Tx (MUST be done in RESET mode only!) */ >> + pucan_tx_abort(priv, PUCAN_TX_ABORT_FLUSH); >> + >> + rtdm_sem_destroy(&rdev->tx_sem); >> + break; >> + } >> + >> + return err; >> +} >> + >> +/* RT-Socket-CAN driver interface */ >> +static int peak_canfd_set_mode(struct rtcan_device *rdev, can_mode_t mode, >> + rtdm_lockctx_t *lock_ctx) >> +{ >> + int err = 0; >> + >> + switch (mode) { >> + case CAN_MODE_STOP: >> + err = peak_canfd_stop(rdev, lock_ctx); >> + break; >> + case CAN_MODE_START: >> + err = peak_canfd_start(rdev, lock_ctx); >> + break; >> + case CAN_MODE_SLEEP: >> + /* Controller must operate, otherwise go out */ >> + if (!CAN_STATE_OPERATING(rdev->state)) { >> + err = -ENETDOWN; >> + break; >> + } >> + if (rdev->state == CAN_STATE_SLEEPING) >> + break; >> + >> + /* fallthrough */ >> + default: >> + err = -EOPNOTSUPP; >> + break; >> + } >> + >> + return err; >> +} >> + >> +static int peak_canfd_set_bittiming(struct rtcan_device *rdev, >> + struct can_bittime *pbt, >> + rtdm_lockctx_t *lock_ctx) >> +{ >> + struct peak_canfd_priv *priv = rdev->priv; >> + struct pucan_timing_slow *cmd; >> + >> + /* can't support BTR0BTR1 mode with clock greater than 8 MHz */ >> + if (pbt->type != CAN_BITTIME_STD) { >> + rtdm_printk(DRV_NAME >> + " CAN%u: unsupported bittiming mode %u\n", >> + priv->index+1, pbt->type); >> + return -EINVAL; >> + } >> + >> + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_TIMING_SLOW); >> + >> + cmd->sjw_t = PUCAN_TSLOW_SJW_T(pbt->std.sjw - 1, >> + priv->rdev->ctrl_mode & >> + CAN_CTRLMODE_3_SAMPLES); >> + >> + cmd->tseg1 = PUCAN_TSLOW_TSEG1(pbt->std.prop_seg + >> + pbt->std.phase_seg1 - 1); >> + cmd->tseg2 = PUCAN_TSLOW_TSEG2(pbt->std.phase_seg2 - 1); >> + cmd->brp = cpu_to_le16(PUCAN_TSLOW_BRP(pbt->std.brp - 1)); >> + >> + cmd->ewl = 96; /* default */ >> + >> + rtdm_printk(DRV_NAME ": nominal: brp=%u tseg1=%u tseg2=%u sjw=%u\n", >> + le16_to_cpu(cmd->brp), cmd->tseg1, cmd->tseg2, cmd->sjw_t); >> + >> + return pucan_write_cmd(priv); >> +} >> + >> +/* hard transmit callback: write the CAN frame to the device */ >> +static netdev_tx_t peak_canfd_start_xmit(struct rtcan_device *rdev, >> + can_frame_t *cf) >> +{ >> + struct peak_canfd_priv *priv = rdev->priv; >> + struct pucan_tx_msg *msg; >> + u16 msg_size, msg_flags; >> + int room_left; >> + const u8 dlc = (cf->can_dlc > CAN_MAX_DLC) ? CAN_MAX_DLC : cf->can_dlc; >> + >> + msg_size = ALIGN(sizeof(*msg) + dlc, 4); >> + msg = priv->alloc_tx_msg(priv, msg_size, &room_left); >> + >> + /* should never happen except under bus-off condition and >> + * (auto-)restart mechanism >> + */ >> + if (!msg) { >> + rtdm_printk(DRV_NAME >> + " CAN%u: skb lost (No room left in tx buffer)\n", >> + priv->index+1); >> + return 0; >> + } >> + >> + msg->size = cpu_to_le16(msg_size); >> + msg->type = cpu_to_le16(PUCAN_MSG_CAN_TX); >> + msg_flags = 0; >> + if (cf->can_id & CAN_EFF_FLAG) { >> + msg_flags |= PUCAN_MSG_EXT_ID; >> + msg->can_id = cpu_to_le32(cf->can_id & CAN_EFF_MASK); >> + } else { >> + msg->can_id = cpu_to_le32(cf->can_id & CAN_SFF_MASK); >> + } >> + >> + if (cf->can_id & CAN_RTR_FLAG) >> + msg_flags |= PUCAN_MSG_RTR; >> + >> + /* set driver specific bit to differentiate with application >> + * loopback >> + */ >> + if (rdev->ctrl_mode & CAN_CTRLMODE_LOOPBACK) >> + msg_flags |= PUCAN_MSG_LOOPED_BACK; >> + >> + msg->flags = cpu_to_le16(msg_flags); >> + msg->channel_dlc = PUCAN_MSG_CHANNEL_DLC(priv->index, dlc); >> + memcpy(msg->d, cf->data, dlc); >> + >> + /* write the skb on the interface */ >> + priv->write_tx_msg(priv, msg); >> + >> + /* control senders flow */ >> + if (room_left > (sizeof(*msg) + CAN_MAX_DLC)) >> + rtdm_sem_up(&rdev->tx_sem); >> + >> + return 0; >> +} >> + >> +/* allocate a rtcan device for channel #index, with enough space to store >> + * private information. >> + */ >> +struct rtcan_device *alloc_peak_canfd_dev(int sizeof_priv, int index) >> +{ >> + struct rtcan_device *rdev; >> + struct peak_canfd_priv *priv; >> + >> + /* allocate the candev object */ >> + rdev = rtcan_dev_alloc(sizeof_priv, 0); >> + if (!rdev) >> + return NULL; >> + >> + /* RTCAN part initialization */ >> + strncpy(rdev->name, RTCAN_DEV_NAME, IFNAMSIZ); >> + rdev->ctrl_name = RTCAN_CTRLR_NAME; >> + rdev->can_sys_clock = 80*1000*1000; /* default */ >> + rdev->state = CAN_STATE_STOPPED; >> + rdev->hard_start_xmit = peak_canfd_start_xmit; >> + rdev->do_set_mode = peak_canfd_set_mode; >> + rdev->do_set_bit_time = peak_canfd_set_bittiming; >> +#ifndef CONFIG_XENO_DRIVERS_CAN_CALC_BITTIME_OLD >> + rdev->bittiming_const = &peak_canfd_nominal_const; >> +#endif >> + >> + priv = rdev->priv; >> + >> + /* private part initialization */ >> + priv->rdev = rdev; >> + priv->index = index; >> + priv->cmd_len = 0; >> + priv->bec.txerr = 0; >> + priv->bec.rxerr = 0; >> + >> + return rdev; >> +} >> diff --git a/kernel/drivers/can/peak_canfd/rtcan_peak_canfd_user.h >> b/kernel/drivers/can/peak_canfd/rtcan_peak_canfd_user.h >> new file mode 100644 >> index 000000000..1a70053e8 >> --- /dev/null >> +++ b/kernel/drivers/can/peak_canfd/rtcan_peak_canfd_user.h >> @@ -0,0 +1,51 @@ >> +/* SPDX-License-Identifier: GPL-2.0-only */ >> +/* >> + * CAN driver for PEAK System micro-CAN based adapters. >> + * >> + * Copyright (C) 2001-2021 PEAK System-Technik GmbH >> + * Copyright (C) 2019-2021 Stephane Grosjean <s.grosj...@peak-system.com> >> + */ >> +#ifndef PEAK_CANFD_USER_H >> +#define PEAK_CANFD_USER_H >> + >> +#include <linux/can/dev/peak_canfd.h> >> + >> +#define CAN_MAX_DLC 8 >> +#define get_can_dlc(i) (min_t(__u8, (i), CAN_MAX_DLC)) >> + >> +struct peak_berr_counter { >> + __u16 txerr; >> + __u16 rxerr; >> +}; >> + >> +/* data structure private to each uCAN interface */ >> +struct peak_canfd_priv { >> + struct rtcan_device *rdev; /* RTCAN device */ >> + int index; /* channel index */ >> + >> + struct peak_berr_counter bec; >> + >> + int cmd_len; >> + void *cmd_buffer; >> + int cmd_maxlen; >> + >> + int (*pre_cmd)(struct peak_canfd_priv *priv); >> + int (*write_cmd)(struct peak_canfd_priv *priv); >> + int (*post_cmd)(struct peak_canfd_priv *priv); >> + >> + int (*enable_tx_path)(struct peak_canfd_priv *priv); >> + void *(*alloc_tx_msg)(struct peak_canfd_priv *priv, u16 msg_size, >> + int *room_left); >> + int (*write_tx_msg)(struct peak_canfd_priv *priv, >> + struct pucan_tx_msg *msg); >> +}; >> + >> +struct rtcan_device *alloc_peak_canfd_dev(int sizeof_priv, int index); >> +void rtcan_peak_pciefd_remove_proc(struct rtcan_device *rdev); >> +int rtcan_peak_pciefd_create_proc(struct rtcan_device *rdev); >> + >> +int peak_canfd_handle_msg(struct peak_canfd_priv *priv, >> + struct pucan_rx_msg *msg); >> +int peak_canfd_handle_msgs_list(struct peak_canfd_priv *priv, >> + struct pucan_rx_msg *rx_msg, int rx_count); >> +#endif >> diff --git a/kernel/drivers/can/peak_canfd/rtcan_peak_pciefd.c >> b/kernel/drivers/can/peak_canfd/rtcan_peak_pciefd.c >> new file mode 100644 >> index 000000000..a40b0cd58 >> --- /dev/null >> +++ b/kernel/drivers/can/peak_canfd/rtcan_peak_pciefd.c >> @@ -0,0 +1,1069 @@ >> +// SPDX-License-Identifier: GPL-2.0-only >> +/* >> + * CAN driver PCI interface. >> + * >> + * Copyright (C) 2001-2021 PEAK System-Technik GmbH >> + * Copyright (C) 2019-2021 Stephane Grosjean <s.grosj...@peak-system.com> >> + */ >> +#include <linux/kernel.h> >> +#include <linux/module.h> >> +#include <linux/interrupt.h> >> +#include <linux/delay.h> >> +#include <linux/pci.h> >> +#include <linux/io.h> >> + >> +#include <rtdm/driver.h> >> + >> +#include <rtdm/can.h> >> + >> +#include "rtcan_dev.h" >> +#include "rtcan_raw.h" >> +#include "rtcan_peak_canfd_user.h" >> + >> +#ifdef CONFIG_PCI_MSI >> +#define PCIEFD_USES_MSI >> +#endif >> + >> +#ifndef struct_size >> +#define struct_size(p, member, n) ((n)*sizeof(*(p)->member) + \ >> + sizeof(*(p))) >> +#endif >> + >> +#define DRV_NAME "xeno_peak_pciefd" >> + >> +static char *pciefd_board_name = "PEAK-PCIe FD"; >> + >> +MODULE_AUTHOR("Stephane Grosjean <s.grosj...@peak-system.com>"); >> +MODULE_DESCRIPTION("RTCAN driver for PEAK PCAN PCIe/M.2 FD family cards"); >> +MODULE_SUPPORTED_DEVICE("PEAK PCAN PCIe/M.2 FD CAN controllers"); >> +MODULE_LICENSE("GPL v2"); >> + >> +#define PEAK_PCI_VENDOR_ID 0x001c /* The PCI device and vendor IDs */ >> +#define PEAK_PCIEFD_ID 0x0013 /* for PCIe slot cards */ >> +#define PCAN_CPCIEFD_ID 0x0014 /* for Compact-PCI Serial slot >> cards */ >> +#define PCAN_PCIE104FD_ID 0x0017 /* for PCIe-104 Express slot cards */ >> +#define PCAN_MINIPCIEFD_ID 0x0018 /* for mini-PCIe slot cards */ >> +#define PCAN_PCIEFD_OEM_ID 0x0019 /* for PCIe slot OEM cards */ >> +#define PCAN_M2_ID 0x001a /* for M2 slot cards */ >> + >> +/* supported device ids. */ >> +static const struct pci_device_id peak_pciefd_tbl[] = { >> + {PEAK_PCI_VENDOR_ID, PEAK_PCIEFD_ID, PCI_ANY_ID, PCI_ANY_ID,}, >> + {PEAK_PCI_VENDOR_ID, PCAN_CPCIEFD_ID, PCI_ANY_ID, PCI_ANY_ID,}, >> + {PEAK_PCI_VENDOR_ID, PCAN_PCIE104FD_ID, PCI_ANY_ID, PCI_ANY_ID,}, >> + {PEAK_PCI_VENDOR_ID, PCAN_MINIPCIEFD_ID, PCI_ANY_ID, PCI_ANY_ID,}, >> + {PEAK_PCI_VENDOR_ID, PCAN_PCIEFD_OEM_ID, PCI_ANY_ID, PCI_ANY_ID,}, >> + {PEAK_PCI_VENDOR_ID, PCAN_M2_ID, PCI_ANY_ID, PCI_ANY_ID,}, >> + {0,} >> +}; >> + >> +MODULE_DEVICE_TABLE(pci, peak_pciefd_tbl); >> + >> +/* PEAK PCIe board access description */ >> +#define PCIEFD_BAR0_SIZE (64 * 1024) >> +#define PCIEFD_RX_DMA_SIZE (4 * 1024) >> +#define PCIEFD_TX_DMA_SIZE (4 * 1024) >> + >> +#define PCIEFD_TX_PAGE_SIZE (2 * 1024) >> + >> +/* System Control Registers */ >> +#define PCIEFD_REG_SYS_CTL_SET 0x0000 /* set bits */ >> +#define PCIEFD_REG_SYS_CTL_CLR 0x0004 /* clear bits */ >> + >> +/* Version info registers */ >> +#define PCIEFD_REG_SYS_VER1 0x0040 /* version reg #1 */ >> +#define PCIEFD_REG_SYS_VER2 0x0044 /* version reg #2 */ >> + >> +#define PCIEFD_FW_VERSION(x, y, z) (((u32)(x) << 24) | \ >> + ((u32)(y) << 16) | \ >> + ((u32)(z) << 8)) >> + >> +/* System Control Registers Bits */ >> +#define PCIEFD_SYS_CTL_TS_RST 0x00000001 /* timestamp >> clock */ >> +#define PCIEFD_SYS_CTL_CLK_EN 0x00000002 /* system >> clock */ >> + >> +/* CAN-FD channel addresses */ >> +#define PCIEFD_CANX_OFF(c) (((c) + 1) * 0x1000) >> + >> +#define PCIEFD_ECHO_SKB_MAX PCANFD_ECHO_SKB_DEF >> + >> +/* CAN-FD channel registers */ >> +#define PCIEFD_REG_CAN_MISC 0x0000 /* Misc. control */ >> +#define PCIEFD_REG_CAN_CLK_SEL 0x0008 /* Clock selector */ >> +#define PCIEFD_REG_CAN_CMD_PORT_L 0x0010 /* 64-bits command port */ >> +#define PCIEFD_REG_CAN_CMD_PORT_H 0x0014 >> +#define PCIEFD_REG_CAN_TX_REQ_ACC 0x0020 /* Tx request accumulator */ >> +#define PCIEFD_REG_CAN_TX_CTL_SET 0x0030 /* Tx control set register */ >> +#define PCIEFD_REG_CAN_TX_CTL_CLR 0x0038 /* Tx control clear register */ >> +#define PCIEFD_REG_CAN_TX_DMA_ADDR_L 0x0040 /* 64-bits addr for Tx DMA */ >> +#define PCIEFD_REG_CAN_TX_DMA_ADDR_H 0x0044 >> +#define PCIEFD_REG_CAN_RX_CTL_SET 0x0050 /* Rx control set register */ >> +#define PCIEFD_REG_CAN_RX_CTL_CLR 0x0058 /* Rx control clear register */ >> +#define PCIEFD_REG_CAN_RX_CTL_WRT 0x0060 /* Rx control write register */ >> +#define PCIEFD_REG_CAN_RX_CTL_ACK 0x0068 /* Rx control ACK register */ >> +#define PCIEFD_REG_CAN_RX_DMA_ADDR_L 0x0070 /* 64-bits addr for Rx DMA */ >> +#define PCIEFD_REG_CAN_RX_DMA_ADDR_H 0x0074 >> + >> +/* CAN-FD channel misc register bits */ >> +#define CANFD_MISC_TS_RST 0x00000001 /* timestamp cnt rst */ >> + >> +/* CAN-FD channel Clock SELector Source & DIVider */ >> +#define CANFD_CLK_SEL_DIV_MASK 0x00000007 >> +#define CANFD_CLK_SEL_DIV_60MHZ 0x00000000 /* SRC=240MHz >> only */ >> +#define CANFD_CLK_SEL_DIV_40MHZ 0x00000001 /* SRC=240MHz >> only */ >> +#define CANFD_CLK_SEL_DIV_30MHZ 0x00000002 /* SRC=240MHz >> only */ >> +#define CANFD_CLK_SEL_DIV_24MHZ 0x00000003 /* SRC=240MHz >> only */ >> +#define CANFD_CLK_SEL_DIV_20MHZ 0x00000004 /* SRC=240MHz >> only */ >> + >> +#define CANFD_CLK_SEL_SRC_MASK 0x00000008 /* 0=80MHz, >> 1=240MHz */ >> +#define CANFD_CLK_SEL_SRC_240MHZ 0x00000008 >> +#define CANFD_CLK_SEL_SRC_80MHZ (~CANFD_CLK_SEL_SRC_240MHZ & \ >> + CANFD_CLK_SEL_SRC_MASK) >> + >> +#define CANFD_CLK_SEL_20MHZ (CANFD_CLK_SEL_SRC_240MHZ |\ >> + CANFD_CLK_SEL_DIV_20MHZ) >> +#define CANFD_CLK_SEL_24MHZ (CANFD_CLK_SEL_SRC_240MHZ |\ >> + CANFD_CLK_SEL_DIV_24MHZ) >> +#define CANFD_CLK_SEL_30MHZ (CANFD_CLK_SEL_SRC_240MHZ |\ >> + CANFD_CLK_SEL_DIV_30MHZ) >> +#define CANFD_CLK_SEL_40MHZ (CANFD_CLK_SEL_SRC_240MHZ |\ >> + CANFD_CLK_SEL_DIV_40MHZ) >> +#define CANFD_CLK_SEL_60MHZ (CANFD_CLK_SEL_SRC_240MHZ |\ >> + CANFD_CLK_SEL_DIV_60MHZ) >> +#define CANFD_CLK_SEL_80MHZ (CANFD_CLK_SEL_SRC_80MHZ) >> + >> +/* CAN-FD channel Rx/Tx control register bits */ >> +#define CANFD_CTL_UNC_BIT 0x00010000 /* Uncached DMA mem */ >> +#define CANFD_CTL_RST_BIT 0x00020000 /* reset DMA action */ >> +#define CANFD_CTL_IEN_BIT 0x00040000 /* IRQ enable */ >> + >> +/* Rx IRQ Count and Time Limits */ >> +#define CANFD_CTL_IRQ_CL_DEF 16 /* Rx msg max nb per IRQ in Rx DMA */ >> +#define CANFD_CTL_IRQ_TL_DEF 5 /* Time before IRQ if < CL (x100 µs) */ >> + >> +#define CANFD_OPTIONS_SET (CANFD_OPTION_ERROR | CANFD_OPTION_BUSLOAD) >> + >> +/* Tx anticipation window (link logical address should be aligned on 2K >> + * boundary) >> + */ >> +#define PCIEFD_TX_PAGE_COUNT (PCIEFD_TX_DMA_SIZE / PCIEFD_TX_PAGE_SIZE) >> + >> +#define CANFD_MSG_LNK_TX 0x1001 /* Tx msgs link */ >> + >> +/* 32-bit IRQ status fields, heading Rx DMA area */ >> +static inline int pciefd_irq_tag(u32 irq_status) >> +{ >> + return irq_status & 0x0000000f; >> +} >> + >> +static inline int pciefd_irq_rx_cnt(u32 irq_status) >> +{ >> + return (irq_status & 0x000007f0) >> 4; >> +} >> + >> +static inline int pciefd_irq_is_lnk(u32 irq_status) >> +{ >> + return irq_status & 0x00010000; >> +} >> + >> +/* Rx record */ >> +struct pciefd_rx_dma { >> + __le32 irq_status; >> + __le32 sys_time_low; >> + __le32 sys_time_high; >> + struct pucan_rx_msg msg[0]; >> +} __packed __aligned(4); >> + >> +/* Tx Link record */ >> +struct pciefd_tx_link { >> + __le16 size; >> + __le16 type; >> + __le32 laddr_lo; >> + __le32 laddr_hi; >> +} __packed __aligned(4); >> + >> +/* Tx page descriptor */ >> +struct pciefd_page { >> + void *vbase; /* page virtual address */ >> + dma_addr_t lbase; /* page logical address */ >> + u32 offset; >> + u32 size; >> +}; >> + >> +/* CAN channel object */ >> +struct pciefd_board; >> +struct pciefd_can { >> + struct peak_canfd_priv ucan; /* must be the first member */ >> + void __iomem *reg_base; /* channel config base addr */ >> + struct pciefd_board *board; /* reverse link */ >> + >> + struct pucan_command pucan_cmd; /* command buffer */ >> + >> + dma_addr_t rx_dma_laddr; /* DMA virtual and logical addr */ >> + void *rx_dma_vaddr; /* for Rx and Tx areas */ >> + dma_addr_t tx_dma_laddr; >> + void *tx_dma_vaddr; >> + >> + struct pciefd_page tx_pages[PCIEFD_TX_PAGE_COUNT]; >> + u16 tx_pages_free; /* free Tx pages counter */ >> + u16 tx_page_index; /* current page used for Tx */ >> + rtdm_lock_t tx_lock; >> + u32 irq_status; >> + u32 irq_tag; /* next irq tag */ >> + int irq; >> + >> + u32 flags; >> +}; >> + >> +/* PEAK-PCIe FD board object */ >> +struct pciefd_board { >> + void __iomem *reg_base; >> + struct pci_dev *pci_dev; >> + int can_count; >> + int irq_flags; /* IRQF_SHARED or 0 */ >> + rtdm_lock_t cmd_lock; /* 64-bits cmds must be atomic */ >> + struct pciefd_can *can[0]; /* array of network devices */ >> +}; >> + >> +#define CANFD_CTL_IRQ_CL_MIN 1 >> +#define CANFD_CTL_IRQ_CL_MAX 127 /* 7-bit field */ >> + >> +#define CANFD_CTL_IRQ_TL_MIN 1 >> +#define CANFD_CTL_IRQ_TL_MAX 15 /* 4-bit field */ >> + >> +static uint irqcl = CANFD_CTL_IRQ_CL_DEF; >> +module_param(irqcl, uint, 0644); >> +MODULE_PARM_DESC(irqcl, >> +" PCIe FD IRQ Count Limit (default=" __stringify(CANFD_CTL_IRQ_CL_DEF) ")"); >> + >> +static uint irqtl = CANFD_CTL_IRQ_TL_DEF; >> +module_param(irqtl, uint, 0644); >> +MODULE_PARM_DESC(irqtl, >> +" PCIe FD IRQ Time Limit (default=" __stringify(CANFD_CTL_IRQ_TL_DEF) ")"); >> + >> +#ifdef PCIEFD_USES_MSI >> + >> +#ifdef CONFIG_XENO_OPT_SHIRQ >> +/* default behaviour: run as mainline driver in INTx mode */ >> +#define PCIEFD_USEMSI_DEFAULT 0 >> +#else >> +/* default behaviour: run in MSI mode (one IRQ per channel) */ >> +#define PCIEFD_USEMSI_DEFAULT 1 >> +#endif >> + >> +static uint usemsi = PCIEFD_USEMSI_DEFAULT; >> +module_param(usemsi, uint, 0644); >> +MODULE_PARM_DESC(usemsi, >> +" 0=INTA; 1=MSI (def=" __stringify(PCIEFD_USEMSI_DEFAULT) ")"); >> +#endif >> + >> +#ifdef CONFIG_XENO_DRIVERS_CAN_DEBUG >> +#include "rtcan_internal.h" >> + >> +static int rtcan_peak_pciefd_proc_regs(struct seq_file *p, void *data) >> +{ >> + struct rtcan_device *rdev = (struct rtcan_device *)data; >> + struct pciefd_can *priv = rdev->priv; >> + u16 i; >> + >> + seq_printf(p, "CANFD registers at %p\n", priv->reg_base); >> + >> + for (i = 0; i < 0x80; i += 8) { >> + u32 l = readl(priv->reg_base + i); >> + u32 h = readl(priv->reg_base + i + 4); >> + >> + seq_printf(p, "%04x: %08x-%08x\n", i, l, h); >> + } >> + >> + return 0; >> +} >> + >> +static int rtcan_peak_pciefd_proc_regs_open(struct inode *inode, >> + struct file *file) >> +{ >> + return single_open(file, rtcan_peak_pciefd_proc_regs, PDE_DATA(inode)); >> +} >> + >> +static const struct file_operations rtcan_peak_pciefd_proc_regs_ops = { >> + .open = rtcan_peak_pciefd_proc_regs_open, >> + .read = seq_read, >> + .llseek = seq_lseek, >> + .release = single_release, >> +}; >> + >> +int rtcan_peak_pciefd_create_proc(struct rtcan_device *rdev) >> +{ >> + if (!rdev->proc_root) >> + return -EINVAL; >> + >> + proc_create_data("registers", S_IFREG | 0644, >> + rdev->proc_root, >> + &rtcan_peak_pciefd_proc_regs_ops, rdev); >> + return 0; >> +} >> + >> +void rtcan_peak_pciefd_remove_proc(struct rtcan_device *rdev) >> +{ >> + if (!rdev->proc_root) >> + return; >> + >> + remove_proc_entry("registers", rdev->proc_root); >> +} >> + >> +#else /* !CONFIG_XENO_DRIVERS_CAN_DEBUG */ >> + >> +void rtcan_peak_pciefd_remove_proc(struct rtcan_device *dev) >> +{ >> +} >> + >> +int rtcan_peak_pciefd_create_proc(struct rtcan_device *dev) >> +{ >> + return 0; >> +} >> +#endif /* CONFIG_XENO_DRIVERS_CAN_DEBUG */ >> + >> +/* read a 32 bit value from a SYS block register */ >> +static inline u32 pciefd_sys_readreg(const struct pciefd_board *priv, u16 >> reg) >> +{ >> + return readl(priv->reg_base + reg); >> +} >> + >> +/* write a 32 bit value into a SYS block register */ >> +static inline void pciefd_sys_writereg(const struct pciefd_board *priv, >> + u32 val, u16 reg) >> +{ >> + writel(val, priv->reg_base + reg); >> +} >> + >> +/* read a 32 bits value from CAN-FD block register */ >> +static inline u32 pciefd_can_readreg(const struct pciefd_can *priv, u16 reg) >> +{ >> + return readl(priv->reg_base + reg); >> +} >> + >> +/* write a 32 bits value into a CAN-FD block register */ >> +static inline void pciefd_can_writereg(const struct pciefd_can *priv, >> + u32 val, u16 reg) >> +{ >> + writel(val, priv->reg_base + reg); >> +} >> + >> +/* give a channel logical Rx DMA address to the board */ >> +static void pciefd_can_setup_rx_dma(struct pciefd_can *priv) >> +{ >> +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT >> + const u32 dma_addr_h = (u32)(priv->rx_dma_laddr >> 32); >> +#else >> + const u32 dma_addr_h = 0; >> +#endif >> + >> + /* (DMA must be reset for Rx) */ >> + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, >> + PCIEFD_REG_CAN_RX_CTL_SET); >> + >> + /* write the logical address of the Rx DMA area for this channel */ >> + pciefd_can_writereg(priv, (u32)priv->rx_dma_laddr, >> + PCIEFD_REG_CAN_RX_DMA_ADDR_L); >> + pciefd_can_writereg(priv, dma_addr_h, PCIEFD_REG_CAN_RX_DMA_ADDR_H); >> + >> + /* also indicates that Rx DMA is cacheable */ >> + pciefd_can_writereg(priv, CANFD_CTL_UNC_BIT, >> + PCIEFD_REG_CAN_RX_CTL_CLR); >> +} >> + >> +/* clear channel logical Rx DMA address from the board */ >> +static void pciefd_can_clear_rx_dma(struct pciefd_can *priv) >> +{ >> + /* DMA must be reset for Rx */ >> + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, >> + PCIEFD_REG_CAN_RX_CTL_SET); >> + >> + /* clear the logical address of the Rx DMA area for this channel */ >> + pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_RX_DMA_ADDR_L); >> + pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_RX_DMA_ADDR_H); >> +} >> + >> +/* give a channel logical Tx DMA address to the board */ >> +static void pciefd_can_setup_tx_dma(struct pciefd_can *priv) >> +{ >> +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT >> + const u32 dma_addr_h = (u32)(priv->tx_dma_laddr >> 32); >> +#else >> + const u32 dma_addr_h = 0; >> +#endif >> + >> + /* (DMA must be reset for Tx) */ >> + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, >> + PCIEFD_REG_CAN_TX_CTL_SET); >> + >> + /* write the logical address of the Tx DMA area for this channel */ >> + pciefd_can_writereg(priv, (u32)priv->tx_dma_laddr, >> + PCIEFD_REG_CAN_TX_DMA_ADDR_L); >> + pciefd_can_writereg(priv, dma_addr_h, PCIEFD_REG_CAN_TX_DMA_ADDR_H); >> + >> + /* also indicates that Tx DMA is cacheable */ >> + pciefd_can_writereg(priv, CANFD_CTL_UNC_BIT, >> + PCIEFD_REG_CAN_TX_CTL_CLR); >> +} >> + >> +/* clear channel logical Tx DMA address from the board */ >> +static void pciefd_can_clear_tx_dma(struct pciefd_can *priv) >> +{ >> + /* DMA must be reset for Tx */ >> + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, >> + PCIEFD_REG_CAN_TX_CTL_SET); >> + >> + /* clear the logical address of the Tx DMA area for this channel */ >> + pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_TX_DMA_ADDR_L); >> + pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_TX_DMA_ADDR_H); >> +} >> + >> +/* acknowledge interrupt to the device */ >> +static void pciefd_can_ack_rx_dma(struct pciefd_can *priv) >> +{ >> + /* read value of current IRQ tag and inc it for next one */ >> + priv->irq_tag = le32_to_cpu(*(__le32 *)priv->rx_dma_vaddr); >> + priv->irq_tag++; >> + priv->irq_tag &= 0xf; >> + >> + /* write the next IRQ tag for this CAN */ >> + pciefd_can_writereg(priv, priv->irq_tag, PCIEFD_REG_CAN_RX_CTL_ACK); >> +} >> + >> +/* IRQ handler */ >> +static int pciefd_irq_handler(rtdm_irq_t *irq_handle) >> +{ >> + struct pciefd_can *priv = rtdm_irq_get_arg(irq_handle, void); >> + struct pciefd_rx_dma *rx_dma = priv->rx_dma_vaddr; >> + >> + /* INTA mode only, dummy read to sync with PCIe transaction */ >> + if (!pci_dev_msi_enabled(priv->board->pci_dev)) >> + (void)pciefd_sys_readreg(priv->board, PCIEFD_REG_SYS_VER1); >> + >> + /* read IRQ status from the first 32-bit of the Rx DMA area */ >> + priv->irq_status = le32_to_cpu(rx_dma->irq_status); >> + >> + /* check if this (shared) IRQ is for this CAN */ >> + if (pciefd_irq_tag(priv->irq_status) != priv->irq_tag) >> + return RTDM_IRQ_NONE; >> + >> + /* handle rx messages (if any) */ >> + peak_canfd_handle_msgs_list(&priv->ucan, >> + rx_dma->msg, >> + pciefd_irq_rx_cnt(priv->irq_status)); >> + >> + /* handle tx link interrupt (if any) */ >> + if (pciefd_irq_is_lnk(priv->irq_status)) { >> + rtdm_lock_get(&priv->tx_lock); >> + priv->tx_pages_free++; >> + rtdm_lock_put(&priv->tx_lock); >> + >> + /* Wake up a sender */ >> + rtdm_sem_up(&priv->ucan.rdev->tx_sem); >> + } >> + >> + /* re-enable Rx DMA transfer for this CAN */ >> + pciefd_can_ack_rx_dma(priv); >> + >> + return RTDM_IRQ_HANDLED; >> +} >> + >> +/* initialize structures used for sending CAN frames */ >> +static int pciefd_enable_tx_path(struct peak_canfd_priv *ucan) >> +{ >> + struct pciefd_can *priv = (struct pciefd_can *)ucan; >> + int i; >> + >> + /* initialize the Tx pages descriptors */ >> + priv->tx_pages_free = PCIEFD_TX_PAGE_COUNT - 1; >> + priv->tx_page_index = 0; >> + >> + priv->tx_pages[0].vbase = priv->tx_dma_vaddr; >> + priv->tx_pages[0].lbase = priv->tx_dma_laddr; >> + >> + for (i = 0; i < PCIEFD_TX_PAGE_COUNT; i++) { >> + priv->tx_pages[i].offset = 0; >> + priv->tx_pages[i].size = PCIEFD_TX_PAGE_SIZE - >> + sizeof(struct pciefd_tx_link); >> + if (i) { >> + priv->tx_pages[i].vbase = >> + priv->tx_pages[i - 1].vbase + >> + PCIEFD_TX_PAGE_SIZE; >> + priv->tx_pages[i].lbase = >> + priv->tx_pages[i - 1].lbase + >> + PCIEFD_TX_PAGE_SIZE; >> + } >> + } >> + >> + /* setup Tx DMA addresses into IP core */ >> + pciefd_can_setup_tx_dma(priv); >> + >> + /* start (TX_RST=0) Tx Path */ >> + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, >> + PCIEFD_REG_CAN_TX_CTL_CLR); >> + >> + return 0; >> +} >> + >> +/* board specific command pre-processing */ >> +static int pciefd_pre_cmd(struct peak_canfd_priv *ucan) >> +{ >> + struct pciefd_can *priv = (struct pciefd_can *)ucan; >> + u16 cmd = pucan_cmd_get_opcode(&priv->pucan_cmd); >> + >> + /* pre-process command */ >> + switch (cmd) { >> + case PUCAN_CMD_NORMAL_MODE: >> + case PUCAN_CMD_LISTEN_ONLY_MODE: >> + >> + if (ucan->rdev->state == CAN_STATE_BUS_OFF) >> + break; >> + >> + /* setup Rx DMA address */ >> + pciefd_can_setup_rx_dma(priv); >> + >> + /* setup max count of msgs per IRQ */ >> + pciefd_can_writereg(priv, (irqtl << 8) | irqcl, >> + PCIEFD_REG_CAN_RX_CTL_WRT); >> + >> + /* clear DMA RST for Rx (Rx start) */ >> + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, >> + PCIEFD_REG_CAN_RX_CTL_CLR); >> + >> + /* reset timestamps */ >> + pciefd_can_writereg(priv, !CANFD_MISC_TS_RST, >> + PCIEFD_REG_CAN_MISC); >> + >> + /* do an initial ACK */ >> + pciefd_can_ack_rx_dma(priv); >> + >> + /* enable IRQ for this CAN after having set next irq_tag */ >> + pciefd_can_writereg(priv, CANFD_CTL_IEN_BIT, >> + PCIEFD_REG_CAN_RX_CTL_SET); >> + >> + /* Tx path will be setup as soon as RX_BARRIER is received */ >> + break; >> + default: >> + break; >> + } >> + >> + return 0; >> +} >> + >> +/* write a command */ >> +static int pciefd_write_cmd(struct peak_canfd_priv *ucan) >> +{ >> + struct pciefd_can *priv = (struct pciefd_can *)ucan; >> + unsigned long flags; >> + >> + /* 64-bit command must be atomic */ >> + rtdm_lock_get_irqsave(&priv->board->cmd_lock, flags); >> + >> + pciefd_can_writereg(priv, *(u32 *)ucan->cmd_buffer, >> + PCIEFD_REG_CAN_CMD_PORT_L); >> + pciefd_can_writereg(priv, *(u32 *)(ucan->cmd_buffer + 4), >> + PCIEFD_REG_CAN_CMD_PORT_H); >> + >> + rtdm_lock_put_irqrestore(&priv->board->cmd_lock, flags); >> + >> + return 0; >> +} >> + >> +/* board specific command post-processing */ >> +static int pciefd_post_cmd(struct peak_canfd_priv *ucan) >> +{ >> + struct pciefd_can *priv = (struct pciefd_can *)ucan; >> + u16 cmd = pucan_cmd_get_opcode(&priv->pucan_cmd); >> + >> + switch (cmd) { >> + case PUCAN_CMD_RESET_MODE: >> + >> + if (ucan->rdev->state == CAN_STATE_STOPPED) >> + break; >> + >> + /* controller now in reset mode: disable IRQ for this CAN */ >> + pciefd_can_writereg(priv, CANFD_CTL_IEN_BIT, >> + PCIEFD_REG_CAN_RX_CTL_CLR); >> + >> + /* stop and reset DMA addresses in Tx/Rx engines */ >> + pciefd_can_clear_tx_dma(priv); >> + pciefd_can_clear_rx_dma(priv); >> + >> + /* wait for above commands to complete (read cycle) */ >> + (void)pciefd_sys_readreg(priv->board, PCIEFD_REG_SYS_VER1); >> + >> + ucan->rdev->state = CAN_STATE_STOPPED; >> + >> + break; >> + } >> + >> + return 0; >> +} >> + >> +/* allocate enough room into the Tx dma area to store a CAN message */ >> +static void *pciefd_alloc_tx_msg(struct peak_canfd_priv *ucan, u16 msg_size, >> + int *room_left) >> +{ >> + struct pciefd_can *priv = (struct pciefd_can *)ucan; >> + struct pciefd_page *page = priv->tx_pages + priv->tx_page_index; >> + unsigned long flags; >> + void *msg; >> + >> + rtdm_lock_get_irqsave(&priv->tx_lock, flags); >> + >> + if (page->offset + msg_size > page->size) { >> + struct pciefd_tx_link *lk; >> + >> + /* not enough space in this page: try another one */ >> + if (!priv->tx_pages_free) { >> + rtdm_lock_put_irqrestore(&priv->tx_lock, flags); >> + /* Tx overflow */ >> + return NULL; >> + } >> + >> + priv->tx_pages_free--; >> + >> + /* keep address of the very last free slot of current page */ >> + lk = page->vbase + page->offset; >> + >> + /* next, move on a new free page */ >> + priv->tx_page_index = (priv->tx_page_index + 1) % >> + PCIEFD_TX_PAGE_COUNT; >> + page = priv->tx_pages + priv->tx_page_index; >> + >> + /* put link record to this new page at the end of prev one */ >> + lk->size = cpu_to_le16(sizeof(*lk)); >> + lk->type = cpu_to_le16(CANFD_MSG_LNK_TX); >> + lk->laddr_lo = cpu_to_le32(page->lbase); >> + >> +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT >> + lk->laddr_hi = cpu_to_le32(page->lbase >> 32); >> +#else >> + lk->laddr_hi = 0; >> +#endif >> + /* next msgs will be put from the begininng of this new page */ >> + page->offset = 0; >> + } >> + >> + *room_left = priv->tx_pages_free * page->size; >> + >> + rtdm_lock_put_irqrestore(&priv->tx_lock, flags); >> + >> + msg = page->vbase + page->offset; >> + >> + /* give back room left in the tx ring */ >> + *room_left += page->size - (page->offset + msg_size); >> + >> + return msg; >> +} >> + >> +/* tell the IP core tha a frame has been written into the Tx DMA area */ >> +static int pciefd_write_tx_msg(struct peak_canfd_priv *ucan, >> + struct pucan_tx_msg *msg) >> +{ >> + struct pciefd_can *priv = (struct pciefd_can *)ucan; >> + struct pciefd_page *page = priv->tx_pages + priv->tx_page_index; >> + >> + /* this slot is now reserved for writing the frame */ >> + page->offset += le16_to_cpu(msg->size); >> + >> + /* tell the board a frame has been written in Tx DMA area */ >> + pciefd_can_writereg(priv, 1, PCIEFD_REG_CAN_TX_REQ_ACC); >> + >> + return 0; >> +} >> + >> +/* probe for CAN channel number #pciefd_board->can_count */ >> +static int pciefd_can_probe(struct pciefd_board *pciefd) >> +{ >> + struct rtcan_device *rdev; >> + struct pciefd_can *priv; >> + u32 clk; >> + int err; >> + >> + /* allocate the RTCAN object */ >> + rdev = alloc_peak_canfd_dev(sizeof(*priv), pciefd->can_count); >> + if (!rdev) { >> + dev_err(&pciefd->pci_dev->dev, >> + "failed to alloc RTCAN device object\n"); >> + goto failure; >> + } >> + >> + /* fill-in board specific parts */ >> + rdev->board_name = pciefd_board_name; >> + >> + /* fill-in rtcan private object */ >> + priv = rdev->priv; >> + >> + /* setup PCIe-FD own callbacks */ >> + priv->ucan.pre_cmd = pciefd_pre_cmd; >> + priv->ucan.write_cmd = pciefd_write_cmd; >> + priv->ucan.post_cmd = pciefd_post_cmd; >> + priv->ucan.enable_tx_path = pciefd_enable_tx_path; >> + priv->ucan.alloc_tx_msg = pciefd_alloc_tx_msg; >> + priv->ucan.write_tx_msg = pciefd_write_tx_msg; >> + >> + /* setup PCIe-FD own command buffer */ >> + priv->ucan.cmd_buffer = &priv->pucan_cmd; >> + priv->ucan.cmd_maxlen = sizeof(priv->pucan_cmd); >> + >> + priv->board = pciefd; >> + >> + /* CAN config regs block address */ >> + priv->reg_base = pciefd->reg_base + PCIEFD_CANX_OFF(priv->ucan.index); >> + rdev->base_addr = (unsigned long)priv->reg_base; >> + >> + /* allocate non-cacheable DMA'able 4KB memory area for Rx */ >> + priv->rx_dma_vaddr = dmam_alloc_coherent(&pciefd->pci_dev->dev, >> + PCIEFD_RX_DMA_SIZE, >> + &priv->rx_dma_laddr, >> + GFP_KERNEL); >> + if (!priv->rx_dma_vaddr) { >> + dev_err(&pciefd->pci_dev->dev, >> + "Rx dmam_alloc_coherent(%u) failure\n", >> + PCIEFD_RX_DMA_SIZE); >> + goto err_free_rtdev; >> + } >> + >> + /* allocate non-cacheable DMA'able 4KB memory area for Tx */ >> + priv->tx_dma_vaddr = dmam_alloc_coherent(&pciefd->pci_dev->dev, >> + PCIEFD_TX_DMA_SIZE, >> + &priv->tx_dma_laddr, >> + GFP_KERNEL); >> + if (!priv->tx_dma_vaddr) { >> + dev_err(&pciefd->pci_dev->dev, >> + "Tx dmam_alloc_coherent(%u) failure\n", >> + PCIEFD_TX_DMA_SIZE); >> + goto err_free_rtdev; >> + } >> + >> + /* CAN clock in RST mode */ >> + pciefd_can_writereg(priv, CANFD_MISC_TS_RST, PCIEFD_REG_CAN_MISC); >> + >> + /* read current clock value */ >> + clk = pciefd_can_readreg(priv, PCIEFD_REG_CAN_CLK_SEL); >> + switch (clk) { >> + case CANFD_CLK_SEL_20MHZ: >> + priv->ucan.rdev->can_sys_clock = 20 * 1000 * 1000; >> + break; >> + case CANFD_CLK_SEL_24MHZ: >> + priv->ucan.rdev->can_sys_clock = 24 * 1000 * 1000; >> + break; >> + case CANFD_CLK_SEL_30MHZ: >> + priv->ucan.rdev->can_sys_clock = 30 * 1000 * 1000; >> + break; >> + case CANFD_CLK_SEL_40MHZ: >> + priv->ucan.rdev->can_sys_clock = 40 * 1000 * 1000; >> + break; >> + case CANFD_CLK_SEL_60MHZ: >> + priv->ucan.rdev->can_sys_clock = 60 * 1000 * 1000; >> + break; >> + default: >> + pciefd_can_writereg(priv, CANFD_CLK_SEL_80MHZ, >> + PCIEFD_REG_CAN_CLK_SEL); >> + >> + /* fallthrough */ >> + case CANFD_CLK_SEL_80MHZ: >> + priv->ucan.rdev->can_sys_clock = 80 * 1000 * 1000; >> + break; >> + } >> + >> +#ifdef PCIEFD_USES_MSI >> + priv->irq = (pciefd->irq_flags & IRQF_SHARED) ? >> + pciefd->pci_dev->irq : >> + pci_irq_vector(pciefd->pci_dev, priv->ucan.index); >> +#else >> + priv->irq = pciefd->pci_dev->irq; >> +#endif >> + >> + /* setup irq handler */ >> + err = rtdm_irq_request(&rdev->irq_handle, >> + priv->irq, >> + pciefd_irq_handler, >> + pciefd->irq_flags, >> + DRV_NAME, >> + priv); >> + if (err) { >> + dev_err(&pciefd->pci_dev->dev, >> + "rtdm_irq_request(IRQ%u) failure err %d\n", >> + priv->irq, err); >> + goto err_free_rtdev; >> + } >> + >> + err = rtcan_dev_register(rdev); >> + if (err) { >> + dev_err(&pciefd->pci_dev->dev, >> + "couldn't register RTCAN device: %d\n", err); >> + goto err_free_irq; >> + } >> + >> + rtdm_lock_init(&priv->tx_lock); >> + >> + /* save the object address in the board structure */ >> + pciefd->can[pciefd->can_count] = priv; >> + >> + rtcan_peak_pciefd_create_proc(rdev); >> + >> + dev_info(&pciefd->pci_dev->dev, "%s at reg_base=0x%p irq=%d\n", >> + rdev->name, priv->reg_base, priv->irq); >> + >> + return 0; >> + >> +err_free_irq: >> + rtdm_irq_free(&rdev->irq_handle); >> + >> +err_free_rtdev: >> + rtcan_dev_free(rdev); >> + >> +failure: >> + return -ENOMEM; >> +} >> + >> +/* wakeup all RT tasks that are blocked on read */ >> +static void pciefd_can_unlock_recv_tasks(struct rtcan_device *rdev) >> +{ >> + struct rtcan_recv *recv_listener = rdev->recv_list; >> + >> + while (recv_listener) { >> + struct rtcan_socket *sock = recv_listener->sock; >> + >> + /* wakeup any rx task */ >> + rtdm_sem_destroy(&sock->recv_sem); >> + >> + recv_listener = recv_listener->next; >> + } >> +} >> + >> +/* remove a CAN-FD channel by releasing all of its resources */ >> +static void pciefd_can_remove(struct pciefd_can *priv) >> +{ >> + struct rtcan_device *rdev = priv->ucan.rdev; >> + >> + /* unlock any tasks that wait for read on a socket bound to this CAN */ >> + pciefd_can_unlock_recv_tasks(rdev); >> + >> + /* in case the driver is removed when the interface is UP >> + * (device MUST be closed before being unregistered) >> + */ >> + rdev->do_set_mode(rdev, CAN_MODE_STOP, NULL); >> + >> + rtcan_peak_pciefd_remove_proc(rdev); >> + rtcan_dev_unregister(rdev); >> + rtdm_irq_disable(&rdev->irq_handle); >> + rtdm_irq_free(&rdev->irq_handle); >> + rtcan_dev_free(rdev); >> +} >> + >> +/* remove all CAN-FD channels by releasing their own resources */ >> +static void pciefd_can_remove_all(struct pciefd_board *pciefd) >> +{ >> + while (pciefd->can_count > 0) >> + pciefd_can_remove(pciefd->can[--pciefd->can_count]); >> +} >> + >> +/* probe for the entire device */ >> +static int peak_pciefd_probe(struct pci_dev *pdev, >> + const struct pci_device_id *ent) >> +{ >> + struct pciefd_board *pciefd; >> + int err, can_count; >> + u16 sub_sys_id; >> + u8 hw_ver_major; >> + u8 hw_ver_minor; >> + u8 hw_ver_sub; >> + u32 v2; >> + >> + err = pci_enable_device(pdev); >> + if (err) >> + return err; >> + >> + err = pci_request_regions(pdev, DRV_NAME); >> + if (err) >> + goto err_disable_pci; >> + >> + /* the number of channels depends on sub-system id */ >> + err = pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &sub_sys_id); >> + if (err) >> + goto err_release_regions; >> + >> + dev_dbg(&pdev->dev, "probing device %04x:%04x:%04x\n", >> + pdev->vendor, pdev->device, sub_sys_id); >> + >> + if (sub_sys_id >= 0x0012) >> + can_count = 4; >> + else if (sub_sys_id >= 0x0010) >> + can_count = 3; >> + else if (sub_sys_id >= 0x0004) >> + can_count = 2; >> + else >> + can_count = 1; >> + >> + /* allocate board structure object */ >> + pciefd = devm_kzalloc(&pdev->dev, struct_size(pciefd, can, can_count), >> + GFP_KERNEL); >> + if (!pciefd) { >> + err = -ENOMEM; >> + goto err_release_regions; >> + } >> + >> + /* initialize the board structure */ >> + pciefd->pci_dev = pdev; >> + rtdm_lock_init(&pciefd->cmd_lock); >> + >> + /* save the PCI BAR0 virtual address for further system regs access */ >> + pciefd->reg_base = pci_iomap(pdev, 0, PCIEFD_BAR0_SIZE); >> + if (!pciefd->reg_base) { >> + dev_err(&pdev->dev, "failed to map PCI resource #0\n"); >> + err = -ENOMEM; >> + goto err_release_regions; >> + } >> + >> + /* read the firmware version number */ >> + v2 = pciefd_sys_readreg(pciefd, PCIEFD_REG_SYS_VER2); >> + >> + hw_ver_major = (v2 & 0x0000f000) >> 12; >> + hw_ver_minor = (v2 & 0x00000f00) >> 8; >> + hw_ver_sub = (v2 & 0x000000f0) >> 4; >> + >> + dev_info(&pdev->dev, >> + "%ux CAN-FD PCAN-PCIe FPGA v%u.%u.%u:\n", can_count, >> + hw_ver_major, hw_ver_minor, hw_ver_sub); >> + >> +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT >> + /* DMA logic doesn't handle mix of 32-bit and 64-bit logical addresses >> + * in fw <= 3.2.x >> + */ >> + if (PCIEFD_FW_VERSION(hw_ver_major, hw_ver_minor, hw_ver_sub) < >> + PCIEFD_FW_VERSION(3, 3, 0)) { >> + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); >> + if (err) >> + dev_warn(&pdev->dev, >> + "warning: can't set DMA mask %llxh (err %d)\n", >> + DMA_BIT_MASK(32), err); >> + } >> +#endif >> + >> + /* default interrupt mode is: shared INTx */ >> + pciefd->irq_flags = IRQF_SHARED; >> + >> +#ifdef PCIEFD_USES_MSI >> + if (usemsi) { >> + err = pci_msi_vec_count(pdev); >> + if (err > 0) { >> + int msi_maxvec = err; >> + >> + err = pci_alloc_irq_vectors_affinity(pdev, can_count, >> + msi_maxvec, >> + PCI_IRQ_MSI, >> + NULL); >> + dev_info(&pdev->dev, >> + "MSI[%u..%u] enabling status: %d\n", >> + can_count, msi_maxvec, err); >> + >> + /* if didn't get the requested count of MSI, fall back >> + * to INTx >> + */ >> + if (err >= can_count) >> + pciefd->irq_flags &= ~IRQF_SHARED; >> + else if (err >= 0) >> + pci_free_irq_vectors(pdev); >> + } >> + } >> +#endif >> + >> + /* stop system clock */ >> + pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_CLK_EN, >> + PCIEFD_REG_SYS_CTL_CLR); >> + >> + pci_set_master(pdev); >> + >> + /* create now the corresponding channels objects */ >> + while (pciefd->can_count < can_count) { >> + err = pciefd_can_probe(pciefd); >> + if (err) >> + goto err_free_canfd; >> + >> + pciefd->can_count++; >> + } >> + >> + /* set system timestamps counter in RST mode */ >> + pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_TS_RST, >> + PCIEFD_REG_SYS_CTL_SET); >> + >> + /* wait a bit (read cycle) */ >> + (void)pciefd_sys_readreg(pciefd, PCIEFD_REG_SYS_VER1); >> + >> + /* free all clocks */ >> + pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_TS_RST, >> + PCIEFD_REG_SYS_CTL_CLR); >> + >> + /* start system clock */ >> + pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_CLK_EN, >> + PCIEFD_REG_SYS_CTL_SET); >> + >> + /* remember the board structure address in the device user data */ >> + pci_set_drvdata(pdev, pciefd); >> + >> + return 0; >> + >> +err_free_canfd: >> + pciefd_can_remove_all(pciefd); >> + >> +#ifdef PCIEFD_USES_MSI >> + pci_free_irq_vectors(pdev); >> +#endif >> + pci_iounmap(pdev, pciefd->reg_base); >> + >> +err_release_regions: >> + pci_release_regions(pdev); >> + >> +err_disable_pci: >> + pci_disable_device(pdev); >> + >> + /* pci_xxx_config_word() return positive PCIBIOS_xxx error codes while >> + * the probe() function must return a negative errno in case of failure >> + * (err is unchanged if negative) >> + */ >> + return pcibios_err_to_errno(err); >> +} >> + >> +/* free the board structure object, as well as its resources: */ >> +static void peak_pciefd_remove(struct pci_dev *pdev) >> +{ >> + struct pciefd_board *pciefd = pci_get_drvdata(pdev); >> + >> + /* release CAN-FD channels resources */ >> + pciefd_can_remove_all(pciefd); >> + >> +#ifdef PCIEFD_USES_MSI >> + pci_free_irq_vectors(pdev); >> +#endif >> + pci_iounmap(pdev, pciefd->reg_base); >> + >> + pci_release_regions(pdev); >> + pci_disable_device(pdev); >> +} >> + >> +static struct pci_driver rtcan_peak_pciefd_driver = { >> + .name = DRV_NAME, >> + .id_table = peak_pciefd_tbl, >> + .probe = peak_pciefd_probe, >> + .remove = peak_pciefd_remove, >> +}; >> + >> +static int __init rtcan_peak_pciefd_init(void) >> +{ >> + if (!realtime_core_enabled()) >> + return 0; >> + >> + return pci_register_driver(&rtcan_peak_pciefd_driver); >> +} >> + >> +static void __exit rtcan_peak_pciefd_exit(void) >> +{ >> + if (realtime_core_enabled()) >> + pci_unregister_driver(&rtcan_peak_pciefd_driver); >> +} >> + >> +module_init(rtcan_peak_pciefd_init); >> +module_exit(rtcan_peak_pciefd_exit); >> > > Scanned through the code, and looks really good as far as I can assess. > Just one question: Did you also test it with Xenomai and I-pipe runtime > debug checks enabled? To make sure that no invalid context calls are > missed. I have no suspect, but I'm also no longer familiar with details > of the RTCAN subsystem. > > Jan > > -- > Siemens AG, T RDA IOT > Corporate Competence Center Embedded Linux > > -- > PEAK-System Technik GmbH > Sitz der Gesellschaft Darmstadt - HRB 9183 > Geschaeftsfuehrung: Alexander Gach / Uwe Wilhelm > Unsere Datenschutzerklaerung mit wichtigen Hinweisen > zur Behandlung personenbezogener Daten finden Sie unter > www.peak-system.com/Datenschutz.483.0.html > -- Siemens AG, T RDA IOT Corporate Competence Center Embedded Linux