Commit 662a14d0 authored by Bryan Whitehead's avatar Bryan Whitehead Committed by David S. Miller

lan743x: Provide Read/Write Access to on chip OTP

The LAN743x includes on chip One-Time-Programmable (OTP) memory.

This patch extends the ethtool EEPROM read/write interface to
access OTP memory space.

The currently existing interface is limited, as it does not
allow OTP read, and OTP writes are restricted to
offset==0, length==512, and data[0]==0xF3.

This patch removes these restrictions and adds a private flag
called OTP_ACCESS, which is used to switch between EEPROM, and
OTP modes.

The private flag OTP_ACCESS is configurable through the
  ethtool --set-priv-flags command.
And visible through the
  ethtool --show-priv-flags command.

By default OTP_ACCESS is false, and there for previously existing
EEPROM commands will work exactly the same. However now access to
OTP requires one extra step of setting OTP_ACCESS to true. This
flag controls the read, write, and length reporting, functions
of ethtool.

EEPROM presence is not checked when setting or clearing this flag.
If the EEPROM is not present, the user, as before, will need to
diagnose that using existing read and write function of ethtool,
while OTP_ACCESS is false.

Updates for V2:
Added comments as to why this patch is needed.
Added comments explaining that EEPROM presence is not check
  when setting or clearing the OTP_ACCESS flag.
Added length checking to all otp/eeprom read/write functions.
Signed-off-by: default avatarBryan Whitehead <Bryan.Whitehead@microchip.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 08c666b7
...@@ -14,61 +14,138 @@ ...@@ -14,61 +14,138 @@
#define EEPROM_INDICATOR_1 (0xA5) #define EEPROM_INDICATOR_1 (0xA5)
#define EEPROM_INDICATOR_2 (0xAA) #define EEPROM_INDICATOR_2 (0xAA)
#define EEPROM_MAC_OFFSET (0x01) #define EEPROM_MAC_OFFSET (0x01)
#define MAX_EEPROM_SIZE 512 #define MAX_EEPROM_SIZE (512)
#define MAX_OTP_SIZE (1024)
#define OTP_INDICATOR_1 (0xF3) #define OTP_INDICATOR_1 (0xF3)
#define OTP_INDICATOR_2 (0xF7) #define OTP_INDICATOR_2 (0xF7)
static int lan743x_otp_write(struct lan743x_adapter *adapter, u32 offset, static int lan743x_otp_power_up(struct lan743x_adapter *adapter)
u32 length, u8 *data) {
u32 reg_value;
reg_value = lan743x_csr_read(adapter, OTP_PWR_DN);
if (reg_value & OTP_PWR_DN_PWRDN_N_) {
/* clear it and wait to be cleared */
reg_value &= ~OTP_PWR_DN_PWRDN_N_;
lan743x_csr_write(adapter, OTP_PWR_DN, reg_value);
usleep_range(100, 20000);
}
return 0;
}
static void lan743x_otp_power_down(struct lan743x_adapter *adapter)
{
u32 reg_value;
reg_value = lan743x_csr_read(adapter, OTP_PWR_DN);
if (!(reg_value & OTP_PWR_DN_PWRDN_N_)) {
/* set power down bit */
reg_value |= OTP_PWR_DN_PWRDN_N_;
lan743x_csr_write(adapter, OTP_PWR_DN, reg_value);
}
}
static void lan743x_otp_set_address(struct lan743x_adapter *adapter,
u32 address)
{
lan743x_csr_write(adapter, OTP_ADDR_HIGH, (address >> 8) & 0x03);
lan743x_csr_write(adapter, OTP_ADDR_LOW, address & 0xFF);
}
static void lan743x_otp_read_go(struct lan743x_adapter *adapter)
{
lan743x_csr_write(adapter, OTP_FUNC_CMD, OTP_FUNC_CMD_READ_);
lan743x_csr_write(adapter, OTP_CMD_GO, OTP_CMD_GO_GO_);
}
static int lan743x_otp_wait_till_not_busy(struct lan743x_adapter *adapter)
{ {
unsigned long timeout; unsigned long timeout;
u32 buf; u32 reg_val;
timeout = jiffies + HZ;
do {
if (time_after(jiffies, timeout)) {
netif_warn(adapter, drv, adapter->netdev,
"Timeout on OTP_STATUS completion\n");
return -EIO;
}
udelay(1);
reg_val = lan743x_csr_read(adapter, OTP_STATUS);
} while (reg_val & OTP_STATUS_BUSY_);
return 0;
}
static int lan743x_otp_read(struct lan743x_adapter *adapter, u32 offset,
u32 length, u8 *data)
{
int ret;
int i; int i;
buf = lan743x_csr_read(adapter, OTP_PWR_DN); if (offset + length > MAX_OTP_SIZE)
return -EINVAL;
if (buf & OTP_PWR_DN_PWRDN_N_) { ret = lan743x_otp_power_up(adapter);
/* clear it and wait to be cleared */ if (ret < 0)
lan743x_csr_write(adapter, OTP_PWR_DN, 0); return ret;
timeout = jiffies + HZ; ret = lan743x_otp_wait_till_not_busy(adapter);
do { if (ret < 0)
udelay(1); return ret;
buf = lan743x_csr_read(adapter, OTP_PWR_DN);
if (time_after(jiffies, timeout)) { for (i = 0; i < length; i++) {
netif_warn(adapter, drv, adapter->netdev, lan743x_otp_set_address(adapter, offset + i);
"timeout on OTP_PWR_DN completion\n");
return -EIO; lan743x_otp_read_go(adapter);
} ret = lan743x_otp_wait_till_not_busy(adapter);
} while (buf & OTP_PWR_DN_PWRDN_N_); if (ret < 0)
return ret;
data[i] = lan743x_csr_read(adapter, OTP_READ_DATA);
} }
lan743x_otp_power_down(adapter);
return 0;
}
static int lan743x_otp_write(struct lan743x_adapter *adapter, u32 offset,
u32 length, u8 *data)
{
int ret;
int i;
if (offset + length > MAX_OTP_SIZE)
return -EINVAL;
ret = lan743x_otp_power_up(adapter);
if (ret < 0)
return ret;
ret = lan743x_otp_wait_till_not_busy(adapter);
if (ret < 0)
return ret;
/* set to BYTE program mode */ /* set to BYTE program mode */
lan743x_csr_write(adapter, OTP_PRGM_MODE, OTP_PRGM_MODE_BYTE_); lan743x_csr_write(adapter, OTP_PRGM_MODE, OTP_PRGM_MODE_BYTE_);
for (i = 0; i < length; i++) { for (i = 0; i < length; i++) {
lan743x_csr_write(adapter, OTP_ADDR1, lan743x_otp_set_address(adapter, offset + i);
((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_PRGM_DATA, data[i]);
lan743x_csr_write(adapter, OTP_TST_CMD, OTP_TST_CMD_PRGVRFY_); lan743x_csr_write(adapter, OTP_TST_CMD, OTP_TST_CMD_PRGVRFY_);
lan743x_csr_write(adapter, OTP_CMD_GO, OTP_CMD_GO_GO_); lan743x_csr_write(adapter, OTP_CMD_GO, OTP_CMD_GO_GO_);
timeout = jiffies + HZ; ret = lan743x_otp_wait_till_not_busy(adapter);
do { if (ret < 0)
udelay(1); return ret;
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_);
} }
lan743x_otp_power_down(adapter);
return 0; return 0;
} }
...@@ -120,6 +197,9 @@ static int lan743x_eeprom_read(struct lan743x_adapter *adapter, ...@@ -120,6 +197,9 @@ static int lan743x_eeprom_read(struct lan743x_adapter *adapter,
u32 val; u32 val;
int i; int i;
if (offset + length > MAX_EEPROM_SIZE)
return -EINVAL;
retval = lan743x_eeprom_confirm_not_busy(adapter); retval = lan743x_eeprom_confirm_not_busy(adapter);
if (retval) if (retval)
return retval; return retval;
...@@ -148,6 +228,9 @@ static int lan743x_eeprom_write(struct lan743x_adapter *adapter, ...@@ -148,6 +228,9 @@ static int lan743x_eeprom_write(struct lan743x_adapter *adapter,
u32 val; u32 val;
int i; int i;
if (offset + length > MAX_EEPROM_SIZE)
return -EINVAL;
retval = lan743x_eeprom_confirm_not_busy(adapter); retval = lan743x_eeprom_confirm_not_busy(adapter);
if (retval) if (retval)
return retval; return retval;
...@@ -207,6 +290,11 @@ static void lan743x_ethtool_set_msglevel(struct net_device *netdev, ...@@ -207,6 +290,11 @@ static void lan743x_ethtool_set_msglevel(struct net_device *netdev,
static int lan743x_ethtool_get_eeprom_len(struct net_device *netdev) static int lan743x_ethtool_get_eeprom_len(struct net_device *netdev)
{ {
struct lan743x_adapter *adapter = netdev_priv(netdev);
if (adapter->flags & LAN743X_ADAPTER_FLAG_OTP)
return MAX_OTP_SIZE;
return MAX_EEPROM_SIZE; return MAX_EEPROM_SIZE;
} }
...@@ -214,8 +302,14 @@ static int lan743x_ethtool_get_eeprom(struct net_device *netdev, ...@@ -214,8 +302,14 @@ static int lan743x_ethtool_get_eeprom(struct net_device *netdev,
struct ethtool_eeprom *ee, u8 *data) struct ethtool_eeprom *ee, u8 *data)
{ {
struct lan743x_adapter *adapter = netdev_priv(netdev); struct lan743x_adapter *adapter = netdev_priv(netdev);
int ret = 0;
if (adapter->flags & LAN743X_ADAPTER_FLAG_OTP)
ret = lan743x_otp_read(adapter, ee->offset, ee->len, data);
else
ret = lan743x_eeprom_read(adapter, ee->offset, ee->len, data);
return lan743x_eeprom_read(adapter, ee->offset, ee->len, data); return ret;
} }
static int lan743x_ethtool_set_eeprom(struct net_device *netdev, static int lan743x_ethtool_set_eeprom(struct net_device *netdev,
...@@ -224,17 +318,18 @@ static int lan743x_ethtool_set_eeprom(struct net_device *netdev, ...@@ -224,17 +318,18 @@ static int lan743x_ethtool_set_eeprom(struct net_device *netdev,
struct lan743x_adapter *adapter = netdev_priv(netdev); struct lan743x_adapter *adapter = netdev_priv(netdev);
int ret = -EINVAL; int ret = -EINVAL;
if (ee->magic == LAN743X_EEPROM_MAGIC) if (adapter->flags & LAN743X_ADAPTER_FLAG_OTP) {
ret = lan743x_eeprom_write(adapter, ee->offset, ee->len, /* Beware! OTP is One Time Programming ONLY! */
data); if (ee->magic == LAN743X_OTP_MAGIC) {
/* Beware! OTP is One Time Programming ONLY! ret = lan743x_otp_write(adapter, ee->offset,
* So do some strict condition check before messing up ee->len, data);
*/ }
else if ((ee->magic == LAN743X_OTP_MAGIC) && } else {
(ee->offset == 0) && if (ee->magic == LAN743X_EEPROM_MAGIC) {
(ee->len == MAX_EEPROM_SIZE) && ret = lan743x_eeprom_write(adapter, ee->offset,
(data[0] == OTP_INDICATOR_1)) ee->len, data);
ret = lan743x_otp_write(adapter, ee->offset, ee->len, data); }
}
return ret; return ret;
} }
...@@ -360,6 +455,10 @@ static const u32 lan743x_set2_hw_cnt_addr[] = { ...@@ -360,6 +455,10 @@ static const u32 lan743x_set2_hw_cnt_addr[] = {
STAT_TX_COUNTER_ROLLOVER_STATUS STAT_TX_COUNTER_ROLLOVER_STATUS
}; };
static const char lan743x_priv_flags_strings[][ETH_GSTRING_LEN] = {
"OTP_ACCESS",
};
static void lan743x_ethtool_get_strings(struct net_device *netdev, static void lan743x_ethtool_get_strings(struct net_device *netdev,
u32 stringset, u8 *data) u32 stringset, u8 *data)
{ {
...@@ -375,6 +474,10 @@ static void lan743x_ethtool_get_strings(struct net_device *netdev, ...@@ -375,6 +474,10 @@ static void lan743x_ethtool_get_strings(struct net_device *netdev,
lan743x_set2_hw_cnt_strings, lan743x_set2_hw_cnt_strings,
sizeof(lan743x_set2_hw_cnt_strings)); sizeof(lan743x_set2_hw_cnt_strings));
break; break;
case ETH_SS_PRIV_FLAGS:
memcpy(data, lan743x_priv_flags_strings,
sizeof(lan743x_priv_flags_strings));
break;
} }
} }
...@@ -399,6 +502,22 @@ static void lan743x_ethtool_get_ethtool_stats(struct net_device *netdev, ...@@ -399,6 +502,22 @@ static void lan743x_ethtool_get_ethtool_stats(struct net_device *netdev,
} }
} }
static u32 lan743x_ethtool_get_priv_flags(struct net_device *netdev)
{
struct lan743x_adapter *adapter = netdev_priv(netdev);
return adapter->flags;
}
static int lan743x_ethtool_set_priv_flags(struct net_device *netdev, u32 flags)
{
struct lan743x_adapter *adapter = netdev_priv(netdev);
adapter->flags = flags;
return 0;
}
static int lan743x_ethtool_get_sset_count(struct net_device *netdev, int sset) static int lan743x_ethtool_get_sset_count(struct net_device *netdev, int sset)
{ {
switch (sset) { switch (sset) {
...@@ -411,6 +530,8 @@ static int lan743x_ethtool_get_sset_count(struct net_device *netdev, int sset) ...@@ -411,6 +530,8 @@ static int lan743x_ethtool_get_sset_count(struct net_device *netdev, int sset)
ret += ARRAY_SIZE(lan743x_set2_hw_cnt_strings); ret += ARRAY_SIZE(lan743x_set2_hw_cnt_strings);
return ret; return ret;
} }
case ETH_SS_PRIV_FLAGS:
return ARRAY_SIZE(lan743x_priv_flags_strings);
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
...@@ -705,6 +826,8 @@ const struct ethtool_ops lan743x_ethtool_ops = { ...@@ -705,6 +826,8 @@ const struct ethtool_ops lan743x_ethtool_ops = {
.set_eeprom = lan743x_ethtool_set_eeprom, .set_eeprom = lan743x_ethtool_set_eeprom,
.get_strings = lan743x_ethtool_get_strings, .get_strings = lan743x_ethtool_get_strings,
.get_ethtool_stats = lan743x_ethtool_get_ethtool_stats, .get_ethtool_stats = lan743x_ethtool_get_ethtool_stats,
.get_priv_flags = lan743x_ethtool_get_priv_flags,
.set_priv_flags = lan743x_ethtool_set_priv_flags,
.get_sset_count = lan743x_ethtool_get_sset_count, .get_sset_count = lan743x_ethtool_get_sset_count,
.get_rxnfc = lan743x_ethtool_get_rxnfc, .get_rxnfc = lan743x_ethtool_get_rxnfc,
.get_rxfh_key_size = lan743x_ethtool_get_rxfh_key_size, .get_rxfh_key_size = lan743x_ethtool_get_rxfh_key_size,
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
#define FPGA_REV_GET_MAJOR_(fpga_rev) ((fpga_rev) & 0x000000FF) #define FPGA_REV_GET_MAJOR_(fpga_rev) ((fpga_rev) & 0x000000FF)
#define HW_CFG (0x010) #define HW_CFG (0x010)
#define HW_CFG_RELOAD_TYPE_ALL_ (0x00000FC0)
#define HW_CFG_EE_OTP_RELOAD_ BIT(4)
#define HW_CFG_LRST_ BIT(1) #define HW_CFG_LRST_ BIT(1)
#define PMT_CTL (0x014) #define PMT_CTL (0x014)
...@@ -453,17 +455,19 @@ ...@@ -453,17 +455,19 @@
#define OTP_PWR_DN (0x1000) #define OTP_PWR_DN (0x1000)
#define OTP_PWR_DN_PWRDN_N_ BIT(0) #define OTP_PWR_DN_PWRDN_N_ BIT(0)
#define OTP_ADDR1 (0x1004) #define OTP_ADDR_HIGH (0x1004)
#define OTP_ADDR1_15_11_MASK_ (0x1F) #define OTP_ADDR_LOW (0x1008)
#define OTP_ADDR2 (0x1008)
#define OTP_ADDR2_10_3_MASK_ (0xFF)
#define OTP_PRGM_DATA (0x1010) #define OTP_PRGM_DATA (0x1010)
#define OTP_PRGM_MODE (0x1014) #define OTP_PRGM_MODE (0x1014)
#define OTP_PRGM_MODE_BYTE_ BIT(0) #define OTP_PRGM_MODE_BYTE_ BIT(0)
#define OTP_READ_DATA (0x1018)
#define OTP_FUNC_CMD (0x1020)
#define OTP_FUNC_CMD_READ_ BIT(0)
#define OTP_TST_CMD (0x1024) #define OTP_TST_CMD (0x1024)
#define OTP_TST_CMD_PRGVRFY_ BIT(3) #define OTP_TST_CMD_PRGVRFY_ BIT(3)
...@@ -713,6 +717,9 @@ struct lan743x_adapter { ...@@ -713,6 +717,9 @@ struct lan743x_adapter {
struct lan743x_phy phy; struct lan743x_phy phy;
struct lan743x_tx tx[LAN743X_MAX_TX_CHANNELS]; struct lan743x_tx tx[LAN743X_MAX_TX_CHANNELS];
struct lan743x_rx rx[LAN743X_MAX_RX_CHANNELS]; struct lan743x_rx rx[LAN743X_MAX_RX_CHANNELS];
#define LAN743X_ADAPTER_FLAG_OTP BIT(0)
u32 flags;
}; };
#define LAN743X_COMPONENT_FLAG_RX(channel) BIT(20 + (channel)) #define LAN743X_COMPONENT_FLAG_RX(channel) BIT(20 + (channel))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment