diff options
Diffstat (limited to 'drivers/net/can/spi/mcp251xfd')
-rw-r--r-- | drivers/net/can/spi/mcp251xfd/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/can/spi/mcp251xfd/mcp251xfd-chip-fifo.c | 4 | ||||
-rw-r--r-- | drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c | 349 | ||||
-rw-r--r-- | drivers/net/can/spi/mcp251xfd/mcp251xfd-dump.c | 4 | ||||
-rw-r--r-- | drivers/net/can/spi/mcp251xfd/mcp251xfd-ethtool.c | 143 | ||||
-rw-r--r-- | drivers/net/can/spi/mcp251xfd/mcp251xfd-ram.c | 153 | ||||
-rw-r--r-- | drivers/net/can/spi/mcp251xfd/mcp251xfd-ram.h | 62 | ||||
-rw-r--r-- | drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c | 24 | ||||
-rw-r--r-- | drivers/net/can/spi/mcp251xfd/mcp251xfd-ring.c | 417 | ||||
-rw-r--r-- | drivers/net/can/spi/mcp251xfd/mcp251xfd-rx.c | 22 | ||||
-rw-r--r-- | drivers/net/can/spi/mcp251xfd/mcp251xfd-tef.c | 6 | ||||
-rw-r--r-- | drivers/net/can/spi/mcp251xfd/mcp251xfd.h | 96 |
12 files changed, 1060 insertions, 222 deletions
diff --git a/drivers/net/can/spi/mcp251xfd/Makefile b/drivers/net/can/spi/mcp251xfd/Makefile index a83d685d64e0..94d7de954294 100644 --- a/drivers/net/can/spi/mcp251xfd/Makefile +++ b/drivers/net/can/spi/mcp251xfd/Makefile @@ -6,6 +6,8 @@ mcp251xfd-objs := mcp251xfd-objs += mcp251xfd-chip-fifo.o mcp251xfd-objs += mcp251xfd-core.o mcp251xfd-objs += mcp251xfd-crc16.o +mcp251xfd-objs += mcp251xfd-ethtool.o +mcp251xfd-objs += mcp251xfd-ram.o mcp251xfd-objs += mcp251xfd-regmap.o mcp251xfd-objs += mcp251xfd-ring.o mcp251xfd-objs += mcp251xfd-rx.o diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-chip-fifo.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-chip-fifo.c index 2f9a623d381d..0d96097a2547 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-chip-fifo.c +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-chip-fifo.c @@ -78,7 +78,7 @@ int mcp251xfd_chip_fifo_init(const struct mcp251xfd_priv *priv) if (err) return err; - /* FIFO 1 - TX */ + /* TX FIFO */ val = FIELD_PREP(MCP251XFD_REG_FIFOCON_FSIZE_MASK, tx_ring->obj_num - 1) | MCP251XFD_REG_FIFOCON_TXEN | @@ -99,7 +99,7 @@ int mcp251xfd_chip_fifo_init(const struct mcp251xfd_priv *priv) MCP251XFD_REG_FIFOCON_TXAT_UNLIMITED); err = regmap_write(priv->map_reg, - MCP251XFD_REG_FIFOCON(MCP251XFD_TX_FIFO), + MCP251XFD_REG_FIFOCON(priv->tx->fifo_nr), val); if (err) return err; diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c index 65c9b31666a6..325024be7b04 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c @@ -112,6 +112,22 @@ static const char *mcp251xfd_get_mode_str(const u8 mode) return "<unknown>"; } +static const char * +mcp251xfd_get_osc_str(const u32 osc, const u32 osc_reference) +{ + switch (~osc & osc_reference & + (MCP251XFD_REG_OSC_OSCRDY | MCP251XFD_REG_OSC_PLLRDY)) { + case MCP251XFD_REG_OSC_PLLRDY: + return "PLL"; + case MCP251XFD_REG_OSC_OSCRDY: + return "Oscillator"; + case MCP251XFD_REG_OSC_PLLRDY | MCP251XFD_REG_OSC_OSCRDY: + return "Oscillator/PLL"; + } + + return "<unknown>"; +} + static inline int mcp251xfd_vdd_enable(const struct mcp251xfd_priv *priv) { if (!priv->reg_vdd) @@ -178,6 +194,11 @@ static int mcp251xfd_clks_and_vdd_disable(const struct mcp251xfd_priv *priv) return 0; } +static inline bool mcp251xfd_reg_invalid(u32 reg) +{ + return reg == 0x0 || reg == 0xffffffff; +} + static inline int mcp251xfd_chip_get_mode(const struct mcp251xfd_priv *priv, u8 *mode) { @@ -197,34 +218,55 @@ static int __mcp251xfd_chip_set_mode(const struct mcp251xfd_priv *priv, const u8 mode_req, bool nowait) { - u32 con, con_reqop; + u32 con = 0, con_reqop, osc = 0; + u8 mode; int err; con_reqop = FIELD_PREP(MCP251XFD_REG_CON_REQOP_MASK, mode_req); err = regmap_update_bits(priv->map_reg, MCP251XFD_REG_CON, MCP251XFD_REG_CON_REQOP_MASK, con_reqop); - if (err) + if (err == -EBADMSG) { + netdev_err(priv->ndev, + "Failed to set Requested Operation Mode.\n"); + + return -ENODEV; + } else if (err) { return err; + } if (mode_req == MCP251XFD_REG_CON_MODE_SLEEP || nowait) return 0; err = regmap_read_poll_timeout(priv->map_reg, MCP251XFD_REG_CON, con, + !mcp251xfd_reg_invalid(con) && FIELD_GET(MCP251XFD_REG_CON_OPMOD_MASK, con) == mode_req, MCP251XFD_POLL_SLEEP_US, MCP251XFD_POLL_TIMEOUT_US); - if (err) { - u8 mode = FIELD_GET(MCP251XFD_REG_CON_OPMOD_MASK, con); + if (err != -ETIMEDOUT && err != -EBADMSG) + return err; + + /* Ignore return value. + * Print below error messages, even if this fails. + */ + regmap_read(priv->map_reg, MCP251XFD_REG_OSC, &osc); + if (mcp251xfd_reg_invalid(con)) { netdev_err(priv->ndev, - "Controller failed to enter mode %s Mode (%u) and stays in %s Mode (%u).\n", - mcp251xfd_get_mode_str(mode_req), mode_req, - mcp251xfd_get_mode_str(mode), mode); - return err; + "Failed to read CAN Control Register (con=0x%08x, osc=0x%08x).\n", + con, osc); + + return -ENODEV; } - return 0; + mode = FIELD_GET(MCP251XFD_REG_CON_OPMOD_MASK, con); + netdev_err(priv->ndev, + "Controller failed to enter mode %s Mode (%u) and stays in %s Mode (%u) (con=0x%08x, osc=0x%08x).\n", + mcp251xfd_get_mode_str(mode_req), mode_req, + mcp251xfd_get_mode_str(mode), mode, + con, osc); + + return -ETIMEDOUT; } static inline int @@ -241,27 +283,58 @@ mcp251xfd_chip_set_mode_nowait(const struct mcp251xfd_priv *priv, return __mcp251xfd_chip_set_mode(priv, mode_req, true); } -static inline bool mcp251xfd_osc_invalid(u32 reg) +static int +mcp251xfd_chip_wait_for_osc_ready(const struct mcp251xfd_priv *priv, + u32 osc_reference, u32 osc_mask) { - return reg == 0x0 || reg == 0xffffffff; + u32 osc; + int err; + + err = regmap_read_poll_timeout(priv->map_reg, MCP251XFD_REG_OSC, osc, + !mcp251xfd_reg_invalid(osc) && + (osc & osc_mask) == osc_reference, + MCP251XFD_OSC_STAB_SLEEP_US, + MCP251XFD_OSC_STAB_TIMEOUT_US); + if (err != -ETIMEDOUT) + return err; + + if (mcp251xfd_reg_invalid(osc)) { + netdev_err(priv->ndev, + "Failed to read Oscillator Configuration Register (osc=0x%08x).\n", + osc); + return -ENODEV; + } + + netdev_err(priv->ndev, + "Timeout waiting for %s ready (osc=0x%08x, osc_reference=0x%08x, osc_mask=0x%08x).\n", + mcp251xfd_get_osc_str(osc, osc_reference), + osc, osc_reference, osc_mask); + + return -ETIMEDOUT; } -static int mcp251xfd_chip_clock_enable(const struct mcp251xfd_priv *priv) +static int mcp251xfd_chip_wake(const struct mcp251xfd_priv *priv) { u32 osc, osc_reference, osc_mask; int err; - /* Set Power On Defaults for "Clock Output Divisor" and remove - * "Oscillator Disable" bit. + /* For normal sleep on MCP2517FD and MCP2518FD, clearing + * "Oscillator Disable" will wake the chip. For low power mode + * on MCP2518FD, asserting the chip select will wake the + * chip. Writing to the Oscillator register will wake it in + * both cases. */ osc = FIELD_PREP(MCP251XFD_REG_OSC_CLKODIV_MASK, MCP251XFD_REG_OSC_CLKODIV_10); + + /* We cannot check for the PLL ready bit (either set or + * unset), as the PLL might be enabled. This can happen if the + * system reboots, while the mcp251xfd stays powered. + */ osc_reference = MCP251XFD_REG_OSC_OSCRDY; - osc_mask = MCP251XFD_REG_OSC_OSCRDY | MCP251XFD_REG_OSC_PLLRDY; + osc_mask = MCP251XFD_REG_OSC_OSCRDY; - /* Note: - * - * If the controller is in Sleep Mode the following write only + /* If the controller is in Sleep Mode the following write only * removes the "Oscillator Disable" bit and powers it up. All * other bits are unaffected. */ @@ -269,24 +342,31 @@ static int mcp251xfd_chip_clock_enable(const struct mcp251xfd_priv *priv) if (err) return err; - /* Wait for "Oscillator Ready" bit */ - err = regmap_read_poll_timeout(priv->map_reg, MCP251XFD_REG_OSC, osc, - (osc & osc_mask) == osc_reference, - MCP251XFD_OSC_STAB_SLEEP_US, - MCP251XFD_OSC_STAB_TIMEOUT_US); - if (mcp251xfd_osc_invalid(osc)) { - netdev_err(priv->ndev, - "Failed to detect %s (osc=0x%08x).\n", - mcp251xfd_get_model_str(priv), osc); - return -ENODEV; - } else if (err == -ETIMEDOUT) { - netdev_err(priv->ndev, - "Timeout waiting for Oscillator Ready (osc=0x%08x, osc_reference=0x%08x)\n", - osc, osc_reference); - return -ETIMEDOUT; + /* Sometimes the PLL is stuck enabled, the controller never + * sets the OSC Ready bit, and we get an -ETIMEDOUT. Our + * caller takes care of retry. + */ + return mcp251xfd_chip_wait_for_osc_ready(priv, osc_reference, osc_mask); +} + +static inline int mcp251xfd_chip_sleep(const struct mcp251xfd_priv *priv) +{ + if (priv->pll_enable) { + u32 osc; + int err; + + /* Turn off PLL */ + osc = FIELD_PREP(MCP251XFD_REG_OSC_CLKODIV_MASK, + MCP251XFD_REG_OSC_CLKODIV_10); + err = regmap_write(priv->map_reg, MCP251XFD_REG_OSC, osc); + if (err) + netdev_err(priv->ndev, + "Failed to disable PLL.\n"); + + priv->spi->max_speed_hz = priv->spi_max_speed_hz_slow; } - return err; + return mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_SLEEP); } static int mcp251xfd_chip_softreset_do(const struct mcp251xfd_priv *priv) @@ -294,10 +374,10 @@ static int mcp251xfd_chip_softreset_do(const struct mcp251xfd_priv *priv) const __be16 cmd = mcp251xfd_cmd_reset(); int err; - /* The Set Mode and SPI Reset command only seems to works if - * the controller is not in Sleep Mode. + /* The Set Mode and SPI Reset command only works if the + * controller is not in Sleep Mode. */ - err = mcp251xfd_chip_clock_enable(priv); + err = mcp251xfd_chip_wake(priv); if (err) return err; @@ -311,34 +391,29 @@ static int mcp251xfd_chip_softreset_do(const struct mcp251xfd_priv *priv) static int mcp251xfd_chip_softreset_check(const struct mcp251xfd_priv *priv) { - u32 osc, osc_reference; + u32 osc_reference, osc_mask; u8 mode; int err; - err = mcp251xfd_chip_get_mode(priv, &mode); - if (err) - return err; - - if (mode != MCP251XFD_REG_CON_MODE_CONFIG) { - netdev_info(priv->ndev, - "Controller not in Config Mode after reset, but in %s Mode (%u).\n", - mcp251xfd_get_mode_str(mode), mode); - return -ETIMEDOUT; - } - + /* Check for reset defaults of OSC reg. + * This will take care of stabilization period. + */ osc_reference = MCP251XFD_REG_OSC_OSCRDY | FIELD_PREP(MCP251XFD_REG_OSC_CLKODIV_MASK, MCP251XFD_REG_OSC_CLKODIV_10); + osc_mask = osc_reference | MCP251XFD_REG_OSC_PLLRDY; + err = mcp251xfd_chip_wait_for_osc_ready(priv, osc_reference, osc_mask); + if (err) + return err; - /* check reset defaults of OSC reg */ - err = regmap_read(priv->map_reg, MCP251XFD_REG_OSC, &osc); + err = mcp251xfd_chip_get_mode(priv, &mode); if (err) return err; - if (osc != osc_reference) { + if (mode != MCP251XFD_REG_CON_MODE_CONFIG) { netdev_info(priv->ndev, - "Controller failed to reset. osc=0x%08x, reference value=0x%08x.\n", - osc, osc_reference); + "Controller not in Config Mode after reset, but in %s Mode (%u).\n", + mcp251xfd_get_mode_str(mode), mode); return -ETIMEDOUT; } @@ -374,7 +449,7 @@ static int mcp251xfd_chip_softreset(const struct mcp251xfd_priv *priv) static int mcp251xfd_chip_clock_init(const struct mcp251xfd_priv *priv) { - u32 osc; + u32 osc, osc_reference, osc_mask; int err; /* Activate Low Power Mode on Oscillator Disable. This only @@ -384,10 +459,29 @@ static int mcp251xfd_chip_clock_init(const struct mcp251xfd_priv *priv) osc = MCP251XFD_REG_OSC_LPMEN | FIELD_PREP(MCP251XFD_REG_OSC_CLKODIV_MASK, MCP251XFD_REG_OSC_CLKODIV_10); + osc_reference = MCP251XFD_REG_OSC_OSCRDY; + osc_mask = MCP251XFD_REG_OSC_OSCRDY | MCP251XFD_REG_OSC_PLLRDY; + + if (priv->pll_enable) { + osc |= MCP251XFD_REG_OSC_PLLEN; + osc_reference |= MCP251XFD_REG_OSC_PLLRDY; + } + err = regmap_write(priv->map_reg, MCP251XFD_REG_OSC, osc); if (err) return err; + err = mcp251xfd_chip_wait_for_osc_ready(priv, osc_reference, osc_mask); + if (err) + return err; + + priv->spi->max_speed_hz = priv->spi_max_speed_hz_fast; + + return 0; +} + +static int mcp251xfd_chip_timestamp_init(const struct mcp251xfd_priv *priv) +{ /* Set Time Base Counter Prescaler to 1. * * This means an overflow of the 32 bit Time Base Counter @@ -628,14 +722,14 @@ static int mcp251xfd_chip_interrupts_disable(const struct mcp251xfd_priv *priv) return regmap_write(priv->map_reg, MCP251XFD_REG_CRC, 0); } -static int mcp251xfd_chip_stop(struct mcp251xfd_priv *priv, - const enum can_state state) +static void mcp251xfd_chip_stop(struct mcp251xfd_priv *priv, + const enum can_state state) { priv->can.state = state; mcp251xfd_chip_interrupts_disable(priv); mcp251xfd_chip_rx_int_disable(priv); - return mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_SLEEP); + mcp251xfd_chip_sleep(priv); } static int mcp251xfd_chip_start(struct mcp251xfd_priv *priv) @@ -650,6 +744,10 @@ static int mcp251xfd_chip_start(struct mcp251xfd_priv *priv) if (err) goto out_chip_stop; + err = mcp251xfd_chip_timestamp_init(priv); + if (err) + goto out_chip_stop; + err = mcp251xfd_set_bittiming(priv); if (err) goto out_chip_stop; @@ -662,7 +760,9 @@ static int mcp251xfd_chip_start(struct mcp251xfd_priv *priv) if (err) goto out_chip_stop; - mcp251xfd_ring_init(priv); + err = mcp251xfd_ring_init(priv); + if (err) + goto out_chip_stop; err = mcp251xfd_chip_fifo_init(priv); if (err) @@ -1284,6 +1384,20 @@ static int mcp251xfd_handle_spicrcif(struct mcp251xfd_priv *priv) return 0; } +static int mcp251xfd_read_regs_status(struct mcp251xfd_priv *priv) +{ + const int val_bytes = regmap_get_val_bytes(priv->map_reg); + size_t len; + + if (priv->rx_ring_num == 1) + len = sizeof(priv->regs_status.intf); + else + len = sizeof(priv->regs_status); + + return regmap_bulk_read(priv->map_reg, MCP251XFD_REG_INT, + &priv->regs_status, len / val_bytes); +} + #define mcp251xfd_handle(priv, irq, ...) \ ({ \ struct mcp251xfd_priv *_priv = (priv); \ @@ -1300,7 +1414,6 @@ static int mcp251xfd_handle_spicrcif(struct mcp251xfd_priv *priv) static irqreturn_t mcp251xfd_irq(int irq, void *dev_id) { struct mcp251xfd_priv *priv = dev_id; - const int val_bytes = regmap_get_val_bytes(priv->map_reg); irqreturn_t handled = IRQ_NONE; int err; @@ -1312,21 +1425,28 @@ static irqreturn_t mcp251xfd_irq(int irq, void *dev_id) if (!rx_pending) break; + /* Assume 1st RX-FIFO pending, if other FIFOs + * are pending the main IRQ handler will take + * care. + */ + priv->regs_status.rxif = BIT(priv->rx[0]->fifo_nr); err = mcp251xfd_handle(priv, rxif); if (err) goto out_fail; handled = IRQ_HANDLED; - } while (1); + + /* We don't know which RX-FIFO is pending, but only + * handle the 1st RX-FIFO. Leave loop here if we have + * more than 1 RX-FIFO to avoid starvation. + */ + } while (priv->rx_ring_num == 1); do { u32 intf_pending, intf_pending_clearable; bool set_normal_mode = false; - err = regmap_bulk_read(priv->map_reg, MCP251XFD_REG_INT, - &priv->regs_status, - sizeof(priv->regs_status) / - val_bytes); + err = mcp251xfd_read_regs_status(priv); if (err) goto out_fail; @@ -1478,6 +1598,7 @@ static int mcp251xfd_open(struct net_device *ndev) goto out_transceiver_disable; mcp251xfd_timestamp_init(priv); + clear_bit(MCP251XFD_FLAGS_DOWN, priv->flags); can_rx_offload_enable(&priv->offload); err = request_threaded_irq(spi->irq, NULL, mcp251xfd_irq, @@ -1498,6 +1619,7 @@ static int mcp251xfd_open(struct net_device *ndev) free_irq(spi->irq, priv); out_can_rx_offload_disable: can_rx_offload_disable(&priv->offload); + set_bit(MCP251XFD_FLAGS_DOWN, priv->flags); mcp251xfd_timestamp_stop(priv); out_transceiver_disable: mcp251xfd_transceiver_disable(priv); @@ -1517,6 +1639,8 @@ static int mcp251xfd_stop(struct net_device *ndev) struct mcp251xfd_priv *priv = netdev_priv(ndev); netif_stop_queue(ndev); + set_bit(MCP251XFD_FLAGS_DOWN, priv->flags); + hrtimer_cancel(&priv->rx_irq_timer); mcp251xfd_chip_interrupts_disable(priv); free_irq(ndev->irq, priv); can_rx_offload_disable(&priv->offload); @@ -1621,8 +1745,9 @@ static int mcp251xfd_register_check_rx_int(struct mcp251xfd_priv *priv) } static int -mcp251xfd_register_get_dev_id(const struct mcp251xfd_priv *priv, - u32 *dev_id, u32 *effective_speed_hz) +mcp251xfd_register_get_dev_id(const struct mcp251xfd_priv *priv, u32 *dev_id, + u32 *effective_speed_hz_slow, + u32 *effective_speed_hz_fast) { struct mcp251xfd_map_buf_nocrc *buf_rx; struct mcp251xfd_map_buf_nocrc *buf_tx; @@ -1641,16 +1766,20 @@ mcp251xfd_register_get_dev_id(const struct mcp251xfd_priv *priv, xfer[0].tx_buf = buf_tx; xfer[0].len = sizeof(buf_tx->cmd); + xfer[0].speed_hz = priv->spi_max_speed_hz_slow; xfer[1].rx_buf = buf_rx->data; xfer[1].len = sizeof(dev_id); + xfer[1].speed_hz = priv->spi_max_speed_hz_fast; mcp251xfd_spi_cmd_read_nocrc(&buf_tx->cmd, MCP251XFD_REG_DEVID); + err = spi_sync_transfer(priv->spi, xfer, ARRAY_SIZE(xfer)); if (err) goto out_kfree_buf_tx; *dev_id = be32_to_cpup((__be32 *)buf_rx->data); - *effective_speed_hz = xfer->effective_speed_hz; + *effective_speed_hz_slow = xfer[0].effective_speed_hz; + *effective_speed_hz_fast = xfer[1].effective_speed_hz; out_kfree_buf_tx: kfree(buf_tx); @@ -1666,34 +1795,45 @@ mcp251xfd_register_get_dev_id(const struct mcp251xfd_priv *priv, static int mcp251xfd_register_done(const struct mcp251xfd_priv *priv) { - u32 dev_id, effective_speed_hz; + u32 dev_id, effective_speed_hz_slow, effective_speed_hz_fast; + unsigned long clk_rate; int err; err = mcp251xfd_register_get_dev_id(priv, &dev_id, - &effective_speed_hz); + &effective_speed_hz_slow, + &effective_speed_hz_fast); if (err) return err; + clk_rate = clk_get_rate(priv->clk); + netdev_info(priv->ndev, - "%s rev%lu.%lu (%cRX_INT %cMAB_NO_WARN %cCRC_REG %cCRC_RX %cCRC_TX %cECC %cHD c:%u.%02uMHz m:%u.%02uMHz r:%u.%02uMHz e:%u.%02uMHz) successfully initialized.\n", + "%s rev%lu.%lu (%cRX_INT %cPLL %cMAB_NO_WARN %cCRC_REG %cCRC_RX %cCRC_TX %cECC %cHD o:%lu.%02luMHz c:%u.%02uMHz m:%u.%02uMHz rs:%u.%02uMHz es:%u.%02uMHz rf:%u.%02uMHz ef:%u.%02uMHz) successfully initialized.\n", mcp251xfd_get_model_str(priv), FIELD_GET(MCP251XFD_REG_DEVID_ID_MASK, dev_id), FIELD_GET(MCP251XFD_REG_DEVID_REV_MASK, dev_id), priv->rx_int ? '+' : '-', + priv->pll_enable ? '+' : '-', MCP251XFD_QUIRK_ACTIVE(MAB_NO_WARN), MCP251XFD_QUIRK_ACTIVE(CRC_REG), MCP251XFD_QUIRK_ACTIVE(CRC_RX), MCP251XFD_QUIRK_ACTIVE(CRC_TX), MCP251XFD_QUIRK_ACTIVE(ECC), MCP251XFD_QUIRK_ACTIVE(HALF_DUPLEX), + clk_rate / 1000000, + clk_rate % 1000000 / 1000 / 10, priv->can.clock.freq / 1000000, priv->can.clock.freq % 1000000 / 1000 / 10, priv->spi_max_speed_hz_orig / 1000000, priv->spi_max_speed_hz_orig % 1000000 / 1000 / 10, - priv->spi->max_speed_hz / 1000000, - priv->spi->max_speed_hz % 1000000 / 1000 / 10, - effective_speed_hz / 1000000, - effective_speed_hz % 1000000 / 1000 / 10); + priv->spi_max_speed_hz_slow / 1000000, + priv->spi_max_speed_hz_slow % 1000000 / 1000 / 10, + effective_speed_hz_slow / 1000000, + effective_speed_hz_slow % 1000000 / 1000 / 10, + priv->spi_max_speed_hz_fast / 1000000, + priv->spi_max_speed_hz_fast % 1000000 / 1000 / 10, + effective_speed_hz_fast / 1000000, + effective_speed_hz_fast % 1000000 / 1000 / 10); return 0; } @@ -1719,19 +1859,27 @@ static int mcp251xfd_register(struct mcp251xfd_priv *priv) if (err == -ENODEV) goto out_runtime_disable; if (err) - goto out_chip_set_mode_sleep; + goto out_chip_sleep; + + err = mcp251xfd_chip_clock_init(priv); + if (err == -ENODEV) + goto out_runtime_disable; + if (err) + goto out_chip_sleep; err = mcp251xfd_register_chip_detect(priv); if (err) - goto out_chip_set_mode_sleep; + goto out_chip_sleep; err = mcp251xfd_register_check_rx_int(priv); if (err) - goto out_chip_set_mode_sleep; + goto out_chip_sleep; + + mcp251xfd_ethtool_init(priv); err = register_candev(ndev); if (err) - goto out_chip_set_mode_sleep; + goto out_chip_sleep; err = mcp251xfd_register_done(priv); if (err) @@ -1741,7 +1889,7 @@ static int mcp251xfd_register(struct mcp251xfd_priv *priv) * disable the clocks and vdd. If CONFIG_PM is not enabled, * the clocks and vdd will stay powered. */ - err = mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_SLEEP); + err = mcp251xfd_chip_sleep(priv); if (err) goto out_unregister_candev; @@ -1751,8 +1899,8 @@ static int mcp251xfd_register(struct mcp251xfd_priv *priv) out_unregister_candev: unregister_candev(ndev); - out_chip_set_mode_sleep: - mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_SLEEP); + out_chip_sleep: + mcp251xfd_chip_sleep(priv); out_runtime_disable: pm_runtime_disable(ndev->dev.parent); out_runtime_put_noidle: @@ -1768,10 +1916,10 @@ static inline void mcp251xfd_unregister(struct mcp251xfd_priv *priv) unregister_candev(ndev); - pm_runtime_get_sync(ndev->dev.parent); - pm_runtime_put_noidle(ndev->dev.parent); - mcp251xfd_clks_and_vdd_disable(priv); - pm_runtime_disable(ndev->dev.parent); + if (pm_runtime_enabled(ndev->dev.parent)) + pm_runtime_disable(ndev->dev.parent); + else + mcp251xfd_clks_and_vdd_disable(priv); } static const struct of_device_id mcp251xfd_of_match[] = { @@ -1814,6 +1962,7 @@ static int mcp251xfd_probe(struct spi_device *spi) struct gpio_desc *rx_int; struct regulator *reg_vdd, *reg_xceiver; struct clk *clk; + bool pll_enable = false; u32 freq = 0; int err; @@ -1864,12 +2013,8 @@ static int mcp251xfd_probe(struct spi_device *spi) return -ERANGE; } - if (freq <= MCP251XFD_SYSCLOCK_HZ_MAX / MCP251XFD_OSC_PLL_MULTIPLIER) { - dev_err(&spi->dev, - "Oscillator frequency (%u Hz) is too low and PLL is not supported.\n", - freq); - return -ERANGE; - } + if (freq <= MCP251XFD_SYSCLOCK_HZ_MAX / MCP251XFD_OSC_PLL_MULTIPLIER) + pll_enable = true; ndev = alloc_candev(sizeof(struct mcp251xfd_priv), MCP251XFD_TX_OBJ_NUM_MAX); @@ -1885,6 +2030,8 @@ static int mcp251xfd_probe(struct spi_device *spi) priv = netdev_priv(ndev); spi_set_drvdata(spi, priv); priv->can.clock.freq = freq; + if (pll_enable) + priv->can.clock.freq *= MCP251XFD_OSC_PLL_MULTIPLIER; priv->can.do_set_mode = mcp251xfd_set_mode; priv->can.do_get_berr_counter = mcp251xfd_get_berr_counter; priv->can.bittiming_const = &mcp251xfd_bittiming_const; @@ -1893,10 +2040,12 @@ static int mcp251xfd_probe(struct spi_device *spi) CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_BERR_REPORTING | CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO | CAN_CTRLMODE_CC_LEN8_DLC; + set_bit(MCP251XFD_FLAGS_DOWN, priv->flags); priv->ndev = ndev; priv->spi = spi; priv->rx_int = rx_int; priv->clk = clk; + priv->pll_enable = pll_enable; priv->reg_vdd = reg_vdd; priv->reg_xceiver = reg_xceiver; @@ -1934,7 +2083,16 @@ static int mcp251xfd_probe(struct spi_device *spi) * */ priv->spi_max_speed_hz_orig = spi->max_speed_hz; - spi->max_speed_hz = min(spi->max_speed_hz, freq / 2 / 1000 * 850); + priv->spi_max_speed_hz_slow = min(spi->max_speed_hz, + freq / 2 / 1000 * 850); + if (priv->pll_enable) + priv->spi_max_speed_hz_fast = min(spi->max_speed_hz, + freq * + MCP251XFD_OSC_PLL_MULTIPLIER / + 2 / 1000 * 850); + else + priv->spi_max_speed_hz_fast = priv->spi_max_speed_hz_slow; + spi->max_speed_hz = priv->spi_max_speed_hz_slow; spi->bits_per_word = 8; spi->rt = true; err = spi_setup(spi); @@ -1951,8 +2109,11 @@ static int mcp251xfd_probe(struct spi_device *spi) goto out_free_candev; err = mcp251xfd_register(priv); - if (err) + if (err) { + dev_err_probe(&spi->dev, err, "Failed to detect %s.\n", + mcp251xfd_get_model_str(priv)); goto out_can_rx_offload_del; + } return 0; diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-dump.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-dump.c index ffae8fdd3af0..c991b30bc9f0 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-dump.c +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-dump.c @@ -207,10 +207,10 @@ static void mcp251xfd_dump_tx_ring(const struct mcp251xfd_priv *priv, .val = tx->base, }, { .key = MCP251XFD_DUMP_OBJECT_RING_KEY_NR, - .val = 0, + .val = tx->nr, }, { .key = MCP251XFD_DUMP_OBJECT_RING_KEY_FIFO_NR, - .val = MCP251XFD_TX_FIFO, + .val = tx->fifo_nr, }, { .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_NUM, .val = tx->obj_num, diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-ethtool.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ethtool.c new file mode 100644 index 000000000000..6c7a57f16cc6 --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ethtool.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mcp251xfd - Microchip MCP251xFD Family CAN controller driver +// +// Copyright (c) 2021, 2022 Pengutronix, +// Marc Kleine-Budde <kernel@pengutronix.de> +// + +#include <linux/ethtool.h> + +#include "mcp251xfd.h" +#include "mcp251xfd-ram.h" + +static void +mcp251xfd_ring_get_ringparam(struct net_device *ndev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kernel_ring, + struct netlink_ext_ack *extack) +{ + const struct mcp251xfd_priv *priv = netdev_priv(ndev); + const bool fd_mode = mcp251xfd_is_fd_mode(priv); + struct can_ram_layout layout; + + can_ram_get_layout(&layout, &mcp251xfd_ram_config, NULL, NULL, fd_mode); + ring->rx_max_pending = layout.max_rx; + ring->tx_max_pending = layout.max_tx; + + ring->rx_pending = priv->rx_obj_num; + ring->tx_pending = priv->tx->obj_num; +} + +static int +mcp251xfd_ring_set_ringparam(struct net_device *ndev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kernel_ring, + struct netlink_ext_ack *extack) +{ + struct mcp251xfd_priv *priv = netdev_priv(ndev); + const bool fd_mode = mcp251xfd_is_fd_mode(priv); + struct can_ram_layout layout; + + can_ram_get_layout(&layout, &mcp251xfd_ram_config, ring, NULL, fd_mode); + if ((layout.cur_rx != priv->rx_obj_num || + layout.cur_tx != priv->tx->obj_num) && + netif_running(ndev)) + return -EBUSY; + + priv->rx_obj_num = layout.cur_rx; + priv->rx_obj_num_coalesce_irq = layout.rx_coalesce; + priv->tx->obj_num = layout.cur_tx; + + return 0; +} + +static int mcp251xfd_ring_get_coalesce(struct net_device *ndev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kec, + struct netlink_ext_ack *ext_ack) +{ + struct mcp251xfd_priv *priv = netdev_priv(ndev); + u32 rx_max_frames, tx_max_frames; + + /* The ethtool doc says: + * To disable coalescing, set usecs = 0 and max_frames = 1. + */ + if (priv->rx_obj_num_coalesce_irq == 0) + rx_max_frames = 1; + else + rx_max_frames = priv->rx_obj_num_coalesce_irq; + + ec->rx_max_coalesced_frames_irq = rx_max_frames; + ec->rx_coalesce_usecs_irq = priv->rx_coalesce_usecs_irq; + + if (priv->tx_obj_num_coalesce_irq == 0) + tx_max_frames = 1; + else + tx_max_frames = priv->tx_obj_num_coalesce_irq; + + ec->tx_max_coalesced_frames_irq = tx_max_frames; + ec->tx_coalesce_usecs_irq = priv->tx_coalesce_usecs_irq; + + return 0; +} + +static int mcp251xfd_ring_set_coalesce(struct net_device *ndev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kec, + struct netlink_ext_ack *ext_ack) +{ + struct mcp251xfd_priv *priv = netdev_priv(ndev); + const bool fd_mode = mcp251xfd_is_fd_mode(priv); + const struct ethtool_ringparam ring = { + .rx_pending = priv->rx_obj_num, + .tx_pending = priv->tx->obj_num, + }; + struct can_ram_layout layout; + + can_ram_get_layout(&layout, &mcp251xfd_ram_config, &ring, ec, fd_mode); + + if ((layout.rx_coalesce != priv->rx_obj_num_coalesce_irq || + ec->rx_coalesce_usecs_irq != priv->rx_coalesce_usecs_irq || + layout.tx_coalesce != priv->tx_obj_num_coalesce_irq || + ec->tx_coalesce_usecs_irq != priv->tx_coalesce_usecs_irq) && + netif_running(ndev)) + return -EBUSY; + + priv->rx_obj_num = layout.cur_rx; + priv->rx_obj_num_coalesce_irq = layout.rx_coalesce; + priv->rx_coalesce_usecs_irq = ec->rx_coalesce_usecs_irq; + + priv->tx->obj_num = layout.cur_tx; + priv->tx_obj_num_coalesce_irq = layout.tx_coalesce; + priv->tx_coalesce_usecs_irq = ec->tx_coalesce_usecs_irq; + + return 0; +} + +static const struct ethtool_ops mcp251xfd_ethtool_ops = { + .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS_IRQ | + ETHTOOL_COALESCE_RX_MAX_FRAMES_IRQ | + ETHTOOL_COALESCE_TX_USECS_IRQ | + ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ, + .get_ringparam = mcp251xfd_ring_get_ringparam, + .set_ringparam = mcp251xfd_ring_set_ringparam, + .get_coalesce = mcp251xfd_ring_get_coalesce, + .set_coalesce = mcp251xfd_ring_set_coalesce, +}; + +void mcp251xfd_ethtool_init(struct mcp251xfd_priv *priv) +{ + struct can_ram_layout layout; + + priv->ndev->ethtool_ops = &mcp251xfd_ethtool_ops; + + can_ram_get_layout(&layout, &mcp251xfd_ram_config, NULL, NULL, false); + priv->rx_obj_num = layout.default_rx; + priv->tx->obj_num = layout.default_tx; + + priv->rx_obj_num_coalesce_irq = 0; + priv->tx_obj_num_coalesce_irq = 0; + priv->rx_coalesce_usecs_irq = 0; + priv->tx_coalesce_usecs_irq = 0; +} diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-ram.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ram.c new file mode 100644 index 000000000000..9e8e82cdba46 --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ram.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mcp251xfd - Microchip MCP251xFD Family CAN controller driver +// +// Copyright (c) 2021, 2022 Pengutronix, +// Marc Kleine-Budde <kernel@pengutronix.de> +// + +#include "mcp251xfd-ram.h" + +static inline u8 can_ram_clamp(const struct can_ram_config *config, + const struct can_ram_obj_config *obj, + u8 val) +{ + u8 max; + + max = min_t(u8, obj->max, obj->fifo_num * config->fifo_depth); + return clamp(val, obj->min, max); +} + +static u8 +can_ram_rounddown_pow_of_two(const struct can_ram_config *config, + const struct can_ram_obj_config *obj, + const u8 coalesce, u8 val) +{ + u8 fifo_num = obj->fifo_num; + u8 ret = 0, i; + + val = can_ram_clamp(config, obj, val); + + if (coalesce) { + /* Use 1st FIFO for coalescing, if requested. + * + * Either use complete FIFO (and FIFO Full IRQ) for + * coalescing or only half of FIFO (FIFO Half Full + * IRQ) and use remaining half for normal objects. + */ + ret = min_t(u8, coalesce * 2, config->fifo_depth); + val -= ret; + fifo_num--; + } + + for (i = 0; i < fifo_num && val; i++) { + u8 n; + + n = min_t(u8, rounddown_pow_of_two(val), + config->fifo_depth); + + /* skip small FIFOs */ + if (n < obj->fifo_depth_min) + return ret; + + ret += n; + val -= n; + } + + return ret; +} + +void can_ram_get_layout(struct can_ram_layout *layout, + const struct can_ram_config *config, + const struct ethtool_ringparam *ring, + const struct ethtool_coalesce *ec, + const bool fd_mode) +{ + u8 num_rx, num_tx; + u16 ram_free; + + /* default CAN */ + + num_tx = config->tx.def[fd_mode]; + num_tx = can_ram_rounddown_pow_of_two(config, &config->tx, 0, num_tx); + + ram_free = config->size; + ram_free -= config->tx.size[fd_mode] * num_tx; + + num_rx = ram_free / config->rx.size[fd_mode]; + + layout->default_rx = can_ram_rounddown_pow_of_two(config, &config->rx, 0, num_rx); + layout->default_tx = num_tx; + + /* MAX CAN */ + + ram_free = config->size; + ram_free -= config->tx.size[fd_mode] * config->tx.min; + num_rx = ram_free / config->rx.size[fd_mode]; + + ram_free = config->size; + ram_free -= config->rx.size[fd_mode] * config->rx.min; + num_tx = ram_free / config->tx.size[fd_mode]; + + layout->max_rx = can_ram_rounddown_pow_of_two(config, &config->rx, 0, num_rx); + layout->max_tx = can_ram_rounddown_pow_of_two(config, &config->tx, 0, num_tx); + + /* cur CAN */ + + if (ring) { + u8 num_rx_coalesce = 0, num_tx_coalesce = 0; + + num_rx = can_ram_rounddown_pow_of_two(config, &config->rx, 0, ring->rx_pending); + + /* The ethtool doc says: + * To disable coalescing, set usecs = 0 and max_frames = 1. + */ + if (ec && !(ec->rx_coalesce_usecs_irq == 0 && + ec->rx_max_coalesced_frames_irq == 1)) { + u8 max; + + /* use only max half of available objects for coalescing */ + max = min_t(u8, num_rx / 2, config->fifo_depth); + num_rx_coalesce = clamp(ec->rx_max_coalesced_frames_irq, + (u32)config->rx.fifo_depth_coalesce_min, + (u32)max); + num_rx_coalesce = rounddown_pow_of_two(num_rx_coalesce); + + num_rx = can_ram_rounddown_pow_of_two(config, &config->rx, + num_rx_coalesce, num_rx); + } + + ram_free = config->size - config->rx.size[fd_mode] * num_rx; + num_tx = ram_free / config->tx.size[fd_mode]; + num_tx = min_t(u8, ring->tx_pending, num_tx); + num_tx = can_ram_rounddown_pow_of_two(config, &config->tx, 0, num_tx); + + /* The ethtool doc says: + * To disable coalescing, set usecs = 0 and max_frames = 1. + */ + if (ec && !(ec->tx_coalesce_usecs_irq == 0 && + ec->tx_max_coalesced_frames_irq == 1)) { + u8 max; + + /* use only max half of available objects for coalescing */ + max = min_t(u8, num_tx / 2, config->fifo_depth); + num_tx_coalesce = clamp(ec->tx_max_coalesced_frames_irq, + (u32)config->tx.fifo_depth_coalesce_min, + (u32)max); + num_tx_coalesce = rounddown_pow_of_two(num_tx_coalesce); + + num_tx = can_ram_rounddown_pow_of_two(config, &config->tx, + num_tx_coalesce, num_tx); + } + + layout->cur_rx = num_rx; + layout->cur_tx = num_tx; + layout->rx_coalesce = num_rx_coalesce; + layout->tx_coalesce = num_tx_coalesce; + } else { + layout->cur_rx = layout->default_rx; + layout->cur_tx = layout->default_tx; + layout->rx_coalesce = 0; + layout->tx_coalesce = 0; + } +} diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-ram.h b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ram.h new file mode 100644 index 000000000000..7558c1510cbf --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ram.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * mcp251xfd - Microchip MCP251xFD Family CAN controller driver + * + * Copyright (c) 2021, 2022 Pengutronix, + * Marc Kleine-Budde <kernel@pengutronix.de> + */ + +#ifndef _MCP251XFD_RAM_H +#define _MCP251XFD_RAM_H + +#include <linux/ethtool.h> + +#define CAN_RAM_NUM_MAX (-1) + +enum can_ram_mode { + CAN_RAM_MODE_CAN, + CAN_RAM_MODE_CANFD, + __CAN_RAM_MODE_MAX +}; + +struct can_ram_obj_config { + u8 size[__CAN_RAM_MODE_MAX]; + + u8 def[__CAN_RAM_MODE_MAX]; + u8 min; + u8 max; + + u8 fifo_num; + u8 fifo_depth_min; + u8 fifo_depth_coalesce_min; +}; + +struct can_ram_config { + const struct can_ram_obj_config rx; + const struct can_ram_obj_config tx; + + u16 size; + u8 fifo_depth; +}; + +struct can_ram_layout { + u8 default_rx; + u8 default_tx; + + u8 max_rx; + u8 max_tx; + + u8 cur_rx; + u8 cur_tx; + + u8 rx_coalesce; + u8 tx_coalesce; +}; + +void can_ram_get_layout(struct can_ram_layout *layout, + const struct can_ram_config *config, + const struct ethtool_ringparam *ring, + const struct ethtool_coalesce *ec, + const bool fd_mode); + +#endif diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c index 7b120c716228..217510c12af5 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c @@ -2,8 +2,8 @@ // // mcp251xfd - Microchip MCP251xFD Family CAN controller driver // -// Copyright (c) 2019, 2020 Pengutronix, -// Marc Kleine-Budde <kernel@pengutronix.de> +// Copyright (c) 2019, 2020, 2021 Pengutronix, +// Marc Kleine-Budde <kernel@pengutronix.de> // #include "mcp251xfd.h" @@ -47,22 +47,32 @@ mcp251xfd_regmap_nocrc_gather_write(void *context, return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); } -static inline bool mcp251xfd_update_bits_read_reg(unsigned int reg) +static inline bool +mcp251xfd_update_bits_read_reg(const struct mcp251xfd_priv *priv, + unsigned int reg) { + struct mcp251xfd_rx_ring *ring; + int n; + switch (reg) { case MCP251XFD_REG_INT: case MCP251XFD_REG_TEFCON: - case MCP251XFD_REG_FIFOCON(MCP251XFD_RX_FIFO(0)): case MCP251XFD_REG_FLTCON(0): case MCP251XFD_REG_ECCSTAT: case MCP251XFD_REG_CRC: return false; case MCP251XFD_REG_CON: - case MCP251XFD_REG_FIFOSTA(MCP251XFD_RX_FIFO(0)): case MCP251XFD_REG_OSC: case MCP251XFD_REG_ECCCON: return true; default: + mcp251xfd_for_each_rx_ring(priv, ring, n) { + if (reg == MCP251XFD_REG_FIFOCON(ring->fifo_nr)) + return false; + if (reg == MCP251XFD_REG_FIFOSTA(ring->fifo_nr)) + return true; + } + WARN(1, "Status of reg 0x%04x unknown.\n", reg); } @@ -92,7 +102,7 @@ mcp251xfd_regmap_nocrc_update_bits(void *context, unsigned int reg, last_byte = mcp251xfd_last_byte_set(mask); len = last_byte - first_byte + 1; - if (mcp251xfd_update_bits_read_reg(reg)) { + if (mcp251xfd_update_bits_read_reg(priv, reg)) { struct spi_transfer xfer[2] = { }; struct spi_message msg; @@ -368,7 +378,7 @@ mcp251xfd_regmap_crc_read(void *context, * to the caller. It will take care of both cases. * */ - if (reg == MCP251XFD_REG_OSC) { + if (reg == MCP251XFD_REG_OSC && val_len == sizeof(__le32)) { err = 0; goto out; } diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-ring.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ring.c index 92f9e9b01289..bf3f0f150199 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-ring.c +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ring.c @@ -15,6 +15,7 @@ #include <asm/unaligned.h> #include "mcp251xfd.h" +#include "mcp251xfd-ram.h" static inline u8 mcp251xfd_cmd_prepare_write_reg(const struct mcp251xfd_priv *priv, @@ -53,6 +54,72 @@ mcp251xfd_cmd_prepare_write_reg(const struct mcp251xfd_priv *priv, } static void +mcp251xfd_ring_init_tef(struct mcp251xfd_priv *priv, u16 *base) +{ + struct mcp251xfd_tef_ring *tef_ring; + struct spi_transfer *xfer; + u32 val; + u16 addr; + u8 len; + int i; + + /* TEF */ + tef_ring = priv->tef; + tef_ring->head = 0; + tef_ring->tail = 0; + + /* TEF- and TX-FIFO have same number of objects */ + *base = mcp251xfd_get_tef_obj_addr(priv->tx->obj_num); + + /* FIFO IRQ enable */ + addr = MCP251XFD_REG_TEFCON; + val = MCP251XFD_REG_TEFCON_TEFOVIE | MCP251XFD_REG_TEFCON_TEFNEIE; + + len = mcp251xfd_cmd_prepare_write_reg(priv, &tef_ring->irq_enable_buf, + addr, val, val); + tef_ring->irq_enable_xfer.tx_buf = &tef_ring->irq_enable_buf; + tef_ring->irq_enable_xfer.len = len; + spi_message_init_with_transfers(&tef_ring->irq_enable_msg, + &tef_ring->irq_enable_xfer, 1); + + /* FIFO increment TEF tail pointer */ + addr = MCP251XFD_REG_TEFCON; + val = MCP251XFD_REG_TEFCON_UINC; + len = mcp251xfd_cmd_prepare_write_reg(priv, &tef_ring->uinc_buf, + addr, val, val); + + for (i = 0; i < ARRAY_SIZE(tef_ring->uinc_xfer); i++) { + xfer = &tef_ring->uinc_xfer[i]; + xfer->tx_buf = &tef_ring->uinc_buf; + xfer->len = len; + xfer->cs_change = 1; + xfer->cs_change_delay.value = 0; + xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + } + + /* "cs_change == 1" on the last transfer results in an active + * chip select after the complete SPI message. This causes the + * controller to interpret the next register access as + * data. Set "cs_change" of the last transfer to "0" to + * properly deactivate the chip select at the end of the + * message. + */ + xfer->cs_change = 0; + + if (priv->tx_coalesce_usecs_irq || priv->tx_obj_num_coalesce_irq) { + val = MCP251XFD_REG_TEFCON_UINC | + MCP251XFD_REG_TEFCON_TEFOVIE | + MCP251XFD_REG_TEFCON_TEFHIE; + + len = mcp251xfd_cmd_prepare_write_reg(priv, + &tef_ring->uinc_irq_disable_buf, + addr, val, val); + xfer->tx_buf = &tef_ring->uinc_irq_disable_buf; + xfer->len = len; + } +} + +static void mcp251xfd_tx_ring_init_tx_obj(const struct mcp251xfd_priv *priv, const struct mcp251xfd_tx_ring *ring, struct mcp251xfd_tx_obj *tx_obj, @@ -88,84 +155,68 @@ mcp251xfd_tx_ring_init_tx_obj(const struct mcp251xfd_priv *priv, ARRAY_SIZE(tx_obj->xfer)); } -void mcp251xfd_ring_init(struct mcp251xfd_priv *priv) +static void +mcp251xfd_ring_init_tx(struct mcp251xfd_priv *priv, u16 *base, u8 *fifo_nr) { - struct mcp251xfd_tef_ring *tef_ring; struct mcp251xfd_tx_ring *tx_ring; - struct mcp251xfd_rx_ring *rx_ring, *prev_rx_ring = NULL; struct mcp251xfd_tx_obj *tx_obj; - struct spi_transfer *xfer; u32 val; u16 addr; u8 len; - int i, j; - - netdev_reset_queue(priv->ndev); - - /* TEF */ - tef_ring = priv->tef; - tef_ring->head = 0; - tef_ring->tail = 0; - - /* FIFO increment TEF tail pointer */ - addr = MCP251XFD_REG_TEFCON; - val = MCP251XFD_REG_TEFCON_UINC; - len = mcp251xfd_cmd_prepare_write_reg(priv, &tef_ring->uinc_buf, - addr, val, val); - - for (j = 0; j < ARRAY_SIZE(tef_ring->uinc_xfer); j++) { - xfer = &tef_ring->uinc_xfer[j]; - xfer->tx_buf = &tef_ring->uinc_buf; - xfer->len = len; - xfer->cs_change = 1; - xfer->cs_change_delay.value = 0; - xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; - } - - /* "cs_change == 1" on the last transfer results in an active - * chip select after the complete SPI message. This causes the - * controller to interpret the next register access as - * data. Set "cs_change" of the last transfer to "0" to - * properly deactivate the chip select at the end of the - * message. - */ - xfer->cs_change = 0; + int i; - /* TX */ tx_ring = priv->tx; tx_ring->head = 0; tx_ring->tail = 0; - tx_ring->base = mcp251xfd_get_tef_obj_addr(tx_ring->obj_num); + tx_ring->base = *base; + tx_ring->nr = 0; + tx_ring->fifo_nr = *fifo_nr; + + *base = mcp251xfd_get_tx_obj_addr(tx_ring, tx_ring->obj_num); + *fifo_nr += 1; /* FIFO request to send */ - addr = MCP251XFD_REG_FIFOCON(MCP251XFD_TX_FIFO); + addr = MCP251XFD_REG_FIFOCON(tx_ring->fifo_nr); val = MCP251XFD_REG_FIFOCON_TXREQ | MCP251XFD_REG_FIFOCON_UINC; len = mcp251xfd_cmd_prepare_write_reg(priv, &tx_ring->rts_buf, addr, val, val); mcp251xfd_for_each_tx_obj(tx_ring, tx_obj, i) mcp251xfd_tx_ring_init_tx_obj(priv, tx_ring, tx_obj, len, i); +} + +static void +mcp251xfd_ring_init_rx(struct mcp251xfd_priv *priv, u16 *base, u8 *fifo_nr) +{ + struct mcp251xfd_rx_ring *rx_ring; + struct spi_transfer *xfer; + u32 val; + u16 addr; + u8 len; + int i, j; - /* RX */ mcp251xfd_for_each_rx_ring(priv, rx_ring, i) { rx_ring->head = 0; rx_ring->tail = 0; + rx_ring->base = *base; rx_ring->nr = i; - rx_ring->fifo_nr = MCP251XFD_RX_FIFO(i); + rx_ring->fifo_nr = *fifo_nr; - if (!prev_rx_ring) - rx_ring->base = - mcp251xfd_get_tx_obj_addr(tx_ring, - tx_ring->obj_num); - else - rx_ring->base = prev_rx_ring->base + - prev_rx_ring->obj_size * - prev_rx_ring->obj_num; + *base = mcp251xfd_get_rx_obj_addr(rx_ring, rx_ring->obj_num); + *fifo_nr += 1; - prev_rx_ring = rx_ring; + /* FIFO IRQ enable */ + addr = MCP251XFD_REG_FIFOCON(rx_ring->fifo_nr); + val = MCP251XFD_REG_FIFOCON_RXOVIE | + MCP251XFD_REG_FIFOCON_TFNRFNIE; + len = mcp251xfd_cmd_prepare_write_reg(priv, &rx_ring->irq_enable_buf, + addr, val, val); + rx_ring->irq_enable_xfer.tx_buf = &rx_ring->irq_enable_buf; + rx_ring->irq_enable_xfer.len = len; + spi_message_init_with_transfers(&rx_ring->irq_enable_msg, + &rx_ring->irq_enable_xfer, 1); /* FIFO increment RX tail pointer */ - addr = MCP251XFD_REG_FIFOCON(rx_ring->fifo_nr); val = MCP251XFD_REG_FIFOCON_UINC; len = mcp251xfd_cmd_prepare_write_reg(priv, &rx_ring->uinc_buf, addr, val, val); @@ -187,9 +238,149 @@ void mcp251xfd_ring_init(struct mcp251xfd_priv *priv) * the chip select at the end of the message. */ xfer->cs_change = 0; + + /* Use 1st RX-FIFO for IRQ coalescing. If enabled + * (rx_coalesce_usecs_irq or rx_max_coalesce_frames_irq + * is activated), use the last transfer to disable: + * + * - TFNRFNIE (Receive FIFO Not Empty Interrupt) + * + * and enable: + * + * - TFHRFHIE (Receive FIFO Half Full Interrupt) + * - or - + * - TFERFFIE (Receive FIFO Full Interrupt) + * + * depending on rx_max_coalesce_frames_irq. + * + * The RXOVIE (Overflow Interrupt) is always enabled. + */ + if (rx_ring->nr == 0 && (priv->rx_coalesce_usecs_irq || + priv->rx_obj_num_coalesce_irq)) { + val = MCP251XFD_REG_FIFOCON_UINC | + MCP251XFD_REG_FIFOCON_RXOVIE; + + if (priv->rx_obj_num_coalesce_irq == rx_ring->obj_num) + val |= MCP251XFD_REG_FIFOCON_TFERFFIE; + else if (priv->rx_obj_num_coalesce_irq) + val |= MCP251XFD_REG_FIFOCON_TFHRFHIE; + + len = mcp251xfd_cmd_prepare_write_reg(priv, + &rx_ring->uinc_irq_disable_buf, + addr, val, val); + xfer->tx_buf = &rx_ring->uinc_irq_disable_buf; + xfer->len = len; + } } } +int mcp251xfd_ring_init(struct mcp251xfd_priv *priv) +{ + const struct mcp251xfd_rx_ring *rx_ring; + u16 base = 0, ram_used; + u8 fifo_nr = 1; + int i; + + netdev_reset_queue(priv->ndev); + + mcp251xfd_ring_init_tef(priv, &base); + mcp251xfd_ring_init_rx(priv, &base, &fifo_nr); + mcp251xfd_ring_init_tx(priv, &base, &fifo_nr); + + /* mcp251xfd_handle_rxif() will iterate over all RX rings. + * Rings with their corresponding bit set in + * priv->regs_status.rxif are read out. + * + * If the chip is configured for only 1 RX-FIFO, and if there + * is an RX interrupt pending (RXIF in INT register is set), + * it must be the 1st RX-FIFO. + * + * We mark the RXIF of the 1st FIFO as pending here, so that + * we can skip the read of the RXIF register in + * mcp251xfd_read_regs_status() for the 1 RX-FIFO only case. + * + * If we use more than 1 RX-FIFO, this value gets overwritten + * in mcp251xfd_read_regs_status(), so set it unconditionally + * here. + */ + priv->regs_status.rxif = BIT(priv->rx[0]->fifo_nr); + + if (priv->tx_obj_num_coalesce_irq) { + netdev_dbg(priv->ndev, + "FIFO setup: TEF: 0x%03x: %2d*%zu bytes = %4zu bytes (coalesce)\n", + mcp251xfd_get_tef_obj_addr(0), + priv->tx_obj_num_coalesce_irq, + sizeof(struct mcp251xfd_hw_tef_obj), + priv->tx_obj_num_coalesce_irq * + sizeof(struct mcp251xfd_hw_tef_obj)); + + netdev_dbg(priv->ndev, + " 0x%03x: %2d*%zu bytes = %4zu bytes\n", + mcp251xfd_get_tef_obj_addr(priv->tx_obj_num_coalesce_irq), + priv->tx->obj_num - priv->tx_obj_num_coalesce_irq, + sizeof(struct mcp251xfd_hw_tef_obj), + (priv->tx->obj_num - priv->tx_obj_num_coalesce_irq) * + sizeof(struct mcp251xfd_hw_tef_obj)); + } else { + netdev_dbg(priv->ndev, + "FIFO setup: TEF: 0x%03x: %2d*%zu bytes = %4zu bytes\n", + mcp251xfd_get_tef_obj_addr(0), + priv->tx->obj_num, sizeof(struct mcp251xfd_hw_tef_obj), + priv->tx->obj_num * sizeof(struct mcp251xfd_hw_tef_obj)); + } + + mcp251xfd_for_each_rx_ring(priv, rx_ring, i) { + if (rx_ring->nr == 0 && priv->rx_obj_num_coalesce_irq) { + netdev_dbg(priv->ndev, + "FIFO setup: RX-%u: FIFO %u/0x%03x: %2u*%u bytes = %4u bytes (coalesce)\n", + rx_ring->nr, rx_ring->fifo_nr, + mcp251xfd_get_rx_obj_addr(rx_ring, 0), + priv->rx_obj_num_coalesce_irq, rx_ring->obj_size, + priv->rx_obj_num_coalesce_irq * rx_ring->obj_size); + + if (priv->rx_obj_num_coalesce_irq == MCP251XFD_FIFO_DEPTH) + continue; + + netdev_dbg(priv->ndev, + " 0x%03x: %2u*%u bytes = %4u bytes\n", + mcp251xfd_get_rx_obj_addr(rx_ring, + priv->rx_obj_num_coalesce_irq), + rx_ring->obj_num - priv->rx_obj_num_coalesce_irq, + rx_ring->obj_size, + (rx_ring->obj_num - priv->rx_obj_num_coalesce_irq) * + rx_ring->obj_size); + } else { + netdev_dbg(priv->ndev, + "FIFO setup: RX-%u: FIFO %u/0x%03x: %2u*%u bytes = %4u bytes\n", + rx_ring->nr, rx_ring->fifo_nr, + mcp251xfd_get_rx_obj_addr(rx_ring, 0), + rx_ring->obj_num, rx_ring->obj_size, + rx_ring->obj_num * rx_ring->obj_size); + } + } + + netdev_dbg(priv->ndev, + "FIFO setup: TX: FIFO %u/0x%03x: %2u*%u bytes = %4u bytes\n", + priv->tx->fifo_nr, + mcp251xfd_get_tx_obj_addr(priv->tx, 0), + priv->tx->obj_num, priv->tx->obj_size, + priv->tx->obj_num * priv->tx->obj_size); + + netdev_dbg(priv->ndev, + "FIFO setup: free: %4d bytes\n", + MCP251XFD_RAM_SIZE - (base - MCP251XFD_RAM_START)); + + ram_used = base - MCP251XFD_RAM_START; + if (ram_used > MCP251XFD_RAM_SIZE) { + netdev_err(priv->ndev, + "Error during ring configuration, using more RAM (%u bytes) than available (%u bytes).\n", + ram_used, MCP251XFD_RAM_SIZE); + return -ENOMEM; + } + + return 0; +} + void mcp251xfd_ring_free(struct mcp251xfd_priv *priv) { int i; @@ -200,40 +391,103 @@ void mcp251xfd_ring_free(struct mcp251xfd_priv *priv) } } +static enum hrtimer_restart mcp251xfd_rx_irq_timer(struct hrtimer *t) +{ + struct mcp251xfd_priv *priv = container_of(t, struct mcp251xfd_priv, + rx_irq_timer); + struct mcp251xfd_rx_ring *ring = priv->rx[0]; + + if (test_bit(MCP251XFD_FLAGS_DOWN, priv->flags)) + return HRTIMER_NORESTART; + + spi_async(priv->spi, &ring->irq_enable_msg); + + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart mcp251xfd_tx_irq_timer(struct hrtimer *t) +{ + struct mcp251xfd_priv *priv = container_of(t, struct mcp251xfd_priv, + tx_irq_timer); + struct mcp251xfd_tef_ring *ring = priv->tef; + + if (test_bit(MCP251XFD_FLAGS_DOWN, priv->flags)) + return HRTIMER_NORESTART; + + spi_async(priv->spi, &ring->irq_enable_msg); + + return HRTIMER_NORESTART; +} + +const struct can_ram_config mcp251xfd_ram_config = { + .rx = { + .size[CAN_RAM_MODE_CAN] = sizeof(struct mcp251xfd_hw_rx_obj_can), + .size[CAN_RAM_MODE_CANFD] = sizeof(struct mcp251xfd_hw_rx_obj_canfd), + .min = MCP251XFD_RX_OBJ_NUM_MIN, + .max = MCP251XFD_RX_OBJ_NUM_MAX, + .def[CAN_RAM_MODE_CAN] = CAN_RAM_NUM_MAX, + .def[CAN_RAM_MODE_CANFD] = CAN_RAM_NUM_MAX, + .fifo_num = MCP251XFD_FIFO_RX_NUM, + .fifo_depth_min = MCP251XFD_RX_FIFO_DEPTH_MIN, + .fifo_depth_coalesce_min = MCP251XFD_RX_FIFO_DEPTH_COALESCE_MIN, + }, + .tx = { + .size[CAN_RAM_MODE_CAN] = sizeof(struct mcp251xfd_hw_tef_obj) + + sizeof(struct mcp251xfd_hw_tx_obj_can), + .size[CAN_RAM_MODE_CANFD] = sizeof(struct mcp251xfd_hw_tef_obj) + + sizeof(struct mcp251xfd_hw_tx_obj_canfd), + .min = MCP251XFD_TX_OBJ_NUM_MIN, + .max = MCP251XFD_TX_OBJ_NUM_MAX, + .def[CAN_RAM_MODE_CAN] = MCP251XFD_TX_OBJ_NUM_CAN_DEFAULT, + .def[CAN_RAM_MODE_CANFD] = MCP251XFD_TX_OBJ_NUM_CANFD_DEFAULT, + .fifo_num = MCP251XFD_FIFO_TX_NUM, + .fifo_depth_min = MCP251XFD_TX_FIFO_DEPTH_MIN, + .fifo_depth_coalesce_min = MCP251XFD_TX_FIFO_DEPTH_COALESCE_MIN, + }, + .size = MCP251XFD_RAM_SIZE, + .fifo_depth = MCP251XFD_FIFO_DEPTH, +}; + int mcp251xfd_ring_alloc(struct mcp251xfd_priv *priv) { - struct mcp251xfd_tx_ring *tx_ring; + const bool fd_mode = mcp251xfd_is_fd_mode(priv); + struct mcp251xfd_tx_ring *tx_ring = priv->tx; struct mcp251xfd_rx_ring *rx_ring; - int tef_obj_size, tx_obj_size, rx_obj_size; - int tx_obj_num; - int ram_free, i; + u8 tx_obj_size, rx_obj_size; + u8 rem, i; - tef_obj_size = sizeof(struct mcp251xfd_hw_tef_obj); - if (mcp251xfd_is_fd_mode(priv)) { - tx_obj_num = MCP251XFD_TX_OBJ_NUM_CANFD; + /* switching from CAN-2.0 to CAN-FD mode or vice versa */ + if (fd_mode != test_bit(MCP251XFD_FLAGS_FD_MODE, priv->flags)) { + struct can_ram_layout layout; + + can_ram_get_layout(&layout, &mcp251xfd_ram_config, NULL, NULL, fd_mode); + priv->rx_obj_num = layout.default_rx; + tx_ring->obj_num = layout.default_tx; + } + + if (fd_mode) { tx_obj_size = sizeof(struct mcp251xfd_hw_tx_obj_canfd); rx_obj_size = sizeof(struct mcp251xfd_hw_rx_obj_canfd); + set_bit(MCP251XFD_FLAGS_FD_MODE, priv->flags); } else { - tx_obj_num = MCP251XFD_TX_OBJ_NUM_CAN; tx_obj_size = sizeof(struct mcp251xfd_hw_tx_obj_can); rx_obj_size = sizeof(struct mcp251xfd_hw_rx_obj_can); + clear_bit(MCP251XFD_FLAGS_FD_MODE, priv->flags); } - tx_ring = priv->tx; - tx_ring->obj_num = tx_obj_num; tx_ring->obj_size = tx_obj_size; - ram_free = MCP251XFD_RAM_SIZE - tx_obj_num * - (tef_obj_size + tx_obj_size); - - for (i = 0; - i < ARRAY_SIZE(priv->rx) && ram_free >= rx_obj_size; - i++) { - int rx_obj_num; + rem = priv->rx_obj_num; + for (i = 0; i < ARRAY_SIZE(priv->rx) && rem; i++) { + u8 rx_obj_num; - rx_obj_num = ram_free / rx_obj_size; - rx_obj_num = min(1 << (fls(rx_obj_num) - 1), - MCP251XFD_RX_OBJ_NUM_MAX); + if (i == 0 && priv->rx_obj_num_coalesce_irq) + rx_obj_num = min_t(u8, priv->rx_obj_num_coalesce_irq * 2, + MCP251XFD_FIFO_DEPTH); + else + rx_obj_num = min_t(u8, rounddown_pow_of_two(rem), + MCP251XFD_FIFO_DEPTH); + rem -= rx_obj_num; rx_ring = kzalloc(sizeof(*rx_ring) + rx_obj_size * rx_obj_num, GFP_KERNEL); @@ -241,29 +495,18 @@ int mcp251xfd_ring_alloc(struct mcp251xfd_priv *priv) mcp251xfd_ring_free(priv); return -ENOMEM; } + rx_ring->obj_num = rx_obj_num; rx_ring->obj_size = rx_obj_size; priv->rx[i] = rx_ring; - - ram_free -= rx_ring->obj_num * rx_ring->obj_size; } priv->rx_ring_num = i; - netdev_dbg(priv->ndev, - "FIFO setup: TEF: %d*%d bytes = %d bytes, TX: %d*%d bytes = %d bytes\n", - tx_obj_num, tef_obj_size, tef_obj_size * tx_obj_num, - tx_obj_num, tx_obj_size, tx_obj_size * tx_obj_num); + hrtimer_init(&priv->rx_irq_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + priv->rx_irq_timer.function = mcp251xfd_rx_irq_timer; - mcp251xfd_for_each_rx_ring(priv, rx_ring, i) { - netdev_dbg(priv->ndev, - "FIFO setup: RX-%d: %d*%d bytes = %d bytes\n", - i, rx_ring->obj_num, rx_ring->obj_size, - rx_ring->obj_size * rx_ring->obj_num); - } - - netdev_dbg(priv->ndev, - "FIFO setup: free: %d bytes\n", - ram_free); + hrtimer_init(&priv->tx_irq_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + priv->tx_irq_timer.function = mcp251xfd_tx_irq_timer; return 0; } diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-rx.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-rx.c index 63f2526464b3..d09f7fbf2ba7 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-rx.c +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-rx.c @@ -19,7 +19,7 @@ static inline int mcp251xfd_rx_head_get_from_chip(const struct mcp251xfd_priv *priv, const struct mcp251xfd_rx_ring *ring, - u8 *rx_head) + u8 *rx_head, bool *fifo_empty) { u32 fifo_sta; int err; @@ -30,6 +30,7 @@ mcp251xfd_rx_head_get_from_chip(const struct mcp251xfd_priv *priv, return err; *rx_head = FIELD_GET(MCP251XFD_REG_FIFOSTA_FIFOCI_MASK, fifo_sta); + *fifo_empty = !(fifo_sta & MCP251XFD_REG_FIFOSTA_TFNRFNIF); return 0; } @@ -84,10 +85,12 @@ mcp251xfd_rx_ring_update(const struct mcp251xfd_priv *priv, { u32 new_head; u8 chip_rx_head; + bool fifo_empty; int err; - err = mcp251xfd_rx_head_get_from_chip(priv, ring, &chip_rx_head); - if (err) + err = mcp251xfd_rx_head_get_from_chip(priv, ring, &chip_rx_head, + &fifo_empty); + if (err || fifo_empty) return err; /* chip_rx_head, is the next RX-Object filled by the HW. @@ -251,10 +254,23 @@ int mcp251xfd_handle_rxif(struct mcp251xfd_priv *priv) int err, n; mcp251xfd_for_each_rx_ring(priv, ring, n) { + /* - if RX IRQ coalescing is active always handle ring 0 + * - only handle rings if RX IRQ is active + */ + if ((ring->nr > 0 || !priv->rx_obj_num_coalesce_irq) && + !(priv->regs_status.rxif & BIT(ring->fifo_nr))) + continue; + err = mcp251xfd_handle_rxif_ring(priv, ring); if (err) return err; } + if (priv->rx_coalesce_usecs_irq) + hrtimer_start(&priv->rx_irq_timer, + ns_to_ktime(priv->rx_coalesce_usecs_irq * + NSEC_PER_USEC), + HRTIMER_MODE_REL); + return 0; } diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-tef.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-tef.c index 406166005b99..237617b0c125 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-tef.c +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-tef.c @@ -256,5 +256,11 @@ int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv) netif_wake_queue(priv->ndev); } + if (priv->tx_coalesce_usecs_irq) + hrtimer_start(&priv->tx_irq_timer, + ns_to_ktime(priv->tx_coalesce_usecs_irq * + NSEC_PER_USEC), + HRTIMER_MODE_REL); + return 0; } diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h index f551c900803e..9cb6b5ad8dda 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h @@ -2,8 +2,8 @@ * * mcp251xfd - Microchip MCP251xFD Family CAN controller driver * - * Copyright (c) 2019 Pengutronix, - * Marc Kleine-Budde <kernel@pengutronix.de> + * Copyright (c) 2019, 2020, 2021 Pengutronix, + * Marc Kleine-Budde <kernel@pengutronix.de> * Copyright (c) 2019 Martin Sperl <kernel@martin.sperl.org> */ @@ -367,25 +367,6 @@ #define MCP251XFD_REG_DEVID_ID_MASK GENMASK(7, 4) #define MCP251XFD_REG_DEVID_REV_MASK GENMASK(3, 0) -/* number of TX FIFO objects, depending on CAN mode - * - * FIFO setup: tef: 8*12 bytes = 96 bytes, tx: 8*16 bytes = 128 bytes - * FIFO setup: tef: 4*12 bytes = 48 bytes, tx: 4*72 bytes = 288 bytes - */ -#define MCP251XFD_RX_OBJ_NUM_MAX 32 -#define MCP251XFD_TX_OBJ_NUM_CAN 8 -#define MCP251XFD_TX_OBJ_NUM_CANFD 4 - -#if MCP251XFD_TX_OBJ_NUM_CAN > MCP251XFD_TX_OBJ_NUM_CANFD -#define MCP251XFD_TX_OBJ_NUM_MAX MCP251XFD_TX_OBJ_NUM_CAN -#else -#define MCP251XFD_TX_OBJ_NUM_MAX MCP251XFD_TX_OBJ_NUM_CANFD -#endif - -#define MCP251XFD_NAPI_WEIGHT 32 -#define MCP251XFD_TX_FIFO 1 -#define MCP251XFD_RX_FIFO(x) (MCP251XFD_TX_FIFO + 1 + (x)) - /* SPI commands */ #define MCP251XFD_SPI_INSTRUCTION_RESET 0x0000 #define MCP251XFD_SPI_INSTRUCTION_WRITE 0x2000 @@ -406,12 +387,38 @@ static_assert(MCP251XFD_TIMESTAMP_WORK_DELAY_SEC < #define MCP251XFD_OSC_STAB_TIMEOUT_US (10 * MCP251XFD_OSC_STAB_SLEEP_US) #define MCP251XFD_POLL_SLEEP_US (10) #define MCP251XFD_POLL_TIMEOUT_US (USEC_PER_MSEC) + +/* Misc */ +#define MCP251XFD_NAPI_WEIGHT 32 #define MCP251XFD_SOFTRESET_RETRIES_MAX 3 #define MCP251XFD_READ_CRC_RETRIES_MAX 3 #define MCP251XFD_ECC_CNT_MAX 2 #define MCP251XFD_SANITIZE_SPI 1 #define MCP251XFD_SANITIZE_CAN 1 +/* FIFO and Ring */ +#define MCP251XFD_FIFO_TEF_NUM 1U +#define MCP251XFD_FIFO_RX_NUM 3U +#define MCP251XFD_FIFO_TX_NUM 1U + +#define MCP251XFD_FIFO_DEPTH 32U + +#define MCP251XFD_RX_OBJ_NUM_MIN 16U +#define MCP251XFD_RX_OBJ_NUM_MAX (MCP251XFD_FIFO_RX_NUM * MCP251XFD_FIFO_DEPTH) +#define MCP251XFD_RX_FIFO_DEPTH_MIN 4U +#define MCP251XFD_RX_FIFO_DEPTH_COALESCE_MIN 8U + +#define MCP251XFD_TX_OBJ_NUM_MIN 2U +#define MCP251XFD_TX_OBJ_NUM_MAX 16U +#define MCP251XFD_TX_OBJ_NUM_CAN_DEFAULT 8U +#define MCP251XFD_TX_OBJ_NUM_CANFD_DEFAULT 4U +#define MCP251XFD_TX_FIFO_DEPTH_MIN 2U +#define MCP251XFD_TX_FIFO_DEPTH_COALESCE_MIN 2U + +static_assert(MCP251XFD_FIFO_TEF_NUM == 1U); +static_assert(MCP251XFD_FIFO_TEF_NUM == MCP251XFD_FIFO_TX_NUM); +static_assert(MCP251XFD_FIFO_RX_NUM <= 4U); + /* Silence TX MAB overflow warnings */ #define MCP251XFD_QUIRK_MAB_NO_WARN BIT(0) /* Use CRC to access registers */ @@ -512,7 +519,12 @@ struct mcp251xfd_tef_ring { /* u8 obj_num equals tx_ring->obj_num */ /* u8 obj_size equals sizeof(struct mcp251xfd_hw_tef_obj) */ + union mcp251xfd_write_reg_buf irq_enable_buf; + struct spi_transfer irq_enable_xfer; + struct spi_message irq_enable_msg; + union mcp251xfd_write_reg_buf uinc_buf; + union mcp251xfd_write_reg_buf uinc_irq_disable_buf; struct spi_transfer uinc_xfer[MCP251XFD_TX_OBJ_NUM_MAX]; }; @@ -521,6 +533,8 @@ struct mcp251xfd_tx_ring { unsigned int tail; u16 base; + u8 nr; + u8 fifo_nr; u8 obj_num; u8 obj_size; @@ -538,8 +552,13 @@ struct mcp251xfd_rx_ring { u8 obj_num; u8 obj_size; + union mcp251xfd_write_reg_buf irq_enable_buf; + struct spi_transfer irq_enable_xfer; + struct spi_message irq_enable_msg; + union mcp251xfd_write_reg_buf uinc_buf; - struct spi_transfer uinc_xfer[MCP251XFD_RX_OBJ_NUM_MAX]; + union mcp251xfd_write_reg_buf uinc_irq_disable_buf; + struct spi_transfer uinc_xfer[MCP251XFD_FIFO_DEPTH]; struct mcp251xfd_hw_rx_obj_canfd obj[]; }; @@ -561,6 +580,7 @@ struct mcp251xfd_ecc { struct mcp251xfd_regs_status { u32 intf; + u32 rxif; }; enum mcp251xfd_model { @@ -574,6 +594,13 @@ struct mcp251xfd_devtype_data { u32 quirks; }; +enum mcp251xfd_flags { + MCP251XFD_FLAGS_DOWN, + MCP251XFD_FLAGS_FD_MODE, + + __MCP251XFD_FLAGS_SIZE__ +}; + struct mcp251xfd_priv { struct can_priv can; struct can_rx_offload offload; @@ -592,12 +619,24 @@ struct mcp251xfd_priv { struct spi_device *spi; u32 spi_max_speed_hz_orig; + u32 spi_max_speed_hz_fast; + u32 spi_max_speed_hz_slow; + + struct mcp251xfd_tef_ring tef[MCP251XFD_FIFO_TEF_NUM]; + struct mcp251xfd_rx_ring *rx[MCP251XFD_FIFO_RX_NUM]; + struct mcp251xfd_tx_ring tx[MCP251XFD_FIFO_TX_NUM]; - struct mcp251xfd_tef_ring tef[1]; - struct mcp251xfd_tx_ring tx[1]; - struct mcp251xfd_rx_ring *rx[1]; + DECLARE_BITMAP(flags, __MCP251XFD_FLAGS_SIZE__); u8 rx_ring_num; + u8 rx_obj_num; + u8 rx_obj_num_coalesce_irq; + u8 tx_obj_num_coalesce_irq; + + u32 rx_coalesce_usecs_irq; + u32 tx_coalesce_usecs_irq; + struct hrtimer rx_irq_timer; + struct hrtimer tx_irq_timer; struct mcp251xfd_ecc ecc; struct mcp251xfd_regs_status regs_status; @@ -608,6 +647,7 @@ struct mcp251xfd_priv { struct gpio_desc *rx_int; struct clk *clk; + bool pll_enable; struct regulator *reg_vdd; struct regulator *reg_xceiver; @@ -776,7 +816,7 @@ mcp251xfd_tx_tail_get_from_chip(const struct mcp251xfd_priv *priv, int err; err = regmap_read(priv->map_reg, - MCP251XFD_REG_FIFOSTA(MCP251XFD_TX_FIFO), + MCP251XFD_REG_FIFOSTA(priv->tx->fifo_nr), &fifo_sta); if (err) return err; @@ -878,8 +918,10 @@ int mcp251xfd_chip_fifo_init(const struct mcp251xfd_priv *priv); u16 mcp251xfd_crc16_compute2(const void *cmd, size_t cmd_size, const void *data, size_t data_size); u16 mcp251xfd_crc16_compute(const void *data, size_t data_size); +void mcp251xfd_ethtool_init(struct mcp251xfd_priv *priv); int mcp251xfd_regmap_init(struct mcp251xfd_priv *priv); -void mcp251xfd_ring_init(struct mcp251xfd_priv *priv); +extern const struct can_ram_config mcp251xfd_ram_config; +int mcp251xfd_ring_init(struct mcp251xfd_priv *priv); void mcp251xfd_ring_free(struct mcp251xfd_priv *priv); int mcp251xfd_ring_alloc(struct mcp251xfd_priv *priv); int mcp251xfd_handle_rxif(struct mcp251xfd_priv *priv); |