// SPDX-License-Identifier: GPL-2.0 // // mcp251xfd - Microchip MCP251xFD Family CAN controller driver // // Copyright (c) 2019, 2020, 2021 Pengutronix, // Marc Kleine-Budde // // Based on: // // CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface // // Copyright (c) 2019 Martin Sperl // #include #include "mcp251xfd.h" static inline u8 mcp251xfd_cmd_prepare_write_reg(const struct mcp251xfd_priv *priv, union mcp251xfd_write_reg_buf *write_reg_buf, const u16 reg, const u32 mask, const u32 val) { u8 first_byte, last_byte, len; u8 *data; __le32 val_le32; first_byte = mcp251xfd_first_byte_set(mask); last_byte = mcp251xfd_last_byte_set(mask); len = last_byte - first_byte + 1; data = mcp251xfd_spi_cmd_write(priv, write_reg_buf, reg + first_byte); val_le32 = cpu_to_le32(val >> BITS_PER_BYTE * first_byte); memcpy(data, &val_le32, len); if (priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_REG) { u16 crc; mcp251xfd_spi_cmd_crc_set_len_in_reg(&write_reg_buf->crc.cmd, len); /* CRC */ len += sizeof(write_reg_buf->crc.cmd); crc = mcp251xfd_crc16_compute(&write_reg_buf->crc, len); put_unaligned_be16(crc, (void *)write_reg_buf + len); /* Total length */ len += sizeof(write_reg_buf->crc.crc); } else { len += sizeof(write_reg_buf->nocrc.cmd); } return 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, const u8 rts_buf_len, const u8 n) { struct spi_transfer *xfer; u16 addr; /* FIFO load */ addr = mcp251xfd_get_tx_obj_addr(ring, n); if (priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_TX) mcp251xfd_spi_cmd_write_crc_set_addr(&tx_obj->buf.crc.cmd, addr); else mcp251xfd_spi_cmd_write_nocrc(&tx_obj->buf.nocrc.cmd, addr); xfer = &tx_obj->xfer[0]; xfer->tx_buf = &tx_obj->buf; xfer->len = 0; /* actual len is assigned on the fly */ xfer->cs_change = 1; xfer->cs_change_delay.value = 0; xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; /* FIFO request to send */ xfer = &tx_obj->xfer[1]; xfer->tx_buf = &ring->rts_buf; xfer->len = rts_buf_len; /* SPI message */ spi_message_init_with_transfers(&tx_obj->msg, tx_obj->xfer, ARRAY_SIZE(tx_obj->xfer)); } void mcp251xfd_ring_init(struct mcp251xfd_priv *priv) { 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; /* 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); /* FIFO request to send */ addr = MCP251XFD_REG_FIFOCON(MCP251XFD_TX_FIFO); 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); /* RX */ mcp251xfd_for_each_rx_ring(priv, rx_ring, i) { rx_ring->head = 0; rx_ring->tail = 0; rx_ring->nr = i; rx_ring->fifo_nr = MCP251XFD_RX_FIFO(i); 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; prev_rx_ring = rx_ring; /* 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); for (j = 0; j < ARRAY_SIZE(rx_ring->uinc_xfer); j++) { xfer = &rx_ring->uinc_xfer[j]; xfer->tx_buf = &rx_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; } } void mcp251xfd_ring_free(struct mcp251xfd_priv *priv) { int i; for (i = ARRAY_SIZE(priv->rx) - 1; i >= 0; i--) { kfree(priv->rx[i]); priv->rx[i] = NULL; } } int mcp251xfd_ring_alloc(struct mcp251xfd_priv *priv) { struct mcp251xfd_tx_ring *tx_ring; struct mcp251xfd_rx_ring *rx_ring; int tef_obj_size, tx_obj_size, rx_obj_size; int tx_obj_num; int ram_free, i; tef_obj_size = sizeof(struct mcp251xfd_hw_tef_obj); if (mcp251xfd_is_fd_mode(priv)) { tx_obj_num = MCP251XFD_TX_OBJ_NUM_CANFD; tx_obj_size = sizeof(struct mcp251xfd_hw_tx_obj_canfd); rx_obj_size = sizeof(struct mcp251xfd_hw_rx_obj_canfd); } 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); } 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; rx_obj_num = ram_free / rx_obj_size; rx_obj_num = min(1 << (fls(rx_obj_num) - 1), MCP251XFD_RX_OBJ_NUM_MAX); rx_ring = kzalloc(sizeof(*rx_ring) + rx_obj_size * rx_obj_num, GFP_KERNEL); if (!rx_ring) { 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); 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); return 0; }