mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
sh_eth: Implement ethtool register dump operations
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>
This commit is contained in:
parent
3365711df0
commit
6b4b4fead3
2 changed files with 195 additions and 11 deletions
|
@ -137,9 +137,6 @@ static const u16 sh_eth_offset_gigabit[SH_ETH_MAX_REGISTER_OFFSET] = {
|
||||||
[TSU_POST3] = 0x0078,
|
[TSU_POST3] = 0x0078,
|
||||||
[TSU_POST4] = 0x007c,
|
[TSU_POST4] = 0x007c,
|
||||||
[TSU_ADRH0] = 0x0100,
|
[TSU_ADRH0] = 0x0100,
|
||||||
[TSU_ADRL0] = 0x0104,
|
|
||||||
[TSU_ADRH31] = 0x01f8,
|
|
||||||
[TSU_ADRL31] = 0x01fc,
|
|
||||||
|
|
||||||
[TXNLCR0] = 0x0080,
|
[TXNLCR0] = 0x0080,
|
||||||
[TXALCR0] = 0x0084,
|
[TXALCR0] = 0x0084,
|
||||||
|
@ -206,9 +203,6 @@ static const u16 sh_eth_offset_fast_rz[SH_ETH_MAX_REGISTER_OFFSET] = {
|
||||||
[TSU_ADSBSY] = 0x0060,
|
[TSU_ADSBSY] = 0x0060,
|
||||||
[TSU_TEN] = 0x0064,
|
[TSU_TEN] = 0x0064,
|
||||||
[TSU_ADRH0] = 0x0100,
|
[TSU_ADRH0] = 0x0100,
|
||||||
[TSU_ADRL0] = 0x0104,
|
|
||||||
[TSU_ADRH31] = 0x01f8,
|
|
||||||
[TSU_ADRL31] = 0x01fc,
|
|
||||||
|
|
||||||
[TXNLCR0] = 0x0080,
|
[TXNLCR0] = 0x0080,
|
||||||
[TXALCR0] = 0x0084,
|
[TXALCR0] = 0x0084,
|
||||||
|
@ -405,8 +399,6 @@ static const u16 sh_eth_offset_fast_sh3_sh2[SH_ETH_MAX_REGISTER_OFFSET] = {
|
||||||
[FWALCR1] = 0x00b4,
|
[FWALCR1] = 0x00b4,
|
||||||
|
|
||||||
[TSU_ADRH0] = 0x0100,
|
[TSU_ADRH0] = 0x0100,
|
||||||
[TSU_ADRL0] = 0x0104,
|
|
||||||
[TSU_ADRL31] = 0x01fc,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void sh_eth_rcv_snd_disable(struct net_device *ndev);
|
static void sh_eth_rcv_snd_disable(struct net_device *ndev);
|
||||||
|
@ -601,6 +593,7 @@ static struct sh_eth_cpu_data sh7757_data = {
|
||||||
.no_ade = 1,
|
.no_ade = 1,
|
||||||
.rpadir = 1,
|
.rpadir = 1,
|
||||||
.rpadir_value = 2 << 16,
|
.rpadir_value = 2 << 16,
|
||||||
|
.rtrate = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SH_GIGA_ETH_BASE 0xfee00000UL
|
#define SH_GIGA_ETH_BASE 0xfee00000UL
|
||||||
|
@ -1945,6 +1938,192 @@ static int sh_eth_set_settings(struct net_device *ndev,
|
||||||
return ret;
|
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)
|
static int sh_eth_nway_reset(struct net_device *ndev)
|
||||||
{
|
{
|
||||||
struct sh_eth_private *mdp = netdev_priv(ndev);
|
struct sh_eth_private *mdp = netdev_priv(ndev);
|
||||||
|
@ -2090,6 +2269,8 @@ static int sh_eth_set_ringparam(struct net_device *ndev,
|
||||||
static const struct ethtool_ops sh_eth_ethtool_ops = {
|
static const struct ethtool_ops sh_eth_ethtool_ops = {
|
||||||
.get_settings = sh_eth_get_settings,
|
.get_settings = sh_eth_get_settings,
|
||||||
.set_settings = sh_eth_set_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,
|
.nway_reset = sh_eth_nway_reset,
|
||||||
.get_msglevel = sh_eth_get_msglevel,
|
.get_msglevel = sh_eth_get_msglevel,
|
||||||
.set_msglevel = sh_eth_set_msglevel,
|
.set_msglevel = sh_eth_set_msglevel,
|
||||||
|
|
|
@ -32,6 +32,10 @@
|
||||||
#define SH_ETH_TSU_CAM_ENTRIES 32
|
#define SH_ETH_TSU_CAM_ENTRIES 32
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
/* IMPORTANT: To keep ethtool register dump working, add new
|
||||||
|
* register names immediately before SH_ETH_MAX_REGISTER_OFFSET.
|
||||||
|
*/
|
||||||
|
|
||||||
/* E-DMAC registers */
|
/* E-DMAC registers */
|
||||||
EDSR = 0,
|
EDSR = 0,
|
||||||
EDMR,
|
EDMR,
|
||||||
|
@ -131,9 +135,7 @@ enum {
|
||||||
TSU_POST3,
|
TSU_POST3,
|
||||||
TSU_POST4,
|
TSU_POST4,
|
||||||
TSU_ADRH0,
|
TSU_ADRH0,
|
||||||
TSU_ADRL0,
|
/* TSU_ADR{H,L}{0..31} are assumed to be contiguous */
|
||||||
TSU_ADRH31,
|
|
||||||
TSU_ADRL31,
|
|
||||||
|
|
||||||
TXNLCR0,
|
TXNLCR0,
|
||||||
TXALCR0,
|
TXALCR0,
|
||||||
|
@ -491,6 +493,7 @@ struct sh_eth_cpu_data {
|
||||||
unsigned select_mii:1; /* EtherC have RMII_MII (MII select register) */
|
unsigned select_mii:1; /* EtherC have RMII_MII (MII select register) */
|
||||||
unsigned shift_rd0:1; /* shift Rx descriptor word 0 right by 16 */
|
unsigned shift_rd0:1; /* shift Rx descriptor word 0 right by 16 */
|
||||||
unsigned rmiimode:1; /* EtherC has RMIIMODE register */
|
unsigned rmiimode:1; /* EtherC has RMIIMODE register */
|
||||||
|
unsigned rtrate:1; /* EtherC has RTRATE register */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sh_eth_private {
|
struct sh_eth_private {
|
||||||
|
|
Loading…
Reference in a new issue