diff --git a/drivers/can/CMakeLists.txt b/drivers/can/CMakeLists.txt index a28ca83df69..775ca34eb2b 100644 --- a/drivers/can/CMakeLists.txt +++ b/drivers/can/CMakeLists.txt @@ -31,6 +31,7 @@ endif() zephyr_library_sources_ifdef(CONFIG_CAN_SJA1000 can_sja1000.c) zephyr_library_sources_ifdef(CONFIG_CAN_ESP32_TWAI can_esp32_twai.c) +zephyr_library_sources_ifdef(CONFIG_CAN_KVASER_PCI can_kvaser_pci.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE can_handlers.c) zephyr_library_sources_ifdef(CONFIG_CAN_SHELL can_shell.c) diff --git a/drivers/can/Kconfig b/drivers/can/Kconfig index 1916498ab05..2cfa3aa1afc 100644 --- a/drivers/can/Kconfig +++ b/drivers/can/Kconfig @@ -101,6 +101,7 @@ source "drivers/can/Kconfig.loopback" source "drivers/can/Kconfig.native_posix_linux" source "drivers/can/Kconfig.sja1000" source "drivers/can/Kconfig.esp32" +source "drivers/can/Kconfig.kvaser" source "drivers/can/transceiver/Kconfig" diff --git a/drivers/can/Kconfig.kvaser b/drivers/can/Kconfig.kvaser new file mode 100644 index 00000000000..0689364dba2 --- /dev/null +++ b/drivers/can/Kconfig.kvaser @@ -0,0 +1,13 @@ +# Kvaser PCIcan configuration options + +# Copyright (c) 2022 Henrik Brix Andersen +# SPDX-License-Identifier: Apache-2.0 + +config CAN_KVASER_PCI + bool "Kvaser PCIcan driver" + default y + depends on DT_HAS_KVASER_PCICAN_ENABLED + select PCIE + select CAN_SJA1000 + help + This enables support for the Kvaser PCIcan. diff --git a/drivers/can/can_kvaser_pci.c b/drivers/can/can_kvaser_pci.c new file mode 100644 index 00000000000..733a18c6f4f --- /dev/null +++ b/drivers/can/can_kvaser_pci.c @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2022 Henrik Brix Andersen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT kvaser_pcican + +#include "can_sja1000.h" + +#include +#include +#include +#include + +LOG_MODULE_REGISTER(can_kvaser_pci, CONFIG_CAN_LOG_LEVEL); + +/* AMCC S5920 I/O BAR registers */ +#define S5920_INTCSR_REG 0x38 +#define S5920_INTCSR_ADDINT_EN BIT(13) +#define S5920_PTCR_REG 0x60 + +/* Xilinx I/O BAR registers */ +#define XLNX_VERINT_REG 0x07 +#define XLNX_VERINT_VERSION_POS 4U + +struct can_kvaser_pci_config { + pcie_bdf_t pcie_bdf; + pcie_id_t pcie_id; + void (*irq_config_func)(const struct device *dev); +}; + +struct can_kvaser_pci_data { + io_port_t sja1000_base; +}; + +static uint8_t can_kvaser_pci_read_reg(const struct device *dev, uint8_t reg) +{ + struct can_sja1000_data *sja1000_data = dev->data; + struct can_kvaser_pci_data *kvaser_data = sja1000_data->custom; + io_port_t addr = kvaser_data->sja1000_base + reg; + + return sys_in8(addr); +} + +static void can_kvaser_pci_write_reg(const struct device *dev, uint8_t reg, uint8_t val) +{ + struct can_sja1000_data *sja1000_data = dev->data; + struct can_kvaser_pci_data *kvaser_data = sja1000_data->custom; + io_port_t addr = kvaser_data->sja1000_base + reg; + + sys_out8(val, addr); +} + +static int can_kvaser_pci_get_core_clock(const struct device *dev, uint32_t *rate) +{ + ARG_UNUSED(dev); + + /* The internal clock operates at half of the oscillator frequency */ + *rate = MHZ(16) / 2; + + return 0; +} + +static int can_kvaser_pci_init(const struct device *dev) +{ + const struct can_sja1000_config *sja1000_config = dev->config; + const struct can_kvaser_pci_config *kvaser_config = sja1000_config->custom; + struct can_sja1000_data *sja1000_data = dev->data; + struct can_kvaser_pci_data *kvaser_data = sja1000_data->custom; + struct pcie_bar iobar; + static io_port_t amcc_base; + static io_port_t xlnx_base; + uint32_t intcsr; + int err; + + if (!pcie_probe(kvaser_config->pcie_bdf, kvaser_config->pcie_id)) { + LOG_ERR("failed to probe PCI(e)"); + return -ENODEV; + } + + pcie_set_cmd(kvaser_config->pcie_bdf, PCIE_CONF_CMDSTAT_IO, true); + + /* AMCC S5920 registers */ + if (!pcie_probe_iobar(kvaser_config->pcie_bdf, 0, &iobar)) { + LOG_ERR("failed to probe AMCC S5920 I/O BAR"); + return -ENODEV; + } + + amcc_base = iobar.phys_addr; + + /* SJA1000 registers */ + if (!pcie_probe_iobar(kvaser_config->pcie_bdf, 1, &iobar)) { + LOG_ERR("failed to probe SJA1000 I/O BAR"); + return -ENODEV; + } + + kvaser_data->sja1000_base = iobar.phys_addr; + + /* Xilinx registers */ + if (!pcie_probe_iobar(kvaser_config->pcie_bdf, 2, &iobar)) { + LOG_ERR("failed to probe Xilinx I/O BAR"); + return -ENODEV; + } + + xlnx_base = iobar.phys_addr; + LOG_DBG("Xilinx version: %d", + sys_in8(xlnx_base + XLNX_VERINT_REG) >> XLNX_VERINT_VERSION_POS); + + /* + * Initialization sequence as per Kvaser PCIcan Hardware Reference Manual (UG 98048 + * v3.0.0). + */ + + /* AMCC S5920 PCI Pass-Thru Configuration Register (PTCR) */ + sys_out32(0x80808080UL, amcc_base + S5920_PTCR_REG); + + /* AMCC S5920 PCI Interrupt Control/Status Register (INTCSR) */ + intcsr = sys_in32(amcc_base + S5920_INTCSR_REG); + intcsr |= S5920_INTCSR_ADDINT_EN; + sys_out32(intcsr, amcc_base + S5920_INTCSR_REG); + + err = can_sja1000_init(dev); + if (err != 0) { + LOG_ERR("failed to initialize controller (err %d)", err); + return err; + } + + kvaser_config->irq_config_func(dev); + + return 0; +} + +const struct can_driver_api can_kvaser_pci_driver_api = { + .get_capabilities = can_sja1000_get_capabilities, + .start = can_sja1000_start, + .stop = can_sja1000_stop, + .set_mode = can_sja1000_set_mode, + .set_timing = can_sja1000_set_timing, + .send = can_sja1000_send, + .add_rx_filter = can_sja1000_add_rx_filter, + .remove_rx_filter = can_sja1000_remove_rx_filter, + .get_state = can_sja1000_get_state, + .set_state_change_callback = can_sja1000_set_state_change_callback, + .get_core_clock = can_kvaser_pci_get_core_clock, + .get_max_filters = can_sja1000_get_max_filters, + .get_max_bitrate = can_sja1000_get_max_bitrate, +#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY + .recover = can_sja1000_recover, +#endif /* !CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ + .timing_min = CAN_SJA1000_TIMING_MIN_INITIALIZER, + .timing_max = CAN_SJA1000_TIMING_MAX_INITIALIZER, +}; + +#define CAN_KVASER_PCI_OCR \ + (CAN_SJA1000_OCR_OCMODE_NORMAL | CAN_SJA1000_OCR_OCTN0 | CAN_SJA1000_OCR_OCTP0 | \ + CAN_SJA1000_OCR_OCTN1 | CAN_SJA1000_OCR_OCTP1) + +#define CAN_KVASER_PCI_CDR (CAN_SJA1000_CDR_CD_DIV2 | CAN_SJA1000_CDR_CLOCK_OFF) + +#define CAN_KVASER_PCI_INIT(inst) \ + static void can_kvaser_pci_config_func_##inst(const struct device *dev); \ + \ + static const struct can_kvaser_pci_config can_kvaser_pci_config_##inst = { \ + .pcie_bdf = DT_INST_REG_ADDR(inst), \ + .pcie_id = DT_INST_REG_SIZE(inst), \ + .irq_config_func = can_kvaser_pci_config_func_##inst \ + }; \ + \ + static const struct can_sja1000_config can_sja1000_config_##inst = \ + CAN_SJA1000_DT_CONFIG_INST_GET(inst, &can_kvaser_pci_config_##inst, \ + can_kvaser_pci_read_reg, can_kvaser_pci_write_reg, \ + CAN_KVASER_PCI_OCR, CAN_KVASER_PCI_CDR); \ + \ + static struct can_kvaser_pci_config can_kvaser_pci_data_##inst; \ + \ + static struct can_sja1000_data can_sja1000_data_##inst = \ + CAN_SJA1000_DATA_INITIALIZER(&can_kvaser_pci_data_##inst); \ + \ + DEVICE_DT_INST_DEFINE(inst, can_kvaser_pci_init, NULL, &can_sja1000_data_##inst, \ + &can_sja1000_config_##inst, POST_KERNEL, CONFIG_CAN_INIT_PRIORITY, \ + &can_kvaser_pci_driver_api); \ + \ + static void can_kvaser_pci_config_func_##inst(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), can_sja1000_isr, \ + DEVICE_DT_INST_GET(inst), DT_INST_IRQ(inst, sense)); \ + irq_enable(DT_INST_IRQN(inst)); \ + } + +DT_INST_FOREACH_STATUS_OKAY(CAN_KVASER_PCI_INIT)