1// SPDX-License-Identifier: GPL-2.0
2/* Copyright Sunplus Technology Co., Ltd.
3 *       All rights reserved.
4 */
5
6#include <linux/netdevice.h>
7#include <linux/bitfield.h>
8#include <linux/of_mdio.h>
9
10#include "spl2sw_register.h"
11#include "spl2sw_define.h"
12#include "spl2sw_phy.h"
13
14static void spl2sw_mii_link_change(struct net_device *ndev)
15{
16	struct spl2sw_mac *mac = netdev_priv(ndev);
17	struct phy_device *phydev = ndev->phydev;
18	struct spl2sw_common *comm = mac->comm;
19	u32 reg;
20
21	reg = readl(comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
22
23	if (phydev->link) {
24		reg |= FIELD_PREP(MAC_FORCE_RMII_LINK, mac->lan_port);
25
26		if (phydev->speed == 100) {
27			reg |= FIELD_PREP(MAC_FORCE_RMII_SPD, mac->lan_port);
28		} else {
29			reg &= FIELD_PREP(MAC_FORCE_RMII_SPD, ~mac->lan_port) |
30			       ~MAC_FORCE_RMII_SPD;
31		}
32
33		if (phydev->duplex) {
34			reg |= FIELD_PREP(MAC_FORCE_RMII_DPX, mac->lan_port);
35		} else {
36			reg &= FIELD_PREP(MAC_FORCE_RMII_DPX, ~mac->lan_port) |
37			       ~MAC_FORCE_RMII_DPX;
38		}
39
40		if (phydev->pause) {
41			reg |= FIELD_PREP(MAC_FORCE_RMII_FC, mac->lan_port);
42		} else {
43			reg &= FIELD_PREP(MAC_FORCE_RMII_FC, ~mac->lan_port) |
44			       ~MAC_FORCE_RMII_FC;
45		}
46	} else {
47		reg &= FIELD_PREP(MAC_FORCE_RMII_LINK, ~mac->lan_port) |
48		       ~MAC_FORCE_RMII_LINK;
49	}
50
51	writel(reg, comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
52
53	phy_print_status(phydev);
54}
55
56int spl2sw_phy_connect(struct spl2sw_common *comm)
57{
58	struct phy_device *phydev;
59	struct net_device *ndev;
60	struct spl2sw_mac *mac;
61	int i;
62
63	for (i = 0; i < MAX_NETDEV_NUM; i++)
64		if (comm->ndev[i]) {
65			ndev = comm->ndev[i];
66			mac = netdev_priv(ndev);
67			phydev = of_phy_connect(ndev, mac->phy_node, spl2sw_mii_link_change,
68						0, mac->phy_mode);
69			if (!phydev)
70				return -ENODEV;
71
72			phy_support_asym_pause(phydev);
73			phy_attached_info(phydev);
74		}
75
76	return 0;
77}
78
79void spl2sw_phy_remove(struct spl2sw_common *comm)
80{
81	struct net_device *ndev;
82	int i;
83
84	for (i = 0; i < MAX_NETDEV_NUM; i++)
85		if (comm->ndev[i]) {
86			ndev = comm->ndev[i];
87			if (ndev)
88				phy_disconnect(ndev->phydev);
89		}
90}
91