Commit 1e846c7a authored by Marc Kleine-Budde's avatar Marc Kleine-Budde

can: mcp251xfd: move TEF handling into separate file

This patch moves the TEF handling from the mcp251xfd core file into a
separate one to make the driver a bit more orderly.

Link: https://lore.kernel.org/all/20220105154300.1258636-11-mkl@pengutronix.deSigned-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent 09b0eb92
......@@ -7,6 +7,7 @@ mcp251xfd-objs += mcp251xfd-core.o
mcp251xfd-objs += mcp251xfd-crc16.o
mcp251xfd-objs += mcp251xfd-regmap.o
mcp251xfd-objs += mcp251xfd-rx.o
mcp251xfd-objs += mcp251xfd-tef.o
mcp251xfd-objs += mcp251xfd-timestamp.o
mcp251xfd-objs += mcp251xfd-tx.o
......
......@@ -216,40 +216,6 @@ mcp251xfd_cmd_prepare_write_reg(const struct mcp251xfd_priv *priv,
return len;
}
static inline int
mcp251xfd_tef_tail_get_from_chip(const struct mcp251xfd_priv *priv,
u8 *tef_tail)
{
u32 tef_ua;
int err;
err = regmap_read(priv->map_reg, MCP251XFD_REG_TEFUA, &tef_ua);
if (err)
return err;
*tef_tail = tef_ua / sizeof(struct mcp251xfd_hw_tef_obj);
return 0;
}
static inline int
mcp251xfd_tx_tail_get_from_chip(const struct mcp251xfd_priv *priv,
u8 *tx_tail)
{
u32 fifo_sta;
int err;
err = regmap_read(priv->map_reg,
MCP251XFD_REG_FIFOSTA(MCP251XFD_TX_FIFO),
&fifo_sta);
if (err)
return err;
*tx_tail = FIELD_GET(MCP251XFD_REG_FIFOSTA_FIFOCI_MASK, fifo_sta);
return 0;
}
static void
mcp251xfd_tx_ring_init_tx_obj(const struct mcp251xfd_priv *priv,
const struct mcp251xfd_tx_ring *ring,
......@@ -931,13 +897,6 @@ static int mcp251xfd_chip_ecc_init(struct mcp251xfd_priv *priv)
return err;
}
static inline void mcp251xfd_ecc_tefif_successful(struct mcp251xfd_priv *priv)
{
struct mcp251xfd_ecc *ecc = &priv->ecc;
ecc->ecc_stat = 0;
}
static u8 mcp251xfd_get_normal_mode(const struct mcp251xfd_priv *priv)
{
u8 mode;
......@@ -1148,226 +1107,6 @@ static int mcp251xfd_get_berr_counter(const struct net_device *ndev,
return __mcp251xfd_get_berr_counter(ndev, bec);
}
static int mcp251xfd_check_tef_tail(const struct mcp251xfd_priv *priv)
{
u8 tef_tail_chip, tef_tail;
int err;
if (!IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY))
return 0;
err = mcp251xfd_tef_tail_get_from_chip(priv, &tef_tail_chip);
if (err)
return err;
tef_tail = mcp251xfd_get_tef_tail(priv);
if (tef_tail_chip != tef_tail) {
netdev_err(priv->ndev,
"TEF tail of chip (0x%02x) and ours (0x%08x) inconsistent.\n",
tef_tail_chip, tef_tail);
return -EILSEQ;
}
return 0;
}
static int
mcp251xfd_handle_tefif_recover(const struct mcp251xfd_priv *priv, const u32 seq)
{
const struct mcp251xfd_tx_ring *tx_ring = priv->tx;
u32 tef_sta;
int err;
err = regmap_read(priv->map_reg, MCP251XFD_REG_TEFSTA, &tef_sta);
if (err)
return err;
if (tef_sta & MCP251XFD_REG_TEFSTA_TEFOVIF) {
netdev_err(priv->ndev,
"Transmit Event FIFO buffer overflow.\n");
return -ENOBUFS;
}
netdev_info(priv->ndev,
"Transmit Event FIFO buffer %s. (seq=0x%08x, tef_tail=0x%08x, tef_head=0x%08x, tx_head=0x%08x).\n",
tef_sta & MCP251XFD_REG_TEFSTA_TEFFIF ?
"full" : tef_sta & MCP251XFD_REG_TEFSTA_TEFNEIF ?
"not empty" : "empty",
seq, priv->tef->tail, priv->tef->head, tx_ring->head);
/* The Sequence Number in the TEF doesn't match our tef_tail. */
return -EAGAIN;
}
static int
mcp251xfd_handle_tefif_one(struct mcp251xfd_priv *priv,
const struct mcp251xfd_hw_tef_obj *hw_tef_obj,
unsigned int *frame_len_ptr)
{
struct net_device_stats *stats = &priv->ndev->stats;
struct sk_buff *skb;
u32 seq, seq_masked, tef_tail_masked, tef_tail;
seq = FIELD_GET(MCP251XFD_OBJ_FLAGS_SEQ_MCP2518FD_MASK,
hw_tef_obj->flags);
/* Use the MCP2517FD mask on the MCP2518FD, too. We only
* compare 7 bits, this should be enough to detect
* net-yet-completed, i.e. old TEF objects.
*/
seq_masked = seq &
field_mask(MCP251XFD_OBJ_FLAGS_SEQ_MCP2517FD_MASK);
tef_tail_masked = priv->tef->tail &
field_mask(MCP251XFD_OBJ_FLAGS_SEQ_MCP2517FD_MASK);
if (seq_masked != tef_tail_masked)
return mcp251xfd_handle_tefif_recover(priv, seq);
tef_tail = mcp251xfd_get_tef_tail(priv);
skb = priv->can.echo_skb[tef_tail];
if (skb)
mcp251xfd_skb_set_timestamp(priv, skb, hw_tef_obj->ts);
stats->tx_bytes +=
can_rx_offload_get_echo_skb(&priv->offload,
tef_tail, hw_tef_obj->ts,
frame_len_ptr);
stats->tx_packets++;
priv->tef->tail++;
return 0;
}
static int mcp251xfd_tef_ring_update(struct mcp251xfd_priv *priv)
{
const struct mcp251xfd_tx_ring *tx_ring = priv->tx;
unsigned int new_head;
u8 chip_tx_tail;
int err;
err = mcp251xfd_tx_tail_get_from_chip(priv, &chip_tx_tail);
if (err)
return err;
/* chip_tx_tail, is the next TX-Object send by the HW.
* The new TEF head must be >= the old head, ...
*/
new_head = round_down(priv->tef->head, tx_ring->obj_num) + chip_tx_tail;
if (new_head <= priv->tef->head)
new_head += tx_ring->obj_num;
/* ... but it cannot exceed the TX head. */
priv->tef->head = min(new_head, tx_ring->head);
return mcp251xfd_check_tef_tail(priv);
}
static inline int
mcp251xfd_tef_obj_read(const struct mcp251xfd_priv *priv,
struct mcp251xfd_hw_tef_obj *hw_tef_obj,
const u8 offset, const u8 len)
{
const struct mcp251xfd_tx_ring *tx_ring = priv->tx;
const int val_bytes = regmap_get_val_bytes(priv->map_rx);
if (IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY) &&
(offset > tx_ring->obj_num ||
len > tx_ring->obj_num ||
offset + len > tx_ring->obj_num)) {
netdev_err(priv->ndev,
"Trying to read too many TEF objects (max=%d, offset=%d, len=%d).\n",
tx_ring->obj_num, offset, len);
return -ERANGE;
}
return regmap_bulk_read(priv->map_rx,
mcp251xfd_get_tef_obj_addr(offset),
hw_tef_obj,
sizeof(*hw_tef_obj) / val_bytes * len);
}
static int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv)
{
struct mcp251xfd_hw_tef_obj hw_tef_obj[MCP251XFD_TX_OBJ_NUM_MAX];
unsigned int total_frame_len = 0;
u8 tef_tail, len, l;
int err, i;
err = mcp251xfd_tef_ring_update(priv);
if (err)
return err;
tef_tail = mcp251xfd_get_tef_tail(priv);
len = mcp251xfd_get_tef_len(priv);
l = mcp251xfd_get_tef_linear_len(priv);
err = mcp251xfd_tef_obj_read(priv, hw_tef_obj, tef_tail, l);
if (err)
return err;
if (l < len) {
err = mcp251xfd_tef_obj_read(priv, &hw_tef_obj[l], 0, len - l);
if (err)
return err;
}
for (i = 0; i < len; i++) {
unsigned int frame_len = 0;
err = mcp251xfd_handle_tefif_one(priv, &hw_tef_obj[i], &frame_len);
/* -EAGAIN means the Sequence Number in the TEF
* doesn't match our tef_tail. This can happen if we
* read the TEF objects too early. Leave loop let the
* interrupt handler call us again.
*/
if (err == -EAGAIN)
goto out_netif_wake_queue;
if (err)
return err;
total_frame_len += frame_len;
}
out_netif_wake_queue:
len = i; /* number of handled goods TEFs */
if (len) {
struct mcp251xfd_tef_ring *ring = priv->tef;
struct mcp251xfd_tx_ring *tx_ring = priv->tx;
int offset;
/* Increment the TEF FIFO tail pointer 'len' times in
* a single SPI message.
*
* Note:
* Calculate offset, so that the SPI transfer ends on
* the last message of the uinc_xfer array, which has
* "cs_change == 0", to properly deactivate the chip
* select.
*/
offset = ARRAY_SIZE(ring->uinc_xfer) - len;
err = spi_sync_transfer(priv->spi,
ring->uinc_xfer + offset, len);
if (err)
return err;
tx_ring->tail += len;
netdev_completed_queue(priv->ndev, len, total_frame_len);
err = mcp251xfd_check_tef_tail(priv);
if (err)
return err;
}
mcp251xfd_ecc_tefif_successful(priv);
if (mcp251xfd_get_tx_free(priv->tx)) {
/* Make sure that anybody stopping the queue after
* this sees the new tx_ring->tail.
*/
smp_mb();
netif_wake_queue(priv->ndev);
}
return 0;
}
static struct sk_buff *
mcp251xfd_alloc_can_err_skb(struct mcp251xfd_priv *priv,
struct can_frame **cf, u32 *timestamp)
......
// SPDX-License-Identifier: GPL-2.0
//
// mcp251xfd - Microchip MCP251xFD Family CAN controller driver
//
// Copyright (c) 2019, 2020, 2021 Pengutronix,
// Marc Kleine-Budde <kernel@pengutronix.de>
//
// Based on:
//
// CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
//
// Copyright (c) 2019 Martin Sperl <kernel@martin.sperl.org>
//
#include <linux/bitfield.h>
#include "mcp251xfd.h"
static inline int
mcp251xfd_tef_tail_get_from_chip(const struct mcp251xfd_priv *priv,
u8 *tef_tail)
{
u32 tef_ua;
int err;
err = regmap_read(priv->map_reg, MCP251XFD_REG_TEFUA, &tef_ua);
if (err)
return err;
*tef_tail = tef_ua / sizeof(struct mcp251xfd_hw_tef_obj);
return 0;
}
static int mcp251xfd_check_tef_tail(const struct mcp251xfd_priv *priv)
{
u8 tef_tail_chip, tef_tail;
int err;
if (!IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY))
return 0;
err = mcp251xfd_tef_tail_get_from_chip(priv, &tef_tail_chip);
if (err)
return err;
tef_tail = mcp251xfd_get_tef_tail(priv);
if (tef_tail_chip != tef_tail) {
netdev_err(priv->ndev,
"TEF tail of chip (0x%02x) and ours (0x%08x) inconsistent.\n",
tef_tail_chip, tef_tail);
return -EILSEQ;
}
return 0;
}
static int
mcp251xfd_handle_tefif_recover(const struct mcp251xfd_priv *priv, const u32 seq)
{
const struct mcp251xfd_tx_ring *tx_ring = priv->tx;
u32 tef_sta;
int err;
err = regmap_read(priv->map_reg, MCP251XFD_REG_TEFSTA, &tef_sta);
if (err)
return err;
if (tef_sta & MCP251XFD_REG_TEFSTA_TEFOVIF) {
netdev_err(priv->ndev,
"Transmit Event FIFO buffer overflow.\n");
return -ENOBUFS;
}
netdev_info(priv->ndev,
"Transmit Event FIFO buffer %s. (seq=0x%08x, tef_tail=0x%08x, tef_head=0x%08x, tx_head=0x%08x).\n",
tef_sta & MCP251XFD_REG_TEFSTA_TEFFIF ?
"full" : tef_sta & MCP251XFD_REG_TEFSTA_TEFNEIF ?
"not empty" : "empty",
seq, priv->tef->tail, priv->tef->head, tx_ring->head);
/* The Sequence Number in the TEF doesn't match our tef_tail. */
return -EAGAIN;
}
static int
mcp251xfd_handle_tefif_one(struct mcp251xfd_priv *priv,
const struct mcp251xfd_hw_tef_obj *hw_tef_obj,
unsigned int *frame_len_ptr)
{
struct net_device_stats *stats = &priv->ndev->stats;
struct sk_buff *skb;
u32 seq, seq_masked, tef_tail_masked, tef_tail;
seq = FIELD_GET(MCP251XFD_OBJ_FLAGS_SEQ_MCP2518FD_MASK,
hw_tef_obj->flags);
/* Use the MCP2517FD mask on the MCP2518FD, too. We only
* compare 7 bits, this should be enough to detect
* net-yet-completed, i.e. old TEF objects.
*/
seq_masked = seq &
field_mask(MCP251XFD_OBJ_FLAGS_SEQ_MCP2517FD_MASK);
tef_tail_masked = priv->tef->tail &
field_mask(MCP251XFD_OBJ_FLAGS_SEQ_MCP2517FD_MASK);
if (seq_masked != tef_tail_masked)
return mcp251xfd_handle_tefif_recover(priv, seq);
tef_tail = mcp251xfd_get_tef_tail(priv);
skb = priv->can.echo_skb[tef_tail];
if (skb)
mcp251xfd_skb_set_timestamp(priv, skb, hw_tef_obj->ts);
stats->tx_bytes +=
can_rx_offload_get_echo_skb(&priv->offload,
tef_tail, hw_tef_obj->ts,
frame_len_ptr);
stats->tx_packets++;
priv->tef->tail++;
return 0;
}
static int mcp251xfd_tef_ring_update(struct mcp251xfd_priv *priv)
{
const struct mcp251xfd_tx_ring *tx_ring = priv->tx;
unsigned int new_head;
u8 chip_tx_tail;
int err;
err = mcp251xfd_tx_tail_get_from_chip(priv, &chip_tx_tail);
if (err)
return err;
/* chip_tx_tail, is the next TX-Object send by the HW.
* The new TEF head must be >= the old head, ...
*/
new_head = round_down(priv->tef->head, tx_ring->obj_num) + chip_tx_tail;
if (new_head <= priv->tef->head)
new_head += tx_ring->obj_num;
/* ... but it cannot exceed the TX head. */
priv->tef->head = min(new_head, tx_ring->head);
return mcp251xfd_check_tef_tail(priv);
}
static inline int
mcp251xfd_tef_obj_read(const struct mcp251xfd_priv *priv,
struct mcp251xfd_hw_tef_obj *hw_tef_obj,
const u8 offset, const u8 len)
{
const struct mcp251xfd_tx_ring *tx_ring = priv->tx;
const int val_bytes = regmap_get_val_bytes(priv->map_rx);
if (IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY) &&
(offset > tx_ring->obj_num ||
len > tx_ring->obj_num ||
offset + len > tx_ring->obj_num)) {
netdev_err(priv->ndev,
"Trying to read too many TEF objects (max=%d, offset=%d, len=%d).\n",
tx_ring->obj_num, offset, len);
return -ERANGE;
}
return regmap_bulk_read(priv->map_rx,
mcp251xfd_get_tef_obj_addr(offset),
hw_tef_obj,
sizeof(*hw_tef_obj) / val_bytes * len);
}
static inline void mcp251xfd_ecc_tefif_successful(struct mcp251xfd_priv *priv)
{
struct mcp251xfd_ecc *ecc = &priv->ecc;
ecc->ecc_stat = 0;
}
int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv)
{
struct mcp251xfd_hw_tef_obj hw_tef_obj[MCP251XFD_TX_OBJ_NUM_MAX];
unsigned int total_frame_len = 0;
u8 tef_tail, len, l;
int err, i;
err = mcp251xfd_tef_ring_update(priv);
if (err)
return err;
tef_tail = mcp251xfd_get_tef_tail(priv);
len = mcp251xfd_get_tef_len(priv);
l = mcp251xfd_get_tef_linear_len(priv);
err = mcp251xfd_tef_obj_read(priv, hw_tef_obj, tef_tail, l);
if (err)
return err;
if (l < len) {
err = mcp251xfd_tef_obj_read(priv, &hw_tef_obj[l], 0, len - l);
if (err)
return err;
}
for (i = 0; i < len; i++) {
unsigned int frame_len = 0;
err = mcp251xfd_handle_tefif_one(priv, &hw_tef_obj[i], &frame_len);
/* -EAGAIN means the Sequence Number in the TEF
* doesn't match our tef_tail. This can happen if we
* read the TEF objects too early. Leave loop let the
* interrupt handler call us again.
*/
if (err == -EAGAIN)
goto out_netif_wake_queue;
if (err)
return err;
total_frame_len += frame_len;
}
out_netif_wake_queue:
len = i; /* number of handled goods TEFs */
if (len) {
struct mcp251xfd_tef_ring *ring = priv->tef;
struct mcp251xfd_tx_ring *tx_ring = priv->tx;
int offset;
/* Increment the TEF FIFO tail pointer 'len' times in
* a single SPI message.
*
* Note:
* Calculate offset, so that the SPI transfer ends on
* the last message of the uinc_xfer array, which has
* "cs_change == 0", to properly deactivate the chip
* select.
*/
offset = ARRAY_SIZE(ring->uinc_xfer) - len;
err = spi_sync_transfer(priv->spi,
ring->uinc_xfer + offset, len);
if (err)
return err;
tx_ring->tail += len;
netdev_completed_queue(priv->ndev, len, total_frame_len);
err = mcp251xfd_check_tef_tail(priv);
if (err)
return err;
}
mcp251xfd_ecc_tefif_successful(priv);
if (mcp251xfd_get_tx_free(priv->tx)) {
/* Make sure that anybody stopping the queue after
* this sees the new tx_ring->tail.
*/
smp_mb();
netif_wake_queue(priv->ndev);
}
return 0;
}
......@@ -10,6 +10,7 @@
#ifndef _MCP251XFD_H
#define _MCP251XFD_H
#include <linux/bitfield.h>
#include <linux/can/core.h>
#include <linux/can/dev.h>
#include <linux/can/rx-offload.h>
......@@ -761,6 +762,24 @@ mcp251xfd_get_rx_obj_addr(const struct mcp251xfd_rx_ring *ring, u8 n)
return ring->base + ring->obj_size * n;
}
static inline int
mcp251xfd_tx_tail_get_from_chip(const struct mcp251xfd_priv *priv,
u8 *tx_tail)
{
u32 fifo_sta;
int err;
err = regmap_read(priv->map_reg,
MCP251XFD_REG_FIFOSTA(MCP251XFD_TX_FIFO),
&fifo_sta);
if (err)
return err;
*tx_tail = FIELD_GET(MCP251XFD_REG_FIFOSTA_FIFOCI_MASK, fifo_sta);
return 0;
}
static inline u8 mcp251xfd_get_tef_head(const struct mcp251xfd_priv *priv)
{
return priv->tef->head & (priv->tx->obj_num - 1);
......@@ -854,6 +873,7 @@ u16 mcp251xfd_crc16_compute2(const void *cmd, size_t cmd_size,
u16 mcp251xfd_crc16_compute(const void *data, size_t data_size);
int mcp251xfd_regmap_init(struct mcp251xfd_priv *priv);
int mcp251xfd_handle_rxif(struct mcp251xfd_priv *priv);
int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv);
void mcp251xfd_skb_set_timestamp(const struct mcp251xfd_priv *priv,
struct sk_buff *skb, u32 timestamp);
void mcp251xfd_timestamp_init(struct mcp251xfd_priv *priv);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment