mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

In MII mode, Tx lines are swapped for port0 and port1, which means
Tx port0 receives data from PRU1 and the Tx port1 receives data from
PRU0. This is an expected hardware behavior and reading the Tx stats
needs to be handled accordingly in the driver. Update the driver to
read Tx stats from the PRU1 for port0 and PRU0 for port1.
Fixes: c1e10d5dc7
("net: ti: icssg-prueth: Add ICSSG Stats")
Signed-off-by: Meghana Malladi <m-malladi@ti.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20250603052904.431203-1-m-malladi@ti.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
95 lines
2.6 KiB
C
95 lines
2.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Texas Instruments ICSSG Ethernet driver
|
|
*
|
|
* Copyright (C) 2018-2021 Texas Instruments Incorporated - https://www.ti.com/
|
|
*
|
|
*/
|
|
|
|
#include "icssg_prueth.h"
|
|
#include "icssg_stats.h"
|
|
#include <linux/regmap.h>
|
|
|
|
#define ICSSG_TX_PACKET_OFFSET 0xA0
|
|
#define ICSSG_TX_BYTE_OFFSET 0xEC
|
|
|
|
static u32 stats_base[] = { 0x54c, /* Slice 0 stats start */
|
|
0xb18, /* Slice 1 stats start */
|
|
};
|
|
|
|
void emac_update_hardware_stats(struct prueth_emac *emac)
|
|
{
|
|
struct prueth *prueth = emac->prueth;
|
|
int slice = prueth_emac_slice(emac);
|
|
u32 base = stats_base[slice];
|
|
u32 tx_pkt_cnt = 0;
|
|
u32 val, reg;
|
|
int i;
|
|
|
|
spin_lock(&prueth->stats_lock);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(icssg_all_miig_stats); i++) {
|
|
/* In MII mode TX lines are swapped inside ICSSG, so read Tx stats
|
|
* from slice1 for port0 and slice0 for port1 to get accurate Tx
|
|
* stats for a given port
|
|
*/
|
|
if (emac->phy_if == PHY_INTERFACE_MODE_MII &&
|
|
icssg_all_miig_stats[i].offset >= ICSSG_TX_PACKET_OFFSET &&
|
|
icssg_all_miig_stats[i].offset <= ICSSG_TX_BYTE_OFFSET)
|
|
base = stats_base[slice ^ 1];
|
|
regmap_read(prueth->miig_rt,
|
|
base + icssg_all_miig_stats[i].offset,
|
|
&val);
|
|
regmap_write(prueth->miig_rt,
|
|
base + icssg_all_miig_stats[i].offset,
|
|
val);
|
|
|
|
if (icssg_all_miig_stats[i].offset == ICSSG_TX_PACKET_OFFSET)
|
|
tx_pkt_cnt = val;
|
|
|
|
emac->stats[i] += val;
|
|
if (icssg_all_miig_stats[i].offset == ICSSG_TX_BYTE_OFFSET)
|
|
emac->stats[i] -= tx_pkt_cnt * 8;
|
|
}
|
|
|
|
if (prueth->pa_stats) {
|
|
for (i = 0; i < ARRAY_SIZE(icssg_all_pa_stats); i++) {
|
|
reg = icssg_all_pa_stats[i].offset +
|
|
slice * sizeof(u32);
|
|
regmap_read(prueth->pa_stats, reg, &val);
|
|
emac->pa_stats[i] += val;
|
|
}
|
|
}
|
|
|
|
spin_unlock(&prueth->stats_lock);
|
|
}
|
|
|
|
void icssg_stats_work_handler(struct work_struct *work)
|
|
{
|
|
struct prueth_emac *emac = container_of(work, struct prueth_emac,
|
|
stats_work.work);
|
|
emac_update_hardware_stats(emac);
|
|
|
|
queue_delayed_work(system_long_wq, &emac->stats_work,
|
|
msecs_to_jiffies((STATS_TIME_LIMIT_1G_MS * 1000) / emac->speed));
|
|
}
|
|
EXPORT_SYMBOL_GPL(icssg_stats_work_handler);
|
|
|
|
int emac_get_stat_by_name(struct prueth_emac *emac, char *stat_name)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(icssg_all_miig_stats); i++) {
|
|
if (!strcmp(icssg_all_miig_stats[i].name, stat_name))
|
|
return emac->stats[icssg_all_miig_stats[i].offset / sizeof(u32)];
|
|
}
|
|
|
|
if (emac->prueth->pa_stats) {
|
|
for (i = 0; i < ARRAY_SIZE(icssg_all_pa_stats); i++) {
|
|
if (!strcmp(icssg_all_pa_stats[i].name, stat_name))
|
|
return emac->pa_stats[i];
|
|
}
|
|
}
|
|
|
|
netdev_err(emac->ndev, "Invalid stats %s\n", stat_name);
|
|
return -EINVAL;
|
|
}
|