/* SPDX-License-Identifier: GPL-2.0+ */
/* Copyright (C) 2018 Microchip Technology Inc. */
#include <linux/netdevice.h>
#include "lan743x_main.h"
#include "lan743x_ethtool.h"
#include <linux/net_tstamp.h>
#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 MAX_OTP_SIZE (1024)
#define OTP_INDICATOR_1 (0xF3)
#define OTP_INDICATOR_2 (0xF7)
static int lan743x_otp_power_up(struct lan743x_adapter *adapter)
{
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;
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;
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;
for (i = 0; i < length; i++) {
lan743x_otp_set_address(adapter, offset + i);
lan743x_otp_read_go(adapter);
ret = lan743x_otp_wait_till_not_busy(adapter);
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 */
lan743x_csr_write(adapter, OTP_PRGM_MODE, OTP_PRGM_MODE_BYTE_);
for (i = 0; i < length; i++) {
lan743x_otp_set_address(adapter, offset + 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_CMD_GO, OTP_CMD_GO_GO_);
ret = lan743x_otp_wait_till_not_busy(adapter);
if (ret < 0)
return ret;
}
lan743x_otp_power_down(adapter);
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 &