lan743x: Add support for ethtool eeprom access
Implement ethtool eeprom access Also provides access to OTP (One Time Programming) Signed-off-by: Bryan Whitehead <Bryan.Whitehead@microchip.com> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
2958337d68
commit
695846047a
@ -7,6 +7,178 @@
|
||||
#include <linux/pci.h>
|
||||
#include <linux/phy.h>
|
||||
|
||||
/* eeprom */
|
||||
#define LAN743X_EEPROM_MAGIC (0x74A5)
|
||||
#define LAN743X_OTP_MAGIC (0x74F3)
|
||||
#define EEPROM_INDICATOR_1 (0xA5)
|
||||
#define EEPROM_INDICATOR_2 (0xAA)
|
||||
#define EEPROM_MAC_OFFSET (0x01)
|
||||
#define MAX_EEPROM_SIZE 512
|
||||
#define OTP_INDICATOR_1 (0xF3)
|
||||
#define OTP_INDICATOR_2 (0xF7)
|
||||
|
||||
static int lan743x_otp_write(struct lan743x_adapter *adapter, u32 offset,
|
||||
u32 length, u8 *data)
|
||||
{
|
||||
unsigned long timeout;
|
||||
u32 buf;
|
||||
int i;
|
||||
|
||||
buf = lan743x_csr_read(adapter, OTP_PWR_DN);
|
||||
|
||||
if (buf & OTP_PWR_DN_PWRDN_N_) {
|
||||
/* clear it and wait to be cleared */
|
||||
lan743x_csr_write(adapter, OTP_PWR_DN, 0);
|
||||
|
||||
timeout = jiffies + HZ;
|
||||
do {
|
||||
udelay(1);
|
||||
buf = lan743x_csr_read(adapter, OTP_PWR_DN);
|
||||
if (time_after(jiffies, timeout)) {
|
||||
netif_warn(adapter, drv, adapter->netdev,
|
||||
"timeout on OTP_PWR_DN completion\n");
|
||||
return -EIO;
|
||||
}
|
||||
} while (buf & OTP_PWR_DN_PWRDN_N_);
|
||||
}
|
||||
|
||||
/* set to BYTE program mode */
|
||||
lan743x_csr_write(adapter, OTP_PRGM_MODE, OTP_PRGM_MODE_BYTE_);
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
lan743x_csr_write(adapter, OTP_ADDR1,
|
||||
((offset + i) >> 8) &
|
||||
OTP_ADDR1_15_11_MASK_);
|
||||
lan743x_csr_write(adapter, OTP_ADDR2,
|
||||
((offset + i) &
|
||||
OTP_ADDR2_10_3_MASK_));
|
||||
lan743x_csr_write(adapter, OTP_PRGM_DATA, data[i]);
|
||||
lan743x_csr_write(adapter, OTP_TST_CMD, OTP_TST_CMD_PRGVRFY_);
|
||||
lan743x_csr_write(adapter, OTP_CMD_GO, OTP_CMD_GO_GO_);
|
||||
|
||||
timeout = jiffies + HZ;
|
||||
do {
|
||||
udelay(1);
|
||||
buf = lan743x_csr_read(adapter, OTP_STATUS);
|
||||
if (time_after(jiffies, timeout)) {
|
||||
netif_warn(adapter, drv, adapter->netdev,
|
||||
"Timeout on OTP_STATUS completion\n");
|
||||
return -EIO;
|
||||
}
|
||||
} while (buf & OTP_STATUS_BUSY_);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lan743x_eeprom_wait(struct lan743x_adapter *adapter)
|
||||
{
|
||||
unsigned long start_time = jiffies;
|
||||
u32 val;
|
||||
|
||||
do {
|
||||
val = lan743x_csr_read(adapter, E2P_CMD);
|
||||
|
||||
if (!(val & E2P_CMD_EPC_BUSY_) ||
|
||||
(val & E2P_CMD_EPC_TIMEOUT_))
|
||||
break;
|
||||
usleep_range(40, 100);
|
||||
} while (!time_after(jiffies, start_time + HZ));
|
||||
|
||||
if (val & (E2P_CMD_EPC_TIMEOUT_ | E2P_CMD_EPC_BUSY_)) {
|
||||
netif_warn(adapter, drv, adapter->netdev,
|
||||
"EEPROM read operation timeout\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lan743x_eeprom_confirm_not_busy(struct lan743x_adapter *adapter)
|
||||
{
|
||||
unsigned long start_time = jiffies;
|
||||
u32 val;
|
||||
|
||||
do {
|
||||
val = lan743x_csr_read(adapter, E2P_CMD);
|
||||
|
||||
if (!(val & E2P_CMD_EPC_BUSY_))
|
||||
return 0;
|
||||
|
||||
usleep_range(40, 100);
|
||||
} while (!time_after(jiffies, start_time + HZ));
|
||||
|
||||
netif_warn(adapter, drv, adapter->netdev, "EEPROM is busy\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int lan743x_eeprom_read(struct lan743x_adapter *adapter,
|
||||
u32 offset, u32 length, u8 *data)
|
||||
{
|
||||
int retval;
|
||||
u32 val;
|
||||
int i;
|
||||
|
||||
retval = lan743x_eeprom_confirm_not_busy(adapter);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_READ_;
|
||||
val |= (offset & E2P_CMD_EPC_ADDR_MASK_);
|
||||
lan743x_csr_write(adapter, E2P_CMD, val);
|
||||
|
||||
retval = lan743x_eeprom_wait(adapter);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
val = lan743x_csr_read(adapter, E2P_DATA);
|
||||
data[i] = val & 0xFF;
|
||||
offset++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lan743x_eeprom_write(struct lan743x_adapter *adapter,
|
||||
u32 offset, u32 length, u8 *data)
|
||||
{
|
||||
int retval;
|
||||
u32 val;
|
||||
int i;
|
||||
|
||||
retval = lan743x_eeprom_confirm_not_busy(adapter);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/* Issue write/erase enable command */
|
||||
val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_EWEN_;
|
||||
lan743x_csr_write(adapter, E2P_CMD, val);
|
||||
|
||||
retval = lan743x_eeprom_wait(adapter);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
/* Fill data register */
|
||||
val = data[i];
|
||||
lan743x_csr_write(adapter, E2P_DATA, val);
|
||||
|
||||
/* Send "write" command */
|
||||
val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_WRITE_;
|
||||
val |= (offset & E2P_CMD_EPC_ADDR_MASK_);
|
||||
lan743x_csr_write(adapter, E2P_CMD, val);
|
||||
|
||||
retval = lan743x_eeprom_wait(adapter);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
offset++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lan743x_ethtool_get_drvinfo(struct net_device *netdev,
|
||||
struct ethtool_drvinfo *info)
|
||||
{
|
||||
@ -32,6 +204,40 @@ static void lan743x_ethtool_set_msglevel(struct net_device *netdev,
|
||||
adapter->msg_enable = msglevel;
|
||||
}
|
||||
|
||||
static int lan743x_ethtool_get_eeprom_len(struct net_device *netdev)
|
||||
{
|
||||
return MAX_EEPROM_SIZE;
|
||||
}
|
||||
|
||||
static int lan743x_ethtool_get_eeprom(struct net_device *netdev,
|
||||
struct ethtool_eeprom *ee, u8 *data)
|
||||
{
|
||||
struct lan743x_adapter *adapter = netdev_priv(netdev);
|
||||
|
||||
return lan743x_eeprom_read(adapter, ee->offset, ee->len, data);
|
||||
}
|
||||
|
||||
static int lan743x_ethtool_set_eeprom(struct net_device *netdev,
|
||||
struct ethtool_eeprom *ee, u8 *data)
|
||||
{
|
||||
struct lan743x_adapter *adapter = netdev_priv(netdev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (ee->magic == LAN743X_EEPROM_MAGIC)
|
||||
ret = lan743x_eeprom_write(adapter, ee->offset, ee->len,
|
||||
data);
|
||||
/* Beware! OTP is One Time Programming ONLY!
|
||||
* So do some strict condition check before messing up
|
||||
*/
|
||||
else if ((ee->magic == LAN743X_OTP_MAGIC) &&
|
||||
(ee->offset == 0) &&
|
||||
(ee->len == MAX_EEPROM_SIZE) &&
|
||||
(data[0] == OTP_INDICATOR_1))
|
||||
ret = lan743x_otp_write(adapter, ee->offset, ee->len, data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char lan743x_set0_hw_cnt_strings[][ETH_GSTRING_LEN] = {
|
||||
"RX FCS Errors",
|
||||
"RX Alignment Errors",
|
||||
@ -215,6 +421,9 @@ const struct ethtool_ops lan743x_ethtool_ops = {
|
||||
.set_msglevel = lan743x_ethtool_set_msglevel,
|
||||
.get_link = ethtool_op_get_link,
|
||||
|
||||
.get_eeprom_len = lan743x_ethtool_get_eeprom_len,
|
||||
.get_eeprom = lan743x_ethtool_get_eeprom,
|
||||
.set_eeprom = lan743x_ethtool_set_eeprom,
|
||||
.get_strings = lan743x_ethtool_get_strings,
|
||||
.get_ethtool_stats = lan743x_ethtool_get_ethtool_stats,
|
||||
.get_sset_count = lan743x_ethtool_get_sset_count,
|
||||
|
@ -42,6 +42,16 @@
|
||||
|
||||
#define DP_DATA_0 (0x030)
|
||||
|
||||
#define E2P_CMD (0x040)
|
||||
#define E2P_CMD_EPC_BUSY_ BIT(31)
|
||||
#define E2P_CMD_EPC_CMD_WRITE_ (0x30000000)
|
||||
#define E2P_CMD_EPC_CMD_EWEN_ (0x20000000)
|
||||
#define E2P_CMD_EPC_CMD_READ_ (0x00000000)
|
||||
#define E2P_CMD_EPC_TIMEOUT_ BIT(10)
|
||||
#define E2P_CMD_EPC_ADDR_MASK_ (0x000001FF)
|
||||
|
||||
#define E2P_DATA (0x044)
|
||||
|
||||
#define FCT_RX_CTL (0xAC)
|
||||
#define FCT_RX_CTL_EN_(channel) BIT(28 + (channel))
|
||||
#define FCT_RX_CTL_DIS_(channel) BIT(24 + (channel))
|
||||
@ -288,6 +298,29 @@
|
||||
#define TX_CFG_C_TX_DMA_INT_STS_AUTO_CLR_ BIT(3)
|
||||
#define TX_CFG_C_TX_INT_STS_R2C_MODE_MASK_ (0x00000007)
|
||||
|
||||
#define OTP_PWR_DN (0x1000)
|
||||
#define OTP_PWR_DN_PWRDN_N_ BIT(0)
|
||||
|
||||
#define OTP_ADDR1 (0x1004)
|
||||
#define OTP_ADDR1_15_11_MASK_ (0x1F)
|
||||
|
||||
#define OTP_ADDR2 (0x1008)
|
||||
#define OTP_ADDR2_10_3_MASK_ (0xFF)
|
||||
|
||||
#define OTP_PRGM_DATA (0x1010)
|
||||
|
||||
#define OTP_PRGM_MODE (0x1014)
|
||||
#define OTP_PRGM_MODE_BYTE_ BIT(0)
|
||||
|
||||
#define OTP_TST_CMD (0x1024)
|
||||
#define OTP_TST_CMD_PRGVRFY_ BIT(3)
|
||||
|
||||
#define OTP_CMD_GO (0x1028)
|
||||
#define OTP_CMD_GO_GO_ BIT(0)
|
||||
|
||||
#define OTP_STATUS (0x1030)
|
||||
#define OTP_STATUS_BUSY_ BIT(0)
|
||||
|
||||
/* MAC statistics registers */
|
||||
#define STAT_RX_FCS_ERRORS (0x1200)
|
||||
#define STAT_RX_ALIGNMENT_ERRORS (0x1204)
|
||||
|
Loading…
Reference in New Issue
Block a user