Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 69584604 authored by Bryan Whitehead's avatar Bryan Whitehead Committed by David S. Miller
Browse files

lan743x: Add support for ethtool eeprom access



Implement ethtool eeprom access
Also provides access to OTP (One Time Programming)

Signed-off-by: default avatarBryan Whitehead <Bryan.Whitehead@microchip.com>
Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 2958337d
Loading
Loading
Loading
Loading
+209 −0
Original line number Diff line number Diff line
@@ -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,
+33 −0
Original line number Diff line number Diff line
@@ -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)