blob: a181fc262fc1b0edda837fcd82c336603c1ab0ca [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (c) 2022 MediaTek Inc.
* Author: Henry Yen <henry.yen@mediatek.com>
*/
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include "mtk_eth_soc.h"
static struct mtk_usxgmii_pcs *pcs_to_mtk_usxgmii_pcs(struct phylink_pcs *pcs)
{
return container_of(pcs, struct mtk_usxgmii_pcs, pcs);
}
int mtk_usxgmii_xfi_pextp_init(struct mtk_usxgmii *ss, struct device_node *r)
{
struct device_node *np;
int i;
for (i = 0; i < MTK_MAX_DEVS; i++) {
np = of_parse_phandle(r, "mediatek,xfi_pextp", i);
if (!np)
break;
ss->pcs[i].regmap_pextp = syscon_node_to_regmap(np);
if (IS_ERR(ss->pcs[i].regmap_pextp))
return PTR_ERR(ss->pcs[i].regmap_pextp);
of_node_put(np);
}
return 0;
}
int mtk_usxgmii_xfi_pll_init(struct mtk_usxgmii *ss, struct device_node *r)
{
struct device_node *np;
int i;
np = of_parse_phandle(r, "mediatek,xfi_pll", 0);
if (!np)
return -1;
for (i = 0; i < MTK_MAX_DEVS; i++) {
ss->pll = syscon_node_to_regmap(np);
if (IS_ERR(ss->pll))
return PTR_ERR(ss->pll);
}
of_node_put(np);
return 0;
}
int mtk_toprgu_init(struct mtk_eth *eth, struct device_node *r)
{
struct device_node *np;
np = of_parse_phandle(r, "mediatek,toprgu", 0);
if (!np)
return -1;
eth->toprgu = syscon_node_to_regmap(np);
if (IS_ERR(eth->toprgu))
return PTR_ERR(eth->toprgu);
return 0;
}
static int mtk_usxgmii_xfi_pll_enable(struct mtk_usxgmii *ss)
{
u32 val = 0;
if (!ss->pll)
return -EINVAL;
/* Add software workaround for USXGMII PLL TCL issue */
regmap_write(ss->pll, XFI_PLL_ANA_GLB8, RG_XFI_PLL_ANA_SWWA);
regmap_read(ss->pll, XFI_PLL_DIG_GLB8, &val);
val |= RG_XFI_PLL_EN;
regmap_write(ss->pll, XFI_PLL_DIG_GLB8, val);
return 0;
}
int mtk_mac2xgmii_id(struct mtk_eth *eth, int mac_id)
{
u32 xgmii_id = mac_id;
if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
switch (mac_id) {
case MTK_GMAC1_ID:
case MTK_GMAC2_ID:
xgmii_id = 1;
break;
case MTK_GMAC3_ID:
xgmii_id = 0;
break;
default:
pr_info("[%s] Warning: get illegal mac_id=%d !=!!!\n",
__func__, mac_id);
}
}
return xgmii_id;
}
int mtk_xgmii2mac_id(struct mtk_eth *eth, int xgmii_id)
{
u32 mac_id = xgmii_id;
if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
switch (xgmii_id) {
case 0:
mac_id = 2;
break;
case 1:
mac_id = 1;
break;
default:
pr_info("[%s] Warning: get illegal xgmii_id=%d !=!!!\n",
__func__, xgmii_id);
}
}
return mac_id;
}
void mtk_usxgmii_setup_phya_usxgmii(struct mtk_usxgmii_pcs *mpcs)
{
if (!mpcs->regmap_pextp)
return;
/* Setup operation mode */
regmap_update_bits(mpcs->regmap_pextp, 0x9024, GENMASK(31, 0),
0x00C9071C);
regmap_update_bits(mpcs->regmap_pextp, 0x2020, GENMASK(31, 0),
0xAA8585AA);
regmap_update_bits(mpcs->regmap_pextp, 0x2030, GENMASK(31, 0),
0x0C020707);
regmap_update_bits(mpcs->regmap_pextp, 0x2034, GENMASK(31, 0),
0x0E050F0F);
regmap_update_bits(mpcs->regmap_pextp, 0x2040, GENMASK(31, 0),
0x00140032);
regmap_update_bits(mpcs->regmap_pextp, 0x50F0, GENMASK(31, 0),
0x00C014AA);
regmap_update_bits(mpcs->regmap_pextp, 0x50E0, GENMASK(31, 0),
0x3777C12B);
regmap_update_bits(mpcs->regmap_pextp, 0x506C, GENMASK(31, 0),
0x005F9CFF);
regmap_update_bits(mpcs->regmap_pextp, 0x5070, GENMASK(31, 0),
0x9D9DFAFA);
regmap_update_bits(mpcs->regmap_pextp, 0x5074, GENMASK(31, 0),
0x27273F3F);
regmap_update_bits(mpcs->regmap_pextp, 0x5078, GENMASK(31, 0),
0xA7883C68);
regmap_update_bits(mpcs->regmap_pextp, 0x507C, GENMASK(31, 0),
0x11661166);
regmap_update_bits(mpcs->regmap_pextp, 0x5080, GENMASK(31, 0),
0x0E000AAF);
regmap_update_bits(mpcs->regmap_pextp, 0x5084, GENMASK(31, 0),
0x08080D0D);
regmap_update_bits(mpcs->regmap_pextp, 0x5088, GENMASK(31, 0),
0x02030909);
regmap_update_bits(mpcs->regmap_pextp, 0x50E4, GENMASK(31, 0),
0x0C0C0000);
regmap_update_bits(mpcs->regmap_pextp, 0x50E8, GENMASK(31, 0),
0x04040000);
regmap_update_bits(mpcs->regmap_pextp, 0x50EC, GENMASK(31, 0),
0x0F0F0C06);
regmap_update_bits(mpcs->regmap_pextp, 0x50A8, GENMASK(31, 0),
0x506E8C8C);
regmap_update_bits(mpcs->regmap_pextp, 0x6004, GENMASK(31, 0),
0x18190000);
regmap_update_bits(mpcs->regmap_pextp, 0x00F8, GENMASK(31, 0),
0x01423342);
/* Force SGDT_OUT off and select PCS */
regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0),
0x80201F20);
/* Force GLB_CKDET_OUT */
regmap_update_bits(mpcs->regmap_pextp, 0x0030, GENMASK(31, 0),
0x00050C00);
/* Force AEQ on */
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x02002800);
ndelay(1020);
/* Setup DA default value */
regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
0x00000020);
regmap_update_bits(mpcs->regmap_pextp, 0x3028, GENMASK(31, 0),
0x00008A01);
regmap_update_bits(mpcs->regmap_pextp, 0x302C, GENMASK(31, 0),
0x0000A884);
regmap_update_bits(mpcs->regmap_pextp, 0x3024, GENMASK(31, 0),
0x00083002);
regmap_update_bits(mpcs->regmap_pextp, 0x3010, GENMASK(31, 0),
0x00022220);
regmap_update_bits(mpcs->regmap_pextp, 0x5064, GENMASK(31, 0),
0x0F020A01);
regmap_update_bits(mpcs->regmap_pextp, 0x50B4, GENMASK(31, 0),
0x06100600);
regmap_update_bits(mpcs->regmap_pextp, 0x3048, GENMASK(31, 0),
0x40704000);
regmap_update_bits(mpcs->regmap_pextp, 0x3050, GENMASK(31, 0),
0xA8000000);
regmap_update_bits(mpcs->regmap_pextp, 0x3054, GENMASK(31, 0),
0x000000AA);
regmap_update_bits(mpcs->regmap_pextp, 0x306C, GENMASK(31, 0),
0x00000F00);
regmap_update_bits(mpcs->regmap_pextp, 0xA060, GENMASK(31, 0),
0x00040000);
regmap_update_bits(mpcs->regmap_pextp, 0x90D0, GENMASK(31, 0),
0x00000001);
/* Release reset */
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0200E800);
udelay(150);
/* Switch to P0 */
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0200C111);
ndelay(1020);
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0200C101);
udelay(15);
/* Switch to Gen3 */
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0202C111);
ndelay(1020);
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0202C101);
udelay(100);
regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
0x00000030);
regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0),
0x80201F00);
regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
0x30000000);
udelay(400);
}
void mtk_usxgmii_setup_phya_5gbaser(struct mtk_usxgmii_pcs *mpcs)
{
if (!mpcs->regmap_pextp)
return;
/* Setup operation mode */
regmap_update_bits(mpcs->regmap_pextp, 0x9024, GENMASK(31, 0),
0x00D9071C);
regmap_update_bits(mpcs->regmap_pextp, 0x2020, GENMASK(31, 0),
0xAAA5A5AA);
regmap_update_bits(mpcs->regmap_pextp, 0x2030, GENMASK(31, 0),
0x0C020707);
regmap_update_bits(mpcs->regmap_pextp, 0x2034, GENMASK(31, 0),
0x0E050F0F);
regmap_update_bits(mpcs->regmap_pextp, 0x2040, GENMASK(31, 0),
0x00140032);
regmap_update_bits(mpcs->regmap_pextp, 0x50F0, GENMASK(31, 0),
0x00C018AA);
regmap_update_bits(mpcs->regmap_pextp, 0x50E0, GENMASK(31, 0),
0x3777812B);
regmap_update_bits(mpcs->regmap_pextp, 0x506C, GENMASK(31, 0),
0x005C9CFF);
regmap_update_bits(mpcs->regmap_pextp, 0x5070, GENMASK(31, 0),
0x9DFAFAFA);
regmap_update_bits(mpcs->regmap_pextp, 0x5074, GENMASK(31, 0),
0x273F3F3F);
regmap_update_bits(mpcs->regmap_pextp, 0x5078, GENMASK(31, 0),
0xA8883868);
regmap_update_bits(mpcs->regmap_pextp, 0x507C, GENMASK(31, 0),
0x14661466);
regmap_update_bits(mpcs->regmap_pextp, 0x5080, GENMASK(31, 0),
0x0E001ABF);
regmap_update_bits(mpcs->regmap_pextp, 0x5084, GENMASK(31, 0),
0x080B0D0D);
regmap_update_bits(mpcs->regmap_pextp, 0x5088, GENMASK(31, 0),
0x02050909);
regmap_update_bits(mpcs->regmap_pextp, 0x50E4, GENMASK(31, 0),
0x0C000000);
regmap_update_bits(mpcs->regmap_pextp, 0x50E8, GENMASK(31, 0),
0x04000000);
regmap_update_bits(mpcs->regmap_pextp, 0x50EC, GENMASK(31, 0),
0x0F0F0C06);
regmap_update_bits(mpcs->regmap_pextp, 0x50A8, GENMASK(31, 0),
0x50808C8C);
regmap_update_bits(mpcs->regmap_pextp, 0x6004, GENMASK(31, 0),
0x18000000);
regmap_update_bits(mpcs->regmap_pextp, 0x00F8, GENMASK(31, 0),
0x00A132A1);
/* Force SGDT_OUT off and select PCS */
regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0),
0x80201F20);
/* Force GLB_CKDET_OUT */
regmap_update_bits(mpcs->regmap_pextp, 0x0030, GENMASK(31, 0),
0x00050C00);
/* Force AEQ on */
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x02002800);
ndelay(1020);
/* Setup DA default value */
regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
0x00000020);
regmap_update_bits(mpcs->regmap_pextp, 0x3028, GENMASK(31, 0),
0x00008A01);
regmap_update_bits(mpcs->regmap_pextp, 0x302C, GENMASK(31, 0),
0x0000A884);
regmap_update_bits(mpcs->regmap_pextp, 0x3024, GENMASK(31, 0),
0x00083002);
regmap_update_bits(mpcs->regmap_pextp, 0x3010, GENMASK(31, 0),
0x00022220);
regmap_update_bits(mpcs->regmap_pextp, 0x5064, GENMASK(31, 0),
0x0F020A01);
regmap_update_bits(mpcs->regmap_pextp, 0x50B4, GENMASK(31, 0),
0x06100600);
regmap_update_bits(mpcs->regmap_pextp, 0x3048, GENMASK(31, 0),
0x40704000);
regmap_update_bits(mpcs->regmap_pextp, 0x3050, GENMASK(31, 0),
0xA8000000);
regmap_update_bits(mpcs->regmap_pextp, 0x3054, GENMASK(31, 0),
0x000000AA);
regmap_update_bits(mpcs->regmap_pextp, 0x306C, GENMASK(31, 0),
0x00000F00);
regmap_update_bits(mpcs->regmap_pextp, 0xA060, GENMASK(31, 0),
0x00040000);
regmap_update_bits(mpcs->regmap_pextp, 0x90D0, GENMASK(31, 0),
0x00000003);
/* Release reset */
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0200E800);
udelay(150);
/* Switch to P0 */
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0200C111);
ndelay(1020);
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0200C101);
udelay(15);
/* Switch to Gen3 */
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0202C111);
ndelay(1020);
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0202C101);
udelay(100);
regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
0x00000030);
regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0),
0x80201F00);
regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
0x30000000);
udelay(400);
}
void mtk_usxgmii_setup_phya_10gbaser(struct mtk_usxgmii_pcs *mpcs)
{
if (!mpcs->regmap_pextp)
return;
/* Setup operation mode */
regmap_update_bits(mpcs->regmap_pextp, 0x9024, GENMASK(31, 0),
0x00C9071C);
regmap_update_bits(mpcs->regmap_pextp, 0x2020, GENMASK(31, 0),
0xAA8585AA);
regmap_update_bits(mpcs->regmap_pextp, 0x2030, GENMASK(31, 0),
0x0C020707);
regmap_update_bits(mpcs->regmap_pextp, 0x2034, GENMASK(31, 0),
0x0E050F0F);
regmap_update_bits(mpcs->regmap_pextp, 0x2040, GENMASK(31, 0),
0x00140032);
regmap_update_bits(mpcs->regmap_pextp, 0x50F0, GENMASK(31, 0),
0x00C014AA);
regmap_update_bits(mpcs->regmap_pextp, 0x50E0, GENMASK(31, 0),
0x3777C12B);
regmap_update_bits(mpcs->regmap_pextp, 0x506C, GENMASK(31, 0),
0x005F9CFF);
regmap_update_bits(mpcs->regmap_pextp, 0x5070, GENMASK(31, 0),
0x9D9DFAFA);
regmap_update_bits(mpcs->regmap_pextp, 0x5074, GENMASK(31, 0),
0x27273F3F);
regmap_update_bits(mpcs->regmap_pextp, 0x5078, GENMASK(31, 0),
0xA7883C68);
regmap_update_bits(mpcs->regmap_pextp, 0x507C, GENMASK(31, 0),
0x11661166);
regmap_update_bits(mpcs->regmap_pextp, 0x5080, GENMASK(31, 0),
0x0E000AAF);
regmap_update_bits(mpcs->regmap_pextp, 0x5084, GENMASK(31, 0),
0x08080D0D);
regmap_update_bits(mpcs->regmap_pextp, 0x5088, GENMASK(31, 0),
0x02030909);
regmap_update_bits(mpcs->regmap_pextp, 0x50E4, GENMASK(31, 0),
0x0C0C0000);
regmap_update_bits(mpcs->regmap_pextp, 0x50E8, GENMASK(31, 0),
0x04040000);
regmap_update_bits(mpcs->regmap_pextp, 0x50EC, GENMASK(31, 0),
0x0F0F0C06);
regmap_update_bits(mpcs->regmap_pextp, 0x50A8, GENMASK(31, 0),
0x506E8C8C);
regmap_update_bits(mpcs->regmap_pextp, 0x6004, GENMASK(31, 0),
0x18190000);
regmap_update_bits(mpcs->regmap_pextp, 0x00F8, GENMASK(31, 0),
0x01423342);
/* Force SGDT_OUT off and select PCS */
regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0),
0x80201F20);
/* Force GLB_CKDET_OUT */
regmap_update_bits(mpcs->regmap_pextp, 0x0030, GENMASK(31, 0),
0x00050C00);
/* Force AEQ on */
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x02002800);
ndelay(1020);
/* Setup DA default value */
regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
0x00000020);
regmap_update_bits(mpcs->regmap_pextp, 0x3028, GENMASK(31, 0),
0x00008A01);
regmap_update_bits(mpcs->regmap_pextp, 0x302C, GENMASK(31, 0),
0x0000A884);
regmap_update_bits(mpcs->regmap_pextp, 0x3024, GENMASK(31, 0),
0x00083002);
regmap_update_bits(mpcs->regmap_pextp, 0x3010, GENMASK(31, 0),
0x00022220);
regmap_update_bits(mpcs->regmap_pextp, 0x5064, GENMASK(31, 0),
0x0F020A01);
regmap_update_bits(mpcs->regmap_pextp, 0x50B4, GENMASK(31, 0),
0x06100600);
regmap_update_bits(mpcs->regmap_pextp, 0x3048, GENMASK(31, 0),
0x47684100);
regmap_update_bits(mpcs->regmap_pextp, 0x3050, GENMASK(31, 0),
0x00000000);
regmap_update_bits(mpcs->regmap_pextp, 0x3054, GENMASK(31, 0),
0x00000000);
regmap_update_bits(mpcs->regmap_pextp, 0x306C, GENMASK(31, 0),
0x00000F00);
if (mpcs->id == 0)
regmap_update_bits(mpcs->regmap_pextp, 0xA008, GENMASK(31, 0),
0x0007B400);
regmap_update_bits(mpcs->regmap_pextp, 0xA060, GENMASK(31, 0),
0x00040000);
regmap_update_bits(mpcs->regmap_pextp, 0x90D0, GENMASK(31, 0),
0x00000001);
/* Release reset */
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0200E800);
udelay(150);
/* Switch to P0 */
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0200C111);
ndelay(1020);
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0200C101);
udelay(15);
/* Switch to Gen3 */
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0202C111);
ndelay(1020);
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
0x0202C101);
udelay(100);
regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
0x00000030);
regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0),
0x80201F00);
regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
0x30000000);
udelay(400);
}
void mtk_usxgmii_reset(struct mtk_eth *eth, int id)
{
u32 val = 0;
if (id >= MTK_MAX_DEVS || !eth->toprgu)
return;
switch (id) {
case 0:
/* Enable software reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val);
val |= SWSYSRST_XFI_PEXPT0_GRST |
SWSYSRST_XFI0_GRST;
regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val);
/* Assert USXGMII reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val);
val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88) |
SWSYSRST_XFI_PEXPT0_GRST |
SWSYSRST_XFI0_GRST;
regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val);
udelay(100);
/* De-assert USXGMII reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val);
val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88);
val &= ~(SWSYSRST_XFI_PEXPT0_GRST |
SWSYSRST_XFI0_GRST);
regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val);
/* Disable software reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val);
val &= ~(SWSYSRST_XFI_PEXPT0_GRST |
SWSYSRST_XFI0_GRST);
regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val);
break;
case 1:
/* Enable software reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val);
val |= SWSYSRST_XFI_PEXPT1_GRST |
SWSYSRST_XFI1_GRST;
regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val);
/* Assert USXGMII reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val);
val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88) |
SWSYSRST_XFI_PEXPT1_GRST |
SWSYSRST_XFI1_GRST;
regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val);
udelay(100);
/* De-assert USXGMII reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val);
val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88);
val &= ~(SWSYSRST_XFI_PEXPT1_GRST |
SWSYSRST_XFI1_GRST);
regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val);
/* Disable software reset */
regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val);
val &= ~(SWSYSRST_XFI_PEXPT1_GRST |
SWSYSRST_XFI1_GRST);
regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val);
break;
}
mdelay(10);
}
static int mtk_usxgmii_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
phy_interface_t interface,
const unsigned long *advertising,
bool permit_pause_to_mac)
{
struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs);
struct mtk_eth *eth = mpcs->eth;
unsigned int an_ctrl = 0, link_timer = 0, xfi_mode = 0, adapt_mode = 0;
bool mode_changed = false;
if (interface == PHY_INTERFACE_MODE_USXGMII) {
an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0x1FF) |
USXGMII_AN_ENABLE;
link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x7B) |
FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x7B) |
FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x7B);
xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_RX_MODE_10G) |
FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_TX_MODE_10G);
} else if (interface == PHY_INTERFACE_MODE_10GKR) {
an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0x1FF);
link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x7B) |
FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x7B) |
FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x7B);
xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_RX_MODE_10G) |
FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_TX_MODE_10G);
adapt_mode = USXGMII_RATE_UPDATE_MODE;
} else if (interface == PHY_INTERFACE_MODE_5GBASER) {
an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0xFF);
link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x3D) |
FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x3D) |
FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x3D);
xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_RX_MODE_5G) |
FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_TX_MODE_5G);
adapt_mode = USXGMII_RATE_UPDATE_MODE;
} else
return -EINVAL;
adapt_mode |= FIELD_PREP(USXGMII_RATE_ADAPT_MODE, USXGMII_RATE_ADAPT_MODE_X1);
if (mpcs->interface != interface) {
mpcs->interface = interface;
mode_changed = true;
}
mtk_usxgmii_xfi_pll_enable(eth->usxgmii);
mtk_usxgmii_reset(eth, mpcs->id);
/* Setup USXGMII AN ctrl */
regmap_update_bits(mpcs->regmap, RG_PCS_AN_CTRL0,
USXGMII_AN_SYNC_CNT | USXGMII_AN_ENABLE,
an_ctrl);
regmap_update_bits(mpcs->regmap, RG_PCS_AN_CTRL2,
USXGMII_LINK_TIMER_IDLE_DETECT |
USXGMII_LINK_TIMER_COMP_ACK_DETECT |
USXGMII_LINK_TIMER_AN_RESTART,
link_timer);
/* Gated MAC CK */
regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1,
USXGMII_MAC_CK_GATED, USXGMII_MAC_CK_GATED);
/* Enable interface force mode */
regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1,
USXGMII_IF_FORCE_EN, USXGMII_IF_FORCE_EN);
/* Setup USXGMII adapt mode */
regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1,
USXGMII_RATE_UPDATE_MODE | USXGMII_RATE_ADAPT_MODE,
adapt_mode);
/* Setup USXGMII speed */
regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1,
USXGMII_XFI_RX_MODE | USXGMII_XFI_TX_MODE,
xfi_mode);
udelay(1);
/* Un-gated MAC CK */
regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1,
USXGMII_MAC_CK_GATED, 0);
udelay(1);
/* Disable interface force mode for the AN mode */
if (an_ctrl & USXGMII_AN_ENABLE)
regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1,
USXGMII_IF_FORCE_EN, 0);
/* Setup USXGMIISYS with the determined property */
if (interface == PHY_INTERFACE_MODE_USXGMII)
mtk_usxgmii_setup_phya_usxgmii(mpcs);
else if (interface == PHY_INTERFACE_MODE_10GKR)
mtk_usxgmii_setup_phya_10gbaser(mpcs);
else if (interface == PHY_INTERFACE_MODE_5GBASER)
mtk_usxgmii_setup_phya_5gbaser(mpcs);
return mode_changed;
}
static void mtk_usxgmii_pcs_get_state(struct phylink_pcs *pcs,
struct phylink_link_state *state)
{
struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs);
struct mtk_eth *eth = mpcs->eth;
struct mtk_mac *mac = eth->mac[mtk_xgmii2mac_id(eth, mpcs->id)];
u32 val = 0;
regmap_read(mpcs->regmap, RG_PCS_AN_CTRL0, &val);
if (FIELD_GET(USXGMII_AN_ENABLE, val)) {
/* Refresh LPA by inverting LPA_LATCH */
regmap_read(mpcs->regmap, RG_PCS_AN_STS0, &val);
regmap_update_bits(mpcs->regmap, RG_PCS_AN_STS0,
USXGMII_LPA_LATCH,
!(val & USXGMII_LPA_LATCH));
regmap_read(mpcs->regmap, RG_PCS_AN_STS0, &val);
state->interface = mpcs->interface;
state->link = FIELD_GET(USXGMII_LPA_LINK, val);
state->duplex = FIELD_GET(USXGMII_LPA_DUPLEX, val);
switch (FIELD_GET(USXGMII_LPA_SPEED_MASK, val)) {
case USXGMII_LPA_SPEED_10:
state->speed = SPEED_10;
break;
case USXGMII_LPA_SPEED_100:
state->speed = SPEED_100;
break;
case USXGMII_LPA_SPEED_1000:
state->speed = SPEED_1000;
break;
case USXGMII_LPA_SPEED_2500:
state->speed = SPEED_2500;
break;
case USXGMII_LPA_SPEED_5000:
state->speed = SPEED_5000;
break;
case USXGMII_LPA_SPEED_10000:
state->speed = SPEED_10000;
break;
}
} else {
val = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id));
if (mac->id == MTK_GMAC2_ID)
val = val >> 16;
switch (FIELD_GET(MTK_USXGMII_PCS_MODE, val)) {
case 0:
state->speed = SPEED_10000;
break;
case 1:
state->speed = SPEED_5000;
break;
case 2:
state->speed = SPEED_2500;
break;
case 3:
state->speed = SPEED_1000;
break;
}
state->interface = mpcs->interface;
state->link = FIELD_GET(MTK_USXGMII_PCS_LINK, val);
state->duplex = DUPLEX_FULL;
}
if (state->link == 0)
mtk_usxgmii_pcs_config(pcs, MLO_AN_INBAND,
state->interface, NULL, false);
}
void mtk_usxgmii_pcs_restart_an(struct phylink_pcs *pcs)
{
struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs);
unsigned int val = 0;
if (!mpcs->regmap)
return;
regmap_read(mpcs->regmap, RG_PCS_AN_CTRL0, &val);
val |= USXGMII_AN_RESTART;
regmap_write(mpcs->regmap, RG_PCS_AN_CTRL0, val);
}
static void mtk_usxgmii_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
phy_interface_t interface,
int speed, int duplex)
{
/* Reconfiguring USXGMII to ensure the quality of the RX signal
* after the line side link up.
*/
mtk_usxgmii_pcs_config(pcs, mode,
interface, NULL, false);
}
static const struct phylink_pcs_ops mtk_usxgmii_pcs_ops = {
.pcs_config = mtk_usxgmii_pcs_config,
.pcs_get_state = mtk_usxgmii_pcs_get_state,
.pcs_an_restart = mtk_usxgmii_pcs_restart_an,
.pcs_link_up = mtk_usxgmii_pcs_link_up,
};
int mtk_usxgmii_init(struct mtk_eth *eth, struct device_node *r)
{
struct mtk_usxgmii *ss = eth->usxgmii;
struct device_node *np;
int ret, i;
for (i = 0; i < MTK_MAX_DEVS; i++) {
np = of_parse_phandle(r, "mediatek,usxgmiisys", i);
if (!np)
break;
ss->pcs[i].id = i;
ss->pcs[i].eth = eth;
ss->pcs[i].regmap = syscon_node_to_regmap(np);
if (IS_ERR(ss->pcs[i].regmap))
return PTR_ERR(ss->pcs[i].regmap);
ss->pcs[i].pcs.ops = &mtk_usxgmii_pcs_ops;
ss->pcs[i].pcs.poll = true;
ss->pcs[i].interface = PHY_INTERFACE_MODE_NA;
of_node_put(np);
}
ret = mtk_usxgmii_xfi_pextp_init(ss, r);
if (ret)
return ret;
ret = mtk_usxgmii_xfi_pll_init(ss, r);
if (ret)
return ret;
return 0;
}
struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_usxgmii *ss, int id)
{
if (!ss->pcs[id].regmap)
return NULL;
return &ss->pcs[id].pcs;
}
int mtk_dump_usxgmii(struct regmap *pmap, char *name, u32 offset, u32 range)
{
unsigned int cur = offset;
unsigned int val1 = 0, val2 = 0, val3 = 0, val4 = 0;
pr_info("\n============ %s ============ pmap:%lx\n",
name, (unsigned long)pmap);
while (cur < offset + range) {
regmap_read(pmap, cur, &val1);
regmap_read(pmap, cur + 0x4, &val2);
regmap_read(pmap, cur + 0x8, &val3);
regmap_read(pmap, cur + 0xc, &val4);
pr_info("0x%x: %08x %08x %08x %08x\n", cur,
val1, val2, val3, val4);
cur += 0x10;
}
return 0;
}