Files
Linux_Drivers/linux_5.10/drivers/net/phy/cvitek.c
wangliang.wang 4f810186ab [linux] porting cvitek asic chips.
1. update cv182x/cv183x configuration file
	2. update cv181x/cv180x configuration file
	3. update clk driver for cvitek
	4. update dma driver for cvitek
	5. update soc driver for cvitek
	6. porting cvitek ion driver from kernel-4.19
	7. compatible with riscv

Change-Id: Icff9fafe0ebe7d6bab824bbadb952e08bdc66c19
2023-03-10 20:33:10 +08:00

189 lines
5.1 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/* Driver for CVITEK PHYs */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/phy.h>
#include <linux/netdevice.h>
#include <linux/bitops.h>
#define CVI_ETH_PHY_ADC_CTRL 16
#define CVI_ETH_PHY_INT_STS 16
#define CVI_ETH_AUTO_CALIBRATE 16
#define CVI_ETH_PHY_INT_MASK 17
#define CVI_ETH_PHY_APS 18
#define CVI_ETH_PHY_UAPS 19
#define CVI_ETH_PHY_LB_CTL 18
#define CVI_ETH_PHY_AFE_TX 20
#define CVI_ETH_PHY_AFE_DRV2 21
#define CVI_ETH_PHY_CP 23
#define CVI_ETH_PHY_ADC_OP_BIAS 24
#define CVI_ETH_PHY_STATUS 25
#define CVI_ETH_PHY_RX_SIGNAL 25
#define CVI_ETH_PHY_2_MISC 28
#define CVI_ETH_PHY_TXDATA_CTRL 24
#define CVI_ETH_PHY_PAGE_SEL 31
#define CVI_ETH_PHY_PAGE_0 0x0000
#define CVI_ETH_PHY_PAGE_1 0x0100
#define CVI_ETH_PHY_PAGE_2 0x0200
#define CVI_ETH_PHY_PAGE_3 0x0300
#define CVI_ETH_PHY_PAGE_6 0x0600
#define CVI_ETH_PHY_PAGE_8 0x0800
#define CVI_ETH_PHY_PAGE_9 0x0900
#define CVI_ETH_PHY_LOOPBACK
#define LOOPBACK_XMII2MAC 0x8000
#define LOOPBACK_PCS2MAC 0x2000
#define LOOPBACK_PMA2MAC 0x1000
#define LOOPBACK_RMII2PHY 0x0080
#define CVI_LNK_STS_CHG_INT_MSK BIT(15)
#define CVI_MGC_PKT_DET_INT_MSK BIT(14)
#define CVI_TX_LPI_RCV_INT_MSK BIT(13)
#define CVI_RX_LPI_RCV_INT_MSK BIT(12)
#define CVI_UAPS_ENABLE BIT(15)
#define CVI_CKSELIN BIT(13)
#define CVI_INT_EVENTS \
(CVI_LNK_STS_CHG_INT_MSK | CVI_MGC_PKT_DET_INT_MSK)
static int cvi_phy_config_intr(struct phy_device *phydev)
{
int rc = phy_write(phydev, CVI_ETH_PHY_INT_MASK,
((phydev->interrupts == PHY_INTERRUPT_ENABLED)
? CVI_INT_EVENTS
: 0));
return rc < 0 ? rc : 0;
}
static int cvi_phy_ack_interrupt(struct phy_device *phydev)
{
int err;
pr_debug("%s, clear interrupts\n", __func__);
err = phy_write(phydev, CVI_ETH_PHY_INT_STS, 0xf000);
return err < 0 ? err : 0;
}
static int cvi_read_status(struct phy_device *phydev)
{
int err = genphy_read_status(phydev);
pr_debug("%s, speed=%d, duplex=%d, ", __func__, phydev->speed, phydev->duplex);
pr_debug("pasue=%d, asym_pause=%d, autoneg=%d ", phydev->pause, phydev->asym_pause, phydev->autoneg);
pr_debug("cvi_Status=0x%04x\n", phy_read(phydev, CVI_ETH_PHY_STATUS));
return err;
}
#if defined(CONFIG_CVITEK_PHY_UAPS)
/* Ultra Auto Power Saving mode */
static int cvi_phy_aps_enable(struct phy_device *phydev)
{
int val;
pr_info("Enable CVITEK ethernet phy ultra auto power saving mode\n");
phy_write(phydev, CVI_ETH_PHY_PAGE_SEL, CVI_ETH_PHY_PAGE_1);
val = phy_read(phydev, CVI_ETH_PHY_UAPS);
phy_write(phydev, CVI_ETH_PHY_UAPS, CVI_UAPS_ENABLE | val);
phy_write(phydev, CVI_ETH_PHY_PAGE_SEL, CVI_ETH_PHY_PAGE_0);
return 0;
}
#endif
static int cvi_phy_config_aneg(struct phy_device *phydev)
{
int ret;
/* switch to page 1 */
phy_write(phydev, CVI_ETH_PHY_PAGE_SEL, CVI_ETH_PHY_PAGE_1);
/* Disable APS */
phy_write(phydev, CVI_ETH_PHY_APS, 0x4824);
/* switch to page 2 */
phy_write(phydev, CVI_ETH_PHY_PAGE_SEL, CVI_ETH_PHY_PAGE_2);
/* PHYAFE TRX optimization */
phy_write(phydev, CVI_ETH_PHY_TXDATA_CTRL, 0x0000);
/* switch to page 6 */
phy_write(phydev, CVI_ETH_PHY_PAGE_SEL, CVI_ETH_PHY_PAGE_6);
/* PHYAFE ADC optimization */
phy_write(phydev, CVI_ETH_PHY_ADC_CTRL, 0x555b);
/* PHYAFE TX optimization */
phy_write(phydev, CVI_ETH_PHY_AFE_TX, 0x508f);
/* enable a_CLKSELIN to avoid CRC error and increase 10M/100M amplitude*/
phy_write(phydev, CVI_ETH_PHY_AFE_DRV2, 0x3030);
/* PHYAFE CP current optimization */
phy_write(phydev, CVI_ETH_PHY_CP, 0x0575);
/* PHYAFE ADC OP BIAS optimization */
phy_write(phydev, CVI_ETH_PHY_ADC_OP_BIAS, 0x0000);
/* PHYAFE RX signal detector level optimization */
phy_write(phydev, CVI_ETH_PHY_RX_SIGNAL, 0x0408);
/* Enable PD control optimization */
phy_write(phydev, CVI_ETH_PHY_2_MISC, 0x8880);
/* switch to page 8 */
phy_write(phydev, CVI_ETH_PHY_PAGE_SEL, CVI_ETH_PHY_PAGE_8);
/* Stop auto-calibrate */
phy_write(phydev, CVI_ETH_AUTO_CALIBRATE, 0x0844);
#if defined(CONFIG_CVITEK_PHY_UAPS)
cvi_phy_aps_enable(phydev); /* if phy not work, disable this function for try */
#endif
/* switch back to page 0 */
phy_write(phydev, CVI_ETH_PHY_PAGE_SEL, CVI_ETH_PHY_PAGE_0);
ret = genphy_config_aneg(phydev);
if (ret < 0)
return ret;
return 0;
}
static int cvi_phy_config_init(struct phy_device *phydev)
{
int ret;
ret = genphy_read_abilities(phydev);
if (ret < 0)
return ret;
return 0;
}
static struct phy_driver cvitek_phy_driver[] = {
{
.phy_id = 0x00441400,
.phy_id_mask = 0xfffffff0,
.name = "CVITEK CVI18XX",
.get_features = genphy_read_abilities,
.read_status = cvi_read_status,
/* IRQ related */
.ack_interrupt = cvi_phy_ack_interrupt,
.config_intr = cvi_phy_config_intr,
.aneg_done = genphy_aneg_done,
.suspend = genphy_suspend,
.resume = genphy_resume,
.set_loopback = genphy_loopback,
} };
module_phy_driver(cvitek_phy_driver);
MODULE_DESCRIPTION("CVITEK PHY driver");
MODULE_AUTHOR("Ethan Chen");
MODULE_LICENSE("GPL");
static struct mdio_device_id __maybe_unused cvitek_tbl[] = {
{ 0x00441400, 0xfffffff0 },
{ }
};
MODULE_DEVICE_TABLE(mdio, cvitek_tbl);