162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright Sunplus Technology Co., Ltd.
362306a36Sopenharmony_ci *       All rights reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/netdevice.h>
762306a36Sopenharmony_ci#include <linux/bitfield.h>
862306a36Sopenharmony_ci#include <linux/of_mdio.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "spl2sw_register.h"
1162306a36Sopenharmony_ci#include "spl2sw_define.h"
1262306a36Sopenharmony_ci#include "spl2sw_phy.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic void spl2sw_mii_link_change(struct net_device *ndev)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	struct spl2sw_mac *mac = netdev_priv(ndev);
1762306a36Sopenharmony_ci	struct phy_device *phydev = ndev->phydev;
1862306a36Sopenharmony_ci	struct spl2sw_common *comm = mac->comm;
1962306a36Sopenharmony_ci	u32 reg;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	reg = readl(comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	if (phydev->link) {
2462306a36Sopenharmony_ci		reg |= FIELD_PREP(MAC_FORCE_RMII_LINK, mac->lan_port);
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci		if (phydev->speed == 100) {
2762306a36Sopenharmony_ci			reg |= FIELD_PREP(MAC_FORCE_RMII_SPD, mac->lan_port);
2862306a36Sopenharmony_ci		} else {
2962306a36Sopenharmony_ci			reg &= FIELD_PREP(MAC_FORCE_RMII_SPD, ~mac->lan_port) |
3062306a36Sopenharmony_ci			       ~MAC_FORCE_RMII_SPD;
3162306a36Sopenharmony_ci		}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci		if (phydev->duplex) {
3462306a36Sopenharmony_ci			reg |= FIELD_PREP(MAC_FORCE_RMII_DPX, mac->lan_port);
3562306a36Sopenharmony_ci		} else {
3662306a36Sopenharmony_ci			reg &= FIELD_PREP(MAC_FORCE_RMII_DPX, ~mac->lan_port) |
3762306a36Sopenharmony_ci			       ~MAC_FORCE_RMII_DPX;
3862306a36Sopenharmony_ci		}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci		if (phydev->pause) {
4162306a36Sopenharmony_ci			reg |= FIELD_PREP(MAC_FORCE_RMII_FC, mac->lan_port);
4262306a36Sopenharmony_ci		} else {
4362306a36Sopenharmony_ci			reg &= FIELD_PREP(MAC_FORCE_RMII_FC, ~mac->lan_port) |
4462306a36Sopenharmony_ci			       ~MAC_FORCE_RMII_FC;
4562306a36Sopenharmony_ci		}
4662306a36Sopenharmony_ci	} else {
4762306a36Sopenharmony_ci		reg &= FIELD_PREP(MAC_FORCE_RMII_LINK, ~mac->lan_port) |
4862306a36Sopenharmony_ci		       ~MAC_FORCE_RMII_LINK;
4962306a36Sopenharmony_ci	}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	writel(reg, comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	phy_print_status(phydev);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ciint spl2sw_phy_connect(struct spl2sw_common *comm)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct phy_device *phydev;
5962306a36Sopenharmony_ci	struct net_device *ndev;
6062306a36Sopenharmony_ci	struct spl2sw_mac *mac;
6162306a36Sopenharmony_ci	int i;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	for (i = 0; i < MAX_NETDEV_NUM; i++)
6462306a36Sopenharmony_ci		if (comm->ndev[i]) {
6562306a36Sopenharmony_ci			ndev = comm->ndev[i];
6662306a36Sopenharmony_ci			mac = netdev_priv(ndev);
6762306a36Sopenharmony_ci			phydev = of_phy_connect(ndev, mac->phy_node, spl2sw_mii_link_change,
6862306a36Sopenharmony_ci						0, mac->phy_mode);
6962306a36Sopenharmony_ci			if (!phydev)
7062306a36Sopenharmony_ci				return -ENODEV;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci			phy_support_asym_pause(phydev);
7362306a36Sopenharmony_ci			phy_attached_info(phydev);
7462306a36Sopenharmony_ci		}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	return 0;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_civoid spl2sw_phy_remove(struct spl2sw_common *comm)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct net_device *ndev;
8262306a36Sopenharmony_ci	int i;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	for (i = 0; i < MAX_NETDEV_NUM; i++)
8562306a36Sopenharmony_ci		if (comm->ndev[i]) {
8662306a36Sopenharmony_ci			ndev = comm->ndev[i];
8762306a36Sopenharmony_ci			if (ndev)
8862306a36Sopenharmony_ci				phy_disconnect(ndev->phydev);
8962306a36Sopenharmony_ci		}
9062306a36Sopenharmony_ci}
91