Files
MilkV-Duo/linux_5.10/drivers/net/phy/cvitek.c
forum_service 7e1529d0dc linux-5.10: version release v4.1.0.3
0debf7abc [dma] When using DMA, if the DMA addr spans 128MB boundary   we have to split the DMA transfer into two so that each one     doesn't exceed the boundary.
4f7ed7449 Merge "[eth] change rxterm and vcm to link DianXin router" into v4.1.0
feb60bba5 Merge "[audio][lt9611]add drivers" into v4.1.0
a3dde371c [eth] change rxterm and vcm to link DianXin router
e615d3a43 [audio][lt9611]add drivers

Change-Id: If4fdcbc32df6aeaed04da0efbd23455376e2a53c
2023-12-21 16:51:57 +08:00

375 lines
11 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>
#include <linux/io.h>
#include <linux/cv180x_efuse.h>
#define REG_EPHY_TOP_WRAP 0x03009800
#define REG_EPHY_BASE 0x03009000
#define EPHY_EFUSE_TXECHORC_FLAG 0x00000100 // bit 8
#define EPHY_EFUSE_TXITUNE_FLAG 0x00000200 // bit 9
#define EPHY_EFUSE_TXRXTERM_FLAG 0x00000800 // bit 11
#define CVI_INT_EVENTS \
(CVI_LNK_STS_CHG_INT_MSK | CVI_MGC_PKT_DET_INT_MSK)
static int cv182xa_phy_config_intr(struct phy_device *phydev)
{
return 0;
}
static int cv182xa_phy_ack_interrupt(struct phy_device *phydev)
{
return 0;
}
static int cv182xa_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);
return err;
}
#if defined(CONFIG_CVITEK_PHY_UAPS)
/* Ultra Auto Power Saving mode */
static int cv182xa_phy_aps_enable(struct phy_device *phydev)
{
return 0;
}
#endif
static int cv182xa_phy_config_aneg(struct phy_device *phydev)
{
int ret;
#if defined(CONFIG_CVITEK_PHY_UAPS)
cv182xa_phy_aps_enable(phydev); /* if phy not work, disable this function for try */
#endif
ret = genphy_config_aneg(phydev);
if (ret < 0)
return ret;
return 0;
}
static int cv182xa_phy_config_init(struct phy_device *phydev)
{
int ret = 0;
u32 val = 0;
void __iomem *reg_ephy_top_wrap = NULL;
void __iomem *reg_ephy_base = NULL;
reg_ephy_top_wrap = ioremap(REG_EPHY_TOP_WRAP, 0x8);
if (!reg_ephy_top_wrap) {
ret = -EBUSY;
goto err_ephy_mem_1;
}
reg_ephy_base = ioremap(REG_EPHY_BASE, 0x80);
if (!reg_ephy_base) {
ret = -EBUSY;
goto err_ephy_mem_2;
}
// set rg_ephy_apb_rw_sel 0x0804@[0]=1/APB by using APB interface
writel(0x0001, reg_ephy_top_wrap + 4);
writel(0x0, reg_ephy_base + 0x7c);
/* do this in board.c */
// Release 0x0800[0]=0/shutdown
// writel(0x0900, reg_ephy_top_wrap);
// // Release 0x0800[2]=1/dig_rst_n, Let mii_reg can be accessabile
// writel(0x0904, reg_ephy_top_wrap);
// //mdelay(10);
// // ANA INIT (PD/EN), switch to MII-page5
// writel(0x0500, reg_ephy_base + 0x7c);
// // Release ANA_PD p5.0x10@[13:8] = 6'b001100
// writel(0x0c00, reg_ephy_base + 0x40);
// // Release ANA_EN p5.0x10@[7:0] = 8'b01111110
// writel(0x0c7e, reg_ephy_base + 0x40);
// // Wait PLL_Lock, Lock_Status p5.0x12@[15] = 1
// //mdelay(1);
// // Release 0x0800[1] = 1/ana_rst_n
// writel(0x0906, reg_ephy_top_wrap);
// // ANA INIT
// // @Switch to MII-page5
// writel(0x0500, reg_ephy_base + 0x7c);
// Efuse register
// Set Double Bias Current
//Set rg_eth_txitune1 reg_ephy_base + 0x64 [15:8]
//Set rg_eth_txitune0 reg_ephy_base + 0x64 [7:0]
if ((cvi_efuse_read_from_shadow(0x20) & EPHY_EFUSE_TXITUNE_FLAG) ==
EPHY_EFUSE_TXITUNE_FLAG) {
val = ((cvi_efuse_read_from_shadow(0x24) >> 24) & 0xFF) |
(((cvi_efuse_read_from_shadow(0x24) >> 16) & 0xFF) << 8);
writel((readl(reg_ephy_base + 0x64) & ~0xFFFF) | val, reg_ephy_base + 0x64);
} else
writel(0x5a5a, reg_ephy_base + 0x64);
// Set Echo_I
// Set rg_eth_txechoiadj reg_ephy_base + 0x54 [15:8]
if ((cvi_efuse_read_from_shadow(0x20) & EPHY_EFUSE_TXECHORC_FLAG) ==
EPHY_EFUSE_TXECHORC_FLAG) {
writel((readl(reg_ephy_base + 0x54) & ~0xFF00) |
(((cvi_efuse_read_from_shadow(0x24) >> 8) & 0xFF) << 8), reg_ephy_base + 0x54);
} else
writel(0x0000, reg_ephy_base + 0x54);
//Set TX_Rterm & Echo_RC_Delay
// Set rg_eth_txrterm_p1 reg_ephy_base + 0x58 [11:8]
// Set rg_eth_txrterm reg_ephy_base + 0x58 [7:4]
// Set rg_eth_txechorcadj reg_ephy_base + 0x58 [3:0]
if ((cvi_efuse_read_from_shadow(0x20) & EPHY_EFUSE_TXRXTERM_FLAG) ==
EPHY_EFUSE_TXRXTERM_FLAG) {
val = (((cvi_efuse_read_from_shadow(0x20) >> 28) & 0xF) << 4) |
(((cvi_efuse_read_from_shadow(0x20) >> 24) & 0xF) << 8);
writel((readl(reg_ephy_base + 0x58) & ~0xFF0) | val, reg_ephy_base + 0x58);
} else
writel(0x0bb0, reg_ephy_base + 0x58);
// ETH_100BaseT
// Set Rise update
writel(0x0c10, reg_ephy_base + 0x5c);
// Set Falling phase
writel(0x0003, reg_ephy_base + 0x68);
// Set Double TX Bias Current
writel(0x0000, reg_ephy_base + 0x54);
// Switch to MII-page16
writel(0x1000, reg_ephy_base + 0x7c);
// Set MLT3 Positive phase code, Set MLT3 +0
writel(0x1000, reg_ephy_base + 0x68);
writel(0x3020, reg_ephy_base + 0x6c);
writel(0x5040, reg_ephy_base + 0x70);
writel(0x7060, reg_ephy_base + 0x74);
// Set MLT3 +I
writel(0x1708, reg_ephy_base + 0x58);
writel(0x3827, reg_ephy_base + 0x5c);
writel(0x5748, reg_ephy_base + 0x60);
writel(0x7867, reg_ephy_base + 0x64);
// Switch to MII-page17
writel(0x1100, reg_ephy_base + 0x7c);
// Set MLT3 Negative phase code, Set MLT3 -0
writel(0x9080, reg_ephy_base + 0x40);
writel(0xb0a0, reg_ephy_base + 0x44);
writel(0xd0c0, reg_ephy_base + 0x48);
writel(0xf0e0, reg_ephy_base + 0x4c);
// Set MLT3 -I
writel(0x9788, reg_ephy_base + 0x50);
writel(0xb8a7, reg_ephy_base + 0x54);
writel(0xd7c8, reg_ephy_base + 0x58);
writel(0xf8e7, reg_ephy_base + 0x5c);
// @Switch to MII-page5
writel(0x0500, reg_ephy_base + 0x7c);
// En TX_Rterm
writel((0x0001 | readl(reg_ephy_base + 0x40)), reg_ephy_base + 0x40);
//Change rx vcm
writel((0x820 |readl(reg_ephy_base + 0x4c)), reg_ephy_base + 0x4c);
// Link Pulse
// Switch to MII-page10
writel(0x0a00, reg_ephy_base + 0x7c);
#if 1
// Set Link Pulse
writel(0x3e00, reg_ephy_base + 0x40);
writel(0x7864, reg_ephy_base + 0x44);
writel(0x6470, reg_ephy_base + 0x48);
writel(0x5f62, reg_ephy_base + 0x4c);
writel(0x5a5a, reg_ephy_base + 0x50);
writel(0x5458, reg_ephy_base + 0x54);
writel(0xb23a, reg_ephy_base + 0x58);
writel(0x94a0, reg_ephy_base + 0x5c);
writel(0x9092, reg_ephy_base + 0x60);
writel(0x8a8e, reg_ephy_base + 0x64);
writel(0x8688, reg_ephy_base + 0x68);
writel(0x8484, reg_ephy_base + 0x6c);
writel(0x0082, reg_ephy_base + 0x70);
#else
// from sean
// Fix err: the status is still linkup when removed the network cable.
writel(0x2000, reg_ephy_base + 0x40);
writel(0x3832, reg_ephy_base + 0x44);
writel(0x3132, reg_ephy_base + 0x48);
writel(0x2d2f, reg_ephy_base + 0x4c);
writel(0x2c2d, reg_ephy_base + 0x50);
writel(0x1b2b, reg_ephy_base + 0x54);
writel(0x94a0, reg_ephy_base + 0x58);
writel(0x8990, reg_ephy_base + 0x5c);
writel(0x8788, reg_ephy_base + 0x60);
writel(0x8485, reg_ephy_base + 0x64);
writel(0x8283, reg_ephy_base + 0x68);
writel(0x8182, reg_ephy_base + 0x6c);
writel(0x0081, reg_ephy_base + 0x70);
#endif
// TP_IDLE
// Switch to MII-page11
writel(0x0b00, reg_ephy_base + 0x7c);
// Set TP_IDLE
writel(0x5252, reg_ephy_base + 0x40);
writel(0x5252, reg_ephy_base + 0x44);
writel(0x4B52, reg_ephy_base + 0x48);
writel(0x3D47, reg_ephy_base + 0x4c);
writel(0xAA99, reg_ephy_base + 0x50);
writel(0x989E, reg_ephy_base + 0x54);
writel(0x9395, reg_ephy_base + 0x58);
writel(0x9091, reg_ephy_base + 0x5c);
writel(0x8E8F, reg_ephy_base + 0x60);
writel(0x8D8E, reg_ephy_base + 0x64);
writel(0x8C8C, reg_ephy_base + 0x68);
writel(0x8B8B, reg_ephy_base + 0x6c);
writel(0x008A, reg_ephy_base + 0x70);
// ETH 10BaseT Data
// Switch to MII-page13
writel(0x0d00, reg_ephy_base + 0x7c);
writel(0x1E0A, reg_ephy_base + 0x40);
writel(0x3862, reg_ephy_base + 0x44);
writel(0x1E62, reg_ephy_base + 0x48);
writel(0x2A08, reg_ephy_base + 0x4c);
writel(0x244C, reg_ephy_base + 0x50);
writel(0x1A44, reg_ephy_base + 0x54);
writel(0x061C, reg_ephy_base + 0x58);
// Switch to MII-page14
writel(0x0e00, reg_ephy_base + 0x7c);
writel(0x2D30, reg_ephy_base + 0x40);
writel(0x3470, reg_ephy_base + 0x44);
writel(0x0648, reg_ephy_base + 0x48);
writel(0x261C, reg_ephy_base + 0x4c);
writel(0x3160, reg_ephy_base + 0x50);
writel(0x2D5E, reg_ephy_base + 0x54);
// Switch to MII-page15
writel(0x0f00, reg_ephy_base + 0x7c);
writel(0x2922, reg_ephy_base + 0x40);
writel(0x366E, reg_ephy_base + 0x44);
writel(0x0752, reg_ephy_base + 0x48);
writel(0x2556, reg_ephy_base + 0x4c);
writel(0x2348, reg_ephy_base + 0x50);
writel(0x0C30, reg_ephy_base + 0x54);
// Switch to MII-page16
writel(0x1000, reg_ephy_base + 0x7c);
writel(0x1E08, reg_ephy_base + 0x40);
writel(0x3868, reg_ephy_base + 0x44);
writel(0x1462, reg_ephy_base + 0x48);
writel(0x1A0E, reg_ephy_base + 0x4c);
writel(0x305E, reg_ephy_base + 0x50);
writel(0x2F62, reg_ephy_base + 0x54);
// LED
// Switch to MII-page1
writel(0x0100, reg_ephy_base + 0x7c);
// select LED_LNK/SPD/DPX out to LED_PAD
writel((readl(reg_ephy_base + 0x68) & ~0x0f00), reg_ephy_base + 0x68);
// Switch to MII-page19
writel(0x1300, reg_ephy_base + 0x7c);
writel(0x0012, reg_ephy_base + 0x58);
// set agc max/min swing
writel(0x6848, reg_ephy_base + 0x5c);
// Switch to MII-page18
writel(0x1200, reg_ephy_base + 0x7c);
#if IS_ENABLED(CONFIG_ARCH_CV181X)
/* mars LPF(8, 8, 8, 8) HPF(-8, 50(+32), -36, -8) */
// lpf
writel(0x0808, reg_ephy_base + 0x48);
writel(0x0808, reg_ephy_base + 0x4c);
// hpf
writel(0x32f8, reg_ephy_base + 0x50);
writel(0xf8dc, reg_ephy_base + 0x54);
#elif IS_ENABLED(CONFIG_ARCH_CV180X)
/* phobos LPF:(1 8 23 23 8 1) HPF:(-4,58,-45,8,-5, 0) from sean PPT */
// lpf
writel(0x0801, reg_ephy_base + 0x48);
writel(0x1717, reg_ephy_base + 0x4C);
writel(0x0108, reg_ephy_base + 0x5C);
// hpf
writel(0x3afc, reg_ephy_base + 0x50);
writel(0x08d3, reg_ephy_base + 0x54);
writel(0x00fb, reg_ephy_base + 0x60);
#endif
// Switch to MII-page0
writel(0x0000, reg_ephy_base + 0x7c);
// EPHY start auto-neg procedure
writel(0x090e, reg_ephy_top_wrap);
// from jinyu.zhao
/* EPHY is configured as half-duplex after reset, but we need force full-duplex */
writel((readl(reg_ephy_base) | 0x100), reg_ephy_base);
// switch to MDIO control by ETH_MAC
writel(0x0000, reg_ephy_top_wrap + 4);
iounmap(reg_ephy_base);
err_ephy_mem_2:
iounmap(reg_ephy_top_wrap);
err_ephy_mem_1:
return ret;
}
static struct phy_driver cv182xa_phy_driver[] = {
{
.phy_id = 0x00435649,
.phy_id_mask = 0xffffffff,
.name = "CVITEK CV182XA",
.config_init = cv182xa_phy_config_init,
.config_aneg = cv182xa_phy_config_aneg,
.read_status = cv182xa_read_status,
/* IRQ related */
.ack_interrupt = cv182xa_phy_ack_interrupt,
.config_intr = cv182xa_phy_config_intr,
.aneg_done = genphy_aneg_done,
.suspend = genphy_suspend,
.resume = genphy_resume,
.set_loopback = genphy_loopback,
} };
module_phy_driver(cv182xa_phy_driver);
MODULE_DESCRIPTION("CV182XA EPHY driver");
MODULE_AUTHOR("Ethan Chen");
MODULE_LICENSE("GPL");
static struct mdio_device_id __maybe_unused cv182xa_tbl[] = {
{ 0x00435649, 0xffffffff },
{ }
};
MODULE_DEVICE_TABLE(mdio, cv182xa_tbl);