mirror of
https://github.com/torvalds/linux.git
synced 2024-12-24 20:01:55 +00:00
1bd4487038
The most commonly called function in the driver is long due for a rename. The "packed" word is redundant (it doesn't make sense to transfer an unpacked structure, since that is in CPU endianness yadda yadda), and the "spi" word is also redundant since argument 2 of the function is SPI_READ or SPI_WRITE. As for the sja1105_spi_send_long_packed_buf function, it is only being used from sja1105_spi.c, so remove its global prototype. Signed-off-by: Vladimir Oltean <olteanv@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
420 lines
11 KiB
C
420 lines
11 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com>
|
|
*/
|
|
#include "sja1105.h"
|
|
|
|
#define SJA1105_SIZE_MAC_AREA (0x02 * 4)
|
|
#define SJA1105_SIZE_HL1_AREA (0x10 * 4)
|
|
#define SJA1105_SIZE_HL2_AREA (0x4 * 4)
|
|
#define SJA1105_SIZE_QLEVEL_AREA (0x8 * 4) /* 0x4 to 0xB */
|
|
|
|
struct sja1105_port_status_mac {
|
|
u64 n_runt;
|
|
u64 n_soferr;
|
|
u64 n_alignerr;
|
|
u64 n_miierr;
|
|
u64 typeerr;
|
|
u64 sizeerr;
|
|
u64 tctimeout;
|
|
u64 priorerr;
|
|
u64 nomaster;
|
|
u64 memov;
|
|
u64 memerr;
|
|
u64 invtyp;
|
|
u64 intcyov;
|
|
u64 domerr;
|
|
u64 pcfbagdrop;
|
|
u64 spcprior;
|
|
u64 ageprior;
|
|
u64 portdrop;
|
|
u64 lendrop;
|
|
u64 bagdrop;
|
|
u64 policeerr;
|
|
u64 drpnona664err;
|
|
u64 spcerr;
|
|
u64 agedrp;
|
|
};
|
|
|
|
struct sja1105_port_status_hl1 {
|
|
u64 n_n664err;
|
|
u64 n_vlanerr;
|
|
u64 n_unreleased;
|
|
u64 n_sizeerr;
|
|
u64 n_crcerr;
|
|
u64 n_vlnotfound;
|
|
u64 n_ctpolerr;
|
|
u64 n_polerr;
|
|
u64 n_rxfrmsh;
|
|
u64 n_rxfrm;
|
|
u64 n_rxbytesh;
|
|
u64 n_rxbyte;
|
|
u64 n_txfrmsh;
|
|
u64 n_txfrm;
|
|
u64 n_txbytesh;
|
|
u64 n_txbyte;
|
|
};
|
|
|
|
struct sja1105_port_status_hl2 {
|
|
u64 n_qfull;
|
|
u64 n_part_drop;
|
|
u64 n_egr_disabled;
|
|
u64 n_not_reach;
|
|
u64 qlevel_hwm[8]; /* Only for P/Q/R/S */
|
|
u64 qlevel[8]; /* Only for P/Q/R/S */
|
|
};
|
|
|
|
struct sja1105_port_status {
|
|
struct sja1105_port_status_mac mac;
|
|
struct sja1105_port_status_hl1 hl1;
|
|
struct sja1105_port_status_hl2 hl2;
|
|
};
|
|
|
|
static void
|
|
sja1105_port_status_mac_unpack(void *buf,
|
|
struct sja1105_port_status_mac *status)
|
|
{
|
|
/* Make pointer arithmetic work on 4 bytes */
|
|
u32 *p = buf;
|
|
|
|
sja1105_unpack(p + 0x0, &status->n_runt, 31, 24, 4);
|
|
sja1105_unpack(p + 0x0, &status->n_soferr, 23, 16, 4);
|
|
sja1105_unpack(p + 0x0, &status->n_alignerr, 15, 8, 4);
|
|
sja1105_unpack(p + 0x0, &status->n_miierr, 7, 0, 4);
|
|
sja1105_unpack(p + 0x1, &status->typeerr, 27, 27, 4);
|
|
sja1105_unpack(p + 0x1, &status->sizeerr, 26, 26, 4);
|
|
sja1105_unpack(p + 0x1, &status->tctimeout, 25, 25, 4);
|
|
sja1105_unpack(p + 0x1, &status->priorerr, 24, 24, 4);
|
|
sja1105_unpack(p + 0x1, &status->nomaster, 23, 23, 4);
|
|
sja1105_unpack(p + 0x1, &status->memov, 22, 22, 4);
|
|
sja1105_unpack(p + 0x1, &status->memerr, 21, 21, 4);
|
|
sja1105_unpack(p + 0x1, &status->invtyp, 19, 19, 4);
|
|
sja1105_unpack(p + 0x1, &status->intcyov, 18, 18, 4);
|
|
sja1105_unpack(p + 0x1, &status->domerr, 17, 17, 4);
|
|
sja1105_unpack(p + 0x1, &status->pcfbagdrop, 16, 16, 4);
|
|
sja1105_unpack(p + 0x1, &status->spcprior, 15, 12, 4);
|
|
sja1105_unpack(p + 0x1, &status->ageprior, 11, 8, 4);
|
|
sja1105_unpack(p + 0x1, &status->portdrop, 6, 6, 4);
|
|
sja1105_unpack(p + 0x1, &status->lendrop, 5, 5, 4);
|
|
sja1105_unpack(p + 0x1, &status->bagdrop, 4, 4, 4);
|
|
sja1105_unpack(p + 0x1, &status->policeerr, 3, 3, 4);
|
|
sja1105_unpack(p + 0x1, &status->drpnona664err, 2, 2, 4);
|
|
sja1105_unpack(p + 0x1, &status->spcerr, 1, 1, 4);
|
|
sja1105_unpack(p + 0x1, &status->agedrp, 0, 0, 4);
|
|
}
|
|
|
|
static void
|
|
sja1105_port_status_hl1_unpack(void *buf,
|
|
struct sja1105_port_status_hl1 *status)
|
|
{
|
|
/* Make pointer arithmetic work on 4 bytes */
|
|
u32 *p = buf;
|
|
|
|
sja1105_unpack(p + 0xF, &status->n_n664err, 31, 0, 4);
|
|
sja1105_unpack(p + 0xE, &status->n_vlanerr, 31, 0, 4);
|
|
sja1105_unpack(p + 0xD, &status->n_unreleased, 31, 0, 4);
|
|
sja1105_unpack(p + 0xC, &status->n_sizeerr, 31, 0, 4);
|
|
sja1105_unpack(p + 0xB, &status->n_crcerr, 31, 0, 4);
|
|
sja1105_unpack(p + 0xA, &status->n_vlnotfound, 31, 0, 4);
|
|
sja1105_unpack(p + 0x9, &status->n_ctpolerr, 31, 0, 4);
|
|
sja1105_unpack(p + 0x8, &status->n_polerr, 31, 0, 4);
|
|
sja1105_unpack(p + 0x7, &status->n_rxfrmsh, 31, 0, 4);
|
|
sja1105_unpack(p + 0x6, &status->n_rxfrm, 31, 0, 4);
|
|
sja1105_unpack(p + 0x5, &status->n_rxbytesh, 31, 0, 4);
|
|
sja1105_unpack(p + 0x4, &status->n_rxbyte, 31, 0, 4);
|
|
sja1105_unpack(p + 0x3, &status->n_txfrmsh, 31, 0, 4);
|
|
sja1105_unpack(p + 0x2, &status->n_txfrm, 31, 0, 4);
|
|
sja1105_unpack(p + 0x1, &status->n_txbytesh, 31, 0, 4);
|
|
sja1105_unpack(p + 0x0, &status->n_txbyte, 31, 0, 4);
|
|
status->n_rxfrm += status->n_rxfrmsh << 32;
|
|
status->n_rxbyte += status->n_rxbytesh << 32;
|
|
status->n_txfrm += status->n_txfrmsh << 32;
|
|
status->n_txbyte += status->n_txbytesh << 32;
|
|
}
|
|
|
|
static void
|
|
sja1105_port_status_hl2_unpack(void *buf,
|
|
struct sja1105_port_status_hl2 *status)
|
|
{
|
|
/* Make pointer arithmetic work on 4 bytes */
|
|
u32 *p = buf;
|
|
|
|
sja1105_unpack(p + 0x3, &status->n_qfull, 31, 0, 4);
|
|
sja1105_unpack(p + 0x2, &status->n_part_drop, 31, 0, 4);
|
|
sja1105_unpack(p + 0x1, &status->n_egr_disabled, 31, 0, 4);
|
|
sja1105_unpack(p + 0x0, &status->n_not_reach, 31, 0, 4);
|
|
}
|
|
|
|
static void
|
|
sja1105pqrs_port_status_qlevel_unpack(void *buf,
|
|
struct sja1105_port_status_hl2 *status)
|
|
{
|
|
/* Make pointer arithmetic work on 4 bytes */
|
|
u32 *p = buf;
|
|
int i;
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
sja1105_unpack(p + i, &status->qlevel_hwm[i], 24, 16, 4);
|
|
sja1105_unpack(p + i, &status->qlevel[i], 8, 0, 4);
|
|
}
|
|
}
|
|
|
|
static int sja1105_port_status_get_mac(struct sja1105_private *priv,
|
|
struct sja1105_port_status_mac *status,
|
|
int port)
|
|
{
|
|
const struct sja1105_regs *regs = priv->info->regs;
|
|
u8 packed_buf[SJA1105_SIZE_MAC_AREA] = {0};
|
|
int rc;
|
|
|
|
/* MAC area */
|
|
rc = sja1105_xfer_buf(priv, SPI_READ, regs->mac[port], packed_buf,
|
|
SJA1105_SIZE_MAC_AREA);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
sja1105_port_status_mac_unpack(packed_buf, status);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sja1105_port_status_get_hl1(struct sja1105_private *priv,
|
|
struct sja1105_port_status_hl1 *status,
|
|
int port)
|
|
{
|
|
const struct sja1105_regs *regs = priv->info->regs;
|
|
u8 packed_buf[SJA1105_SIZE_HL1_AREA] = {0};
|
|
int rc;
|
|
|
|
rc = sja1105_xfer_buf(priv, SPI_READ, regs->mac_hl1[port], packed_buf,
|
|
SJA1105_SIZE_HL1_AREA);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
sja1105_port_status_hl1_unpack(packed_buf, status);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sja1105_port_status_get_hl2(struct sja1105_private *priv,
|
|
struct sja1105_port_status_hl2 *status,
|
|
int port)
|
|
{
|
|
const struct sja1105_regs *regs = priv->info->regs;
|
|
u8 packed_buf[SJA1105_SIZE_QLEVEL_AREA] = {0};
|
|
int rc;
|
|
|
|
rc = sja1105_xfer_buf(priv, SPI_READ, regs->mac_hl2[port], packed_buf,
|
|
SJA1105_SIZE_HL2_AREA);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
sja1105_port_status_hl2_unpack(packed_buf, status);
|
|
|
|
/* Code below is strictly P/Q/R/S specific. */
|
|
if (priv->info->device_id == SJA1105E_DEVICE_ID ||
|
|
priv->info->device_id == SJA1105T_DEVICE_ID)
|
|
return 0;
|
|
|
|
rc = sja1105_xfer_buf(priv, SPI_READ, regs->qlevel[port], packed_buf,
|
|
SJA1105_SIZE_QLEVEL_AREA);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
sja1105pqrs_port_status_qlevel_unpack(packed_buf, status);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sja1105_port_status_get(struct sja1105_private *priv,
|
|
struct sja1105_port_status *status,
|
|
int port)
|
|
{
|
|
int rc;
|
|
|
|
rc = sja1105_port_status_get_mac(priv, &status->mac, port);
|
|
if (rc < 0)
|
|
return rc;
|
|
rc = sja1105_port_status_get_hl1(priv, &status->hl1, port);
|
|
if (rc < 0)
|
|
return rc;
|
|
rc = sja1105_port_status_get_hl2(priv, &status->hl2, port);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static char sja1105_port_stats[][ETH_GSTRING_LEN] = {
|
|
/* MAC-Level Diagnostic Counters */
|
|
"n_runt",
|
|
"n_soferr",
|
|
"n_alignerr",
|
|
"n_miierr",
|
|
/* MAC-Level Diagnostic Flags */
|
|
"typeerr",
|
|
"sizeerr",
|
|
"tctimeout",
|
|
"priorerr",
|
|
"nomaster",
|
|
"memov",
|
|
"memerr",
|
|
"invtyp",
|
|
"intcyov",
|
|
"domerr",
|
|
"pcfbagdrop",
|
|
"spcprior",
|
|
"ageprior",
|
|
"portdrop",
|
|
"lendrop",
|
|
"bagdrop",
|
|
"policeerr",
|
|
"drpnona664err",
|
|
"spcerr",
|
|
"agedrp",
|
|
/* High-Level Diagnostic Counters */
|
|
"n_n664err",
|
|
"n_vlanerr",
|
|
"n_unreleased",
|
|
"n_sizeerr",
|
|
"n_crcerr",
|
|
"n_vlnotfound",
|
|
"n_ctpolerr",
|
|
"n_polerr",
|
|
"n_rxfrm",
|
|
"n_rxbyte",
|
|
"n_txfrm",
|
|
"n_txbyte",
|
|
"n_qfull",
|
|
"n_part_drop",
|
|
"n_egr_disabled",
|
|
"n_not_reach",
|
|
};
|
|
|
|
static char sja1105pqrs_extra_port_stats[][ETH_GSTRING_LEN] = {
|
|
/* Queue Levels */
|
|
"qlevel_hwm_0",
|
|
"qlevel_hwm_1",
|
|
"qlevel_hwm_2",
|
|
"qlevel_hwm_3",
|
|
"qlevel_hwm_4",
|
|
"qlevel_hwm_5",
|
|
"qlevel_hwm_6",
|
|
"qlevel_hwm_7",
|
|
"qlevel_0",
|
|
"qlevel_1",
|
|
"qlevel_2",
|
|
"qlevel_3",
|
|
"qlevel_4",
|
|
"qlevel_5",
|
|
"qlevel_6",
|
|
"qlevel_7",
|
|
};
|
|
|
|
void sja1105_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data)
|
|
{
|
|
struct sja1105_private *priv = ds->priv;
|
|
struct sja1105_port_status status;
|
|
int rc, i, k = 0;
|
|
|
|
memset(&status, 0, sizeof(status));
|
|
|
|
rc = sja1105_port_status_get(priv, &status, port);
|
|
if (rc < 0) {
|
|
dev_err(ds->dev, "Failed to read port %d counters: %d\n",
|
|
port, rc);
|
|
return;
|
|
}
|
|
memset(data, 0, ARRAY_SIZE(sja1105_port_stats) * sizeof(u64));
|
|
data[k++] = status.mac.n_runt;
|
|
data[k++] = status.mac.n_soferr;
|
|
data[k++] = status.mac.n_alignerr;
|
|
data[k++] = status.mac.n_miierr;
|
|
data[k++] = status.mac.typeerr;
|
|
data[k++] = status.mac.sizeerr;
|
|
data[k++] = status.mac.tctimeout;
|
|
data[k++] = status.mac.priorerr;
|
|
data[k++] = status.mac.nomaster;
|
|
data[k++] = status.mac.memov;
|
|
data[k++] = status.mac.memerr;
|
|
data[k++] = status.mac.invtyp;
|
|
data[k++] = status.mac.intcyov;
|
|
data[k++] = status.mac.domerr;
|
|
data[k++] = status.mac.pcfbagdrop;
|
|
data[k++] = status.mac.spcprior;
|
|
data[k++] = status.mac.ageprior;
|
|
data[k++] = status.mac.portdrop;
|
|
data[k++] = status.mac.lendrop;
|
|
data[k++] = status.mac.bagdrop;
|
|
data[k++] = status.mac.policeerr;
|
|
data[k++] = status.mac.drpnona664err;
|
|
data[k++] = status.mac.spcerr;
|
|
data[k++] = status.mac.agedrp;
|
|
data[k++] = status.hl1.n_n664err;
|
|
data[k++] = status.hl1.n_vlanerr;
|
|
data[k++] = status.hl1.n_unreleased;
|
|
data[k++] = status.hl1.n_sizeerr;
|
|
data[k++] = status.hl1.n_crcerr;
|
|
data[k++] = status.hl1.n_vlnotfound;
|
|
data[k++] = status.hl1.n_ctpolerr;
|
|
data[k++] = status.hl1.n_polerr;
|
|
data[k++] = status.hl1.n_rxfrm;
|
|
data[k++] = status.hl1.n_rxbyte;
|
|
data[k++] = status.hl1.n_txfrm;
|
|
data[k++] = status.hl1.n_txbyte;
|
|
data[k++] = status.hl2.n_qfull;
|
|
data[k++] = status.hl2.n_part_drop;
|
|
data[k++] = status.hl2.n_egr_disabled;
|
|
data[k++] = status.hl2.n_not_reach;
|
|
|
|
if (priv->info->device_id == SJA1105E_DEVICE_ID ||
|
|
priv->info->device_id == SJA1105T_DEVICE_ID)
|
|
return;
|
|
|
|
memset(data + k, 0, ARRAY_SIZE(sja1105pqrs_extra_port_stats) *
|
|
sizeof(u64));
|
|
for (i = 0; i < 8; i++) {
|
|
data[k++] = status.hl2.qlevel_hwm[i];
|
|
data[k++] = status.hl2.qlevel[i];
|
|
}
|
|
}
|
|
|
|
void sja1105_get_strings(struct dsa_switch *ds, int port,
|
|
u32 stringset, u8 *data)
|
|
{
|
|
struct sja1105_private *priv = ds->priv;
|
|
u8 *p = data;
|
|
int i;
|
|
|
|
switch (stringset) {
|
|
case ETH_SS_STATS:
|
|
for (i = 0; i < ARRAY_SIZE(sja1105_port_stats); i++) {
|
|
strlcpy(p, sja1105_port_stats[i], ETH_GSTRING_LEN);
|
|
p += ETH_GSTRING_LEN;
|
|
}
|
|
if (priv->info->device_id == SJA1105E_DEVICE_ID ||
|
|
priv->info->device_id == SJA1105T_DEVICE_ID)
|
|
return;
|
|
for (i = 0; i < ARRAY_SIZE(sja1105pqrs_extra_port_stats); i++) {
|
|
strlcpy(p, sja1105pqrs_extra_port_stats[i],
|
|
ETH_GSTRING_LEN);
|
|
p += ETH_GSTRING_LEN;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
int sja1105_get_sset_count(struct dsa_switch *ds, int port, int sset)
|
|
{
|
|
int count = ARRAY_SIZE(sja1105_port_stats);
|
|
struct sja1105_private *priv = ds->priv;
|
|
|
|
if (sset != ETH_SS_STATS)
|
|
return -EOPNOTSUPP;
|
|
|
|
if (priv->info->device_id == SJA1105PR_DEVICE_ID ||
|
|
priv->info->device_id == SJA1105QS_DEVICE_ID)
|
|
count += ARRAY_SIZE(sja1105pqrs_extra_port_stats);
|
|
|
|
return count;
|
|
}
|