Skip to content

Commit

Permalink
sh_eth: Implement ethtool register dump operations
Browse files Browse the repository at this point in the history
There are many different sets of registers implemented by the
different versions of this controller, and we can only expect this to
get more complicated in future.  Limit how much ethtool needs to know
by including an explicit bitmap of which registers are included in the
dump, allowing room for future growth in the number of possible
registers.

As I don't have datasheets for all of these, I've only included
registers that are:

- defined in all 5 register type arrays, or
- used by the driver, or
- documented in the datasheet I have

Add one new capability flag so we can tell whether the RTRATE
register is implemented.

Delete the TSU_ADRL0 and TSU_ADR{H,L}31 definitions, as they weren't
used and the address table is already assumed to be contiguous.

Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Ben Hutchings authored and David S. Miller committed Mar 4, 2015
1 parent 3365711 commit 6b4b4fe
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 11 deletions.
197 changes: 189 additions & 8 deletions drivers/net/ethernet/renesas/sh_eth.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,6 @@ static const u16 sh_eth_offset_gigabit[SH_ETH_MAX_REGISTER_OFFSET] = {
[TSU_POST3] = 0x0078,
[TSU_POST4] = 0x007c,
[TSU_ADRH0] = 0x0100,
[TSU_ADRL0] = 0x0104,
[TSU_ADRH31] = 0x01f8,
[TSU_ADRL31] = 0x01fc,

[TXNLCR0] = 0x0080,
[TXALCR0] = 0x0084,
Expand Down Expand Up @@ -206,9 +203,6 @@ static const u16 sh_eth_offset_fast_rz[SH_ETH_MAX_REGISTER_OFFSET] = {
[TSU_ADSBSY] = 0x0060,
[TSU_TEN] = 0x0064,
[TSU_ADRH0] = 0x0100,
[TSU_ADRL0] = 0x0104,
[TSU_ADRH31] = 0x01f8,
[TSU_ADRL31] = 0x01fc,

[TXNLCR0] = 0x0080,
[TXALCR0] = 0x0084,
Expand Down Expand Up @@ -405,8 +399,6 @@ static const u16 sh_eth_offset_fast_sh3_sh2[SH_ETH_MAX_REGISTER_OFFSET] = {
[FWALCR1] = 0x00b4,

[TSU_ADRH0] = 0x0100,
[TSU_ADRL0] = 0x0104,
[TSU_ADRL31] = 0x01fc,
};

static void sh_eth_rcv_snd_disable(struct net_device *ndev);
Expand Down Expand Up @@ -601,6 +593,7 @@ static struct sh_eth_cpu_data sh7757_data = {
.no_ade = 1,
.rpadir = 1,
.rpadir_value = 2 << 16,
.rtrate = 1,
};

#define SH_GIGA_ETH_BASE 0xfee00000UL
Expand Down Expand Up @@ -1945,6 +1938,192 @@ static int sh_eth_set_settings(struct net_device *ndev,
return ret;
}

/* If it is ever necessary to increase SH_ETH_REG_DUMP_MAX_REGS, the
* version must be bumped as well. Just adding registers up to that
* limit is fine, as long as the existing register indices don't
* change.
*/
#define SH_ETH_REG_DUMP_VERSION 1
#define SH_ETH_REG_DUMP_MAX_REGS 256

static size_t __sh_eth_get_regs(struct net_device *ndev, u32 *buf)
{
struct sh_eth_private *mdp = netdev_priv(ndev);
struct sh_eth_cpu_data *cd = mdp->cd;
u32 *valid_map;
size_t len;

BUILD_BUG_ON(SH_ETH_MAX_REGISTER_OFFSET > SH_ETH_REG_DUMP_MAX_REGS);

/* Dump starts with a bitmap that tells ethtool which
* registers are defined for this chip.
*/
len = DIV_ROUND_UP(SH_ETH_REG_DUMP_MAX_REGS, 32);
if (buf) {
valid_map = buf;
buf += len;
} else {
valid_map = NULL;
}

/* Add a register to the dump, if it has a defined offset.
* This automatically skips most undefined registers, but for
* some it is also necessary to check a capability flag in
* struct sh_eth_cpu_data.
*/
#define mark_reg_valid(reg) valid_map[reg / 32] |= 1U << (reg % 32)
#define add_reg_from(reg, read_expr) do { \
if (mdp->reg_offset[reg] != SH_ETH_OFFSET_INVALID) { \
if (buf) { \
mark_reg_valid(reg); \
*buf++ = read_expr; \
} \
++len; \
} \
} while (0)
#define add_reg(reg) add_reg_from(reg, sh_eth_read(ndev, reg))
#define add_tsu_reg(reg) add_reg_from(reg, sh_eth_tsu_read(mdp, reg))

add_reg(EDSR);
add_reg(EDMR);
add_reg(EDTRR);
add_reg(EDRRR);
add_reg(EESR);
add_reg(EESIPR);
add_reg(TDLAR);
add_reg(TDFAR);
add_reg(TDFXR);
add_reg(TDFFR);
add_reg(RDLAR);
add_reg(RDFAR);
add_reg(RDFXR);
add_reg(RDFFR);
add_reg(TRSCER);
add_reg(RMFCR);
add_reg(TFTR);
add_reg(FDR);
add_reg(RMCR);
add_reg(TFUCR);
add_reg(RFOCR);
if (cd->rmiimode)
add_reg(RMIIMODE);
add_reg(FCFTR);
if (cd->rpadir)
add_reg(RPADIR);
if (!cd->no_trimd)
add_reg(TRIMD);
add_reg(ECMR);
add_reg(ECSR);
add_reg(ECSIPR);
add_reg(PIR);
if (!cd->no_psr)
add_reg(PSR);
add_reg(RDMLR);
add_reg(RFLR);
add_reg(IPGR);
if (cd->apr)
add_reg(APR);
if (cd->mpr)
add_reg(MPR);
add_reg(RFCR);
add_reg(RFCF);
if (cd->tpauser)
add_reg(TPAUSER);
add_reg(TPAUSECR);
add_reg(GECMR);
if (cd->bculr)
add_reg(BCULR);
add_reg(MAHR);
add_reg(MALR);
add_reg(TROCR);
add_reg(CDCR);
add_reg(LCCR);
add_reg(CNDCR);
add_reg(CEFCR);
add_reg(FRECR);
add_reg(TSFRCR);
add_reg(TLFRCR);
add_reg(CERCR);
add_reg(CEECR);
add_reg(MAFCR);
if (cd->rtrate)
add_reg(RTRATE);
if (cd->hw_crc)
add_reg(CSMR);
if (cd->select_mii)
add_reg(RMII_MII);
add_reg(ARSTR);
if (cd->tsu) {
add_tsu_reg(TSU_CTRST);
add_tsu_reg(TSU_FWEN0);
add_tsu_reg(TSU_FWEN1);
add_tsu_reg(TSU_FCM);
add_tsu_reg(TSU_BSYSL0);
add_tsu_reg(TSU_BSYSL1);
add_tsu_reg(TSU_PRISL0);
add_tsu_reg(TSU_PRISL1);
add_tsu_reg(TSU_FWSL0);
add_tsu_reg(TSU_FWSL1);
add_tsu_reg(TSU_FWSLC);
add_tsu_reg(TSU_QTAG0);
add_tsu_reg(TSU_QTAG1);
add_tsu_reg(TSU_QTAGM0);
add_tsu_reg(TSU_QTAGM1);
add_tsu_reg(TSU_FWSR);
add_tsu_reg(TSU_FWINMK);
add_tsu_reg(TSU_ADQT0);
add_tsu_reg(TSU_ADQT1);
add_tsu_reg(TSU_VTAG0);
add_tsu_reg(TSU_VTAG1);
add_tsu_reg(TSU_ADSBSY);
add_tsu_reg(TSU_TEN);
add_tsu_reg(TSU_POST1);
add_tsu_reg(TSU_POST2);
add_tsu_reg(TSU_POST3);
add_tsu_reg(TSU_POST4);
if (mdp->reg_offset[TSU_ADRH0] != SH_ETH_OFFSET_INVALID) {
/* This is the start of a table, not just a single
* register.
*/
if (buf) {
unsigned int i;

mark_reg_valid(TSU_ADRH0);
for (i = 0; i < SH_ETH_TSU_CAM_ENTRIES * 2; i++)
*buf++ = ioread32(
mdp->tsu_addr +
mdp->reg_offset[TSU_ADRH0] +
i * 4);
}
len += SH_ETH_TSU_CAM_ENTRIES * 2;
}
}

#undef mark_reg_valid
#undef add_reg_from
#undef add_reg
#undef add_tsu_reg

return len * 4;
}

static int sh_eth_get_regs_len(struct net_device *ndev)
{
return __sh_eth_get_regs(ndev, NULL);
}

static void sh_eth_get_regs(struct net_device *ndev, struct ethtool_regs *regs,
void *buf)
{
struct sh_eth_private *mdp = netdev_priv(ndev);

regs->version = SH_ETH_REG_DUMP_VERSION;

pm_runtime_get_sync(&mdp->pdev->dev);
__sh_eth_get_regs(ndev, buf);
pm_runtime_put_sync(&mdp->pdev->dev);
}

static int sh_eth_nway_reset(struct net_device *ndev)
{
struct sh_eth_private *mdp = netdev_priv(ndev);
Expand Down Expand Up @@ -2090,6 +2269,8 @@ static int sh_eth_set_ringparam(struct net_device *ndev,
static const struct ethtool_ops sh_eth_ethtool_ops = {
.get_settings = sh_eth_get_settings,
.set_settings = sh_eth_set_settings,
.get_regs_len = sh_eth_get_regs_len,
.get_regs = sh_eth_get_regs,
.nway_reset = sh_eth_nway_reset,
.get_msglevel = sh_eth_get_msglevel,
.set_msglevel = sh_eth_set_msglevel,
Expand Down
9 changes: 6 additions & 3 deletions drivers/net/ethernet/renesas/sh_eth.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
#define SH_ETH_TSU_CAM_ENTRIES 32

enum {
/* IMPORTANT: To keep ethtool register dump working, add new
* register names immediately before SH_ETH_MAX_REGISTER_OFFSET.
*/

/* E-DMAC registers */
EDSR = 0,
EDMR,
Expand Down Expand Up @@ -131,9 +135,7 @@ enum {
TSU_POST3,
TSU_POST4,
TSU_ADRH0,
TSU_ADRL0,
TSU_ADRH31,
TSU_ADRL31,
/* TSU_ADR{H,L}{0..31} are assumed to be contiguous */

TXNLCR0,
TXALCR0,
Expand Down Expand Up @@ -491,6 +493,7 @@ struct sh_eth_cpu_data {
unsigned select_mii:1; /* EtherC have RMII_MII (MII select register) */
unsigned shift_rd0:1; /* shift Rx descriptor word 0 right by 16 */
unsigned rmiimode:1; /* EtherC has RMIIMODE register */
unsigned rtrate:1; /* EtherC has RTRATE register */
};

struct sh_eth_private {
Expand Down

0 comments on commit 6b4b4fe

Please sign in to comment.