// SPDX-License-Identifier: GPL-2.0+
/*
* drivers/net/phy/at803x.c
*
* Driver for Qualcomm Atheros AR803x PHY
*
* Author: Matus Ujhelyi <ujhelyi.m@gmail.com>
*/
#include <linux/phy.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool_netlink.h>
#include <linux/bitfield.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/consumer.h>
#include <linux/of.h>
#include <linux/phylink.h>
#include <linux/sfp.h>
#include <dt-bindings/net/qca-ar803x.h>
#include "qcom.h"
#define AT803X_LED_CONTROL 0x18
#define AT803X_PHY_MMD3_WOL_CTRL 0x8012
#define AT803X_WOL_EN BIT(5)
#define AT803X_REG_CHIP_CONFIG 0x1f
#define AT803X_BT_BX_REG_SEL 0x8000
#define AT803X_MODE_CFG_MASK 0x0F
#define AT803X_MODE_CFG_BASET_RGMII 0x00
#define AT803X_MODE_CFG_BASET_SGMII 0x01
#define AT803X_MODE_CFG_BX1000_RGMII_50OHM 0x02
#define AT803X_MODE_CFG_BX1000_RGMII_75OHM 0x03
#define AT803X_MODE_CFG_BX1000_CONV_50OHM 0x04
#define AT803X_MODE_CFG_BX1000_CONV_75OHM 0x05
#define AT803X_MODE_CFG_FX100_RGMII_50OHM 0x06
#define AT803X_MODE_CFG_FX100_CONV_50OHM 0x07
#define AT803X_MODE_CFG_RGMII_AUTO_MDET 0x0B
#define AT803X_MODE_CFG_FX100_RGMII_75OHM 0x0E
#define AT803X_MODE_CFG_FX100_CONV_75OHM 0x0F
#define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/
#define AT803X_PSSR_MR_AN_COMPLETE 0x0200
#define AT803X_DEBUG_REG_1F 0x1F
#define AT803X_DEBUG_PLL_ON BIT(2)
#define AT803X_DEBUG_RGMII_1V8 BIT(3)
/* AT803x supports either the XTAL input pad, an internal PLL or the
* DSP as clock reference for the clock output pad. The XTAL reference
* is only used for 25 MHz output, all other frequencies need the PLL.
* The DSP as a clock reference is used in synchronous ethernet
* applications.
*
* By default the PLL is only enabled if there is a link. Otherwise
* the PHY will go into low power state and disabled the PLL. You can
* set the PLL_ON bit (see debug register 0x1f) to keep the PLL always
* enabled.
*/
#define AT803X_MMD7_CLK25M 0x8016
#define AT803X_CLK_OUT_MASK GENMASK(4, 2)
#define AT803X_CLK_OUT_25MHZ_XTAL 0
#define AT803X_CLK_OUT_25MHZ_DSP 1
#define AT803X_CLK_OUT_50MHZ_PLL 2
#define AT803X_CLK_OUT_50MHZ_DSP 3
#define AT803X_CLK_OUT_62_5MHZ_PLL 4
#define AT803X_CLK_OUT_62_5MHZ_DSP 5
#define AT803X_CLK_OUT_125MHZ_PLL 6
#define AT803X_CLK_OUT_125MHZ_DSP 7
/* The AR8035 has another mask which is compatible with the AR8031/AR8033 mask
* but doesn't support choosing between XTAL/PLL and DSP.
*/
#define AT8035_CLK_OUT_MASK GENMASK(4, 3)
#define AT803X_CLK_OUT_STRENGTH_MASK GENMASK(8, 7)
#define AT803X_CLK_OUT_STRENGTH_FULL 0
#define AT803X_CLK_OUT_STRENGTH_HALF 1
#define AT803X_CLK_OUT_STRENGTH_QUARTER 2
#define AT803X_MMD3_SMARTEEE_CTL1 0x805b
#define AT803X_MMD3_SMARTEEE_CTL2 0x805c
#define AT803X_MMD3_SMARTEEE_CTL3 0x805d
#define AT803X_MMD3_SMARTEEE_CTL3_LPI_EN BIT(8)
#define ATH9331_PHY_ID 0x004dd041
#define ATH8030_PHY_ID 0x004dd076
#define ATH8031_PHY_ID 0x004dd074
#define ATH8032_PHY_ID 0x004dd023
#define ATH8035_PHY_ID 0x004dd072
#define AT8030_PHY_ID_MASK 0xffffffef
#define QCA9561_PHY_ID 0x004dd042
#define AT803X_PAGE_FIBER 0
#define AT803X_PAGE_COPPER 1
/* don't turn off internal PLL */
#define AT803X_KEEP_PLL_ENABLED BIT(0)
#define AT803X_DISABLE_SMARTEEE BIT(1)
/* disable hibernation mode */
#define AT803X_DISABLE_HIBERNATION_MODE BIT(2)
MODULE_DESCRIPTION("Qualcomm Atheros AR803x PHY driver");
MODULE_AUTHOR("Matus Ujhelyi");
MODULE_LICENSE("GPL");
struct at803x_priv {
int flags;
u16 clk_25m_reg;
u16 clk_25m_mask;
u8 smarteee_lpi_tw_1g;
u8 smarteee_lpi_tw_100m;
bool is_fiber;
bool is_1000basex;
struct regulator_dev *vddio_rdev;
struct regulator_dev *vddh_rdev;
};
struct at803x_context {
u16 bmcr;
u16 advertise;
u16 control1000;
u16 int_enable;
u16 smart_speed;
u16 led_control;
};
static int at803x_write_page(struct phy_device *phydev, int page)
{
int mask;
int set;
if (page == AT803X_PAGE_COPPER) {
set = AT803X_BT_BX_REG_SEL;
mask = 0;
} else {
set = 0;
mask = AT803X_BT_BX_REG_SEL;
}
return __phy_modify(phydev, AT803X_REG_CHIP_CONFIG, mask, set);
}
static int at803x_read_page(struct phy_device *phydev)
{
int ccr = __phy_read(phydev, AT803X_REG_CHIP_CONFIG);
if (ccr < 0)
return ccr;
if (ccr & AT803X_BT_BX_REG_SEL)
return AT803X_PAGE_COPPER;
return AT803X_PAGE_FIBER;
}
static int at803x_enable_rx_delay(struct phy_device *phydev)
{
return at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0,
AT803X_DEBUG_RX_CLK_DLY_EN);
}
static int at803x_enable_tx_delay(struct phy_device *phydev)
{
return at803x_debug_reg_mask(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0,
AT803X_DEBUG_TX_CLK_DLY_EN);
}
static int at803x_disable_rx_delay(struct phy_device *phydev)
{
return at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
AT803X_DEBUG_RX_CLK_DLY_EN, 0);
}
static int at803x_disable_tx_delay(struct phy_device *phydev)
{
return at803x_debug_reg_mask(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE,
AT803X_DEBUG_TX_CLK_DLY_EN, 0);
}
/* save relevant PHY registers to private copy */
static void at803x_context_save(struct phy_device *phydev,
struct at803x_context *context)
{
context->