diff options
Diffstat (limited to 'drivers/net/can')
30 files changed, 1385 insertions, 1620 deletions
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 3048ad77edb3..cd34e8dc9394 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -198,14 +198,6 @@ config CAN_XILINXCAN Xilinx CAN driver. This driver supports both soft AXI CAN IP and Zynq CANPS IP. -config PCH_CAN - tristate "Intel EG20T PCH CAN controller" - depends on PCI && (X86_32 || COMPILE_TEST) - help - This driver is for PCH CAN of Topcliff (Intel EG20T PCH) which - is an IOH for x86 embedded processor (Intel Atom E6xx series). - This driver can access CAN bus. - source "drivers/net/can/c_can/Kconfig" source "drivers/net/can/cc770/Kconfig" source "drivers/net/can/ctucanfd/Kconfig" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 61c75ce9d500..52b0f6e10668 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -30,6 +30,5 @@ obj-$(CONFIG_CAN_SJA1000) += sja1000/ obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o -obj-$(CONFIG_PCH_CAN) += pch_can.o subdir-ccflags-$(CONFIG_CAN_DEBUG_DEVICES) += -DDEBUG diff --git a/drivers/net/can/c_can/Kconfig b/drivers/net/can/c_can/Kconfig index 962725788b0a..1f0e9acb69ec 100644 --- a/drivers/net/can/c_can/Kconfig +++ b/drivers/net/can/c_can/Kconfig @@ -20,5 +20,6 @@ config CAN_C_CAN_PCI depends on PCI help This driver adds support for the C_CAN/D_CAN chips connected - to the PCI bus. + to the PCI bus. E.g. for the C_CAN controller IP inside the + Intel Atom E6xx series IOH (aka EG20T 'PCH CAN'). endif diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c index 86e95e9d6533..03ccb7cfacaf 100644 --- a/drivers/net/can/c_can/c_can_platform.c +++ b/drivers/net/can/c_can/c_can_platform.c @@ -290,8 +290,7 @@ static int c_can_plat_probe(struct platform_device *pdev) goto exit; } - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - addr = devm_ioremap_resource(&pdev->dev, mem); + addr = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); if (IS_ERR(addr)) { ret = PTR_ERR(addr); goto exit; diff --git a/drivers/net/can/ctucanfd/Kconfig b/drivers/net/can/ctucanfd/Kconfig index 6e2073351a8f..f52407f5c5d8 100644 --- a/drivers/net/can/ctucanfd/Kconfig +++ b/drivers/net/can/ctucanfd/Kconfig @@ -23,7 +23,7 @@ config CAN_CTUCANFD_PCI config CAN_CTUCANFD_PLATFORM tristate "CTU CAN-FD IP core platform (FPGA, SoC) driver" - depends on HAS_IOMEM && (OF || COMPILE_TEST) + depends on HAS_IOMEM && OF select CAN_CTUCANFD help The core has been tested together with OpenCores SJA1000 diff --git a/drivers/net/can/flexcan/flexcan-core.c b/drivers/net/can/flexcan/flexcan-core.c index 9bdadd716f4e..6d638c93977b 100644 --- a/drivers/net/can/flexcan/flexcan-core.c +++ b/drivers/net/can/flexcan/flexcan-core.c @@ -345,6 +345,15 @@ static struct flexcan_devtype_data fsl_imx8mp_devtype_data = { FLEXCAN_QUIRK_SUPPORT_RX_MAILBOX_RTR, }; +static struct flexcan_devtype_data fsl_imx93_devtype_data = { + .quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS | + FLEXCAN_QUIRK_DISABLE_MECR | FLEXCAN_QUIRK_USE_RX_MAILBOX | + FLEXCAN_QUIRK_BROKEN_PERR_STATE | FLEXCAN_QUIRK_AUTO_STOP_MODE | + FLEXCAN_QUIRK_SUPPORT_FD | FLEXCAN_QUIRK_SUPPORT_ECC | + FLEXCAN_QUIRK_SUPPORT_RX_MAILBOX | + FLEXCAN_QUIRK_SUPPORT_RX_MAILBOX_RTR, +}; + static const struct flexcan_devtype_data fsl_vf610_devtype_data = { .quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS | FLEXCAN_QUIRK_DISABLE_MECR | FLEXCAN_QUIRK_USE_RX_MAILBOX | @@ -532,9 +541,14 @@ static inline int flexcan_enter_stop_mode(struct flexcan_priv *priv) ret = flexcan_stop_mode_enable_scfw(priv, true); if (ret < 0) return ret; - } else { + } else if (priv->devtype_data.quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR) { regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr, 1 << priv->stm.req_bit, 1 << priv->stm.req_bit); + } else if (priv->devtype_data.quirks & FLEXCAN_QUIRK_AUTO_STOP_MODE) { + /* For the auto stop mode, software do nothing, hardware will cover + * all the operation automatically after system go into low power mode. + */ + return 0; } return flexcan_low_power_enter_ack(priv); @@ -551,7 +565,7 @@ static inline int flexcan_exit_stop_mode(struct flexcan_priv *priv) ret = flexcan_stop_mode_enable_scfw(priv, false); if (ret < 0) return ret; - } else { + } else if (priv->devtype_data.quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR) { regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr, 1 << priv->stm.req_bit, 0); } @@ -560,6 +574,12 @@ static inline int flexcan_exit_stop_mode(struct flexcan_priv *priv) reg_mcr &= ~FLEXCAN_MCR_SLF_WAK; priv->write(reg_mcr, ®s->mcr); + /* For the auto stop mode, hardware will exist stop mode + * automatically after system go out of low power mode. + */ + if (priv->devtype_data.quirks & FLEXCAN_QUIRK_AUTO_STOP_MODE) + return 0; + return flexcan_low_power_exit_ack(priv); } @@ -1974,6 +1994,8 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev) ret = flexcan_setup_stop_mode_scfw(pdev); else if (priv->devtype_data.quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR) ret = flexcan_setup_stop_mode_gpr(pdev); + else if (priv->devtype_data.quirks & FLEXCAN_QUIRK_AUTO_STOP_MODE) + ret = 0; else /* return 0 directly if doesn't support stop mode feature */ return 0; @@ -1992,6 +2014,7 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev) static const struct of_device_id flexcan_of_match[] = { { .compatible = "fsl,imx8qm-flexcan", .data = &fsl_imx8qm_devtype_data, }, { .compatible = "fsl,imx8mp-flexcan", .data = &fsl_imx8mp_devtype_data, }, + { .compatible = "fsl,imx93-flexcan", .data = &fsl_imx93_devtype_data, }, { .compatible = "fsl,imx6q-flexcan", .data = &fsl_imx6q_devtype_data, }, { .compatible = "fsl,imx28-flexcan", .data = &fsl_imx28_devtype_data, }, { .compatible = "fsl,imx53-flexcan", .data = &fsl_imx25_devtype_data, }, @@ -2299,8 +2322,16 @@ static int __maybe_unused flexcan_noirq_suspend(struct device *device) if (netif_running(dev)) { int err; - if (device_may_wakeup(device)) + if (device_may_wakeup(device)) { flexcan_enable_wakeup_irq(priv, true); + /* For auto stop mode, need to keep the clock on before + * system go into low power mode. After system go into + * low power mode, hardware will config the flexcan into + * stop mode, and gate off the clock automatically. + */ + if (priv->devtype_data.quirks & FLEXCAN_QUIRK_AUTO_STOP_MODE) + return 0; + } err = pm_runtime_force_suspend(device); if (err) @@ -2318,9 +2349,15 @@ static int __maybe_unused flexcan_noirq_resume(struct device *device) if (netif_running(dev)) { int err; - err = pm_runtime_force_resume(device); - if (err) - return err; + /* For the wakeup in auto stop mode, no need to gate on the + * clock here, hardware will do this automatically. + */ + if (!(device_may_wakeup(device) && + priv->devtype_data.quirks & FLEXCAN_QUIRK_AUTO_STOP_MODE)) { + err = pm_runtime_force_resume(device); + if (err) + return err; + } if (device_may_wakeup(device)) flexcan_enable_wakeup_irq(priv, false); diff --git a/drivers/net/can/flexcan/flexcan.h b/drivers/net/can/flexcan/flexcan.h index 025c3417031f..91402977780b 100644 --- a/drivers/net/can/flexcan/flexcan.h +++ b/drivers/net/can/flexcan/flexcan.h @@ -68,6 +68,8 @@ #define FLEXCAN_QUIRK_SUPPORT_RX_MAILBOX_RTR BIT(15) /* Device supports RX via FIFO */ #define FLEXCAN_QUIRK_SUPPORT_RX_FIFO BIT(16) +/* auto enter stop mode to support wakeup */ +#define FLEXCAN_QUIRK_AUTO_STOP_MODE BIT(17) struct flexcan_devtype_data { u32 quirks; /* quirks needed for different IP cores */ diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index e5575d2755e4..8e83d6963d85 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -9,20 +9,20 @@ */ #include <linux/bitfield.h> +#include <linux/can/dev.h> #include <linux/ethtool.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/netdevice.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> -#include <linux/iopoll.h> -#include <linux/can/dev.h> -#include <linux/pinctrl/consumer.h> -#include <linux/phy/phy.h> #include "m_can.h" @@ -156,6 +156,7 @@ enum m_can_reg { #define PSR_EW BIT(6) #define PSR_EP BIT(5) #define PSR_LEC_MASK GENMASK(2, 0) +#define PSR_DLEC_MASK GENMASK(10, 8) /* Interrupt Register (IR) */ #define IR_ALL_INT 0xffffffff @@ -209,7 +210,7 @@ enum m_can_reg { /* Interrupts for version >= 3.1.x */ #define IR_ERR_LEC_31X (IR_PED | IR_PEA) -#define IR_ERR_BUS_31X (IR_ERR_LEC_31X | IR_WDI | IR_BEU | IR_BEC | \ +#define IR_ERR_BUS_31X (IR_ERR_LEC_31X | IR_WDI | IR_BEU | IR_BEC | \ IR_TOO | IR_MRAF | IR_TSW | IR_TEFL | IR_RF1L | \ IR_RF0L) #define IR_ERR_ALL_31X (IR_ERR_STATE | IR_ERR_BUS_31X) @@ -368,9 +369,14 @@ m_can_txe_fifo_read(struct m_can_classdev *cdev, u32 fgi, u32 offset, u32 *val) return cdev->ops->read_fifo(cdev, addr_offset, val, 1); } +static inline bool _m_can_tx_fifo_full(u32 txfqs) +{ + return !!(txfqs & TXFQS_TFQF); +} + static inline bool m_can_tx_fifo_full(struct m_can_classdev *cdev) { - return !!(m_can_read(cdev, M_CAN_TXFQS) & TXFQS_TFQF); + return _m_can_tx_fifo_full(m_can_read(cdev, M_CAN_TXFQS)); } static void m_can_config_endisable(struct m_can_classdev *cdev, bool enable) @@ -471,19 +477,16 @@ static void m_can_receive_skb(struct m_can_classdev *cdev, } } -static int m_can_read_fifo(struct net_device *dev, u32 rxfs) +static int m_can_read_fifo(struct net_device *dev, u32 fgi) { struct net_device_stats *stats = &dev->stats; struct m_can_classdev *cdev = netdev_priv(dev); struct canfd_frame *cf; struct sk_buff *skb; struct id_and_dlc fifo_header; - u32 fgi; u32 timestamp = 0; int err; - /* calculate the fifo get index for where to read data */ - fgi = FIELD_GET(RXFS_FGI_MASK, rxfs); err = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_ID, &fifo_header, 2); if (err) goto out_fail; @@ -527,9 +530,6 @@ static int m_can_read_fifo(struct net_device *dev, u32 rxfs) } stats->rx_packets++; - /* acknowledge rx fifo 0 */ - m_can_write(cdev, M_CAN_RXF0A, fgi); - timestamp = FIELD_GET(RX_BUF_RXTS_MASK, fifo_header.dlc) << 16; m_can_receive_skb(cdev, skb, timestamp); @@ -548,7 +548,11 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota) struct m_can_classdev *cdev = netdev_priv(dev); u32 pkts = 0; u32 rxfs; - int err; + u32 rx_count; + u32 fgi; + int ack_fgi = -1; + int i; + int err = 0; rxfs = m_can_read(cdev, M_CAN_RXF0S); if (!(rxfs & RXFS_FFL_MASK)) { @@ -556,16 +560,26 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota) return 0; } - while ((rxfs & RXFS_FFL_MASK) && (quota > 0)) { - err = m_can_read_fifo(dev, rxfs); + rx_count = FIELD_GET(RXFS_FFL_MASK, rxfs); + fgi = FIELD_GET(RXFS_FGI_MASK, rxfs); + + for (i = 0; i < rx_count && quota > 0; ++i) { + err = m_can_read_fifo(dev, fgi); if (err) - return err; + break; quota--; pkts++; - rxfs = m_can_read(cdev, M_CAN_RXF0S); + ack_fgi = fgi; + fgi = (++fgi >= cdev->mcfg[MRAM_RXF0].num ? 0 : fgi); } + if (ack_fgi != -1) + m_can_write(cdev, M_CAN_RXF0A, ack_fgi); + + if (err) + return err; + return pkts; } @@ -816,11 +830,9 @@ static void m_can_handle_other_err(struct net_device *dev, u32 irqstatus) netdev_err(dev, "Message RAM access failure occurred\n"); } -static inline bool is_lec_err(u32 psr) +static inline bool is_lec_err(u8 lec) { - psr &= LEC_UNUSED; - - return psr && (psr != LEC_UNUSED); + return lec != LEC_NO_ERROR && lec != LEC_NO_CHANGE; } static inline bool m_can_is_protocol_err(u32 irqstatus) @@ -875,9 +887,20 @@ static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus, work_done += m_can_handle_lost_msg(dev); /* handle lec errors on the bus */ - if ((cdev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) && - is_lec_err(psr)) - work_done += m_can_handle_lec_err(dev, psr & LEC_UNUSED); + if (cdev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) { + u8 lec = FIELD_GET(PSR_LEC_MASK, psr); + u8 dlec = FIELD_GET(PSR_DLEC_MASK, psr); + + if (is_lec_err(lec)) { + netdev_dbg(dev, "Arbitration phase error detected\n"); + work_done += m_can_handle_lec_err(dev, lec); + } + + if (is_lec_err(dlec)) { + netdev_dbg(dev, "Data phase error detected\n"); + work_done += m_can_handle_lec_err(dev, dlec); + } + } /* handle protocol errors in arbitration phase */ if ((cdev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) && @@ -890,14 +913,12 @@ static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus, return work_done; } -static int m_can_rx_handler(struct net_device *dev, int quota) +static int m_can_rx_handler(struct net_device *dev, int quota, u32 irqstatus) { struct m_can_classdev *cdev = netdev_priv(dev); int rx_work_or_err; int work_done = 0; - u32 irqstatus, psr; - irqstatus = cdev->irqstatus | m_can_read(cdev, M_CAN_IR); if (!irqstatus) goto end; @@ -922,13 +943,13 @@ static int m_can_rx_handler(struct net_device *dev, int quota) } } - psr = m_can_read(cdev, M_CAN_PSR); - if (irqstatus & IR_ERR_STATE) - work_done += m_can_handle_state_errors(dev, psr); + work_done += m_can_handle_state_errors(dev, + m_can_read(cdev, M_CAN_PSR)); if (irqstatus & IR_ERR_BUS_30X) - work_done += m_can_handle_bus_errors(dev, irqstatus, psr); + work_done += m_can_handle_bus_errors(dev, irqstatus, + m_can_read(cdev, M_CAN_PSR)); if (irqstatus & IR_RF0N) { rx_work_or_err = m_can_do_rx_poll(dev, (quota - work_done)); @@ -941,12 +962,12 @@ end: return work_done; } -static int m_can_rx_peripheral(struct net_device *dev) +static int m_can_rx_peripheral(struct net_device *dev, u32 irqstatus) { struct m_can_classdev *cdev = netdev_priv(dev); int work_done; - work_done = m_can_rx_handler(dev, NAPI_POLL_WEIGHT); + work_done = m_can_rx_handler(dev, NAPI_POLL_WEIGHT, irqstatus); /* Don't re-enable interrupts if the driver had a fatal error * (e.g., FIFO read failure). @@ -962,8 +983,11 @@ static int m_can_poll(struct napi_struct *napi, int quota) struct net_device *dev = napi->dev; struct m_can_classdev *cdev = netdev_priv(dev); int work_done; + u32 irqstatus; + + irqstatus = cdev->irqstatus | m_can_read(cdev, M_CAN_IR); - work_done = m_can_rx_handler(dev, quota); + work_done = m_can_rx_handler(dev, quota, irqstatus); /* Don't re-enable interrupts if the driver had a fatal error * (e.g., FIFO read failure). @@ -1004,7 +1028,9 @@ static int m_can_echo_tx_event(struct net_device *dev) u32 txe_count = 0; u32 m_can_txefs; u32 fgi = 0; + int ack_fgi = -1; int i = 0; + int err = 0; unsigned int msg_mark; struct m_can_classdev *cdev = netdev_priv(dev); @@ -1014,34 +1040,34 @@ static int m_can_echo_tx_event(struct net_device *dev) /* Get Tx Event fifo element count */ txe_count = FIELD_GET(TXEFS_EFFL_MASK, m_can_txefs); + fgi = FIELD_GET(TXEFS_EFGI_MASK, m_can_txefs); /* Get and process all sent elements */ for (i = 0; i < txe_count; i++) { u32 txe, timestamp = 0; - int err; - - /* retrieve get index */ - fgi = FIELD_GET(TXEFS_EFGI_MASK, m_can_read(cdev, M_CAN_TXEFS)); /* get message marker, timestamp */ err = m_can_txe_fifo_read(cdev, fgi, 4, &txe); if (err) { netdev_err(dev, "TXE FIFO read returned %d\n", err); - return err; + break; } msg_mark = FIELD_GET(TX_EVENT_MM_MASK, txe); timestamp = FIELD_GET(TX_EVENT_TXTS_MASK, txe) << 16; - /* ack txe element */ - m_can_write(cdev, M_CAN_TXEFA, FIELD_PREP(TXEFA_EFAI_MASK, - fgi)); + ack_fgi = fgi; + fgi = (++fgi >= cdev->mcfg[MRAM_TXE].num ? 0 : fgi); /* update stats */ m_can_tx_update_stats(cdev, msg_mark, timestamp); } - return 0; + if (ack_fgi != -1) + m_can_write(cdev, M_CAN_TXEFA, FIELD_PREP(TXEFA_EFAI_MASK, + ack_fgi)); + + return err; } static irqreturn_t m_can_isr(int irq, void *dev_id) @@ -1073,7 +1099,7 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) m_can_disable_all_interrupts(cdev); if (!cdev->is_peripheral) napi_schedule(&cdev->napi); - else if (m_can_rx_peripheral(dev) < 0) + else if (m_can_rx_peripheral(dev, ir) < 0) goto out_fail; } @@ -1233,10 +1259,17 @@ static int m_can_set_bittiming(struct net_device *dev) * - setup bittiming * - configure timestamp generation */ -static void m_can_chip_config(struct net_device *dev) +static int m_can_chip_config(struct net_device *dev) { struct m_can_classdev *cdev = netdev_priv(dev); u32 cccr, test; + int err; + + err = m_can_init_ram(cdev); + if (err) { + dev_err(cdev->dev, "Message RAM configuration failed\n"); + return err; + } m_can_config_endisable(cdev, true); @@ -1360,18 +1393,25 @@ static void m_can_chip_config(struct net_device *dev) if (cdev->ops->init) cdev->ops->init(cdev); + + return 0; } -static void m_can_start(struct net_device *dev) +static int m_can_start(struct net_device *dev) { struct m_can_classdev *cdev = netdev_priv(dev); + int ret; /* basic m_can configuration */ - m_can_chip_config(dev); + ret = m_can_chip_config(dev); + if (ret) + return ret; cdev->can.state = CAN_STATE_ERROR_ACTIVE; m_can_enable_all_interrupts(cdev); + + return 0; } static int m_can_set_mode(struct net_device *dev, enum can_mode mode) @@ -1585,6 +1625,7 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) struct sk_buff *skb = cdev->tx_skb; struct id_and_dlc fifo_header; u32 cccr, fdflags; + u32 txfqs; int err; int putidx; @@ -1641,8 +1682,10 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) } else { /* Transmit routine for version >= v3.1.x */ + txfqs = m_can_read(cdev, M_CAN_TXFQS); + /* Check if FIFO full */ - if (m_can_tx_fifo_full(cdev)) { + if (_m_can_tx_fifo_full(txfqs)) { /* This shouldn't happen */ netif_stop_queue(dev); netdev_warn(dev, @@ -1658,8 +1701,7 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) } /* get put index for frame */ - putidx = FIELD_GET(TXFQS_TFQPI_MASK, - m_can_read(cdev, M_CAN_TXFQS)); + putidx = FIELD_GET(TXFQS_TFQPI_MASK, txfqs); /* Construct DLC Field, with CAN-FD configuration. * Use the put index of the fifo as the message marker, @@ -1799,7 +1841,9 @@ static int m_can_open(struct net_device *dev) } /* start the m_can controller */ - m_can_start(dev); + err = m_can_start(dev); + if (err) + goto exit_irq_fail; if (!cdev->is_peripheral) napi_enable(&cdev->napi); @@ -2058,9 +2102,13 @@ int m_can_class_resume(struct device *dev) ret = m_can_clk_start(cdev); if (ret) return ret; + ret = m_can_start(ndev); + if (ret) { + m_can_clk_stop(cdev); + + return ret; + } - m_can_init_ram(cdev); - m_can_start(ndev); netif_device_attach(ndev); netif_start_queue(ndev); } diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h index 4c0267f9f297..a839dc71dc9b 100644 --- a/drivers/net/can/m_can/m_can.h +++ b/drivers/net/can/m_can/m_can.h @@ -7,27 +7,27 @@ #define _CAN_M_CAN_H_ #include <linux/can/core.h> +#include <linux/can/dev.h> #include <linux/can/rx-offload.h> +#include <linux/clk.h> #include <linux/completion.h> +#include <linux/delay.h> #include <linux/device.h> #include <linux/dma-mapping.h> #include <linux/freezer.h> -#include <linux/slab.h> -#include <linux/uaccess.h> -#include <linux/clk.h> -#include <linux/delay.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/netdevice.h> #include <linux/of.h> #include <linux/of_device.h> -#include <linux/pm_runtime.h> -#include <linux/iopoll.h> -#include <linux/can/dev.h> -#include <linux/pinctrl/consumer.h> #include <linux/phy/phy.h> +#include <linux/pinctrl/consumer.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/uaccess.h> /* m_can lec values */ enum m_can_lec_type { @@ -38,7 +38,7 @@ enum m_can_lec_type { LEC_BIT1_ERROR, LEC_BIT0_ERROR, LEC_CRC_ERROR, - LEC_UNUSED, + LEC_NO_CHANGE, }; enum m_can_mram_cfg { diff --git a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c index eee47bad0592..9c1dcf838006 100644 --- a/drivers/net/can/m_can/m_can_platform.c +++ b/drivers/net/can/m_can/m_can_platform.c @@ -5,8 +5,8 @@ // // Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/ -#include <linux/platform_device.h> #include <linux/phy/phy.h> +#include <linux/platform_device.h> #include "m_can.h" @@ -140,10 +140,6 @@ static int m_can_plat_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mcan_class); - ret = m_can_init_ram(mcan_class); - if (ret) - goto probe_fail; - pm_runtime_enable(mcan_class->dev); ret = m_can_class_register(mcan_class); if (ret) diff --git a/drivers/net/can/m_can/tcan4x5x-core.c b/drivers/net/can/m_can/tcan4x5x-core.c index 41645a24384c..2342aa011647 100644 --- a/drivers/net/can/m_can/tcan4x5x-core.c +++ b/drivers/net/can/m_can/tcan4x5x-core.c @@ -10,7 +10,7 @@ #define TCAN4X5X_DEV_ID1 0x04 #define TCAN4X5X_REV 0x08 #define TCAN4X5X_STATUS 0x0C -#define TCAN4X5X_ERROR_STATUS 0x10 +#define TCAN4X5X_ERROR_STATUS_MASK 0x10 #define TCAN4X5X_CONTROL 0x14 #define TCAN4X5X_CONFIG 0x800 @@ -204,17 +204,7 @@ static int tcan4x5x_clear_interrupts(struct m_can_classdev *cdev) if (ret) return ret; - ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_MCAN_INT_REG, - TCAN4X5X_ENABLE_MCAN_INT); - if (ret) - return ret; - - ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_INT_FLAGS, - TCAN4X5X_CLEAR_ALL_INT); - if (ret) - return ret; - - return tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_ERROR_STATUS, + return tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_INT_FLAGS, TCAN4X5X_CLEAR_ALL_INT); } @@ -234,8 +224,8 @@ static int tcan4x5x_init(struct m_can_classdev *cdev) if (ret) return ret; - /* Zero out the MCAN buffers */ - ret = m_can_init_ram(cdev); + ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_ERROR_STATUS_MASK, + TCAN4X5X_CLEAR_ALL_INT); if (ret) return ret; diff --git a/drivers/net/can/m_can/tcan4x5x-regmap.c b/drivers/net/can/m_can/tcan4x5x-regmap.c index 26e212b8ca7a..2b218ce04e9f 100644 --- a/drivers/net/can/m_can/tcan4x5x-regmap.c +++ b/drivers/net/can/m_can/tcan4x5x-regmap.c @@ -90,16 +90,47 @@ static int tcan4x5x_regmap_read(void *context, return 0; } -static const struct regmap_range tcan4x5x_reg_table_yes_range[] = { - regmap_reg_range(0x0000, 0x002c), /* Device ID and SPI Registers */ - regmap_reg_range(0x0800, 0x083c), /* Device configuration registers and Interrupt Flags*/ +static const struct regmap_range tcan4x5x_reg_table_wr_range[] = { + /* Device ID and SPI Registers */ + regmap_reg_range(0x000c, 0x0010), + /* Device configuration registers and Interrupt Flags*/ + regmap_reg_range(0x0800, 0x080c), + regmap_reg_range(0x0814, 0x0814), + regmap_reg_range(0x0820, 0x0820), + regmap_reg_range(0x0830, 0x0830), + /* M_CAN */ + regmap_reg_range(0x100c, 0x102c), + regmap_reg_range(0x1048, 0x1048), + regmap_reg_range(0x1050, 0x105c), + regmap_reg_range(0x1080, 0x1088), + regmap_reg_range(0x1090, 0x1090), + regmap_reg_range(0x1098, 0x10a0), + regmap_reg_range(0x10a8, 0x10b0), + regmap_reg_range(0x10b8, 0x10c0), + regmap_reg_range(0x10c8, 0x10c8), + regmap_reg_range(0x10d0, 0x10d4), + regmap_reg_range(0x10e0, 0x10e4), + regmap_reg_range(0x10f0, 0x10f0), + regmap_reg_range(0x10f8, 0x10f8), + /* MRAM */ + regmap_reg_range(0x8000, 0x87fc), +}; + +static const struct regmap_range tcan4x5x_reg_table_rd_range[] = { + regmap_reg_range(0x0000, 0x0010), /* Device ID and SPI Registers */ + regmap_reg_range(0x0800, 0x0830), /* Device configuration registers and Interrupt Flags*/ regmap_reg_range(0x1000, 0x10fc), /* M_CAN */ regmap_reg_range(0x8000, 0x87fc), /* MRAM */ }; -static const struct regmap_access_table tcan4x5x_reg_table = { - .yes_ranges = tcan4x5x_reg_table_yes_range, - .n_yes_ranges = ARRAY_SIZE(tcan4x5x_reg_table_yes_range), +static const struct regmap_access_table tcan4x5x_reg_table_wr = { + .yes_ranges = tcan4x5x_reg_table_wr_range, + .n_yes_ranges = ARRAY_SIZE(tcan4x5x_reg_table_wr_range), +}; + +static const struct regmap_access_table tcan4x5x_reg_table_rd = { + .yes_ranges = tcan4x5x_reg_table_rd_range, + .n_yes_ranges = ARRAY_SIZE(tcan4x5x_reg_table_rd_range), }; static const struct regmap_config tcan4x5x_regmap = { @@ -107,8 +138,8 @@ static const struct regmap_config tcan4x5x_regmap = { .reg_stride = 4, .pad_bits = 8, .val_bits = 32, - .wr_table = &tcan4x5x_reg_table, - .rd_table = &tcan4x5x_reg_table, + .wr_table = &tcan4x5x_reg_table_wr, + .rd_table = &tcan4x5x_reg_table_rd, .max_register = TCAN4X5X_MAX_REGISTER, .cache_type = REGCACHE_NONE, .read_flag_mask = (__force unsigned long) diff --git a/drivers/net/can/pch_can.c b/drivers/net/can/pch_can.c deleted file mode 100644 index 2a44b2803e55..000000000000 --- a/drivers/net/can/pch_can.c +++ /dev/null @@ -1,1249 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 1999 - 2010 Intel Corporation. - * Copyright (C) 2010 LAPIS SEMICONDUCTOR CO., LTD. - */ - -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/ethtool.h> -#include <linux/io.h> -#include <linux/module.h> -#include <linux/sched.h> -#include <linux/pci.h> -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <linux/can.h> -#include <linux/can/dev.h> -#include <linux/can/error.h> - -#define PCH_CTRL_INIT BIT(0) /* The INIT bit of CANCONT register. */ -#define PCH_CTRL_IE BIT(1) /* The IE bit of CAN control register */ -#define PCH_CTRL_IE_SIE_EIE (BIT(3) | BIT(2) | BIT(1)) -#define PCH_CTRL_CCE BIT(6) -#define PCH_CTRL_OPT BIT(7) /* The OPT bit of CANCONT register. */ -#define PCH_OPT_SILENT BIT(3) /* The Silent bit of CANOPT reg. */ -#define PCH_OPT_LBACK BIT(4) /* The LoopBack bit of CANOPT reg. */ - -#define PCH_CMASK_RX_TX_SET 0x00f3 -#define PCH_CMASK_RX_TX_GET 0x0073 -#define PCH_CMASK_ALL 0xff -#define PCH_CMASK_NEWDAT BIT(2) -#define PCH_CMASK_CLRINTPND BIT(3) -#define PCH_CMASK_CTRL BIT(4) -#define PCH_CMASK_ARB BIT(5) -#define PCH_CMASK_MASK BIT(6) -#define PCH_CMASK_RDWR BIT(7) -#define PCH_IF_MCONT_NEWDAT BIT(15) -#define PCH_IF_MCONT_MSGLOST BIT(14) -#define PCH_IF_MCONT_INTPND BIT(13) -#define PCH_IF_MCONT_UMASK BIT(12) -#define PCH_IF_MCONT_TXIE BIT(11) -#define PCH_IF_MCONT_RXIE BIT(10) -#define PCH_IF_MCONT_RMTEN BIT(9) -#define PCH_IF_MCONT_TXRQXT BIT(8) -#define PCH_IF_MCONT_EOB BIT(7) -#define PCH_IF_MCONT_DLC (BIT(0) | BIT(1) | BIT(2) | BIT(3)) -#define PCH_MASK2_MDIR_MXTD (BIT(14) | BIT(15)) -#define PCH_ID2_DIR BIT(13) -#define PCH_ID2_XTD BIT(14) -#define PCH_ID_MSGVAL BIT(15) -#define PCH_IF_CREQ_BUSY BIT(15) - -#define PCH_STATUS_INT 0x8000 -#define PCH_RP 0x00008000 -#define PCH_REC 0x00007f00 -#define PCH_TEC 0x000000ff - -#define PCH_TX_OK BIT(3) -#define PCH_RX_OK BIT(4) -#define PCH_EPASSIV BIT(5) -#define PCH_EWARN BIT(6) -#define PCH_BUS_OFF BIT(7) - -/* bit position of certain controller bits. */ -#define PCH_BIT_BRP_SHIFT 0 -#define PCH_BIT_SJW_SHIFT 6 -#define PCH_BIT_TSEG1_SHIFT 8 -#define PCH_BIT_TSEG2_SHIFT 12 -#define PCH_BIT_BRPE_BRPE_SHIFT 6 - -#define PCH_MSK_BITT_BRP 0x3f -#define PCH_MSK_BRPE_BRPE 0x3c0 -#define PCH_MSK_CTRL_IE_SIE_EIE 0x07 -#define PCH_COUNTER_LIMIT 10 - -#define PCH_CAN_CLK 50000000 /* 50MHz */ - -/* - * Define the number of message object. - * PCH CAN communications are done via Message RAM. - * The Message RAM consists of 32 message objects. - */ -#define PCH_RX_OBJ_NUM 26 -#define PCH_TX_OBJ_NUM 6 -#define PCH_RX_OBJ_START 1 -#define PCH_RX_OBJ_END PCH_RX_OBJ_NUM -#define PCH_TX_OBJ_START (PCH_RX_OBJ_END + 1) -#define PCH_TX_OBJ_END (PCH_RX_OBJ_NUM + PCH_TX_OBJ_NUM) - -#define PCH_FIFO_THRESH 16 - -/* TxRqst2 show status of MsgObjNo.17~32 */ -#define PCH_TREQ2_TX_MASK (((1 << PCH_TX_OBJ_NUM) - 1) <<\ - (PCH_RX_OBJ_END - 16)) - -enum pch_ifreg { - PCH_RX_IFREG, - PCH_TX_IFREG, -}; - -enum pch_can_err { - PCH_STUF_ERR = 1, - PCH_FORM_ERR, - PCH_ACK_ERR, - PCH_BIT1_ERR, - PCH_BIT0_ERR, - PCH_CRC_ERR, - PCH_LEC_ALL, -}; - -enum pch_can_mode { - PCH_CAN_ENABLE, - PCH_CAN_DISABLE, - PCH_CAN_ALL, - PCH_CAN_NONE, - PCH_CAN_STOP, - PCH_CAN_RUN, -}; - -struct pch_can_if_regs { - u32 creq; - u32 cmask; - u32 mask1; - u32 mask2; - u32 id1; - u32 id2; - u32 mcont; - u32 data[4]; - u32 rsv[13]; -}; - -struct pch_can_regs { - u32 cont; - u32 stat; - u32 errc; - u32 bitt; - u32 intr; - u32 opt; - u32 brpe; - u32 reserve; - struct pch_can_if_regs ifregs[2]; /* [0]=if1 [1]=if2 */ - u32 reserve1[8]; - u32 treq1; - u32 treq2; - u32 reserve2[6]; - u32 data1; - u32 data2; - u32 reserve3[6]; - u32 canipend1; - u32 canipend2; - u32 reserve4[6]; - u32 canmval1; - u32 canmval2; - u32 reserve5[37]; - u32 srst; -}; - -struct pch_can_priv { - struct can_priv can; - struct pci_dev *dev; - u32 tx_enable[PCH_TX_OBJ_END]; - u32 rx_enable[PCH_TX_OBJ_END]; - u32 rx_link[PCH_TX_OBJ_END]; - u32 int_enables; - struct net_device *ndev; - struct pch_can_regs __iomem *regs; - struct napi_struct napi; - int tx_obj; /* Point next Tx Obj index */ - int use_msi; -}; - -static const struct can_bittiming_const pch_can_bittiming_const = { - .name = KBUILD_MODNAME, - .tseg1_min = 2, - .tseg1_max = 16, - .tseg2_min = 1, - .tseg2_max = 8, - .sjw_max = 4, - .brp_min = 1, - .brp_max = 1024, /* 6bit + extended 4bit */ - .brp_inc = 1, -}; - -static const struct pci_device_id pch_pci_tbl[] = { - {PCI_VENDOR_ID_INTEL, 0x8818, PCI_ANY_ID, PCI_ANY_ID,}, - {0,} -}; -MODULE_DEVICE_TABLE(pci, pch_pci_tbl); - -static inline void pch_can_bit_set(void __iomem *addr, u32 mask) -{ - iowrite32(ioread32(addr) | mask, addr); -} - -static inline void pch_can_bit_clear(void __iomem *addr, u32 mask) -{ - iowrite32(ioread32(addr) & ~mask, addr); -} - -static void pch_can_set_run_mode(struct pch_can_priv *priv, - enum pch_can_mode mode) -{ - switch (mode) { - case PCH_CAN_RUN: - pch_can_bit_clear(&priv->regs->cont, PCH_CTRL_INIT); - break; - - case PCH_CAN_STOP: - pch_can_bit_set(&priv->regs->cont, PCH_CTRL_INIT); - break; - - default: - netdev_err(priv->ndev, "%s -> Invalid Mode.\n", __func__); - break; - } -} - -static void pch_can_set_optmode(struct pch_can_priv *priv) -{ - u32 reg_val = ioread32(&priv->regs->opt); - - if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) - reg_val |= PCH_OPT_SILENT; - - if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) - reg_val |= PCH_OPT_LBACK; - - pch_can_bit_set(&priv->regs->cont, PCH_CTRL_OPT); - iowrite32(reg_val, &priv->regs->opt); -} - -static void pch_can_rw_msg_obj(void __iomem *creq_addr, u32 num) -{ - int counter = PCH_COUNTER_LIMIT; - u32 ifx_creq; - - iowrite32(num, creq_addr); - while (counter) { - ifx_creq = ioread32(creq_addr) & PCH_IF_CREQ_BUSY; - if (!ifx_creq) - break; - counter--; - udelay(1); - } - if (!counter) - pr_err("%s:IF1 BUSY Flag is set forever.\n", __func__); -} - -static void pch_can_set_int_enables(struct pch_can_priv *priv, - enum pch_can_mode interrupt_no) -{ - switch (interrupt_no) { - case PCH_CAN_DISABLE: - pch_can_bit_clear(&priv->regs->cont, PCH_CTRL_IE); - break; - - case PCH_CAN_ALL: - pch_can_bit_set(&priv->regs->cont, PCH_CTRL_IE_SIE_EIE); - break; - - case PCH_CAN_NONE: - pch_can_bit_clear(&priv->regs->cont, PCH_CTRL_IE_SIE_EIE); - break; - - default: - netdev_err(priv->ndev, "Invalid interrupt number.\n"); - break; - } -} - -static void pch_can_set_rxtx(struct pch_can_priv *priv, u32 buff_num, - int set, enum pch_ifreg dir) -{ - u32 ie; - - if (dir) - ie = PCH_IF_MCONT_TXIE; - else - ie = PCH_IF_MCONT_RXIE; - - /* Reading the Msg buffer from Message RAM to IF1/2 registers. */ - iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[dir].cmask); - pch_can_rw_msg_obj(&priv->regs->ifregs[dir].creq, buff_num); - - /* Setting the IF1/2MASK1 register to access MsgVal and RxIE bits */ - iowrite32(PCH_CMASK_RDWR | PCH_CMASK_ARB | PCH_CMASK_CTRL, - &priv->regs->ifregs[dir].cmask); - - if (set) { - /* Setting the MsgVal and RxIE/TxIE bits */ - pch_can_bit_set(&priv->regs->ifregs[dir].mcont, ie); - pch_can_bit_set(&priv->regs->ifregs[dir].id2, PCH_ID_MSGVAL); - } else { - /* Clearing the MsgVal and RxIE/TxIE bits */ - pch_can_bit_clear(&priv->regs->ifregs[dir].mcont, ie); - pch_can_bit_clear(&priv->regs->ifregs[dir].id2, PCH_ID_MSGVAL); - } - - pch_can_rw_msg_obj(&priv->regs->ifregs[dir].creq, buff_num); -} - -static void pch_can_set_rx_all(struct pch_can_priv *priv, int set) -{ - int i; - - /* Traversing to obtain the object configured as receivers. */ - for (i = PCH_RX_OBJ_START; i <= PCH_RX_OBJ_END; i++) - pch_can_set_rxtx(priv, i, set, PCH_RX_IFREG); -} - -static void pch_can_set_tx_all(struct pch_can_priv *priv, int set) -{ - int i; - - /* Traversing to obtain the object configured as transmit object. */ - for (i = PCH_TX_OBJ_START; i <= PCH_TX_OBJ_END; i++) - pch_can_set_rxtx(priv, i, set, PCH_TX_IFREG); -} - -static u32 pch_can_int_pending(struct pch_can_priv *priv) -{ - return ioread32(&priv->regs->intr) & 0xffff; -} - -static void pch_can_clear_if_buffers(struct pch_can_priv *priv) -{ - int i; /* Msg Obj ID (1~32) */ - - for (i = PCH_RX_OBJ_START; i <= PCH_TX_OBJ_END; i++) { - iowrite32(PCH_CMASK_RX_TX_SET, &priv->regs->ifregs[0].cmask); - iowrite32(0xffff, &priv->regs->ifregs[0].mask1); - iowrite32(0xffff, &priv->regs->ifregs[0].mask2); - iowrite32(0x0, &priv->regs->ifregs[0].id1); - iowrite32(0x0, &priv->regs->ifregs[0].id2); - iowrite32(0x0, &priv->regs->ifregs[0].mcont); - iowrite32(0x0, &priv->regs->ifregs[0].data[0]); - iowrite32(0x0, &priv->regs->ifregs[0].data[1]); - iowrite32(0x0, &priv->regs->ifregs[0].data[2]); - iowrite32(0x0, &priv->regs->ifregs[0].data[3]); - iowrite32(PCH_CMASK_RDWR | PCH_CMASK_MASK | - PCH_CMASK_ARB | PCH_CMASK_CTRL, - &priv->regs->ifregs[0].cmask); - pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, i); - } -} - -static void pch_can_config_rx_tx_buffers(struct pch_can_priv *priv) -{ - int i; - - for (i = PCH_RX_OBJ_START; i <= PCH_RX_OBJ_END; i++) { - iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[0].cmask); - pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, i); - - iowrite32(0x0, &priv->regs->ifregs[0].id1); - iowrite32(0x0, &priv->regs->ifregs[0].id2); - - pch_can_bit_set(&priv->regs->ifregs[0].mcont, - PCH_IF_MCONT_UMASK); - - /* In case FIFO mode, Last EoB of Rx Obj must be 1 */ - if (i == PCH_RX_OBJ_END) - pch_can_bit_set(&priv->regs->ifregs[0].mcont, - PCH_IF_MCONT_EOB); - else - pch_can_bit_clear(&priv->regs->ifregs[0].mcont, - PCH_IF_MCONT_EOB); - - iowrite32(0, &priv->regs->ifregs[0].mask1); - pch_can_bit_clear(&priv->regs->ifregs[0].mask2, - 0x1fff | PCH_MASK2_MDIR_MXTD); - - /* Setting CMASK for writing */ - iowrite32(PCH_CMASK_RDWR | PCH_CMASK_MASK | PCH_CMASK_ARB | - PCH_CMASK_CTRL, &priv->regs->ifregs[0].cmask); - - pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, i); - } - - for (i = PCH_TX_OBJ_START; i <= PCH_TX_OBJ_END; i++) { - iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[1].cmask); - pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, i); - - /* Resetting DIR bit for reception */ - iowrite32(0x0, &priv->regs->ifregs[1].id1); - iowrite32(PCH_ID2_DIR, &priv->regs->ifregs[1].id2); - - /* Setting EOB bit for transmitter */ - iowrite32(PCH_IF_MCONT_EOB | PCH_IF_MCONT_UMASK, - &priv->regs->ifregs[1].mcont); - - iowrite32(0, &priv->regs->ifregs[1].mask1); - pch_can_bit_clear(&priv->regs->ifregs[1].mask2, 0x1fff); - - /* Setting CMASK for writing */ - iowrite32(PCH_CMASK_RDWR | PCH_CMASK_MASK | PCH_CMASK_ARB | - PCH_CMASK_CTRL, &priv->regs->ifregs[1].cmask); - - pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, i); - } -} - -static void pch_can_init(struct pch_can_priv *priv) -{ - /* Stopping the Can device. */ - pch_can_set_run_mode(priv, PCH_CAN_STOP); - - /* Clearing all the message object buffers. */ - pch_can_clear_if_buffers(priv); - - /* Configuring the respective message object as either rx/tx object. */ - pch_can_config_rx_tx_buffers(priv); - - /* Enabling the interrupts. */ - pch_can_set_int_enables(priv, PCH_CAN_ALL); -} - -static void pch_can_release(struct pch_can_priv *priv) -{ - /* Stooping the CAN device. */ - pch_can_set_run_mode(priv, PCH_CAN_STOP); - - /* Disabling the interrupts. */ - pch_can_set_int_enables(priv, PCH_CAN_NONE); - - /* Disabling all the receive object. */ - pch_can_set_rx_all(priv, 0); - - /* Disabling all the transmit object. */ - pch_can_set_tx_all(priv, 0); -} - -/* This function clears interrupt(s) from the CAN device. */ -static void pch_can_int_clr(struct pch_can_priv *priv, u32 mask) -{ - /* Clear interrupt for transmit object */ - if ((mask >= PCH_RX_OBJ_START) && (mask <= PCH_RX_OBJ_END)) { - /* Setting CMASK for clearing the reception interrupts. */ - iowrite32(PCH_CMASK_RDWR | PCH_CMASK_CTRL | PCH_CMASK_ARB, - &priv->regs->ifregs[0].cmask); - - /* Clearing the Dir bit. */ - pch_can_bit_clear(&priv->regs->ifregs[0].id2, PCH_ID2_DIR); - - /* Clearing NewDat & IntPnd */ - pch_can_bit_clear(&priv->regs->ifregs[0].mcont, - PCH_IF_MCONT_NEWDAT | PCH_IF_MCONT_INTPND); - - pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, mask); - } else if ((mask >= PCH_TX_OBJ_START) && (mask <= PCH_TX_OBJ_END)) { - /* - * Setting CMASK for clearing interrupts for frame transmission. - */ - iowrite32(PCH_CMASK_RDWR | PCH_CMASK_CTRL | PCH_CMASK_ARB, - &priv->regs->ifregs[1].cmask); - - /* Resetting the ID registers. */ - pch_can_bit_set(&priv->regs->ifregs[1].id2, - PCH_ID2_DIR | (0x7ff << 2)); - iowrite32(0x0, &priv->regs->ifregs[1].id1); - - /* Clearing NewDat, TxRqst & IntPnd */ - pch_can_bit_clear(&priv->regs->ifregs[1].mcont, - PCH_IF_MCONT_NEWDAT | PCH_IF_MCONT_INTPND | - PCH_IF_MCONT_TXRQXT); - pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, mask); - } -} - -static void pch_can_reset(struct pch_can_priv *priv) -{ - /* write to sw reset register */ - iowrite32(1, &priv->regs->srst); - iowrite32(0, &priv->regs->srst); -} - -static void pch_can_error(struct net_device *ndev, u32 status) -{ - struct sk_buff *skb; - struct pch_can_priv *priv = netdev_priv(ndev); - struct can_frame *cf; - u32 errc, lec; - struct net_device_stats *stats = &(priv->ndev->stats); - enum can_state state = priv->can.state; - - skb = alloc_can_err_skb(ndev, &cf); - if (!skb) - return; - - errc = ioread32(&priv->regs->errc); - if (status & PCH_BUS_OFF) { - pch_can_set_tx_all(priv, 0); - pch_can_set_rx_all(priv, 0); - state = CAN_STATE_BUS_OFF; - cf->can_id |= CAN_ERR_BUSOFF; - priv->can.can_stats.bus_off++; - can_bus_off(ndev); - } else { - cf->can_id |= CAN_ERR_CNT; - cf->data[6] = errc & PCH_TEC; - cf->data[7] = (errc & PCH_REC) >> 8; - } - - /* Warning interrupt. */ - if (status & PCH_EWARN) { - state = CAN_STATE_ERROR_WARNING; - priv->can.can_stats.error_warning++; - cf->can_id |= CAN_ERR_CRTL; - if (((errc & PCH_REC) >> 8) > 96) - cf->data[1] |= CAN_ERR_CRTL_RX_WARNING; - if ((errc & PCH_TEC) > 96) - cf->data[1] |= CAN_ERR_CRTL_TX_WARNING; - netdev_dbg(ndev, - "%s -> Error Counter is more than 96.\n", __func__); - } - /* Error passive interrupt. */ - if (status & PCH_EPASSIV) { - priv->can.can_stats.error_passive++; - state = CAN_STATE_ERROR_PASSIVE; - cf->can_id |= CAN_ERR_CRTL; - if (errc & PCH_RP) - cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; - if ((errc & PCH_TEC) > 127) - cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; - netdev_dbg(ndev, - "%s -> CAN controller is ERROR PASSIVE .\n", __func__); - } - - lec = status & PCH_LEC_ALL; - switch (lec) { - case PCH_STUF_ERR: - cf->data[2] |= CAN_ERR_PROT_STUFF; - priv->can.can_stats.bus_error++; - stats->rx_errors++; - break; - case PCH_FORM_ERR: - cf->data[2] |= CAN_ERR_PROT_FORM; - priv->can.can_stats.bus_error++; - stats->rx_errors++; - break; - case PCH_ACK_ERR: - cf->can_id |= CAN_ERR_ACK; - priv->can.can_stats.bus_error++; - stats->rx_errors++; - break; - case PCH_BIT1_ERR: - case PCH_BIT0_ERR: - cf->data[2] |= CAN_ERR_PROT_BIT; - priv->can.can_stats.bus_error++; - stats->rx_errors++; - break; - case PCH_CRC_ERR: - cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ; - priv->can.can_stats.bus_error++; - stats->rx_errors++; - break; - case PCH_LEC_ALL: /* Written by CPU. No error status */ - break; - } - - priv->can.state = state; - netif_receive_skb(skb); -} - -static irqreturn_t pch_can_interrupt(int irq, void *dev_id) -{ - struct net_device *ndev = (struct net_device *)dev_id; - struct pch_can_priv *priv = netdev_priv(ndev); - - if (!pch_can_int_pending(priv)) - return IRQ_NONE; - - pch_can_set_int_enables(priv, PCH_CAN_NONE); - napi_schedule(&priv->napi); - return IRQ_HANDLED; -} - -static void pch_fifo_thresh(struct pch_can_priv *priv, int obj_id) -{ - if (obj_id < PCH_FIFO_THRESH) { - iowrite32(PCH_CMASK_RDWR | PCH_CMASK_CTRL | - PCH_CMASK_ARB, &priv->regs->ifregs[0].cmask); - - /* Clearing the Dir bit. */ - pch_can_bit_clear(&priv->regs->ifregs[0].id2, PCH_ID2_DIR); - - /* Clearing NewDat & IntPnd */ - pch_can_bit_clear(&priv->regs->ifregs[0].mcont, - PCH_IF_MCONT_INTPND); - pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, obj_id); - } else if (obj_id > PCH_FIFO_THRESH) { - pch_can_int_clr(priv, obj_id); - } else if (obj_id == PCH_FIFO_THRESH) { - int cnt; - for (cnt = 0; cnt < PCH_FIFO_THRESH; cnt++) - pch_can_int_clr(priv, cnt + 1); - } -} - -static void pch_can_rx_msg_lost(struct net_device *ndev, int obj_id) -{ - struct pch_can_priv *priv = netdev_priv(ndev); - struct net_device_stats *stats = &(priv->ndev->stats); - struct sk_buff *skb; - struct can_frame *cf; - - netdev_dbg(priv->ndev, "Msg Obj is overwritten.\n"); - pch_can_bit_clear(&priv->regs->ifregs[0].mcont, - PCH_IF_MCONT_MSGLOST); - iowrite32(PCH_CMASK_RDWR | PCH_CMASK_CTRL, - &priv->regs->ifregs[0].cmask); - pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, obj_id); - - skb = alloc_can_err_skb(ndev, &cf); - if (!skb) - return; - - cf->can_id |= CAN_ERR_CRTL; - cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; - stats->rx_over_errors++; - stats->rx_errors++; - - netif_receive_skb(skb); -} - -static int pch_can_rx_normal(struct net_device *ndev, u32 obj_num, int quota) -{ - u32 reg; - canid_t id; - int rcv_pkts = 0; - struct sk_buff *skb; - struct can_frame *cf; - struct pch_can_priv *priv = netdev_priv(ndev); - struct net_device_stats *stats = &(priv->ndev->stats); - int i; - u32 id2; - u16 data_reg; - - do { - /* Reading the message object from the Message RAM */ - iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[0].cmask); - pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, obj_num); - - /* Reading the MCONT register. */ - reg = ioread32(&priv->regs->ifregs[0].mcont); - - if (reg & PCH_IF_MCONT_EOB) - break; - - /* If MsgLost bit set. */ - if (reg & PCH_IF_MCONT_MSGLOST) { - pch_can_rx_msg_lost(ndev, obj_num); - rcv_pkts++; - quota--; - obj_num++; - continue; - } else if (!(reg & PCH_IF_MCONT_NEWDAT)) { - obj_num++; - continue; - } - - skb = alloc_can_skb(priv->ndev, &cf); - if (!skb) { - netdev_err(ndev, "alloc_can_skb Failed\n"); - return rcv_pkts; - } - - /* Get Received data */ - id2 = ioread32(&priv->regs->ifregs[0].id2); - if (id2 & PCH_ID2_XTD) { - id = (ioread32(&priv->regs->ifregs[0].id1) & 0xffff); - id |= (((id2) & 0x1fff) << 16); - cf->can_id = id | CAN_EFF_FLAG; - } else { - id = (id2 >> 2) & CAN_SFF_MASK; - cf->can_id = id; - } - - cf->len = can_cc_dlc2len((ioread32(&priv->regs-> - ifregs[0].mcont)) & 0xF); - - if (id2 & PCH_ID2_DIR) { - cf->can_id |= CAN_RTR_FLAG; - } else { - for (i = 0; i < cf->len; i += 2) { - data_reg = ioread16(&priv->regs->ifregs[0].data[i / 2]); - cf->data[i] = data_reg; - cf->data[i + 1] = data_reg >> 8; - } - - stats->rx_bytes += cf->len; - } - stats->rx_packets++; - rcv_pkts++; - quota--; - netif_receive_skb(skb); - - pch_fifo_thresh(priv, obj_num); - obj_num++; - } while (quota > 0); - - return rcv_pkts; -} - -static void pch_can_tx_complete(struct net_device *ndev, u32 int_stat) -{ - struct pch_can_priv *priv = netdev_priv(ndev); - struct net_device_stats *stats = &(priv->ndev->stats); - - stats->tx_bytes += can_get_echo_skb(ndev, int_stat - PCH_RX_OBJ_END - 1, - NULL); - stats->tx_packets++; - iowrite32(PCH_CMASK_RX_TX_GET | PCH_CMASK_CLRINTPND, - &priv->regs->ifregs[1].cmask); - pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, int_stat); - if (int_stat == PCH_TX_OBJ_END) - netif_wake_queue(ndev); -} - -static int pch_can_poll(struct napi_struct *napi, int quota) -{ - struct net_device *ndev = napi->dev; - struct pch_can_priv *priv = netdev_priv(ndev); - u32 int_stat; - u32 reg_stat; - int quota_save = quota; - - int_stat = pch_can_int_pending(priv); - if (!int_stat) - goto end; - - if (int_stat == PCH_STATUS_INT) { - reg_stat = ioread32(&priv->regs->stat); - - if ((reg_stat & (PCH_BUS_OFF | PCH_LEC_ALL)) && - ((reg_stat & PCH_LEC_ALL) != PCH_LEC_ALL)) { - pch_can_error(ndev, reg_stat); - quota--; - } - - if (reg_stat & (PCH_TX_OK | PCH_RX_OK)) - pch_can_bit_clear(&priv->regs->stat, - reg_stat & (PCH_TX_OK | PCH_RX_OK)); - - int_stat = pch_can_int_pending(priv); - } - - if (quota == 0) - goto end; - - if ((int_stat >= PCH_RX_OBJ_START) && (int_stat <= PCH_RX_OBJ_END)) { - quota -= pch_can_rx_normal(ndev, int_stat, quota); - } else if ((int_stat >= PCH_TX_OBJ_START) && - (int_stat <= PCH_TX_OBJ_END)) { - /* Handle transmission interrupt */ - pch_can_tx_complete(ndev, int_stat); - } - -end: - napi_complete(napi); - pch_can_set_int_enables(priv, PCH_CAN_ALL); - - return quota_save - quota; -} - -static int pch_set_bittiming(struct net_device *ndev) -{ - struct pch_can_priv *priv = netdev_priv(ndev); - const struct can_bittiming *bt = &priv->can.bittiming; - u32 canbit; - u32 bepe; - - /* Setting the CCE bit for accessing the Can Timing register. */ - pch_can_bit_set(&priv->regs->cont, PCH_CTRL_CCE); - - canbit = (bt->brp - 1) & PCH_MSK_BITT_BRP; - canbit |= (bt->sjw - 1) << PCH_BIT_SJW_SHIFT; - canbit |= (bt->phase_seg1 + bt->prop_seg - 1) << PCH_BIT_TSEG1_SHIFT; - canbit |= (bt->phase_seg2 - 1) << PCH_BIT_TSEG2_SHIFT; - bepe = ((bt->brp - 1) & PCH_MSK_BRPE_BRPE) >> PCH_BIT_BRPE_BRPE_SHIFT; - iowrite32(canbit, &priv->regs->bitt); - iowrite32(bepe, &priv->regs->brpe); - pch_can_bit_clear(&priv->regs->cont, PCH_CTRL_CCE); - - return 0; -} - -static void pch_can_start(struct net_device *ndev) -{ - struct pch_can_priv *priv = netdev_priv(ndev); - - if (priv->can.state != CAN_STATE_STOPPED) - pch_can_reset(priv); - - pch_set_bittiming(ndev); - pch_can_set_optmode(priv); - - pch_can_set_tx_all(priv, 1); - pch_can_set_rx_all(priv, 1); - - /* Setting the CAN to run mode. */ - pch_can_set_run_mode(priv, PCH_CAN_RUN); - - priv->can.state = CAN_STATE_ERROR_ACTIVE; - - return; -} - -static int pch_can_do_set_mode(struct net_device *ndev, enum can_mode mode) -{ - int ret = 0; - - switch (mode) { - case CAN_MODE_START: - pch_can_start(ndev); - netif_wake_queue(ndev); - break; - default: - ret = -EOPNOTSUPP; - break; - } - - return ret; -} - -static int pch_can_open(struct net_device *ndev) -{ - struct pch_can_priv *priv = netdev_priv(ndev); - int retval; - - /* Registering the interrupt. */ - retval = request_irq(priv->dev->irq, pch_can_interrupt, IRQF_SHARED, - ndev->name, ndev); - if (retval) { - netdev_err(ndev, "request_irq failed.\n"); - goto req_irq_err; - } - - /* Open common can device */ - retval = open_candev(ndev); - if (retval) { - netdev_err(ndev, "open_candev() failed %d\n", retval); - goto err_open_candev; - } - - pch_can_init(priv); - pch_can_start(ndev); - napi_enable(&priv->napi); - netif_start_queue(ndev); - - return 0; - -err_open_candev: - free_irq(priv->dev->irq, ndev); -req_irq_err: - pch_can_release(priv); - - return retval; -} - -static int pch_close(struct net_device *ndev) -{ - struct pch_can_priv *priv = netdev_priv(ndev); - - netif_stop_queue(ndev); - napi_disable(&priv->napi); - pch_can_release(priv); - free_irq(priv->dev->irq, ndev); - close_candev(ndev); - priv->can.state = CAN_STATE_STOPPED; - return 0; -} - -static netdev_tx_t pch_xmit(struct sk_buff *skb, struct net_device *ndev) -{ - struct pch_can_priv *priv = netdev_priv(ndev); - struct can_frame *cf = (struct can_frame *)skb->data; - int tx_obj_no; - int i; - u32 id2; - - if (can_dev_dropped_skb(ndev, skb)) - return NETDEV_TX_OK; - - tx_obj_no = priv->tx_obj; - if (priv->tx_obj == PCH_TX_OBJ_END) { - if (ioread32(&priv->regs->treq2) & PCH_TREQ2_TX_MASK) - netif_stop_queue(ndev); - - priv->tx_obj = PCH_TX_OBJ_START; - } else { - priv->tx_obj++; - } - - /* Setting the CMASK register. */ - pch_can_bit_set(&priv->regs->ifregs[1].cmask, PCH_CMASK_ALL); - - /* If ID extended is set. */ - if (cf->can_id & CAN_EFF_FLAG) { - iowrite32(cf->can_id & 0xffff, &priv->regs->ifregs[1].id1); - id2 = ((cf->can_id >> 16) & 0x1fff) | PCH_ID2_XTD; - } else { - iowrite32(0, &priv->regs->ifregs[1].id1); - id2 = (cf->can_id & CAN_SFF_MASK) << 2; - } - - id2 |= PCH_ID_MSGVAL; - - /* If remote frame has to be transmitted.. */ - if (!(cf->can_id & CAN_RTR_FLAG)) - id2 |= PCH_ID2_DIR; - - iowrite32(id2, &priv->regs->ifregs[1].id2); - - /* Copy data to register */ - for (i = 0; i < cf->len; i += 2) { - iowrite16(cf->data[i] | (cf->data[i + 1] << 8), - &priv->regs->ifregs[1].data[i / 2]); - } - - can_put_echo_skb(skb, ndev, tx_obj_no - PCH_RX_OBJ_END - 1, 0); - - /* Set the size of the data. Update if2_mcont */ - iowrite32(cf->len | PCH_IF_MCONT_NEWDAT | PCH_IF_MCONT_TXRQXT | - PCH_IF_MCONT_TXIE, &priv->regs->ifregs[1].mcont); - - pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, tx_obj_no); - - return NETDEV_TX_OK; -} - -static const struct net_device_ops pch_can_netdev_ops = { - .ndo_open = pch_can_open, - .ndo_stop = pch_close, - .ndo_start_xmit = pch_xmit, - .ndo_change_mtu = can_change_mtu, -}; - -static const struct ethtool_ops pch_can_ethtool_ops = { - .get_ts_info = ethtool_op_get_ts_info, -}; - -static void pch_can_remove(struct pci_dev *pdev) -{ - struct net_device *ndev = pci_get_drvdata(pdev); - struct pch_can_priv *priv = netdev_priv(ndev); - - unregister_candev(priv->ndev); - if (priv->use_msi) - pci_disable_msi(priv->dev); - pci_release_regions(pdev); - pci_disable_device(pdev); - pch_can_reset(priv); - pci_iounmap(pdev, priv->regs); - free_candev(priv->ndev); -} - -static void __maybe_unused pch_can_set_int_custom(struct pch_can_priv *priv) -{ - /* Clearing the IE, SIE and EIE bits of Can control register. */ - pch_can_bit_clear(&priv->regs->cont, PCH_CTRL_IE_SIE_EIE); - - /* Appropriately setting them. */ - pch_can_bit_set(&priv->regs->cont, - ((priv->int_enables & PCH_MSK_CTRL_IE_SIE_EIE) << 1)); -} - -/* This function retrieves interrupt enabled for the CAN device. */ -static u32 __maybe_unused pch_can_get_int_enables(struct pch_can_priv *priv) -{ - /* Obtaining the status of IE, SIE and EIE interrupt bits. */ - return (ioread32(&priv->regs->cont) & PCH_CTRL_IE_SIE_EIE) >> 1; -} - -static u32 __maybe_unused pch_can_get_rxtx_ir(struct pch_can_priv *priv, - u32 buff_num, enum pch_ifreg dir) -{ - u32 ie, enable; - - if (dir) - ie = PCH_IF_MCONT_RXIE; - else - ie = PCH_IF_MCONT_TXIE; - - iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[dir].cmask); - pch_can_rw_msg_obj(&priv->regs->ifregs[dir].creq, buff_num); - - if (((ioread32(&priv->regs->ifregs[dir].id2)) & PCH_ID_MSGVAL) && - ((ioread32(&priv->regs->ifregs[dir].mcont)) & ie)) - enable = 1; - else - enable = 0; - - return enable; -} - -static void __maybe_unused pch_can_set_rx_buffer_link(struct pch_can_priv *priv, - u32 buffer_num, int set) -{ - iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[0].cmask); - pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, buffer_num); - iowrite32(PCH_CMASK_RDWR | PCH_CMASK_CTRL, - &priv->regs->ifregs[0].cmask); - if (set) - pch_can_bit_clear(&priv->regs->ifregs[0].mcont, - PCH_IF_MCONT_EOB); - else - pch_can_bit_set(&priv->regs->ifregs[0].mcont, PCH_IF_MCONT_EOB); - - pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, buffer_num); -} - -static u32 __maybe_unused pch_can_get_rx_buffer_link(struct pch_can_priv *priv, - u32 buffer_num) -{ - u32 link; - - iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[0].cmask); - pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, buffer_num); - - if (ioread32(&priv->regs->ifregs[0].mcont) & PCH_IF_MCONT_EOB) - link = 0; - else - link = 1; - return link; -} - -static int __maybe_unused pch_can_get_buffer_status(struct pch_can_priv *priv) -{ - return (ioread32(&priv->regs->treq1) & 0xffff) | - (ioread32(&priv->regs->treq2) << 16); -} - -static int __maybe_unused pch_can_suspend(struct device *dev_d) -{ - int i; - u32 buf_stat; /* Variable for reading the transmit buffer status. */ - int counter = PCH_COUNTER_LIMIT; - - struct net_device *dev = dev_get_drvdata(dev_d); - struct pch_can_priv *priv = netdev_priv(dev); - - /* Stop the CAN controller */ - pch_can_set_run_mode(priv, PCH_CAN_STOP); - - /* Indicate that we are aboutto/in suspend */ - priv->can.state = CAN_STATE_STOPPED; - - /* Waiting for all transmission to complete. */ - while (counter) { - buf_stat = pch_can_get_buffer_status(priv); - if (!buf_stat) - break; - counter--; - udelay(1); - } - if (!counter) - dev_err(dev_d, "%s -> Transmission time out.\n", __func__); - - /* Save interrupt configuration and then disable them */ - priv->int_enables = pch_can_get_int_enables(priv); - pch_can_set_int_enables(priv, PCH_CAN_DISABLE); - - /* Save Tx buffer enable state */ - for (i = PCH_TX_OBJ_START; i <= PCH_TX_OBJ_END; i++) - priv->tx_enable[i - 1] = pch_can_get_rxtx_ir(priv, i, - PCH_TX_IFREG); - - /* Disable all Transmit buffers */ - pch_can_set_tx_all(priv, 0); - - /* Save Rx buffer enable state */ - for (i = PCH_RX_OBJ_START; i <= PCH_RX_OBJ_END; i++) { - priv->rx_enable[i - 1] = pch_can_get_rxtx_ir(priv, i, - PCH_RX_IFREG); - priv->rx_link[i - 1] = pch_can_get_rx_buffer_link(priv, i); - } - - /* Disable all Receive buffers */ - pch_can_set_rx_all(priv, 0); - - return 0; -} - -static int __maybe_unused pch_can_resume(struct device *dev_d) -{ - int i; - struct net_device *dev = dev_get_drvdata(dev_d); - struct pch_can_priv *priv = netdev_priv(dev); - - priv->can.state = CAN_STATE_ERROR_ACTIVE; - - /* Disabling all interrupts. */ - pch_can_set_int_enables(priv, PCH_CAN_DISABLE); - - /* Setting the CAN device in Stop Mode. */ - pch_can_set_run_mode(priv, PCH_CAN_STOP); - - /* Configuring the transmit and receive buffers. */ - pch_can_config_rx_tx_buffers(priv); - - /* Restore the CAN state */ - pch_set_bittiming(dev); - - /* Listen/Active */ - pch_can_set_optmode(priv); - - /* Enabling the transmit buffer. */ - for (i = PCH_TX_OBJ_START; i <= PCH_TX_OBJ_END; i++) - pch_can_set_rxtx(priv, i, priv->tx_enable[i - 1], PCH_TX_IFREG); - - /* Configuring the receive buffer and enabling them. */ - for (i = PCH_RX_OBJ_START; i <= PCH_RX_OBJ_END; i++) { - /* Restore buffer link */ - pch_can_set_rx_buffer_link(priv, i, priv->rx_link[i - 1]); - - /* Restore buffer enables */ - pch_can_set_rxtx(priv, i, priv->rx_enable[i - 1], PCH_RX_IFREG); - } - - /* Enable CAN Interrupts */ - pch_can_set_int_custom(priv); - - /* Restore Run Mode */ - pch_can_set_run_mode(priv, PCH_CAN_RUN); - - return 0; -} - -static int pch_can_get_berr_counter(const struct net_device *dev, - struct can_berr_counter *bec) -{ - struct pch_can_priv *priv = netdev_priv(dev); - u32 errc = ioread32(&priv->regs->errc); - - bec->txerr = errc & PCH_TEC; - bec->rxerr = (errc & PCH_REC) >> 8; - - return 0; -} - -static int pch_can_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - struct net_device *ndev; - struct pch_can_priv *priv; - int rc; - void __iomem *addr; - - rc = pci_enable_device(pdev); - if (rc) { - dev_err(&pdev->dev, "Failed pci_enable_device %d\n", rc); - goto probe_exit_endev; - } - - rc = pci_request_regions(pdev, KBUILD_MODNAME); - if (rc) { - dev_err(&pdev->dev, "Failed pci_request_regions %d\n", rc); - goto probe_exit_pcireq; - } - - addr = pci_iomap(pdev, 1, 0); - if (!addr) { - rc = -EIO; - dev_err(&pdev->dev, "Failed pci_iomap\n"); - goto probe_exit_ipmap; - } - - ndev = alloc_candev(sizeof(struct pch_can_priv), PCH_TX_OBJ_END); - if (!ndev) { - rc = -ENOMEM; - dev_err(&pdev->dev, "Failed alloc_candev\n"); - goto probe_exit_alloc_candev; - } - - priv = netdev_priv(ndev); - priv->ndev = ndev; - priv->regs = addr; - priv->dev = pdev; - priv->can.bittiming_const = &pch_can_bittiming_const; - priv->can.do_set_mode = pch_can_do_set_mode; - priv->can.do_get_berr_counter = pch_can_get_berr_counter; - priv->can.ctrlmode_supported = CAN_CTRLMODE_LISTENONLY | - CAN_CTRLMODE_LOOPBACK; - priv->tx_obj = PCH_TX_OBJ_START; /* Point head of Tx Obj */ - - ndev->irq = pdev->irq; - ndev->flags |= IFF_ECHO; - - pci_set_drvdata(pdev, ndev); - SET_NETDEV_DEV(ndev, &pdev->dev); - ndev->netdev_ops = &pch_can_netdev_ops; - ndev->ethtool_ops = &pch_can_ethtool_ops; - priv->can.clock.freq = PCH_CAN_CLK; /* Hz */ - - netif_napi_add_weight(ndev, &priv->napi, pch_can_poll, PCH_RX_OBJ_END); - - rc = pci_enable_msi(priv->dev); - if (rc) { - netdev_err(ndev, "PCH CAN opened without MSI\n"); - priv->use_msi = 0; - } else { - netdev_err(ndev, "PCH CAN opened with MSI\n"); - pci_set_master(pdev); - priv->use_msi = 1; - } - - rc = register_candev(ndev); - if (rc) { - dev_err(&pdev->dev, "Failed register_candev %d\n", rc); - goto probe_exit_reg_candev; - } - - return 0; - -probe_exit_reg_candev: - if (priv->use_msi) - pci_disable_msi(priv->dev); - free_candev(ndev); -probe_exit_alloc_candev: - pci_iounmap(pdev, addr); -probe_exit_ipmap: - pci_release_regions(pdev); -probe_exit_pcireq: - pci_disable_device(pdev); -probe_exit_endev: - return rc; -} - -static SIMPLE_DEV_PM_OPS(pch_can_pm_ops, - pch_can_suspend, - pch_can_resume); - -static struct pci_driver pch_can_pci_driver = { - .name = "pch_can", - .id_table = pch_pci_tbl, - .probe = pch_can_probe, - .remove = pch_can_remove, - .driver.pm = &pch_can_pm_ops, -}; - -module_pci_driver(pch_can_pci_driver); - -MODULE_DESCRIPTION("Intel EG20T PCH CAN(Controller Area Network) Driver"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION("0.94"); diff --git a/drivers/net/can/rcar/rcar_canfd.c b/drivers/net/can/rcar/rcar_canfd.c index b306cf554634..f6fa7157b99b 100644 --- a/drivers/net/can/rcar/rcar_canfd.c +++ b/drivers/net/can/rcar/rcar_canfd.c @@ -41,12 +41,6 @@ #define RCANFD_DRV_NAME "rcar_canfd" -enum rcanfd_chip_id { - RENESAS_RCAR_GEN3 = 0, - RENESAS_RZG2L, - RENESAS_R8A779A0, -}; - /* Global register bits */ /* RSCFDnCFDGRMCFG */ @@ -522,6 +516,14 @@ enum rcar_canfd_fcanclk { struct rcar_canfd_global; +struct rcar_canfd_hw_info { + u8 max_channels; + u8 postdiv; + /* hardware features */ + unsigned shared_global_irqs:1; /* Has shared global irqs */ + unsigned multi_channel_irqs:1; /* Has multiple channel irqs */ +}; + /* Channel priv data */ struct rcar_canfd_channel { struct can_priv can; /* Must be the first member */ @@ -547,8 +549,7 @@ struct rcar_canfd_global { bool fdmode; /* CAN FD or Classical CAN only mode */ struct reset_control *rstc1; struct reset_control *rstc2; - enum rcanfd_chip_id chip_id; - u32 max_channels; + const struct rcar_canfd_hw_info *info; }; /* CAN FD mode nominal rate constants */ @@ -590,10 +591,28 @@ static const struct can_bittiming_const rcar_canfd_bittiming_const = { .brp_inc = 1, }; +static const struct rcar_canfd_hw_info rcar_gen3_hw_info = { + .max_channels = 2, + .postdiv = 2, + .shared_global_irqs = 1, +}; + +static const struct rcar_canfd_hw_info rzg2l_hw_info = { + .max_channels = 2, + .postdiv = 1, + .multi_channel_irqs = 1, +}; + +static const struct rcar_canfd_hw_info r8a779a0_hw_info = { + .max_channels = 8, + .postdiv = 2, + .shared_global_irqs = 1, +}; + /* Helper functions */ static inline bool is_v3u(struct rcar_canfd_global *gpriv) { - return gpriv->chip_id == RENESAS_R8A779A0; + return gpriv->info == &r8a779a0_hw_info; } static inline u32 reg_v3u(struct rcar_canfd_global *gpriv, @@ -721,7 +740,7 @@ static int rcar_canfd_reset_controller(struct rcar_canfd_global *gpriv) rcar_canfd_set_mode(gpriv); /* Transition all Channels to reset mode */ - for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) { + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) { rcar_canfd_clear_bit(gpriv->base, RCANFD_CCTR(ch), RCANFD_CCTR_CSLPR); @@ -762,7 +781,7 @@ static void rcar_canfd_configure_controller(struct rcar_canfd_global *gpriv) rcar_canfd_set_bit(gpriv->base, RCANFD_GCFG, cfg); /* Channel configuration settings */ - for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) { + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) { rcar_canfd_set_bit(gpriv->base, RCANFD_CCTR(ch), RCANFD_CCTR_ERRD); rcar_canfd_update_bit(gpriv->base, RCANFD_CCTR(ch), @@ -1142,7 +1161,7 @@ static irqreturn_t rcar_canfd_global_err_interrupt(int irq, void *dev_id) struct rcar_canfd_global *gpriv = dev_id; u32 ch; - for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) rcar_canfd_handle_global_err(gpriv, ch); return IRQ_HANDLED; @@ -1174,7 +1193,7 @@ static irqreturn_t rcar_canfd_global_receive_fifo_interrupt(int irq, void *dev_i struct rcar_canfd_global *gpriv = dev_id; u32 ch; - for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) rcar_canfd_handle_global_receive(gpriv, ch); return IRQ_HANDLED; @@ -1188,7 +1207,7 @@ static irqreturn_t rcar_canfd_global_interrupt(int irq, void *dev_id) /* Global error interrupts still indicate a condition specific * to a channel. RxFIFO interrupt is a global interrupt. */ - for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) { + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) { rcar_canfd_handle_global_err(gpriv, ch); rcar_canfd_handle_global_receive(gpriv, ch); } @@ -1284,7 +1303,7 @@ static irqreturn_t rcar_canfd_channel_interrupt(int irq, void *dev_id) u32 ch; /* Common FIFO is a per channel resource */ - for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) { + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) { rcar_canfd_handle_channel_err(gpriv, ch); rcar_canfd_handle_channel_tx(gpriv, ch); } @@ -1696,6 +1715,7 @@ static const struct ethtool_ops rcar_canfd_ethtool_ops = { static int rcar_canfd_channel_probe(struct rcar_canfd_global *gpriv, u32 ch, u32 fcan_freq) { + const struct rcar_canfd_hw_info *info = gpriv->info; struct platform_device *pdev = gpriv->pdev; struct rcar_canfd_channel *priv; struct net_device *ndev; @@ -1718,7 +1738,7 @@ static int rcar_canfd_channel_probe(struct rcar_canfd_global *gpriv, u32 ch, priv->can.clock.freq = fcan_freq; dev_info(&pdev->dev, "can_clk rate is %u\n", priv->can.clock.freq); - if (gpriv->chip_id == RENESAS_RZG2L) { + if (info->multi_channel_irqs) { char *irq_name; int err_irq; int tx_irq; @@ -1818,6 +1838,7 @@ static void rcar_canfd_channel_remove(struct rcar_canfd_global *gpriv, u32 ch) static int rcar_canfd_probe(struct platform_device *pdev) { + const struct rcar_canfd_hw_info *info; void __iomem *addr; u32 sts, ch, fcan_freq; struct rcar_canfd_global *gpriv; @@ -1826,18 +1847,15 @@ static int rcar_canfd_probe(struct platform_device *pdev) int err, ch_irq, g_irq; int g_err_irq, g_recc_irq; bool fdmode = true; /* CAN FD only mode - default */ - enum rcanfd_chip_id chip_id; - int max_channels; char name[9] = "channelX"; int i; - chip_id = (uintptr_t)of_device_get_match_data(&pdev->dev); - max_channels = chip_id == RENESAS_R8A779A0 ? 8 : 2; + info = of_device_get_match_data(&pdev->dev); if (of_property_read_bool(pdev->dev.of_node, "renesas,no-can-fd")) fdmode = false; /* Classical CAN only mode */ - for (i = 0; i < max_channels; ++i) { + for (i = 0; i < info->max_channels; ++i) { name[7] = '0' + i; of_child = of_get_child_by_name(pdev->dev.of_node, name); if (of_child && of_device_is_available(of_child)) @@ -1845,7 +1863,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) of_node_put(of_child); } - if (chip_id != RENESAS_RZG2L) { + if (info->shared_global_irqs) { ch_irq = platform_get_irq_byname_optional(pdev, "ch_int"); if (ch_irq < 0) { /* For backward compatibility get irq by index */ @@ -1879,20 +1897,19 @@ static int rcar_canfd_probe(struct platform_device *pdev) gpriv->pdev = pdev; gpriv->channels_mask = channels_mask; gpriv->fdmode = fdmode; - gpriv->chip_id = chip_id; - gpriv->max_channels = max_channels; - - if (gpriv->chip_id == RENESAS_RZG2L) { - gpriv->rstc1 = devm_reset_control_get_exclusive(&pdev->dev, "rstp_n"); - if (IS_ERR(gpriv->rstc1)) - return dev_err_probe(&pdev->dev, PTR_ERR(gpriv->rstc1), - "failed to get rstp_n\n"); - - gpriv->rstc2 = devm_reset_control_get_exclusive(&pdev->dev, "rstc_n"); - if (IS_ERR(gpriv->rstc2)) - return dev_err_probe(&pdev->dev, PTR_ERR(gpriv->rstc2), - "failed to get rstc_n\n"); - } + gpriv->info = info; + + gpriv->rstc1 = devm_reset_control_get_optional_exclusive(&pdev->dev, + "rstp_n"); + if (IS_ERR(gpriv->rstc1)) + return dev_err_probe(&pdev->dev, PTR_ERR(gpriv->rstc1), + "failed to get rstp_n\n"); + + gpriv->rstc2 = devm_reset_control_get_optional_exclusive(&pdev->dev, + "rstc_n"); + if (IS_ERR(gpriv->rstc2)) + return dev_err_probe(&pdev->dev, PTR_ERR(gpriv->rstc2), + "failed to get rstc_n\n"); /* Peripheral clock */ gpriv->clkp = devm_clk_get(&pdev->dev, "fck"); @@ -1917,9 +1934,9 @@ static int rcar_canfd_probe(struct platform_device *pdev) } fcan_freq = clk_get_rate(gpriv->can_clk); - if (gpriv->fcan == RCANFD_CANFDCLK && gpriv->chip_id != RENESAS_RZG2L) + if (gpriv->fcan == RCANFD_CANFDCLK) /* CANFD clock is further divided by (1/2) within the IP */ - fcan_freq /= 2; + fcan_freq /= info->postdiv; addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(addr)) { @@ -1929,7 +1946,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) gpriv->base = addr; /* Request IRQ that's common for both channels */ - if (gpriv->chip_id != RENESAS_RZG2L) { + if (info->shared_global_irqs) { err = devm_request_irq(&pdev->dev, ch_irq, rcar_canfd_channel_interrupt, 0, "canfd.ch_int", gpriv); @@ -1995,7 +2012,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) rcar_canfd_configure_controller(gpriv); /* Configure per channel attributes */ - for_each_set_bit(ch, &gpriv->channels_mask, max_channels) { + for_each_set_bit(ch, &gpriv->channels_mask, info->max_channels) { /* Configure Channel's Rx fifo */ rcar_canfd_configure_rx(gpriv, ch); @@ -2021,7 +2038,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) goto fail_mode; } - for_each_set_bit(ch, &gpriv->channels_mask, max_channels) { + for_each_set_bit(ch, &gpriv->channels_mask, info->max_channels) { err = rcar_canfd_channel_probe(gpriv, ch, fcan_freq); if (err) goto fail_channel; @@ -2033,7 +2050,7 @@ static int rcar_canfd_probe(struct platform_device *pdev) return 0; fail_channel: - for_each_set_bit(ch, &gpriv->channels_mask, max_channels) + for_each_set_bit(ch, &gpriv->channels_mask, info->max_channels) rcar_canfd_channel_remove(gpriv, ch); fail_mode: rcar_canfd_disable_global_interrupts(gpriv); @@ -2054,7 +2071,7 @@ static int rcar_canfd_remove(struct platform_device *pdev) rcar_canfd_reset_controller(gpriv); rcar_canfd_disable_global_interrupts(gpriv); - for_each_set_bit(ch, &gpriv->channels_mask, gpriv->max_channels) { + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) { rcar_canfd_disable_channel_interrupts(gpriv->ch[ch]); rcar_canfd_channel_remove(gpriv, ch); } @@ -2082,9 +2099,9 @@ static SIMPLE_DEV_PM_OPS(rcar_canfd_pm_ops, rcar_canfd_suspend, rcar_canfd_resume); static const __maybe_unused struct of_device_id rcar_canfd_of_table[] = { - { .compatible = "renesas,rcar-gen3-canfd", .data = (void *)RENESAS_RCAR_GEN3 }, - { .compatible = "renesas,rzg2l-canfd", .data = (void *)RENESAS_RZG2L }, - { .compatible = "renesas,r8a779a0-canfd", .data = (void *)RENESAS_R8A779A0 }, + { .compatible = "renesas,rcar-gen3-canfd", .data = &rcar_gen3_hw_info }, + { .compatible = "renesas,rzg2l-canfd", .data = &rzg2l_hw_info }, + { .compatible = "renesas,r8a779a0-canfd", .data = &r8a779a0_hw_info }, { } }; diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-ethtool.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ethtool.c index 3585f02575df..57eeb066a945 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-ethtool.c +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ethtool.c @@ -48,6 +48,7 @@ mcp251xfd_ring_set_ringparam(struct net_device *ndev, priv->rx_obj_num = layout.cur_rx; priv->rx_obj_num_coalesce_irq = layout.rx_coalesce; priv->tx->obj_num = layout.cur_tx; + priv->tx_obj_num_coalesce_irq = layout.tx_coalesce; return 0; } diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig index 1218f9642f33..445504ababce 100644 --- a/drivers/net/can/usb/Kconfig +++ b/drivers/net/can/usb/Kconfig @@ -30,6 +30,7 @@ config CAN_ESD_USB config CAN_ETAS_ES58X tristate "ETAS ES58X CAN/USB interfaces" select CRC16 + select NET_DEVLINK help This driver supports the ES581.4, ES582.1 and ES584.1 interfaces from ETAS GmbH (https://www.etas.com/en/products/es58x.php). @@ -38,10 +39,13 @@ config CAN_ETAS_ES58X will be called etas_es58x. config CAN_GS_USB - tristate "Geschwister Schneider UG interfaces" + tristate "Geschwister Schneider UG and candleLight compatible interfaces" help - This driver supports the Geschwister Schneider and bytewerk.org - candleLight USB CAN interfaces USB/CAN devices + This driver supports the Geschwister Schneider and + bytewerk.org candleLight compatible + (https://github.com/candle-usb/candleLight_fw) USB/CAN + interfaces. + If unsure choose N, choose Y for built in support, M to compile as module (module will be named: gs_usb). diff --git a/drivers/net/can/usb/etas_es58x/Makefile b/drivers/net/can/usb/etas_es58x/Makefile index a129b4aa0215..d6667ebe259f 100644 --- a/drivers/net/can/usb/etas_es58x/Makefile +++ b/drivers/net/can/usb/etas_es58x/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_CAN_ETAS_ES58X) += etas_es58x.o -etas_es58x-y = es58x_core.o es581_4.o es58x_fd.o +etas_es58x-y = es58x_core.o es58x_devlink.o es581_4.o es58x_fd.o diff --git a/drivers/net/can/usb/etas_es58x/es581_4.c b/drivers/net/can/usb/etas_es58x/es581_4.c index 1bcdcece5ec7..4151b18fd045 100644 --- a/drivers/net/can/usb/etas_es58x/es581_4.c +++ b/drivers/net/can/usb/etas_es58x/es581_4.c @@ -6,12 +6,12 @@ * * Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved. * Copyright (c) 2020 ETAS K.K.. All rights reserved. - * Copyright (c) 2020, 2021 Vincent Mailhol <mailhol.vincent@wanadoo.fr> + * Copyright (c) 2020-2022 Vincent Mailhol <mailhol.vincent@wanadoo.fr> */ +#include <asm/unaligned.h> #include <linux/kernel.h> #include <linux/units.h> -#include <asm/unaligned.h> #include "es58x_core.h" #include "es581_4.h" diff --git a/drivers/net/can/usb/etas_es58x/es58x_core.c b/drivers/net/can/usb/etas_es58x/es58x_core.c index ddb7c5735c9a..0c7f7505632c 100644 --- a/drivers/net/can/usb/etas_es58x/es58x_core.c +++ b/drivers/net/can/usb/etas_es58x/es58x_core.c @@ -7,15 +7,16 @@ * * Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved. * Copyright (c) 2020 ETAS K.K.. All rights reserved. - * Copyright (c) 2020, 2021 Vincent Mailhol <mailhol.vincent@wanadoo.fr> + * Copyright (c) 2020-2022 Vincent Mailhol <mailhol.vincent@wanadoo.fr> */ +#include <asm/unaligned.h> +#include <linux/crc16.h> #include <linux/ethtool.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/usb.h> -#include <linux/crc16.h> -#include <asm/unaligned.h> +#include <net/devlink.h> #include "es58x_core.h" @@ -2038,10 +2039,16 @@ static int es58x_set_mode(struct net_device *netdev, enum can_mode mode) * @es58x_dev: ES58X device. * @priv: ES58X private parameters related to the network device. * @channel_idx: Index of the network device. + * + * Return: zero on success, errno if devlink port could not be + * properly registered. */ -static void es58x_init_priv(struct es58x_device *es58x_dev, - struct es58x_priv *priv, int channel_idx) +static int es58x_init_priv(struct es58x_device *es58x_dev, + struct es58x_priv *priv, int channel_idx) { + struct devlink_port_attrs attrs = { + .flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL, + }; const struct es58x_parameters *param = es58x_dev->param; struct can_priv *can = &priv->can; @@ -2060,6 +2067,10 @@ static void es58x_init_priv(struct es58x_device *es58x_dev, can->state = CAN_STATE_STOPPED; can->ctrlmode_supported = param->ctrlmode_supported; can->do_set_mode = es58x_set_mode; + + devlink_port_attrs_set(&priv->devlink_port, &attrs); + return devlink_port_register(priv_to_devlink(es58x_dev), + &priv->devlink_port, channel_idx); } /** @@ -2083,7 +2094,10 @@ static int es58x_init_netdev(struct es58x_device *es58x_dev, int channel_idx) } SET_NETDEV_DEV(netdev, dev); es58x_dev->netdev[channel_idx] = netdev; - es58x_init_priv(es58x_dev, es58x_priv(netdev), channel_idx); + ret = es58x_init_priv(es58x_dev, es58x_priv(netdev), channel_idx); + if (ret) + goto free_candev; + SET_NETDEV_DEVLINK_PORT(netdev, &es58x_priv(netdev)->devlink_port); netdev->netdev_ops = &es58x_netdev_ops; netdev->ethtool_ops = &es58x_ethtool_ops; @@ -2091,16 +2105,20 @@ static int es58x_init_netdev(struct es58x_device *es58x_dev, int channel_idx) netdev->dev_port = channel_idx; ret = register_candev(netdev); - if (ret) { - es58x_dev->netdev[channel_idx] = NULL; - free_candev(netdev); - return ret; - } + if (ret) + goto devlink_port_unregister; netdev_queue_set_dql_min_limit(netdev_get_tx_queue(netdev, 0), es58x_dev->param->dql_min_limit); return ret; + + devlink_port_unregister: + devlink_port_unregister(&es58x_priv(netdev)->devlink_port); + free_candev: + es58x_dev->netdev[channel_idx] = NULL; + free_candev(netdev); + return ret; } /** @@ -2117,54 +2135,13 @@ static void es58x_free_netdevs(struct es58x_device *es58x_dev) if (!netdev) continue; unregister_candev(netdev); + devlink_port_unregister(&es58x_priv(netdev)->devlink_port); es58x_dev->netdev[i] = NULL; free_candev(netdev); } } /** - * es58x_get_product_info() - Get the product information and print them. - * @es58x_dev: ES58X device. - * - * Do a synchronous call to get the product information. - * - * Return: zero on success, errno when any error occurs. - */ -static int es58x_get_product_info(struct es58x_device *es58x_dev) -{ - struct usb_device *udev = es58x_dev->udev; - const int es58x_prod_info_idx = 6; - /* Empirical tests show a prod_info length of maximum 83, - * below should be more than enough. - */ - const size_t prod_info_len = 127; - char *prod_info; - int ret; - - prod_info = kmalloc(prod_info_len, GFP_KERNEL); - if (!prod_info) - return -ENOMEM; - - ret = usb_string(udev, es58x_prod_info_idx, prod_info, prod_info_len); - if (ret < 0) { - dev_err(es58x_dev->dev, - "%s: Could not read the product info: %pe\n", - __func__, ERR_PTR(ret)); - goto out_free; - } - if (ret >= prod_info_len - 1) { - dev_warn(es58x_dev->dev, - "%s: Buffer is too small, result might be truncated\n", - __func__); - } - dev_info(es58x_dev->dev, "Product info: %s\n", prod_info); - - out_free: - kfree(prod_info); - return ret < 0 ? ret : 0; -} - -/** * es58x_init_es58x_dev() - Initialize the ES58X device. * @intf: USB interface. * @driver_info: Quirks of the device. @@ -2177,6 +2154,7 @@ static struct es58x_device *es58x_init_es58x_dev(struct usb_interface *intf, { struct device *dev = &intf->dev; struct es58x_device *es58x_dev; + struct devlink *devlink; const struct es58x_parameters *param; const struct es58x_operators *ops; struct usb_device *udev = interface_to_usbdev(intf); @@ -2199,11 +2177,12 @@ static struct es58x_device *es58x_init_es58x_dev(struct usb_interface *intf, ops = &es581_4_ops; } - es58x_dev = devm_kzalloc(dev, es58x_sizeof_es58x_device(param), - GFP_KERNEL); - if (!es58x_dev) + devlink = devlink_alloc(&es58x_dl_ops, es58x_sizeof_es58x_device(param), + dev); + if (!devlink) return ERR_PTR(-ENOMEM); + es58x_dev = devlink_priv(devlink); es58x_dev->param = param; es58x_dev->ops = ops; es58x_dev->dev = dev; @@ -2240,25 +2219,24 @@ static int es58x_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct es58x_device *es58x_dev; - int ch_idx, ret; + int ch_idx; es58x_dev = es58x_init_es58x_dev(intf, id->driver_info); if (IS_ERR(es58x_dev)) return PTR_ERR(es58x_dev); - ret = es58x_get_product_info(es58x_dev); - if (ret) - return ret; + es58x_parse_product_info(es58x_dev); + devlink_register(priv_to_devlink(es58x_dev)); for (ch_idx = 0; ch_idx < es58x_dev->num_can_ch; ch_idx++) { - ret = es58x_init_netdev(es58x_dev, ch_idx); + int ret = es58x_init_netdev(es58x_dev, ch_idx); if (ret) { es58x_free_netdevs(es58x_dev); return ret; } } - return ret; + return 0; } /** @@ -2275,8 +2253,10 @@ static void es58x_disconnect(struct usb_interface *intf) dev_info(&intf->dev, "Disconnecting %s %s\n", es58x_dev->udev->manufacturer, es58x_dev->udev->product); + devlink_unregister(priv_to_devlink(es58x_dev)); es58x_free_netdevs(es58x_dev); es58x_free_urbs(es58x_dev); + devlink_free(priv_to_devlink(es58x_dev)); usb_set_intfdata(intf, NULL); } diff --git a/drivers/net/can/usb/etas_es58x/es58x_core.h b/drivers/net/can/usb/etas_es58x/es58x_core.h index 640fe0a1df63..c1ba1a4e8857 100644 --- a/drivers/net/can/usb/etas_es58x/es58x_core.h +++ b/drivers/net/can/usb/etas_es58x/es58x_core.h @@ -6,17 +6,18 @@ * * Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved. * Copyright (c) 2020 ETAS K.K.. All rights reserved. - * Copyright (c) 2020, 2021 Vincent Mailhol <mailhol.vincent@wanadoo.fr> + * Copyright (c) 2020-2022 Vincent Mailhol <mailhol.vincent@wanadoo.fr> */ #ifndef __ES58X_COMMON_H__ #define __ES58X_COMMON_H__ -#include <linux/types.h> -#include <linux/usb.h> -#include <linux/netdevice.h> #include <linux/can.h> #include <linux/can/dev.h> +#include <linux/netdevice.h> +#include <linux/types.h> +#include <linux/usb.h> +#include <net/devlink.h> #include "es581_4.h" #include "es58x_fd.h" @@ -230,6 +231,7 @@ union es58x_urb_cmd { * @can: struct can_priv must be the first member (Socket CAN relies * on the fact that function netdev_priv() returns a pointer to * a struct can_priv). + * @devlink_port: devlink instance for the network interface. * @es58x_dev: pointer to the corresponding ES58X device. * @tx_urb: Used as a buffer to concatenate the TX messages and to do * a bulk send. Please refer to es58x_start_xmit() for more @@ -255,6 +257,7 @@ union es58x_urb_cmd { */ struct es58x_priv { struct can_priv can; + struct devlink_port devlink_port; struct es58x_device *es58x_dev; struct urb *tx_urb; @@ -357,6 +360,39 @@ struct es58x_operators { }; /** + * struct es58x_sw_version - Version number of the firmware or the + * bootloader. + * @major: Version major number, represented on two digits. + * @minor: Version minor number, represented on two digits. + * @revision: Version revision number, represented on two digits. + * + * The firmware and the bootloader share the same format: "xx.xx.xx" + * where 'x' is a digit. Both can be retrieved from the product + * information string. + */ +struct es58x_sw_version { + u8 major; + u8 minor; + u8 revision; +}; + +/** + * struct es58x_hw_revision - Hardware revision number. + * @letter: Revision letter. + * @major: Version major number, represented on three digits. + * @minor: Version minor number, represented on three digits. + * + * The hardware revision uses its own format: "axxx/xxx" where 'a' is + * a letter and 'x' a digit. It can be retrieved from the product + * information string. + */ +struct es58x_hw_revision { + char letter; + u16 major; + u16 minor; +}; + +/** * struct es58x_device - All information specific to an ES58X device. * @dev: Device information. * @udev: USB device information. @@ -373,6 +409,9 @@ struct es58x_operators { * queue wake/stop logic should prevent this URB from getting * empty. Please refer to es58x_get_tx_urb() for more details. * @tx_urbs_idle_cnt: number of urbs in @tx_urbs_idle. + * @firmware_version: The firmware version number. + * @bootloader_version: The bootloader version number. + * @hardware_revision: The hardware revision number. * @ktime_req_ns: kernel timestamp when es58x_set_realtime_diff_ns() * was called. * @realtime_diff_ns: difference in nanoseconds between the clocks of @@ -408,6 +447,10 @@ struct es58x_device { struct usb_anchor tx_urbs_idle; atomic_t tx_urbs_idle_cnt; + struct es58x_sw_version firmware_version; + struct es58x_sw_version bootloader_version; + struct es58x_hw_revision hardware_revision; + u64 ktime_req_ns; s64 realtime_diff_ns; @@ -674,6 +717,7 @@ static inline enum es58x_flag es58x_get_flags(const struct sk_buff *skb) return es58x_flags; } +/* es58x_core.c. */ int es58x_can_get_echo_skb(struct net_device *netdev, u32 packet_idx, u64 *tstamps, unsigned int pkts); int es58x_tx_ack_msg(struct net_device *netdev, u16 tx_free_entries, @@ -691,9 +735,15 @@ int es58x_rx_cmd_ret_u32(struct net_device *netdev, int es58x_send_msg(struct es58x_device *es58x_dev, u8 cmd_type, u8 cmd_id, const void *msg, u16 cmd_len, int channel_idx); +/* es58x_devlink.c. */ +void es58x_parse_product_info(struct es58x_device *es58x_dev); +extern const struct devlink_ops es58x_dl_ops; + +/* es581_4.c. */ extern const struct es58x_parameters es581_4_param; extern const struct es58x_operators es581_4_ops; +/* es58x_fd.c. */ extern const struct es58x_parameters es58x_fd_param; extern const struct es58x_operators es58x_fd_ops; diff --git a/drivers/net/can/usb/etas_es58x/es58x_devlink.c b/drivers/net/can/usb/etas_es58x/es58x_devlink.c new file mode 100644 index 000000000000..9fba29e2f57c --- /dev/null +++ b/drivers/net/can/usb/etas_es58x/es58x_devlink.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* Driver for ETAS GmbH ES58X USB CAN(-FD) Bus Interfaces. + * + * File es58x_devlink.c: report the product information using devlink. + * + * Copyright (c) 2022 Vincent Mailhol <mailhol.vincent@wanadoo.fr> + */ + +#include <linux/ctype.h> +#include <linux/device.h> +#include <linux/usb.h> +#include <net/devlink.h> + +#include "es58x_core.h" + +/* USB descriptor index containing the product information string. */ +#define ES58X_PROD_INFO_IDX 6 + +/** + * es58x_parse_sw_version() - Extract boot loader or firmware version. + * @es58x_dev: ES58X device. + * @prod_info: USB custom string returned by the device. + * @prefix: Select which information should be parsed. Set it to "FW" + * to parse the firmware version or to "BL" to parse the + * bootloader version. + * + * The @prod_info string contains the firmware and the bootloader + * version number all prefixed by a magic string and concatenated with + * other numbers. Depending on the device, the firmware (bootloader) + * format is either "FW_Vxx.xx.xx" ("BL_Vxx.xx.xx") or "FW:xx.xx.xx" + * ("BL:xx.xx.xx") where 'x' represents a digit. @prod_info must + * contains the common part of those prefixes: "FW" or "BL". + * + * Parse @prod_info and store the version number in + * &es58x_dev.firmware_version or &es58x_dev.bootloader_version + * according to @prefix value. + * + * Return: zero on success, -EINVAL if @prefix contains an invalid + * value and -EBADMSG if @prod_info could not be parsed. + */ +static int es58x_parse_sw_version(struct es58x_device *es58x_dev, + const char *prod_info, const char *prefix) +{ + struct es58x_sw_version *version; + int major, minor, revision; + + if (!strcmp(prefix, "FW")) + version = &es58x_dev->firmware_version; + else if (!strcmp(prefix, "BL")) + version = &es58x_dev->bootloader_version; + else + return -EINVAL; + + /* Go to prefix */ + prod_info = strstr(prod_info, prefix); + if (!prod_info) + return -EBADMSG; + /* Go to beginning of the version number */ + while (!isdigit(*prod_info)) { + prod_info++; + if (!*prod_info) + return -EBADMSG; + } + + if (sscanf(prod_info, "%2u.%2u.%2u", &major, &minor, &revision) != 3) + return -EBADMSG; + + version->major = major; + version->minor = minor; + version->revision = revision; + + return 0; +} + +/** + * es58x_parse_hw_rev() - Extract hardware revision number. + * @es58x_dev: ES58X device. + * @prod_info: USB custom string returned by the device. + * + * @prod_info contains the hardware revision prefixed by a magic + * string and conquenated together with other numbers. Depending on + * the device, the hardware revision format is either + * "HW_VER:axxx/xxx" or "HR:axxx/xxx" where 'a' represents a letter + * and 'x' a digit. + * + * Parse @prod_info and store the hardware revision number in + * &es58x_dev.hardware_revision. + * + * Return: zero on success, -EBADMSG if @prod_info could not be + * parsed. + */ +static int es58x_parse_hw_rev(struct es58x_device *es58x_dev, + const char *prod_info) +{ + char letter; + int major, minor; + + /* The only occurrence of 'H' is in the hardware revision prefix. */ + prod_info = strchr(prod_info, 'H'); + if (!prod_info) + return -EBADMSG; + /* Go to beginning of the hardware revision */ + prod_info = strchr(prod_info, ':'); + if (!prod_info) + return -EBADMSG; + prod_info++; + + if (sscanf(prod_info, "%c%3u/%3u", &letter, &major, &minor) != 3) + return -EBADMSG; + + es58x_dev->hardware_revision.letter = letter; + es58x_dev->hardware_revision.major = major; + es58x_dev->hardware_revision.minor = minor; + + return 0; +} + +/** + * es58x_parse_product_info() - Parse the ES58x product information + * string. + * @es58x_dev: ES58X device. + * + * Retrieve the product information string and parse it to extract the + * firmware version, the bootloader version and the hardware + * revision. + * + * If the function fails, simply emit a log message and continue + * because product information is not critical for the driver to + * operate. + */ +void es58x_parse_product_info(struct es58x_device *es58x_dev) +{ + char *prod_info; + + prod_info = usb_cache_string(es58x_dev->udev, ES58X_PROD_INFO_IDX); + if (!prod_info) { + dev_warn(es58x_dev->dev, + "could not retrieve the product info string\n"); + return; + } + + if (es58x_parse_sw_version(es58x_dev, prod_info, "FW") || + es58x_parse_sw_version(es58x_dev, prod_info, "BL") || + es58x_parse_hw_rev(es58x_dev, prod_info)) + dev_info(es58x_dev->dev, + "could not parse product info: '%s'\n", prod_info); + + kfree(prod_info); +} + +/** + * es58x_sw_version_is_set() - Check if the version is a valid number. + * @sw_ver: Version number of either the firmware or the bootloader. + * + * If &es58x_sw_version.major, &es58x_sw_version.minor and + * &es58x_sw_version.revision are all zero, the product string could + * not be parsed and the version number is invalid. + */ +static inline bool es58x_sw_version_is_set(struct es58x_sw_version *sw_ver) +{ + return sw_ver->major || sw_ver->minor || sw_ver->revision; +} + +/** + * es58x_hw_revision_is_set() - Check if the revision is a valid number. + * @hw_rev: Revision number of the hardware. + * + * If &es58x_hw_revision.letter is the null character, the product + * string could not be parsed and the hardware revision number is + * invalid. + */ +static inline bool es58x_hw_revision_is_set(struct es58x_hw_revision *hw_rev) +{ + return hw_rev->letter != '\0'; +} + +/** + * es58x_devlink_info_get() - Report the product information. + * @devlink: Devlink. + * @req: skb wrapper where to put requested information. + * @extack: Unused. + * + * Report the firmware version, the bootloader version, the hardware + * revision and the serial number through netlink. + * + * Return: zero on success, errno when any error occurs. + */ +static int es58x_devlink_info_get(struct devlink *devlink, + struct devlink_info_req *req, + struct netlink_ext_ack *extack) +{ + struct es58x_device *es58x_dev = devlink_priv(devlink); + struct es58x_sw_version *fw_ver = &es58x_dev->firmware_version; + struct es58x_sw_version *bl_ver = &es58x_dev->bootloader_version; + struct es58x_hw_revision *hw_rev = &es58x_dev->hardware_revision; + char buf[max(sizeof("xx.xx.xx"), sizeof("axxx/xxx"))]; + int ret = 0; + + if (es58x_sw_version_is_set(fw_ver)) { + snprintf(buf, sizeof(buf), "%02u.%02u.%02u", + fw_ver->major, fw_ver->minor, fw_ver->revision); + ret = devlink_info_version_running_put(req, + DEVLINK_INFO_VERSION_GENERIC_FW, + buf); + if (ret) + return ret; + } + + if (es58x_sw_version_is_set(bl_ver)) { + snprintf(buf, sizeof(buf), "%02u.%02u.%02u", + bl_ver->major, bl_ver->minor, bl_ver->revision); + ret = devlink_info_version_running_put(req, + DEVLINK_INFO_VERSION_GENERIC_FW_BOOTLOADER, + buf); + if (ret) + return ret; + } + + if (es58x_hw_revision_is_set(hw_rev)) { + snprintf(buf, sizeof(buf), "%c%03u/%03u", + hw_rev->letter, hw_rev->major, hw_rev->minor); + ret = devlink_info_version_fixed_put(req, + DEVLINK_INFO_VERSION_GENERIC_BOARD_REV, + buf); + if (ret) + return ret; + } + + return devlink_info_serial_number_put(req, es58x_dev->udev->serial); +} + +const struct devlink_ops es58x_dl_ops = { + .info_get = es58x_devlink_info_get, +}; diff --git a/drivers/net/can/usb/etas_es58x/es58x_fd.c b/drivers/net/can/usb/etas_es58x/es58x_fd.c index c97ffa71fd75..fa87b0b78e3e 100644 --- a/drivers/net/can/usb/etas_es58x/es58x_fd.c +++ b/drivers/net/can/usb/etas_es58x/es58x_fd.c @@ -8,12 +8,12 @@ * * Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved. * Copyright (c) 2020 ETAS K.K.. All rights reserved. - * Copyright (c) 2020, 2021 Vincent Mailhol <mailhol.vincent@wanadoo.fr> + * Copyright (c) 2020-2022 Vincent Mailhol <mailhol.vincent@wanadoo.fr> */ +#include <asm/unaligned.h> #include <linux/kernel.h> #include <linux/units.h> -#include <asm/unaligned.h> #include "es58x_core.h" #include "es58x_fd.h" diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index 9c2c25fde3d1..d476c2884008 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -66,6 +66,7 @@ enum gs_usb_breq { GS_USB_BREQ_BT_CONST_EXT, GS_USB_BREQ_SET_TERMINATION, GS_USB_BREQ_GET_TERMINATION, + GS_USB_BREQ_GET_STATE, }; enum gs_can_mode { @@ -134,6 +135,8 @@ struct gs_device_config { /* GS_CAN_FEATURE_REQ_USB_QUIRK_LPC546XX BIT(9) */ /* GS_CAN_FEATURE_BT_CONST_EXT BIT(10) */ /* GS_CAN_FEATURE_TERMINATION BIT(11) */ +#define GS_CAN_MODE_BERR_REPORTING BIT(12) +/* GS_CAN_FEATURE_GET_STATE BIT(13) */ struct gs_device_mode { __le32 mode; @@ -174,7 +177,9 @@ struct gs_device_termination_state { #define GS_CAN_FEATURE_REQ_USB_QUIRK_LPC546XX BIT(9) #define GS_CAN_FEATURE_BT_CONST_EXT BIT(10) #define GS_CAN_FEATURE_TERMINATION BIT(11) -#define GS_CAN_FEATURE_MASK GENMASK(11, 0) +#define GS_CAN_FEATURE_BERR_REPORTING BIT(12) +#define GS_CAN_FEATURE_GET_STATE BIT(13) +#define GS_CAN_FEATURE_MASK GENMASK(13, 0) /* internal quirks - keep in GS_CAN_FEATURE space for now */ @@ -294,7 +299,6 @@ struct gs_can { struct net_device *netdev; struct usb_device *udev; - struct usb_interface *iface; struct can_bittiming_const bt_const, data_bt_const; unsigned int channel; /* channel number */ @@ -378,8 +382,7 @@ static int gs_cmd_reset(struct gs_can *dev) .mode = GS_CAN_MODE_RESET, }; - return usb_control_msg_send(interface_to_usbdev(dev->iface), 0, - GS_USB_BREQ_MODE, + return usb_control_msg_send(dev->udev, 0, GS_USB_BREQ_MODE, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, dev->channel, 0, &dm, sizeof(dm), 1000, GFP_KERNEL); @@ -391,8 +394,7 @@ static inline int gs_usb_get_timestamp(const struct gs_can *dev, __le32 timestamp; int rc; - rc = usb_control_msg_recv(interface_to_usbdev(dev->iface), 0, - GS_USB_BREQ_TIMESTAMP, + rc = usb_control_msg_recv(dev->udev, 0, GS_USB_BREQ_TIMESTAMP, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, dev->channel, 0, ×tamp, sizeof(timestamp), @@ -669,8 +671,7 @@ static int gs_usb_set_bittiming(struct net_device *netdev) }; /* request bit timings */ - return usb_control_msg_send(interface_to_usbdev(dev->iface), 0, - GS_USB_BREQ_BITTIMING, + return usb_control_msg_send(dev->udev, 0, GS_USB_BREQ_BITTIMING, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, dev->channel, 0, &dbt, sizeof(dbt), 1000, GFP_KERNEL); @@ -693,8 +694,7 @@ static int gs_usb_set_data_bittiming(struct net_device *netdev) request = GS_USB_BREQ_QUIRK_CANTACT_PRO_DATA_BITTIMING; /* request data bit timings */ - return usb_control_msg_send(interface_to_usbdev(dev->iface), 0, - request, + return usb_control_msg_send(dev->udev, 0, request, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, dev->channel, 0, &dbt, sizeof(dbt), 1000, GFP_KERNEL); @@ -843,8 +843,6 @@ static int gs_can_open(struct net_device *netdev) ctrlmode = dev->can.ctrlmode; if (ctrlmode & CAN_CTRLMODE_FD) { - flags |= GS_CAN_MODE_FD; - if (dev->feature & GS_CAN_FEATURE_REQ_USB_QUIRK_LPC546XX) dev->hf_size_tx = struct_size(hf, canfd_quirk, 1); else @@ -911,31 +909,34 @@ static int gs_can_open(struct net_device *netdev) /* flags */ if (ctrlmode & CAN_CTRLMODE_LOOPBACK) flags |= GS_CAN_MODE_LOOP_BACK; - else if (ctrlmode & CAN_CTRLMODE_LISTENONLY) + + if (ctrlmode & CAN_CTRLMODE_LISTENONLY) flags |= GS_CAN_MODE_LISTEN_ONLY; - /* Controller is not allowed to retry TX - * this mode is unavailable on atmels uc3c hardware - */ + if (ctrlmode & CAN_CTRLMODE_3_SAMPLES) + flags |= GS_CAN_MODE_TRIPLE_SAMPLE; + if (ctrlmode & CAN_CTRLMODE_ONE_SHOT) flags |= GS_CAN_MODE_ONE_SHOT; - if (ctrlmode & CAN_CTRLMODE_3_SAMPLES) - flags |= GS_CAN_MODE_TRIPLE_SAMPLE; + if (ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + flags |= GS_CAN_MODE_BERR_REPORTING; + + if (ctrlmode & CAN_CTRLMODE_FD) + flags |= GS_CAN_MODE_FD; /* if hardware supports timestamps, enable it */ - if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP) + if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP) { flags |= GS_CAN_MODE_HW_TIMESTAMP; - /* start polling timestamp */ - if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP) + /* start polling timestamp */ gs_usb_timestamp_init(dev); + } /* finally start device */ dev->can.state = CAN_STATE_ERROR_ACTIVE; dm.flags = cpu_to_le32(flags); - rc = usb_control_msg_send(interface_to_usbdev(dev->iface), 0, - GS_USB_BREQ_MODE, + rc = usb_control_msg_send(dev->udev, 0, GS_USB_BREQ_MODE, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, dev->channel, 0, &dm, sizeof(dm), 1000, GFP_KERNEL); @@ -954,6 +955,41 @@ static int gs_can_open(struct net_device *netdev) return 0; } +static int gs_usb_get_state(const struct net_device *netdev, + struct can_berr_counter *bec, + enum can_state *state) +{ + struct gs_can *dev = netdev_priv(netdev); + struct gs_device_state ds; + int rc; + + rc = usb_control_msg_recv(dev->udev, 0, GS_USB_BREQ_GET_STATE, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + dev->channel, 0, + &ds, sizeof(ds), + USB_CTRL_GET_TIMEOUT, + GFP_KERNEL); + if (rc) + return rc; + + if (le32_to_cpu(ds.state) >= CAN_STATE_MAX) + return -EOPNOTSUPP; + + *state = le32_to_cpu(ds.state); + bec->txerr = le32_to_cpu(ds.txerr); + bec->rxerr = le32_to_cpu(ds.rxerr); + + return 0; +} + +static int gs_usb_can_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + enum can_state state; + + return gs_usb_get_state(netdev, bec, &state); +} + static int gs_can_close(struct net_device *netdev) { int rc; @@ -1021,8 +1057,7 @@ static int gs_usb_set_identify(struct net_device *netdev, bool do_identify) else imode.mode = cpu_to_le32(GS_CAN_IDENTIFY_OFF); - return usb_control_msg_send(interface_to_usbdev(dev->iface), 0, - GS_USB_BREQ_IDENTIFY, + return usb_control_msg_send(dev->udev, 0, GS_USB_BREQ_IDENTIFY, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, dev->channel, 0, &imode, sizeof(imode), 100, GFP_KERNEL); @@ -1075,8 +1110,7 @@ static int gs_usb_get_termination(struct net_device *netdev, u16 *term) struct gs_device_termination_state term_state; int rc; - rc = usb_control_msg_recv(interface_to_usbdev(dev->iface), 0, - GS_USB_BREQ_GET_TERMINATION, + rc = usb_control_msg_recv(dev->udev, 0, GS_USB_BREQ_GET_TERMINATION, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, dev->channel, 0, &term_state, sizeof(term_state), 1000, @@ -1102,8 +1136,7 @@ static int gs_usb_set_termination(struct net_device *netdev, u16 term) else term_state.state = cpu_to_le32(GS_CAN_TERMINATION_STATE_OFF); - return usb_control_msg_send(interface_to_usbdev(dev->iface), 0, - GS_USB_BREQ_SET_TERMINATION, + return usb_control_msg_send(dev->udev, 0, GS_USB_BREQ_SET_TERMINATION, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, dev->channel, 0, &term_state, sizeof(term_state), 1000, @@ -1153,6 +1186,7 @@ static struct gs_can *gs_make_candev(unsigned int channel, netdev->ethtool_ops = &gs_usb_ethtool_ops; netdev->flags |= IFF_ECHO; /* we support full roundtrip echo */ + netdev->dev_id = channel; /* dev setup */ strcpy(dev->bt_const.name, KBUILD_MODNAME); @@ -1166,7 +1200,6 @@ static struct gs_can *gs_make_candev(unsigned int channel, dev->bt_const.brp_inc = le32_to_cpu(bt_const.brp_inc); dev->udev = interface_to_usbdev(intf); - dev->iface = intf; dev->netdev = netdev; dev->channel = channel; @@ -1224,6 +1257,12 @@ static struct gs_can *gs_make_candev(unsigned int channel, } } + if (feature & GS_CAN_FEATURE_BERR_REPORTING) + dev->can.ctrlmode_supported |= CAN_CTRLMODE_BERR_REPORTING; + + if (feature & GS_CAN_FEATURE_GET_STATE) + dev->can.do_get_berr_counter = gs_usb_can_get_berr_counter; + /* The CANtact Pro from LinkLayer Labs is based on the * LPC54616 µC, which is affected by the NXP LPC USB transfer * erratum. However, the current firmware (version 2) doesn't diff --git a/drivers/net/can/usb/kvaser_usb/Makefile b/drivers/net/can/usb/kvaser_usb/Makefile index b20d951a0790..cf260044f0b9 100644 --- a/drivers/net/can/usb/kvaser_usb/Makefile +++ b/drivers/net/can/usb/kvaser_usb/Makefile @@ -1,8 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o kvaser_usb-y = kvaser_usb_core.o kvaser_usb_leaf.o kvaser_usb_hydra.o - -# FIXME: temporarily silence -Warray-bounds on non W=1+ builds -ifndef KBUILD_EXTRA_WARN -CFLAGS_kvaser_usb_hydra.o += -Wno-array-bounds -endif diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h index f6c0938027ec..ff10b3790d84 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb.h +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb.h @@ -76,6 +76,14 @@ struct kvaser_usb_tx_urb_context { u32 echo_index; }; +struct kvaser_usb_busparams { + __le32 bitrate; + u8 tseg1; + u8 tseg2; + u8 sjw; + u8 nsamples; +} __packed; + struct kvaser_usb { struct usb_device *udev; struct usb_interface *intf; @@ -104,13 +112,19 @@ struct kvaser_usb_net_priv { struct can_priv can; struct can_berr_counter bec; + /* subdriver-specific data */ + void *sub_priv; + struct kvaser_usb *dev; struct net_device *netdev; int channel; - struct completion start_comp, stop_comp, flush_comp; + struct completion start_comp, stop_comp, flush_comp, + get_busparams_comp; struct usb_anchor tx_submitted; + struct kvaser_usb_busparams busparams_nominal, busparams_data; + spinlock_t tx_contexts_lock; /* lock for active_tx_contexts */ int active_tx_contexts; struct kvaser_usb_tx_urb_context tx_contexts[]; @@ -120,11 +134,15 @@ struct kvaser_usb_net_priv { * struct kvaser_usb_dev_ops - Device specific functions * @dev_set_mode: used for can.do_set_mode * @dev_set_bittiming: used for can.do_set_bittiming + * @dev_get_busparams: readback arbitration busparams * @dev_set_data_bittiming: used for can.do_set_data_bittiming + * @dev_get_data_busparams: readback data busparams * @dev_get_berr_counter: used for can.do_get_berr_counter * * @dev_setup_endpoints: setup USB in and out endpoints * @dev_init_card: initialize card + * @dev_init_channel: initialize channel + * @dev_remove_channel: uninitialize channel * @dev_get_software_info: get software info * @dev_get_software_details: get software details * @dev_get_card_info: get card info @@ -140,12 +158,18 @@ struct kvaser_usb_net_priv { */ struct kvaser_usb_dev_ops { int (*dev_set_mode)(struct net_device *netdev, enum can_mode mode); - int (*dev_set_bittiming)(struct net_device *netdev); - int (*dev_set_data_bittiming)(struct net_device *netdev); + int (*dev_set_bittiming)(const struct net_device *netdev, + const struct kvaser_usb_busparams *busparams); + int (*dev_get_busparams)(struct kvaser_usb_net_priv *priv); + int (*dev_set_data_bittiming)(const struct net_device *netdev, + const struct kvaser_usb_busparams *busparams); + int (*dev_get_data_busparams)(struct kvaser_usb_net_priv *priv); int (*dev_get_berr_counter)(const struct net_device *netdev, struct can_berr_counter *bec); int (*dev_setup_endpoints)(struct kvaser_usb *dev); int (*dev_init_card)(struct kvaser_usb *dev); + int (*dev_init_channel)(struct kvaser_usb_net_priv *priv); + void (*dev_remove_channel)(struct kvaser_usb_net_priv *priv); int (*dev_get_software_info)(struct kvaser_usb *dev); int (*dev_get_software_details)(struct kvaser_usb *dev); int (*dev_get_card_info)(struct kvaser_usb *dev); diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c index 802e27c0eced..d4c5356d5884 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c @@ -440,10 +440,6 @@ static int kvaser_usb_open(struct net_device *netdev) if (err) return err; - err = kvaser_usb_setup_rx_urbs(dev); - if (err) - goto error; - err = ops->dev_set_opt_mode(priv); if (err) goto error; @@ -534,6 +530,91 @@ static int kvaser_usb_close(struct net_device *netdev) return 0; } +static int kvaser_usb_set_bittiming(struct net_device *netdev) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct kvaser_usb *dev = priv->dev; + const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops; + struct can_bittiming *bt = &priv->can.bittiming; + struct kvaser_usb_busparams busparams; + int tseg1 = bt->prop_seg + bt->phase_seg1; + int tseg2 = bt->phase_seg2; + int sjw = bt->sjw; + int err; + + busparams.bitrate = cpu_to_le32(bt->bitrate); + busparams.sjw = (u8)sjw; + busparams.tseg1 = (u8)tseg1; + busparams.tseg2 = (u8)tseg2; + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + busparams.nsamples = 3; + else + busparams.nsamples = 1; + + err = ops->dev_set_bittiming(netdev, &busparams); + if (err) + return err; + + err = kvaser_usb_setup_rx_urbs(priv->dev); + if (err) + return err; + + err = ops->dev_get_busparams(priv); + if (err) { + /* Treat EOPNOTSUPP as success */ + if (err == -EOPNOTSUPP) + err = 0; + return err; + } + + if (memcmp(&busparams, &priv->busparams_nominal, + sizeof(priv->busparams_nominal)) != 0) + err = -EINVAL; + + return err; +} + +static int kvaser_usb_set_data_bittiming(struct net_device *netdev) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct kvaser_usb *dev = priv->dev; + const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops; + struct can_bittiming *dbt = &priv->can.data_bittiming; + struct kvaser_usb_busparams busparams; + int tseg1 = dbt->prop_seg + dbt->phase_seg1; + int tseg2 = dbt->phase_seg2; + int sjw = dbt->sjw; + int err; + + if (!ops->dev_set_data_bittiming || + !ops->dev_get_data_busparams) + return -EOPNOTSUPP; + + busparams.bitrate = cpu_to_le32(dbt->bitrate); + busparams.sjw = (u8)sjw; + busparams.tseg1 = (u8)tseg1; + busparams.tseg2 = (u8)tseg2; + busparams.nsamples = 1; + + err = ops->dev_set_data_bittiming(netdev, &busparams); + if (err) + return err; + + err = kvaser_usb_setup_rx_urbs(priv->dev); + if (err) + return err; + + err = ops->dev_get_data_busparams(priv); + if (err) + return err; + + if (memcmp(&busparams, &priv->busparams_data, + sizeof(priv->busparams_data)) != 0) + err = -EINVAL; + + return err; +} + static void kvaser_usb_write_bulk_callback(struct urb *urb) { struct kvaser_usb_tx_urb_context *context = urb->context; @@ -684,6 +765,7 @@ static const struct ethtool_ops kvaser_usb_ethtool_ops_hwts = { static void kvaser_usb_remove_interfaces(struct kvaser_usb *dev) { + const struct kvaser_usb_dev_ops *ops = dev->driver_info->ops; int i; for (i = 0; i < dev->nchannels; i++) { @@ -699,6 +781,9 @@ static void kvaser_usb_remove_interfaces(struct kvaser_usb *dev) if (!dev->nets[i]) continue; + if (ops->dev_remove_channel) + ops->dev_remove_channel(dev->nets[i]); + free_candev(dev->nets[i]->netdev); } } @@ -730,6 +815,7 @@ static int kvaser_usb_init_one(struct kvaser_usb *dev, int channel) init_completion(&priv->start_comp); init_completion(&priv->stop_comp); init_completion(&priv->flush_comp); + init_completion(&priv->get_busparams_comp); priv->can.ctrlmode_supported = 0; priv->dev = dev; @@ -742,7 +828,7 @@ static int kvaser_usb_init_one(struct kvaser_usb *dev, int channel) priv->can.state = CAN_STATE_STOPPED; priv->can.clock.freq = dev->cfg->clock.freq; priv->can.bittiming_const = dev->cfg->bittiming_const; - priv->can.do_set_bittiming = ops->dev_set_bittiming; + priv->can.do_set_bittiming = kvaser_usb_set_bittiming; priv->can.do_set_mode = ops->dev_set_mode; if ((driver_info->quirks & KVASER_USB_QUIRK_HAS_TXRX_ERRORS) || (priv->dev->card_data.capabilities & KVASER_USB_CAP_BERR_CAP)) @@ -754,7 +840,7 @@ static int kvaser_usb_init_one(struct kvaser_usb *dev, int channel) if (priv->can.ctrlmode_supported & CAN_CTRLMODE_FD) { priv->can.data_bittiming_const = dev->cfg->data_bittiming_const; - priv->can.do_set_data_bittiming = ops->dev_set_data_bittiming; + priv->can.do_set_data_bittiming = kvaser_usb_set_data_bittiming; } netdev->flags |= IFF_ECHO; @@ -772,17 +858,26 @@ static int kvaser_usb_init_one(struct kvaser_usb *dev, int channel) dev->nets[channel] = priv; + if (ops->dev_init_channel) { + err = ops->dev_init_channel(priv); + if (err) + goto err; + } + err = register_candev(netdev); if (err) { dev_err(&dev->intf->dev, "Failed to register CAN device\n"); - free_candev(netdev); - dev->nets[channel] = NULL; - return err; + goto err; } netdev_dbg(netdev, "device registered\n"); return 0; + +err: + free_candev(netdev); + dev->nets[channel] = NULL; + return err; } static int kvaser_usb_probe(struct usb_interface *intf, diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c index 66f672ea631b..ef341c4254fc 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c @@ -45,6 +45,8 @@ static const struct kvaser_usb_dev_cfg kvaser_usb_hydra_dev_cfg_rt; /* Minihydra command IDs */ #define CMD_SET_BUSPARAMS_REQ 16 +#define CMD_GET_BUSPARAMS_REQ 17 +#define CMD_GET_BUSPARAMS_RESP 18 #define CMD_GET_CHIP_STATE_REQ 19 #define CMD_CHIP_STATE_EVENT 20 #define CMD_SET_DRIVERMODE_REQ 21 @@ -196,21 +198,26 @@ struct kvaser_cmd_chip_state_event { #define KVASER_USB_HYDRA_BUS_MODE_CANFD_ISO 0x01 #define KVASER_USB_HYDRA_BUS_MODE_NONISO 0x02 struct kvaser_cmd_set_busparams { - __le32 bitrate; - u8 tseg1; - u8 tseg2; - u8 sjw; - u8 nsamples; + struct kvaser_usb_busparams busparams_nominal; u8 reserved0[4]; - __le32 bitrate_d; - u8 tseg1_d; - u8 tseg2_d; - u8 sjw_d; - u8 nsamples_d; + struct kvaser_usb_busparams busparams_data; u8 canfd_mode; u8 reserved1[7]; } __packed; +/* Busparam type */ +#define KVASER_USB_HYDRA_BUSPARAM_TYPE_CAN 0x00 +#define KVASER_USB_HYDRA_BUSPARAM_TYPE_CANFD 0x01 +struct kvaser_cmd_get_busparams_req { + u8 type; + u8 reserved[27]; +} __packed; + +struct kvaser_cmd_get_busparams_res { + struct kvaser_usb_busparams busparams; + u8 reserved[20]; +} __packed; + /* Ctrl modes */ #define KVASER_USB_HYDRA_CTRLMODE_NORMAL 0x01 #define KVASER_USB_HYDRA_CTRLMODE_LISTEN 0x02 @@ -281,6 +288,8 @@ struct kvaser_cmd { struct kvaser_cmd_error_event error_event; struct kvaser_cmd_set_busparams set_busparams_req; + struct kvaser_cmd_get_busparams_req get_busparams_req; + struct kvaser_cmd_get_busparams_res get_busparams_res; struct kvaser_cmd_chip_state_event chip_state_event; @@ -363,6 +372,10 @@ struct kvaser_cmd_ext { } __packed; } __packed; +struct kvaser_usb_net_hydra_priv { + int pending_get_busparams_type; +}; + static const struct can_bittiming_const kvaser_usb_hydra_kcan_bittiming_c = { .name = "kvaser_usb_kcan", .tseg1_min = 1, @@ -532,6 +545,7 @@ static int kvaser_usb_hydra_send_simple_cmd(struct kvaser_usb *dev, u8 cmd_no, int channel) { struct kvaser_cmd *cmd; + size_t cmd_len; int err; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -539,6 +553,7 @@ static int kvaser_usb_hydra_send_simple_cmd(struct kvaser_usb *dev, return -ENOMEM; cmd->header.cmd_no = cmd_no; + cmd_len = kvaser_usb_hydra_cmd_size(cmd); if (channel < 0) { kvaser_usb_hydra_set_cmd_dest_he (cmd, KVASER_USB_HYDRA_HE_ADDRESS_ILLEGAL); @@ -555,7 +570,7 @@ static int kvaser_usb_hydra_send_simple_cmd(struct kvaser_usb *dev, kvaser_usb_hydra_set_cmd_transid (cmd, kvaser_usb_hydra_get_next_transid(dev)); - err = kvaser_usb_send_cmd(dev, cmd, kvaser_usb_hydra_cmd_size(cmd)); + err = kvaser_usb_send_cmd(dev, cmd, cmd_len); if (err) goto end; @@ -571,6 +586,7 @@ kvaser_usb_hydra_send_simple_cmd_async(struct kvaser_usb_net_priv *priv, { struct kvaser_cmd *cmd; struct kvaser_usb *dev = priv->dev; + size_t cmd_len; int err; cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC); @@ -578,14 +594,14 @@ kvaser_usb_hydra_send_simple_cmd_async(struct kvaser_usb_net_priv *priv, return -ENOMEM; cmd->header.cmd_no = cmd_no; + cmd_len = kvaser_usb_hydra_cmd_size(cmd); kvaser_usb_hydra_set_cmd_dest_he (cmd, dev->card_data.hydra.channel_to_he[priv->channel]); kvaser_usb_hydra_set_cmd_transid (cmd, kvaser_usb_hydra_get_next_transid(dev)); - err = kvaser_usb_send_cmd_async(priv, cmd, - kvaser_usb_hydra_cmd_size(cmd)); + err = kvaser_usb_send_cmd_async(priv, cmd, cmd_len); if (err) kfree(cmd); @@ -729,6 +745,7 @@ static int kvaser_usb_hydra_get_single_capability(struct kvaser_usb *dev, { struct kvaser_usb_dev_card_data *card_data = &dev->card_data; struct kvaser_cmd *cmd; + size_t cmd_len; u32 value = 0; u32 mask = 0; u16 cap_cmd_res; @@ -740,13 +757,14 @@ static int kvaser_usb_hydra_get_single_capability(struct kvaser_usb *dev, return -ENOMEM; cmd->header.cmd_no = CMD_GET_CAPABILITIES_REQ; + cmd_len = kvaser_usb_hydra_cmd_size(cmd); cmd->cap_req.cap_cmd = cpu_to_le16(cap_cmd_req); kvaser_usb_hydra_set_cmd_dest_he(cmd, card_data->hydra.sysdbg_he); kvaser_usb_hydra_set_cmd_transid (cmd, kvaser_usb_hydra_get_next_transid(dev)); - err = kvaser_usb_send_cmd(dev, cmd, kvaser_usb_hydra_cmd_size(cmd)); + err = kvaser_usb_send_cmd(dev, cmd, cmd_len); if (err) goto end; @@ -840,6 +858,39 @@ static void kvaser_usb_hydra_flush_queue_reply(const struct kvaser_usb *dev, complete(&priv->flush_comp); } +static void kvaser_usb_hydra_get_busparams_reply(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + struct kvaser_usb_net_hydra_priv *hydra; + + priv = kvaser_usb_hydra_net_priv_from_cmd(dev, cmd); + if (!priv) + return; + + hydra = priv->sub_priv; + if (!hydra) + return; + + switch (hydra->pending_get_busparams_type) { + case KVASER_USB_HYDRA_BUSPARAM_TYPE_CAN: + memcpy(&priv->busparams_nominal, &cmd->get_busparams_res.busparams, + sizeof(priv->busparams_nominal)); + break; + case KVASER_USB_HYDRA_BUSPARAM_TYPE_CANFD: + memcpy(&priv->busparams_data, &cmd->get_busparams_res.busparams, + sizeof(priv->busparams_nominal)); + break; + default: + dev_warn(&dev->intf->dev, "Unknown get_busparams_type %d\n", + hydra->pending_get_busparams_type); + break; + } + hydra->pending_get_busparams_type = -1; + + complete(&priv->get_busparams_comp); +} + static void kvaser_usb_hydra_bus_status_to_can_state(const struct kvaser_usb_net_priv *priv, u8 bus_status, @@ -1326,6 +1377,10 @@ static void kvaser_usb_hydra_handle_cmd_std(const struct kvaser_usb *dev, kvaser_usb_hydra_state_event(dev, cmd); break; + case CMD_GET_BUSPARAMS_RESP: + kvaser_usb_hydra_get_busparams_reply(dev, cmd); + break; + case CMD_ERROR_EVENT: kvaser_usb_hydra_error_event(dev, cmd); break; @@ -1522,15 +1577,61 @@ static int kvaser_usb_hydra_set_mode(struct net_device *netdev, return err; } -static int kvaser_usb_hydra_set_bittiming(struct net_device *netdev) +static int kvaser_usb_hydra_get_busparams(struct kvaser_usb_net_priv *priv, + int busparams_type) +{ + struct kvaser_usb *dev = priv->dev; + struct kvaser_usb_net_hydra_priv *hydra = priv->sub_priv; + struct kvaser_cmd *cmd; + size_t cmd_len; + int err; + + if (!hydra) + return -EINVAL; + + cmd = kcalloc(1, sizeof(struct kvaser_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->header.cmd_no = CMD_GET_BUSPARAMS_REQ; + cmd_len = kvaser_usb_hydra_cmd_size(cmd); + kvaser_usb_hydra_set_cmd_dest_he + (cmd, dev->card_data.hydra.channel_to_he[priv->channel]); + kvaser_usb_hydra_set_cmd_transid + (cmd, kvaser_usb_hydra_get_next_transid(dev)); + cmd->get_busparams_req.type = busparams_type; + hydra->pending_get_busparams_type = busparams_type; + + reinit_completion(&priv->get_busparams_comp); + + err = kvaser_usb_send_cmd(dev, cmd, cmd_len); + if (err) + return err; + + if (!wait_for_completion_timeout(&priv->get_busparams_comp, + msecs_to_jiffies(KVASER_USB_TIMEOUT))) + return -ETIMEDOUT; + + return err; +} + +static int kvaser_usb_hydra_get_nominal_busparams(struct kvaser_usb_net_priv *priv) +{ + return kvaser_usb_hydra_get_busparams(priv, KVASER_USB_HYDRA_BUSPARAM_TYPE_CAN); +} + +static int kvaser_usb_hydra_get_data_busparams(struct kvaser_usb_net_priv *priv) +{ + return kvaser_usb_hydra_get_busparams(priv, KVASER_USB_HYDRA_BUSPARAM_TYPE_CANFD); +} + +static int kvaser_usb_hydra_set_bittiming(const struct net_device *netdev, + const struct kvaser_usb_busparams *busparams) { struct kvaser_cmd *cmd; struct kvaser_usb_net_priv *priv = netdev_priv(netdev); - struct can_bittiming *bt = &priv->can.bittiming; struct kvaser_usb *dev = priv->dev; - int tseg1 = bt->prop_seg + bt->phase_seg1; - int tseg2 = bt->phase_seg2; - int sjw = bt->sjw; + size_t cmd_len; int err; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -1538,33 +1639,29 @@ static int kvaser_usb_hydra_set_bittiming(struct net_device *netdev) return -ENOMEM; cmd->header.cmd_no = CMD_SET_BUSPARAMS_REQ; - cmd->set_busparams_req.bitrate = cpu_to_le32(bt->bitrate); - cmd->set_busparams_req.sjw = (u8)sjw; - cmd->set_busparams_req.tseg1 = (u8)tseg1; - cmd->set_busparams_req.tseg2 = (u8)tseg2; - cmd->set_busparams_req.nsamples = 1; + cmd_len = kvaser_usb_hydra_cmd_size(cmd); + memcpy(&cmd->set_busparams_req.busparams_nominal, busparams, + sizeof(cmd->set_busparams_req.busparams_nominal)); kvaser_usb_hydra_set_cmd_dest_he (cmd, dev->card_data.hydra.channel_to_he[priv->channel]); kvaser_usb_hydra_set_cmd_transid (cmd, kvaser_usb_hydra_get_next_transid(dev)); - err = kvaser_usb_send_cmd(dev, cmd, kvaser_usb_hydra_cmd_size(cmd)); + err = kvaser_usb_send_cmd(dev, cmd, cmd_len); kfree(cmd); return err; } -static int kvaser_usb_hydra_set_data_bittiming(struct net_device *netdev) +static int kvaser_usb_hydra_set_data_bittiming(const struct net_device *netdev, + const struct kvaser_usb_busparams *busparams) { struct kvaser_cmd *cmd; struct kvaser_usb_net_priv *priv = netdev_priv(netdev); - struct can_bittiming *dbt = &priv->can.data_bittiming; struct kvaser_usb *dev = priv->dev; - int tseg1 = dbt->prop_seg + dbt->phase_seg1; - int tseg2 = dbt->phase_seg2; - int sjw = dbt->sjw; + size_t cmd_len; int err; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -1572,11 +1669,9 @@ static int kvaser_usb_hydra_set_data_bittiming(struct net_device *netdev) return -ENOMEM; cmd->header.cmd_no = CMD_SET_BUSPARAMS_FD_REQ; - cmd->set_busparams_req.bitrate_d = cpu_to_le32(dbt->bitrate); - cmd->set_busparams_req.sjw_d = (u8)sjw; - cmd->set_busparams_req.tseg1_d = (u8)tseg1; - cmd->set_busparams_req.tseg2_d = (u8)tseg2; - cmd->set_busparams_req.nsamples_d = 1; + cmd_len = kvaser_usb_hydra_cmd_size(cmd); + memcpy(&cmd->set_busparams_req.busparams_data, busparams, + sizeof(cmd->set_busparams_req.busparams_data)); if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) @@ -1592,7 +1687,7 @@ static int kvaser_usb_hydra_set_data_bittiming(struct net_device *netdev) kvaser_usb_hydra_set_cmd_transid (cmd, kvaser_usb_hydra_get_next_transid(dev)); - err = kvaser_usb_send_cmd(dev, cmd, kvaser_usb_hydra_cmd_size(cmd)); + err = kvaser_usb_send_cmd(dev, cmd, cmd_len); kfree(cmd); @@ -1683,6 +1778,19 @@ static int kvaser_usb_hydra_init_card(struct kvaser_usb *dev) return 0; } +static int kvaser_usb_hydra_init_channel(struct kvaser_usb_net_priv *priv) +{ + struct kvaser_usb_net_hydra_priv *hydra; + + hydra = devm_kzalloc(&priv->dev->intf->dev, sizeof(*hydra), GFP_KERNEL); + if (!hydra) + return -ENOMEM; + + priv->sub_priv = hydra; + + return 0; +} + static int kvaser_usb_hydra_get_software_info(struct kvaser_usb *dev) { struct kvaser_cmd cmd; @@ -1707,6 +1815,7 @@ static int kvaser_usb_hydra_get_software_info(struct kvaser_usb *dev) static int kvaser_usb_hydra_get_software_details(struct kvaser_usb *dev) { struct kvaser_cmd *cmd; + size_t cmd_len; int err; u32 flags; struct kvaser_usb_dev_card_data *card_data = &dev->card_data; @@ -1716,6 +1825,7 @@ static int kvaser_usb_hydra_get_software_details(struct kvaser_usb *dev) return -ENOMEM; cmd->header.cmd_no = CMD_GET_SOFTWARE_DETAILS_REQ; + cmd_len = kvaser_usb_hydra_cmd_size(cmd); cmd->sw_detail_req.use_ext_cmd = 1; kvaser_usb_hydra_set_cmd_dest_he (cmd, KVASER_USB_HYDRA_HE_ADDRESS_ILLEGAL); @@ -1723,7 +1833,7 @@ static int kvaser_usb_hydra_get_software_details(struct kvaser_usb *dev) kvaser_usb_hydra_set_cmd_transid (cmd, kvaser_usb_hydra_get_next_transid(dev)); - err = kvaser_usb_send_cmd(dev, cmd, kvaser_usb_hydra_cmd_size(cmd)); + err = kvaser_usb_send_cmd(dev, cmd, cmd_len); if (err) goto end; @@ -1841,6 +1951,7 @@ static int kvaser_usb_hydra_set_opt_mode(const struct kvaser_usb_net_priv *priv) { struct kvaser_usb *dev = priv->dev; struct kvaser_cmd *cmd; + size_t cmd_len; int err; if ((priv->can.ctrlmode & @@ -1856,6 +1967,7 @@ static int kvaser_usb_hydra_set_opt_mode(const struct kvaser_usb_net_priv *priv) return -ENOMEM; cmd->header.cmd_no = CMD_SET_DRIVERMODE_REQ; + cmd_len = kvaser_usb_hydra_cmd_size(cmd); kvaser_usb_hydra_set_cmd_dest_he (cmd, dev->card_data.hydra.channel_to_he[priv->channel]); kvaser_usb_hydra_set_cmd_transid @@ -1865,7 +1977,7 @@ static int kvaser_usb_hydra_set_opt_mode(const struct kvaser_usb_net_priv *priv) else cmd->set_ctrlmode.mode = KVASER_USB_HYDRA_CTRLMODE_NORMAL; - err = kvaser_usb_send_cmd(dev, cmd, kvaser_usb_hydra_cmd_size(cmd)); + err = kvaser_usb_send_cmd(dev, cmd, cmd_len); kfree(cmd); return err; @@ -2027,10 +2139,13 @@ kvaser_usb_hydra_frame_to_cmd(const struct kvaser_usb_net_priv *priv, const struct kvaser_usb_dev_ops kvaser_usb_hydra_dev_ops = { .dev_set_mode = kvaser_usb_hydra_set_mode, .dev_set_bittiming = kvaser_usb_hydra_set_bittiming, + .dev_get_busparams = kvaser_usb_hydra_get_nominal_busparams, .dev_set_data_bittiming = kvaser_usb_hydra_set_data_bittiming, + .dev_get_data_busparams = kvaser_usb_hydra_get_data_busparams, .dev_get_berr_counter = kvaser_usb_hydra_get_berr_counter, .dev_setup_endpoints = kvaser_usb_hydra_setup_endpoints, .dev_init_card = kvaser_usb_hydra_init_card, + .dev_init_channel = kvaser_usb_hydra_init_channel, .dev_get_software_info = kvaser_usb_hydra_get_software_info, .dev_get_software_details = kvaser_usb_hydra_get_software_details, .dev_get_card_info = kvaser_usb_hydra_get_card_info, diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c index 19958037720f..1c2f99ce4c6c 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -21,6 +21,7 @@ #include <linux/types.h> #include <linux/units.h> #include <linux/usb.h> +#include <linux/workqueue.h> #include <linux/can.h> #include <linux/can/dev.h> @@ -56,6 +57,9 @@ #define CMD_RX_EXT_MESSAGE 14 #define CMD_TX_EXT_MESSAGE 15 #define CMD_SET_BUS_PARAMS 16 +#define CMD_GET_BUS_PARAMS 17 +#define CMD_GET_BUS_PARAMS_REPLY 18 +#define CMD_GET_CHIP_STATE 19 #define CMD_CHIP_STATE_EVENT 20 #define CMD_SET_CTRL_MODE 21 #define CMD_RESET_CHIP 24 @@ -70,10 +74,13 @@ #define CMD_GET_CARD_INFO_REPLY 35 #define CMD_GET_SOFTWARE_INFO 38 #define CMD_GET_SOFTWARE_INFO_REPLY 39 +#define CMD_ERROR_EVENT 45 #define CMD_FLUSH_QUEUE 48 #define CMD_TX_ACKNOWLEDGE 50 #define CMD_CAN_ERROR_EVENT 51 #define CMD_FLUSH_QUEUE_REPLY 68 +#define CMD_GET_CAPABILITIES_REQ 95 +#define CMD_GET_CAPABILITIES_RESP 96 #define CMD_LEAF_LOG_MESSAGE 106 @@ -83,6 +90,8 @@ #define KVASER_USB_LEAF_SWOPTION_FREQ_32_MHZ_CLK BIT(5) #define KVASER_USB_LEAF_SWOPTION_FREQ_24_MHZ_CLK BIT(6) +#define KVASER_USB_LEAF_SWOPTION_EXT_CAP BIT(12) + /* error factors */ #define M16C_EF_ACKE BIT(0) #define M16C_EF_CRCE BIT(1) @@ -157,11 +166,7 @@ struct usbcan_cmd_softinfo { struct kvaser_cmd_busparams { u8 tid; u8 channel; - __le32 bitrate; - u8 tseg1; - u8 tseg2; - u8 sjw; - u8 no_samp; + struct kvaser_usb_busparams busparams; } __packed; struct kvaser_cmd_tx_can { @@ -230,7 +235,7 @@ struct kvaser_cmd_tx_acknowledge_header { u8 tid; } __packed; -struct leaf_cmd_error_event { +struct leaf_cmd_can_error_event { u8 tid; u8 flags; __le16 time[3]; @@ -242,7 +247,7 @@ struct leaf_cmd_error_event { u8 error_factor; } __packed; -struct usbcan_cmd_error_event { +struct usbcan_cmd_can_error_event { u8 tid; u8 padding; u8 tx_errors_count_ch0; @@ -254,6 +259,28 @@ struct usbcan_cmd_error_event { __le16 time; } __packed; +/* CMD_ERROR_EVENT error codes */ +#define KVASER_USB_LEAF_ERROR_EVENT_TX_QUEUE_FULL 0x8 +#define KVASER_USB_LEAF_ERROR_EVENT_PARAM 0x9 + +struct leaf_cmd_error_event { + u8 tid; + u8 error_code; + __le16 timestamp[3]; + __le16 padding; + __le16 info1; + __le16 info2; +} __packed; + +struct usbcan_cmd_error_event { + u8 tid; + u8 error_code; + __le16 info1; + __le16 info2; + __le16 timestamp; + __le16 padding; +} __packed; + struct kvaser_cmd_ctrl_mode { u8 tid; u8 channel; @@ -278,6 +305,28 @@ struct leaf_cmd_log_message { u8 data[8]; } __packed; +/* Sub commands for cap_req and cap_res */ +#define KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE 0x02 +#define KVASER_USB_LEAF_CAP_CMD_ERR_REPORT 0x05 +struct kvaser_cmd_cap_req { + __le16 padding0; + __le16 cap_cmd; + __le16 padding1; + __le16 channel; +} __packed; + +/* Status codes for cap_res */ +#define KVASER_USB_LEAF_CAP_STAT_OK 0x00 +#define KVASER_USB_LEAF_CAP_STAT_NOT_IMPL 0x01 +#define KVASER_USB_LEAF_CAP_STAT_UNAVAIL 0x02 +struct kvaser_cmd_cap_res { + __le16 padding; + __le16 cap_cmd; + __le16 status; + __le32 mask; + __le32 value; +} __packed; + struct kvaser_cmd { u8 len; u8 id; @@ -293,14 +342,18 @@ struct kvaser_cmd { struct leaf_cmd_softinfo softinfo; struct leaf_cmd_rx_can rx_can; struct leaf_cmd_chip_state_event chip_state_event; - struct leaf_cmd_error_event error_event; + struct leaf_cmd_can_error_event can_error_event; struct leaf_cmd_log_message log_message; + struct leaf_cmd_error_event error_event; + struct kvaser_cmd_cap_req cap_req; + struct kvaser_cmd_cap_res cap_res; } __packed leaf; union { struct usbcan_cmd_softinfo softinfo; struct usbcan_cmd_rx_can rx_can; struct usbcan_cmd_chip_state_event chip_state_event; + struct usbcan_cmd_can_error_event can_error_event; struct usbcan_cmd_error_event error_event; } __packed usbcan; @@ -323,7 +376,10 @@ static const u8 kvaser_usb_leaf_cmd_sizes_leaf[] = { [CMD_RX_EXT_MESSAGE] = kvaser_fsize(u.leaf.rx_can), [CMD_LEAF_LOG_MESSAGE] = kvaser_fsize(u.leaf.log_message), [CMD_CHIP_STATE_EVENT] = kvaser_fsize(u.leaf.chip_state_event), - [CMD_CAN_ERROR_EVENT] = kvaser_fsize(u.leaf.error_event), + [CMD_CAN_ERROR_EVENT] = kvaser_fsize(u.leaf.can_error_event), + [CMD_GET_CAPABILITIES_RESP] = kvaser_fsize(u.leaf.cap_res), + [CMD_GET_BUS_PARAMS_REPLY] = kvaser_fsize(u.busparams), + [CMD_ERROR_EVENT] = kvaser_fsize(u.leaf.error_event), /* ignored events: */ [CMD_FLUSH_QUEUE_REPLY] = CMD_SIZE_ANY, }; @@ -337,7 +393,8 @@ static const u8 kvaser_usb_leaf_cmd_sizes_usbcan[] = { [CMD_RX_STD_MESSAGE] = kvaser_fsize(u.usbcan.rx_can), [CMD_RX_EXT_MESSAGE] = kvaser_fsize(u.usbcan.rx_can), [CMD_CHIP_STATE_EVENT] = kvaser_fsize(u.usbcan.chip_state_event), - [CMD_CAN_ERROR_EVENT] = kvaser_fsize(u.usbcan.error_event), + [CMD_CAN_ERROR_EVENT] = kvaser_fsize(u.usbcan.can_error_event), + [CMD_ERROR_EVENT] = kvaser_fsize(u.usbcan.error_event), /* ignored events: */ [CMD_USBCAN_CLOCK_OVERFLOW_EVENT] = CMD_SIZE_ANY, }; @@ -365,6 +422,15 @@ struct kvaser_usb_err_summary { }; }; +struct kvaser_usb_net_leaf_priv { + struct kvaser_usb_net_priv *net; + + struct delayed_work chip_state_req_work; + + /* started but not reported as bus-on yet */ + bool joining_bus; +}; + static const struct can_bittiming_const kvaser_usb_leaf_m16c_bittiming_const = { .name = "kvaser_usb_ucii", .tseg1_min = 4, @@ -606,6 +672,9 @@ static void kvaser_usb_leaf_get_software_info_leaf(struct kvaser_usb *dev, dev->fw_version = le32_to_cpu(softinfo->fw_version); dev->max_tx_urbs = le16_to_cpu(softinfo->max_outstanding_tx); + if (sw_options & KVASER_USB_LEAF_SWOPTION_EXT_CAP) + dev->card_data.capabilities |= KVASER_USB_CAP_EXT_CAP; + if (dev->driver_info->quirks & KVASER_USB_QUIRK_IGNORE_CLK_FREQ) { /* Firmware expects bittiming parameters calculated for 16MHz * clock, regardless of the actual clock @@ -693,6 +762,116 @@ static int kvaser_usb_leaf_get_card_info(struct kvaser_usb *dev) return 0; } +static int kvaser_usb_leaf_get_single_capability(struct kvaser_usb *dev, + u16 cap_cmd_req, u16 *status) +{ + struct kvaser_usb_dev_card_data *card_data = &dev->card_data; + struct kvaser_cmd *cmd; + u32 value = 0; + u32 mask = 0; + u16 cap_cmd_res; + int err; + int i; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->id = CMD_GET_CAPABILITIES_REQ; + cmd->u.leaf.cap_req.cap_cmd = cpu_to_le16(cap_cmd_req); + cmd->len = CMD_HEADER_LEN + sizeof(struct kvaser_cmd_cap_req); + + err = kvaser_usb_send_cmd(dev, cmd, cmd->len); + if (err) + goto end; + + err = kvaser_usb_leaf_wait_cmd(dev, CMD_GET_CAPABILITIES_RESP, cmd); + if (err) + goto end; + + *status = le16_to_cpu(cmd->u.leaf.cap_res.status); + + if (*status != KVASER_USB_LEAF_CAP_STAT_OK) + goto end; + + cap_cmd_res = le16_to_cpu(cmd->u.leaf.cap_res.cap_cmd); + switch (cap_cmd_res) { + case KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE: + case KVASER_USB_LEAF_CAP_CMD_ERR_REPORT: + value = le32_to_cpu(cmd->u.leaf.cap_res.value); + mask = le32_to_cpu(cmd->u.leaf.cap_res.mask); + break; + default: + dev_warn(&dev->intf->dev, "Unknown capability command %u\n", + cap_cmd_res); + break; + } + + for (i = 0; i < dev->nchannels; i++) { + if (BIT(i) & (value & mask)) { + switch (cap_cmd_res) { + case KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE: + card_data->ctrlmode_supported |= + CAN_CTRLMODE_LISTENONLY; + break; + case KVASER_USB_LEAF_CAP_CMD_ERR_REPORT: + card_data->capabilities |= + KVASER_USB_CAP_BERR_CAP; + break; + } + } + } + +end: + kfree(cmd); + + return err; +} + +static int kvaser_usb_leaf_get_capabilities_leaf(struct kvaser_usb *dev) +{ + int err; + u16 status; + + if (!(dev->card_data.capabilities & KVASER_USB_CAP_EXT_CAP)) { + dev_info(&dev->intf->dev, + "No extended capability support. Upgrade device firmware.\n"); + return 0; + } + + err = kvaser_usb_leaf_get_single_capability(dev, + KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE, + &status); + if (err) + return err; + if (status) + dev_info(&dev->intf->dev, + "KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE failed %u\n", + status); + + err = kvaser_usb_leaf_get_single_capability(dev, + KVASER_USB_LEAF_CAP_CMD_ERR_REPORT, + &status); + if (err) + return err; + if (status) + dev_info(&dev->intf->dev, + "KVASER_USB_LEAF_CAP_CMD_ERR_REPORT failed %u\n", + status); + + return 0; +} + +static int kvaser_usb_leaf_get_capabilities(struct kvaser_usb *dev) +{ + int err = 0; + + if (dev->driver_info->family == KVASER_LEAF) + err = kvaser_usb_leaf_get_capabilities_leaf(dev); + + return err; +} + static void kvaser_usb_leaf_tx_acknowledge(const struct kvaser_usb *dev, const struct kvaser_cmd *cmd) { @@ -721,7 +900,7 @@ static void kvaser_usb_leaf_tx_acknowledge(const struct kvaser_usb *dev, context = &priv->tx_contexts[tid % dev->max_tx_urbs]; /* Sometimes the state change doesn't come after a bus-off event */ - if (priv->can.restart_ms && priv->can.state >= CAN_STATE_BUS_OFF) { + if (priv->can.restart_ms && priv->can.state == CAN_STATE_BUS_OFF) { struct sk_buff *skb; struct can_frame *cf; @@ -774,11 +953,22 @@ static int kvaser_usb_leaf_simple_cmd_async(struct kvaser_usb_net_priv *priv, return err; } +static void kvaser_usb_leaf_chip_state_req_work(struct work_struct *work) +{ + struct kvaser_usb_net_leaf_priv *leaf = + container_of(work, struct kvaser_usb_net_leaf_priv, + chip_state_req_work.work); + struct kvaser_usb_net_priv *priv = leaf->net; + + kvaser_usb_leaf_simple_cmd_async(priv, CMD_GET_CHIP_STATE); +} + static void kvaser_usb_leaf_rx_error_update_can_state(struct kvaser_usb_net_priv *priv, const struct kvaser_usb_err_summary *es, struct can_frame *cf) { + struct kvaser_usb_net_leaf_priv *leaf = priv->sub_priv; struct kvaser_usb *dev = priv->dev; struct net_device_stats *stats = &priv->netdev->stats; enum can_state cur_state, new_state, tx_state, rx_state; @@ -792,20 +982,32 @@ kvaser_usb_leaf_rx_error_update_can_state(struct kvaser_usb_net_priv *priv, new_state = CAN_STATE_BUS_OFF; } else if (es->status & M16C_STATE_BUS_PASSIVE) { new_state = CAN_STATE_ERROR_PASSIVE; - } else if (es->status & M16C_STATE_BUS_ERROR) { + } else if ((es->status & M16C_STATE_BUS_ERROR) && + cur_state >= CAN_STATE_BUS_OFF) { /* Guard against spurious error events after a busoff */ - if (cur_state < CAN_STATE_BUS_OFF) { - if (es->txerr >= 128 || es->rxerr >= 128) - new_state = CAN_STATE_ERROR_PASSIVE; - else if (es->txerr >= 96 || es->rxerr >= 96) - new_state = CAN_STATE_ERROR_WARNING; - else if (cur_state > CAN_STATE_ERROR_ACTIVE) - new_state = CAN_STATE_ERROR_ACTIVE; - } + } else if (es->txerr >= 128 || es->rxerr >= 128) { + new_state = CAN_STATE_ERROR_PASSIVE; + } else if (es->txerr >= 96 || es->rxerr >= 96) { + new_state = CAN_STATE_ERROR_WARNING; + } else { + new_state = CAN_STATE_ERROR_ACTIVE; } - if (!es->status) - new_state = CAN_STATE_ERROR_ACTIVE; + /* 0bfd:0124 FW 4.18.778 was observed to send the initial + * CMD_CHIP_STATE_EVENT after CMD_START_CHIP with M16C_STATE_BUS_OFF + * bit set if the channel was bus-off when it was last stopped (even + * across chip resets). This bit will clear shortly afterwards, without + * triggering a second unsolicited chip state event. + * Ignore this initial bus-off. + */ + if (leaf->joining_bus) { + if (new_state == CAN_STATE_BUS_OFF) { + netdev_dbg(priv->netdev, "ignoring bus-off during startup"); + new_state = cur_state; + } else { + leaf->joining_bus = false; + } + } if (new_state != cur_state) { tx_state = (es->txerr >= es->rxerr) ? new_state : 0; @@ -815,7 +1017,7 @@ kvaser_usb_leaf_rx_error_update_can_state(struct kvaser_usb_net_priv *priv, } if (priv->can.restart_ms && - cur_state >= CAN_STATE_BUS_OFF && + cur_state == CAN_STATE_BUS_OFF && new_state < CAN_STATE_BUS_OFF) priv->can.can_stats.restarts++; @@ -849,6 +1051,7 @@ static void kvaser_usb_leaf_rx_error(const struct kvaser_usb *dev, struct sk_buff *skb; struct net_device_stats *stats; struct kvaser_usb_net_priv *priv; + struct kvaser_usb_net_leaf_priv *leaf; enum can_state old_state, new_state; if (es->channel >= dev->nchannels) { @@ -858,8 +1061,13 @@ static void kvaser_usb_leaf_rx_error(const struct kvaser_usb *dev, } priv = dev->nets[es->channel]; + leaf = priv->sub_priv; stats = &priv->netdev->stats; + /* Ignore e.g. state change to bus-off reported just after stopping */ + if (!netif_running(priv->netdev)) + return; + /* Update all of the CAN interface's state and error counters before * trying any memory allocation that can actually fail with -ENOMEM. * @@ -874,6 +1082,17 @@ static void kvaser_usb_leaf_rx_error(const struct kvaser_usb *dev, kvaser_usb_leaf_rx_error_update_can_state(priv, es, &tmp_cf); new_state = priv->can.state; + /* If there are errors, request status updates periodically as we do + * not get automatic notifications of improved state. + * Also request updates if we saw a stale BUS_OFF during startup + * (joining_bus). + */ + if (new_state < CAN_STATE_BUS_OFF && + (es->rxerr || es->txerr || new_state == CAN_STATE_ERROR_PASSIVE || + leaf->joining_bus)) + schedule_delayed_work(&leaf->chip_state_req_work, + msecs_to_jiffies(500)); + skb = alloc_can_err_skb(priv->netdev, &cf); if (!skb) { stats->rx_dropped++; @@ -891,7 +1110,7 @@ static void kvaser_usb_leaf_rx_error(const struct kvaser_usb *dev, } if (priv->can.restart_ms && - old_state >= CAN_STATE_BUS_OFF && + old_state == CAN_STATE_BUS_OFF && new_state < CAN_STATE_BUS_OFF) { cf->can_id |= CAN_ERR_RESTARTED; netif_carrier_on(priv->netdev); @@ -990,11 +1209,11 @@ static void kvaser_usb_leaf_usbcan_rx_error(const struct kvaser_usb *dev, case CMD_CAN_ERROR_EVENT: es.channel = 0; - es.status = cmd->u.usbcan.error_event.status_ch0; - es.txerr = cmd->u.usbcan.error_event.tx_errors_count_ch0; - es.rxerr = cmd->u.usbcan.error_event.rx_errors_count_ch0; + es.status = cmd->u.usbcan.can_error_event.status_ch0; + es.txerr = cmd->u.usbcan.can_error_event.tx_errors_count_ch0; + es.rxerr = cmd->u.usbcan.can_error_event.rx_errors_count_ch0; es.usbcan.other_ch_status = - cmd->u.usbcan.error_event.status_ch1; + cmd->u.usbcan.can_error_event.status_ch1; kvaser_usb_leaf_usbcan_conditionally_rx_error(dev, &es); /* The USBCAN firmware supports up to 2 channels. @@ -1002,13 +1221,13 @@ static void kvaser_usb_leaf_usbcan_rx_error(const struct kvaser_usb *dev, */ if (dev->nchannels == MAX_USBCAN_NET_DEVICES) { es.channel = 1; - es.status = cmd->u.usbcan.error_event.status_ch1; + es.status = cmd->u.usbcan.can_error_event.status_ch1; es.txerr = - cmd->u.usbcan.error_event.tx_errors_count_ch1; + cmd->u.usbcan.can_error_event.tx_errors_count_ch1; es.rxerr = - cmd->u.usbcan.error_event.rx_errors_count_ch1; + cmd->u.usbcan.can_error_event.rx_errors_count_ch1; es.usbcan.other_ch_status = - cmd->u.usbcan.error_event.status_ch0; + cmd->u.usbcan.can_error_event.status_ch0; kvaser_usb_leaf_usbcan_conditionally_rx_error(dev, &es); } break; @@ -1025,11 +1244,11 @@ static void kvaser_usb_leaf_leaf_rx_error(const struct kvaser_usb *dev, switch (cmd->id) { case CMD_CAN_ERROR_EVENT: - es.channel = cmd->u.leaf.error_event.channel; - es.status = cmd->u.leaf.error_event.status; - es.txerr = cmd->u.leaf.error_event.tx_errors_count; - es.rxerr = cmd->u.leaf.error_event.rx_errors_count; - es.leaf.error_factor = cmd->u.leaf.error_event.error_factor; + es.channel = cmd->u.leaf.can_error_event.channel; + es.status = cmd->u.leaf.can_error_event.status; + es.txerr = cmd->u.leaf.can_error_event.tx_errors_count; + es.rxerr = cmd->u.leaf.can_error_event.rx_errors_count; + es.leaf.error_factor = cmd->u.leaf.can_error_event.error_factor; break; case CMD_LEAF_LOG_MESSAGE: es.channel = cmd->u.leaf.log_message.channel; @@ -1162,6 +1381,74 @@ static void kvaser_usb_leaf_rx_can_msg(const struct kvaser_usb *dev, netif_rx(skb); } +static void kvaser_usb_leaf_error_event_parameter(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + u16 info1 = 0; + + switch (dev->driver_info->family) { + case KVASER_LEAF: + info1 = le16_to_cpu(cmd->u.leaf.error_event.info1); + break; + case KVASER_USBCAN: + info1 = le16_to_cpu(cmd->u.usbcan.error_event.info1); + break; + } + + /* info1 will contain the offending cmd_no */ + switch (info1) { + case CMD_SET_CTRL_MODE: + dev_warn(&dev->intf->dev, + "CMD_SET_CTRL_MODE error in parameter\n"); + break; + + case CMD_SET_BUS_PARAMS: + dev_warn(&dev->intf->dev, + "CMD_SET_BUS_PARAMS error in parameter\n"); + break; + + default: + dev_warn(&dev->intf->dev, + "Unhandled parameter error event cmd_no (%u)\n", + info1); + break; + } +} + +static void kvaser_usb_leaf_error_event(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + u8 error_code = 0; + + switch (dev->driver_info->family) { + case KVASER_LEAF: + error_code = cmd->u.leaf.error_event.error_code; + break; + case KVASER_USBCAN: + error_code = cmd->u.usbcan.error_event.error_code; + break; + } + + switch (error_code) { + case KVASER_USB_LEAF_ERROR_EVENT_TX_QUEUE_FULL: + /* Received additional CAN message, when firmware TX queue is + * already full. Something is wrong with the driver. + * This should never happen! + */ + dev_err(&dev->intf->dev, + "Received error event TX_QUEUE_FULL\n"); + break; + case KVASER_USB_LEAF_ERROR_EVENT_PARAM: + kvaser_usb_leaf_error_event_parameter(dev, cmd); + break; + + default: + dev_warn(&dev->intf->dev, + "Unhandled error event (%d)\n", error_code); + break; + } +} + static void kvaser_usb_leaf_start_chip_reply(const struct kvaser_usb *dev, const struct kvaser_cmd *cmd) { @@ -1202,6 +1489,25 @@ static void kvaser_usb_leaf_stop_chip_reply(const struct kvaser_usb *dev, complete(&priv->stop_comp); } +static void kvaser_usb_leaf_get_busparams_reply(const struct kvaser_usb *dev, + const struct kvaser_cmd *cmd) +{ + struct kvaser_usb_net_priv *priv; + u8 channel = cmd->u.busparams.channel; + + if (channel >= dev->nchannels) { + dev_err(&dev->intf->dev, + "Invalid channel number (%d)\n", channel); + return; + } + + priv = dev->nets[channel]; + memcpy(&priv->busparams_nominal, &cmd->u.busparams.busparams, + sizeof(priv->busparams_nominal)); + + complete(&priv->get_busparams_comp); +} + static void kvaser_usb_leaf_handle_command(const struct kvaser_usb *dev, const struct kvaser_cmd *cmd) { @@ -1240,6 +1546,14 @@ static void kvaser_usb_leaf_handle_command(const struct kvaser_usb *dev, kvaser_usb_leaf_tx_acknowledge(dev, cmd); break; + case CMD_ERROR_EVENT: + kvaser_usb_leaf_error_event(dev, cmd); + break; + + case CMD_GET_BUS_PARAMS_REPLY: + kvaser_usb_leaf_get_busparams_reply(dev, cmd); + break; + /* Ignored commands */ case CMD_USBCAN_CLOCK_OVERFLOW_EVENT: if (dev->driver_info->family != KVASER_USBCAN) @@ -1318,8 +1632,11 @@ static int kvaser_usb_leaf_set_opt_mode(const struct kvaser_usb_net_priv *priv) static int kvaser_usb_leaf_start_chip(struct kvaser_usb_net_priv *priv) { + struct kvaser_usb_net_leaf_priv *leaf = priv->sub_priv; int err; + leaf->joining_bus = true; + reinit_completion(&priv->start_comp); err = kvaser_usb_leaf_send_simple_cmd(priv->dev, CMD_START_CHIP, @@ -1336,10 +1653,13 @@ static int kvaser_usb_leaf_start_chip(struct kvaser_usb_net_priv *priv) static int kvaser_usb_leaf_stop_chip(struct kvaser_usb_net_priv *priv) { + struct kvaser_usb_net_leaf_priv *leaf = priv->sub_priv; int err; reinit_completion(&priv->stop_comp); + cancel_delayed_work(&leaf->chip_state_req_work); + err = kvaser_usb_leaf_send_simple_cmd(priv->dev, CMD_STOP_CHIP, priv->channel); if (err) @@ -1386,10 +1706,35 @@ static int kvaser_usb_leaf_init_card(struct kvaser_usb *dev) return 0; } -static int kvaser_usb_leaf_set_bittiming(struct net_device *netdev) +static int kvaser_usb_leaf_init_channel(struct kvaser_usb_net_priv *priv) +{ + struct kvaser_usb_net_leaf_priv *leaf; + + leaf = devm_kzalloc(&priv->dev->intf->dev, sizeof(*leaf), GFP_KERNEL); + if (!leaf) + return -ENOMEM; + + leaf->net = priv; + INIT_DELAYED_WORK(&leaf->chip_state_req_work, + kvaser_usb_leaf_chip_state_req_work); + + priv->sub_priv = leaf; + + return 0; +} + +static void kvaser_usb_leaf_remove_channel(struct kvaser_usb_net_priv *priv) +{ + struct kvaser_usb_net_leaf_priv *leaf = priv->sub_priv; + + if (leaf) + cancel_delayed_work_sync(&leaf->chip_state_req_work); +} + +static int kvaser_usb_leaf_set_bittiming(const struct net_device *netdev, + const struct kvaser_usb_busparams *busparams) { struct kvaser_usb_net_priv *priv = netdev_priv(netdev); - struct can_bittiming *bt = &priv->can.bittiming; struct kvaser_usb *dev = priv->dev; struct kvaser_cmd *cmd; int rc; @@ -1402,15 +1747,8 @@ static int kvaser_usb_leaf_set_bittiming(struct net_device *netdev) cmd->len = CMD_HEADER_LEN + sizeof(struct kvaser_cmd_busparams); cmd->u.busparams.channel = priv->channel; cmd->u.busparams.tid = 0xff; - cmd->u.busparams.bitrate = cpu_to_le32(bt->bitrate); - cmd->u.busparams.sjw = bt->sjw; - cmd->u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1; - cmd->u.busparams.tseg2 = bt->phase_seg2; - - if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) - cmd->u.busparams.no_samp = 3; - else - cmd->u.busparams.no_samp = 1; + memcpy(&cmd->u.busparams.busparams, busparams, + sizeof(cmd->u.busparams.busparams)); rc = kvaser_usb_send_cmd(dev, cmd, cmd->len); @@ -1418,16 +1756,40 @@ static int kvaser_usb_leaf_set_bittiming(struct net_device *netdev) return rc; } +static int kvaser_usb_leaf_get_busparams(struct kvaser_usb_net_priv *priv) +{ + int err; + + if (priv->dev->driver_info->family == KVASER_USBCAN) + return -EOPNOTSUPP; + + reinit_completion(&priv->get_busparams_comp); + + err = kvaser_usb_leaf_send_simple_cmd(priv->dev, CMD_GET_BUS_PARAMS, + priv->channel); + if (err) + return err; + + if (!wait_for_completion_timeout(&priv->get_busparams_comp, + msecs_to_jiffies(KVASER_USB_TIMEOUT))) + return -ETIMEDOUT; + + return 0; +} + static int kvaser_usb_leaf_set_mode(struct net_device *netdev, enum can_mode mode) { struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct kvaser_usb_net_leaf_priv *leaf = priv->sub_priv; int err; switch (mode) { case CAN_MODE_START: kvaser_usb_unlink_tx_urbs(priv); + leaf->joining_bus = true; + err = kvaser_usb_leaf_simple_cmd_async(priv, CMD_START_CHIP); if (err) return err; @@ -1479,14 +1841,18 @@ static int kvaser_usb_leaf_setup_endpoints(struct kvaser_usb *dev) const struct kvaser_usb_dev_ops kvaser_usb_leaf_dev_ops = { .dev_set_mode = kvaser_usb_leaf_set_mode, .dev_set_bittiming = kvaser_usb_leaf_set_bittiming, + .dev_get_busparams = kvaser_usb_leaf_get_busparams, .dev_set_data_bittiming = NULL, + .dev_get_data_busparams = NULL, .dev_get_berr_counter = kvaser_usb_leaf_get_berr_counter, .dev_setup_endpoints = kvaser_usb_leaf_setup_endpoints, .dev_init_card = kvaser_usb_leaf_init_card, + .dev_init_channel = kvaser_usb_leaf_init_channel, + .dev_remove_channel = kvaser_usb_leaf_remove_channel, .dev_get_software_info = kvaser_usb_leaf_get_software_info, .dev_get_software_details = NULL, .dev_get_card_info = kvaser_usb_leaf_get_card_info, - .dev_get_capabilities = NULL, + .dev_get_capabilities = kvaser_usb_leaf_get_capabilities, .dev_set_opt_mode = kvaser_usb_leaf_set_opt_mode, .dev_start_chip = kvaser_usb_leaf_start_chip, .dev_stop_chip = kvaser_usb_leaf_stop_chip, diff --git a/drivers/net/can/usb/ucan.c b/drivers/net/can/usb/ucan.c index 67c2ff407d06..a0f7bcec719c 100644 --- a/drivers/net/can/usb/ucan.c +++ b/drivers/net/can/usb/ucan.c @@ -245,7 +245,8 @@ struct ucan_message_in { /* CAN transmission complete * (type == UCAN_IN_TX_COMPLETE) */ - struct ucan_tx_complete_entry_t can_tx_complete_msg[0]; + DECLARE_FLEX_ARRAY(struct ucan_tx_complete_entry_t, + can_tx_complete_msg); } __aligned(0x4) msg; } __packed __aligned(0x4); @@ -276,7 +277,6 @@ struct ucan_priv { /* linux USB device structures */ struct usb_device *udev; - struct usb_interface *intf; struct net_device *netdev; /* lock for can->echo_skb (used around @@ -1500,7 +1500,6 @@ static int ucan_probe(struct usb_interface *intf, /* initialize data */ up->udev = udev; - up->intf = intf; up->netdev = netdev; up->intf_index = iface_desc->desc.bInterfaceNumber; up->in_ep_addr = in_ep_addr; @@ -1533,9 +1532,8 @@ static int ucan_probe(struct usb_interface *intf, sizeof(union ucan_ctl_payload)); if (ret > 0) { /* copy string while ensuring zero termination */ - strncpy(firmware_str, up->ctl_msg_buffer->raw, - sizeof(union ucan_ctl_payload)); - firmware_str[sizeof(union ucan_ctl_payload)] = '\0'; + strscpy(firmware_str, up->ctl_msg_buffer->raw, + sizeof(union ucan_ctl_payload) + 1); } else { strcpy(firmware_str, "unknown"); } @@ -1581,7 +1579,7 @@ static void ucan_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); if (up) { - unregister_netdev(up->netdev); + unregister_candev(up->netdev); free_candev(up->netdev); } } diff --git a/drivers/net/can/vxcan.c b/drivers/net/can/vxcan.c index 26a472d2ea58..4068d962203d 100644 --- a/drivers/net/can/vxcan.c +++ b/drivers/net/can/vxcan.c @@ -236,7 +236,7 @@ static int vxcan_newlink(struct net *net, struct net_device *dev, netif_carrier_off(peer); - err = rtnl_configure_link(peer, ifmp); + err = rtnl_configure_link(peer, ifmp, 0, NULL); if (err < 0) goto unregister_network_device; |