lkml.org 
[lkml]   [2021]   [Dec]   [22]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v2 49/50] wilc1000: implement zero-copy transmit support for SPI
    Date
    This enables zero-copy transmits for the SPI driver.  Maybe something
    similar could be implemented for the SDIO driver, but I'm not really
    familiar with it.

    Signed-off-by: David Mosberger-Tang <davidm@egauge.net>
    ---
    drivers/net/wireless/microchip/wilc1000/spi.c | 162 ++++++++++++++++++
    1 file changed, 162 insertions(+)

    diff --git a/drivers/net/wireless/microchip/wilc1000/spi.c b/drivers/net/wireless/microchip/wilc1000/spi.c
    index 8951202ed76e2..8d94f111ffc49 100644
    --- a/drivers/net/wireless/microchip/wilc1000/spi.c
    +++ b/drivers/net/wireless/microchip/wilc1000/spi.c
    @@ -44,6 +44,8 @@ MODULE_PARM_DESC(enable_crc16,
    static const struct wilc_hif_func wilc_hif_spi;

    static int wilc_spi_reset(struct wilc *wilc);
    +static int wilc_spi_write_sk_buffs(struct wilc *wilc, u32 addr,
    + size_t num_skbs, struct sk_buff_head *skbs);

    /********************************************
    *
    @@ -107,9 +109,33 @@ static int wilc_spi_reset(struct wilc *wilc);
    #define DATA_PKT_LOG_SZ DATA_PKT_LOG_SZ_MAX
    #define DATA_PKT_SZ (1 << DATA_PKT_LOG_SZ)

    +#define DATA_START_TAG 0xf0
    +#define DATA_ORDER_FIRST 0x01
    +#define DATA_ORDER_INNER 0x02
    +#define DATA_ORDER_LAST 0x03
    +
    #define WILC_SPI_COMMAND_STAT_SUCCESS 0
    #define WILC_GET_RESP_HDR_START(h) (((h) >> 4) & 0xf)

    +/* wilc_spi_write_sk_buffs() needs the following max. number of SPI
    + * transfers:
    + *
    + * - 1 transfer to send the CMD_DMA_EXT_WRITE command
    + * - 1 transfer per sk_buff (at most WILC_VMM_TBL_SIZE of them)
    + * - for each data packet:
    + * + 1 transfer for the data start tag
    + * + 1 transfer for the current sk_buff (if it spans
    + * the boundary of a data packet)
    + * + 1 transfer for the optional CRC16
    + * - 1 transfer to read the DMA response bytes
    + */
    +#define MAX_DATA_PKTS DIV_ROUND_UP(WILC_TX_BUFF_SIZE, DATA_PKT_SZ)
    +#define MAX_SPI_XFERS \
    + (1 \
    + + WILC_VMM_TBL_SIZE \
    + + 3 * MAX_DATA_PKTS \
    + + 1)
    +
    struct wilc_spi {
    bool isinit; /* true if SPI protocol has been configured */
    bool probing_crc; /* true if we're probing chip's CRC config */
    @@ -119,6 +145,11 @@ struct wilc_spi {
    struct gpio_desc *enable; /* ENABLE GPIO or NULL */
    struct gpio_desc *reset; /* RESET GPIO or NULL */
    } gpios;
    + /* Scratch space used by wilc_spi_write_sk_buffs() for SPI
    + * transfers and the data packets' CRCs.
    + */
    + struct spi_transfer xfer[MAX_SPI_XFERS];
    + u8 crc[2 * MAX_DATA_PKTS];
    };

    struct wilc_spi_cmd {
    @@ -1037,6 +1068,136 @@ static int wilc_spi_write(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
    return spi_data_rsp(wilc, CMD_DMA_EXT_WRITE);
    }

    +static void wilc_spi_add_xfer(struct spi_message *msg,
    + struct spi_transfer **xferp,
    + size_t len, const void *tx_buf, void *rx_buf)
    +{
    + struct spi_transfer *xfer = *xferp;
    +
    + xfer->tx_buf = tx_buf;
    + xfer->rx_buf = rx_buf;
    + xfer->len = len;
    + spi_message_add_tail(xfer, msg);
    + *xferp = xfer + 1;
    +}
    +
    +/**
    + * wilc_spi_write_sk_buffs() - Zero-copy write sk_buffs to the chip.
    + * @wilc: Pointer to the wilc structure.
    + * @addr: The WILC address to transfer the data to.
    + * @num_skbs: The length of the skbs array.
    + * @skbs: The queue containing the sk_buffs to transmit.
    + *
    + * Zero-copy transfer one or more sk_buffs to the WILC chip. At most
    + * WILC_VMM_TBL_SIZE sk_buffs may be transmitted and the total size of
    + * the data in the sk_buffs must not exceed WILC_VMM_TBL_SIZE.
    + *
    + * Context: The caller must hold ownership of the SPI bus through a
    + * call to acquire_bus().
    + *
    + * Return: Zero on success, negative number on error.
    + */
    +static int wilc_spi_write_sk_buffs(struct wilc *wilc, u32 addr, size_t num_skbs,
    + struct sk_buff_head *skbs)
    +{
    + static const u8 data_hdr_first = DATA_START_TAG | DATA_ORDER_FIRST;
    + static const u8 data_hdr_inner = DATA_START_TAG | DATA_ORDER_INNER;
    + static const u8 data_hdr_last = DATA_START_TAG | DATA_ORDER_LAST;
    + size_t num_data_packets = 0, total_bytes = 0, num_sent, n, space;
    + struct spi_device *spi = to_spi_device(wilc->dev);
    + struct wilc_spi *spi_priv = wilc->bus_data;
    + u8 rsp[WILC_SPI_DATA_RSP_BYTES], *crc;
    + int i, ret, cmd_len;
    + struct spi_transfer *xfer;
    + struct wilc_spi_cmd cmd;
    + struct spi_message msg;
    + struct sk_buff *skb;
    + const u8 *data_hdr;
    + u16 crc_calc;
    +
    + /* setup the SPI message and transfers: */
    +
    + spi_message_init(&msg);
    + msg.spi = spi;
    +
    + skb = skb_peek(skbs);
    + for (i = 0; i < num_skbs; ++i) {
    + n = skb->len;
    + total_bytes += n;
    + skb = skb_peek_next(skb, skbs);
    + }
    +
    + num_data_packets = DIV_ROUND_UP(total_bytes, DATA_PKT_SZ);
    + skb = skb_peek(skbs);
    + num_sent = 0;
    + xfer = spi_priv->xfer;
    + crc = spi_priv->crc;
    +
    + cmd_len = wilc_spi_dma_init_cmd(wilc, &cmd, CMD_DMA_EXT_WRITE,
    + addr, total_bytes);
    + if (cmd_len < 0) {
    + dev_err(&spi->dev, "Failed to init DMA command.");
    + return -EINVAL;
    + }
    + wilc_spi_add_xfer(&msg, &xfer, cmd_len, &cmd, NULL);
    +
    + for (i = 0; i < num_data_packets; ++i) {
    + space = DATA_PKT_SZ;
    + crc_calc = 0xffff;
    +
    + /* write data packet's start header: */
    + if (i == num_data_packets - 1)
    + data_hdr = &data_hdr_last;
    + else if (i == 0)
    + data_hdr = &data_hdr_first;
    + else
    + data_hdr = &data_hdr_inner;
    + wilc_spi_add_xfer(&msg, &xfer, 1, data_hdr, NULL);
    +
    + /* write packet data: */
    + do {
    + n = skb->len - num_sent;
    + if (n > space)
    + n = space;
    + wilc_spi_add_xfer(&msg, &xfer, n,
    + skb->data + num_sent, NULL);
    + if (spi_priv->crc16_enabled)
    + crc_calc = crc_itu_t(crc_calc,
    + skb->data + num_sent, n);
    + num_sent += n;
    + space -= n;
    +
    + if (num_sent >= skb->len) {
    + skb = skb_peek_next(skb, skbs);
    + --num_skbs;
    + num_sent = 0;
    + }
    + } while (space > 0 && num_skbs > 0);
    +
    + /* write optional CRC16 checksum: */
    + if (spi_priv->crc16_enabled) {
    + crc[0] = crc_calc >> 8;
    + crc[1] = crc_calc;
    + wilc_spi_add_xfer(&msg, &xfer, 2, crc, NULL);
    + crc += 2;
    + }
    + }
    + /* last transfer reads the response bytes: */
    + wilc_spi_add_xfer(&msg, &xfer, sizeof(rsp), NULL, rsp);
    +
    + WARN_ON((u8 *)xfer - (u8 *)spi_priv->xfer > sizeof(spi_priv->xfer));
    + WARN_ON(crc - spi_priv->crc > sizeof(spi_priv->crc));
    +
    + ret = spi_sync(spi, &msg);
    + if (ret < 0) {
    + dev_err(&spi->dev, "spi_sync() failed: ret=%d\n", ret);
    + return -EINVAL;
    + }
    +
    + /* Check if the chip received the data correctly: */
    + return spi_data_check_rsp(wilc, rsp);
    +}
    +
    /********************************************
    *
    * Bus interfaces
    @@ -1275,6 +1436,7 @@ static const struct wilc_hif_func wilc_hif_spi = {
    .hif_read_size = wilc_spi_read_size,
    .hif_block_tx_ext = wilc_spi_write,
    .hif_block_rx_ext = wilc_spi_read,
    + .hif_sk_buffs_tx = wilc_spi_write_sk_buffs,
    .hif_sync_ext = wilc_spi_sync_ext,
    .hif_reset = wilc_spi_reset,
    };
    --
    2.25.1
    \
     
     \ /
      Last update: 2021-12-23 02:16    [W:2.065 / U:0.012 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site