18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * SGMI module initialisation
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Texas Instruments Incorporated
68c2ecf20Sopenharmony_ci * Authors:	Sandeep Nair <sandeep_n@ti.com>
78c2ecf20Sopenharmony_ci *		Sandeep Paulraj <s-paulraj@ti.com>
88c2ecf20Sopenharmony_ci *		Wingman Kwok <w-kwok2@ti.com>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "netcp.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define SGMII_SRESET_RESET		BIT(0)
158c2ecf20Sopenharmony_ci#define SGMII_SRESET_RTRESET		BIT(1)
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define SGMII_REG_STATUS_LOCK		BIT(4)
188c2ecf20Sopenharmony_ci#define	SGMII_REG_STATUS_LINK		BIT(0)
198c2ecf20Sopenharmony_ci#define SGMII_REG_STATUS_AUTONEG	BIT(2)
208c2ecf20Sopenharmony_ci#define SGMII_REG_CONTROL_AUTONEG	BIT(0)
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define SGMII23_OFFSET(x)	((x - 2) * 0x100)
238c2ecf20Sopenharmony_ci#define SGMII_OFFSET(x)		((x <= 1) ? (x * 0x100) : (SGMII23_OFFSET(x)))
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* SGMII registers */
268c2ecf20Sopenharmony_ci#define SGMII_SRESET_REG(x)   (SGMII_OFFSET(x) + 0x004)
278c2ecf20Sopenharmony_ci#define SGMII_CTL_REG(x)      (SGMII_OFFSET(x) + 0x010)
288c2ecf20Sopenharmony_ci#define SGMII_STATUS_REG(x)   (SGMII_OFFSET(x) + 0x014)
298c2ecf20Sopenharmony_ci#define SGMII_MRADV_REG(x)    (SGMII_OFFSET(x) + 0x018)
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic void sgmii_write_reg(void __iomem *base, int reg, u32 val)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	writel(val, base + reg);
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic u32 sgmii_read_reg(void __iomem *base, int reg)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	return readl(base + reg);
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic void sgmii_write_reg_bit(void __iomem *base, int reg, u32 val)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	writel((readl(base + reg) | val), base + reg);
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* port is 0 based */
478c2ecf20Sopenharmony_ciint netcp_sgmii_reset(void __iomem *sgmii_ofs, int port)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	/* Soft reset */
508c2ecf20Sopenharmony_ci	sgmii_write_reg_bit(sgmii_ofs, SGMII_SRESET_REG(port),
518c2ecf20Sopenharmony_ci			    SGMII_SRESET_RESET);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	while ((sgmii_read_reg(sgmii_ofs, SGMII_SRESET_REG(port)) &
548c2ecf20Sopenharmony_ci		SGMII_SRESET_RESET) != 0x0)
558c2ecf20Sopenharmony_ci		;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return 0;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/* port is 0 based */
618c2ecf20Sopenharmony_cibool netcp_sgmii_rtreset(void __iomem *sgmii_ofs, int port, bool set)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	u32 reg;
648c2ecf20Sopenharmony_ci	bool oldval;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	/* Initiate a soft reset */
678c2ecf20Sopenharmony_ci	reg = sgmii_read_reg(sgmii_ofs, SGMII_SRESET_REG(port));
688c2ecf20Sopenharmony_ci	oldval = (reg & SGMII_SRESET_RTRESET) != 0x0;
698c2ecf20Sopenharmony_ci	if (set)
708c2ecf20Sopenharmony_ci		reg |= SGMII_SRESET_RTRESET;
718c2ecf20Sopenharmony_ci	else
728c2ecf20Sopenharmony_ci		reg &= ~SGMII_SRESET_RTRESET;
738c2ecf20Sopenharmony_ci	sgmii_write_reg(sgmii_ofs, SGMII_SRESET_REG(port), reg);
748c2ecf20Sopenharmony_ci	wmb();
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	return oldval;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ciint netcp_sgmii_get_port_link(void __iomem *sgmii_ofs, int port)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	u32 status = 0, link = 0;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	status = sgmii_read_reg(sgmii_ofs, SGMII_STATUS_REG(port));
848c2ecf20Sopenharmony_ci	if ((status & SGMII_REG_STATUS_LINK) != 0)
858c2ecf20Sopenharmony_ci		link = 1;
868c2ecf20Sopenharmony_ci	return link;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ciint netcp_sgmii_config(void __iomem *sgmii_ofs, int port, u32 interface)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	unsigned int i, status, mask;
928c2ecf20Sopenharmony_ci	u32 mr_adv_ability;
938c2ecf20Sopenharmony_ci	u32 control;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	switch (interface) {
968c2ecf20Sopenharmony_ci	case SGMII_LINK_MAC_MAC_AUTONEG:
978c2ecf20Sopenharmony_ci		mr_adv_ability	= 0x9801;
988c2ecf20Sopenharmony_ci		control		= 0x21;
998c2ecf20Sopenharmony_ci		break;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	case SGMII_LINK_MAC_PHY:
1028c2ecf20Sopenharmony_ci	case SGMII_LINK_MAC_PHY_NO_MDIO:
1038c2ecf20Sopenharmony_ci		mr_adv_ability	= 1;
1048c2ecf20Sopenharmony_ci		control		= 1;
1058c2ecf20Sopenharmony_ci		break;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	case SGMII_LINK_MAC_MAC_FORCED:
1088c2ecf20Sopenharmony_ci		mr_adv_ability	= 0x9801;
1098c2ecf20Sopenharmony_ci		control		= 0x20;
1108c2ecf20Sopenharmony_ci		break;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	case SGMII_LINK_MAC_FIBER:
1138c2ecf20Sopenharmony_ci		mr_adv_ability	= 0x20;
1148c2ecf20Sopenharmony_ci		control		= 0x1;
1158c2ecf20Sopenharmony_ci		break;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	default:
1188c2ecf20Sopenharmony_ci		WARN_ONCE(1, "Invalid sgmii interface: %d\n", interface);
1198c2ecf20Sopenharmony_ci		return -EINVAL;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	sgmii_write_reg(sgmii_ofs, SGMII_CTL_REG(port), 0);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	/* Wait for the SerDes pll to lock */
1258c2ecf20Sopenharmony_ci	for (i = 0; i < 1000; i++)  {
1268c2ecf20Sopenharmony_ci		usleep_range(1000, 2000);
1278c2ecf20Sopenharmony_ci		status = sgmii_read_reg(sgmii_ofs, SGMII_STATUS_REG(port));
1288c2ecf20Sopenharmony_ci		if ((status & SGMII_REG_STATUS_LOCK) != 0)
1298c2ecf20Sopenharmony_ci			break;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	if ((status & SGMII_REG_STATUS_LOCK) == 0)
1338c2ecf20Sopenharmony_ci		pr_err("serdes PLL not locked\n");
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	sgmii_write_reg(sgmii_ofs, SGMII_MRADV_REG(port), mr_adv_ability);
1368c2ecf20Sopenharmony_ci	sgmii_write_reg(sgmii_ofs, SGMII_CTL_REG(port), control);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	mask = SGMII_REG_STATUS_LINK;
1398c2ecf20Sopenharmony_ci	if (control & SGMII_REG_CONTROL_AUTONEG)
1408c2ecf20Sopenharmony_ci		mask |= SGMII_REG_STATUS_AUTONEG;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	for (i = 0; i < 1000; i++)  {
1438c2ecf20Sopenharmony_ci		usleep_range(200, 500);
1448c2ecf20Sopenharmony_ci		status = sgmii_read_reg(sgmii_ofs, SGMII_STATUS_REG(port));
1458c2ecf20Sopenharmony_ci		if ((status & mask) == mask)
1468c2ecf20Sopenharmony_ci			break;
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	return 0;
1508c2ecf20Sopenharmony_ci}
151