Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2// Copyright (c) 2018-2019 MediaTek Inc.
3
4/* A library for MediaTek SGMII circuit
5 *
6 * Author: Sean Wang <sean.wang@mediatek.com>
7 *
8 */
9
10#include <linux/mfd/syscon.h>
11#include <linux/of.h>
12#include <linux/phylink.h>
13#include <linux/regmap.h>
14
15#include "mtk_eth_soc.h"
16
17static struct mtk_pcs *pcs_to_mtk_pcs(struct phylink_pcs *pcs)
18{
19 return container_of(pcs, struct mtk_pcs, pcs);
20}
21
22/* For SGMII interface mode */
23static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
24{
25 unsigned int val;
26
27 /* Setup the link timer and QPHY power up inside SGMIISYS */
28 regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
29 SGMII_LINK_TIMER_DEFAULT);
30
31 regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
32 val |= SGMII_REMOTE_FAULT_DIS;
33 regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
34
35 regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
36 val |= SGMII_AN_RESTART;
37 regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
38
39 regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
40 val &= ~SGMII_PHYA_PWD;
41 regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
42
43 return 0;
44
45}
46
47/* For 1000BASE-X and 2500BASE-X interface modes, which operate at a
48 * fixed speed.
49 */
50static int mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs,
51 phy_interface_t interface)
52{
53 unsigned int val;
54
55 regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
56 val &= ~RG_PHY_SPEED_MASK;
57 if (interface == PHY_INTERFACE_MODE_2500BASEX)
58 val |= RG_PHY_SPEED_3_125G;
59 regmap_write(mpcs->regmap, mpcs->ana_rgc3, val);
60
61 /* Disable SGMII AN */
62 regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
63 val &= ~SGMII_AN_ENABLE;
64 regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
65
66 /* Set the speed etc but leave the duplex unchanged */
67 regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
68 val &= SGMII_DUPLEX_FULL | ~SGMII_IF_MODE_MASK;
69 val |= SGMII_SPEED_1000;
70 regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
71
72 /* Release PHYA power down state */
73 regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
74 val &= ~SGMII_PHYA_PWD;
75 regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
76
77 return 0;
78}
79
80static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
81 phy_interface_t interface,
82 const unsigned long *advertising,
83 bool permit_pause_to_mac)
84{
85 struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
86 int err = 0;
87
88 /* Setup SGMIISYS with the determined property */
89 if (interface != PHY_INTERFACE_MODE_SGMII)
90 err = mtk_pcs_setup_mode_force(mpcs, interface);
91 else if (phylink_autoneg_inband(mode))
92 err = mtk_pcs_setup_mode_an(mpcs);
93
94 return err;
95}
96
97static void mtk_pcs_restart_an(struct phylink_pcs *pcs)
98{
99 struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
100 unsigned int val;
101
102 regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
103 val |= SGMII_AN_RESTART;
104 regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
105}
106
107static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
108 phy_interface_t interface, int speed, int duplex)
109{
110 struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
111 unsigned int val;
112
113 if (!phy_interface_mode_is_8023z(interface))
114 return;
115
116 /* SGMII force duplex setting */
117 regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
118 val &= ~SGMII_DUPLEX_FULL;
119 if (duplex == DUPLEX_FULL)
120 val |= SGMII_DUPLEX_FULL;
121
122 regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
123}
124
125static const struct phylink_pcs_ops mtk_pcs_ops = {
126 .pcs_config = mtk_pcs_config,
127 .pcs_an_restart = mtk_pcs_restart_an,
128 .pcs_link_up = mtk_pcs_link_up,
129};
130
131int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
132{
133 struct device_node *np;
134 int i;
135
136 for (i = 0; i < MTK_MAX_DEVS; i++) {
137 np = of_parse_phandle(r, "mediatek,sgmiisys", i);
138 if (!np)
139 break;
140
141 ss->pcs[i].ana_rgc3 = ana_rgc3;
142 ss->pcs[i].regmap = syscon_node_to_regmap(np);
143 of_node_put(np);
144 if (IS_ERR(ss->pcs[i].regmap))
145 return PTR_ERR(ss->pcs[i].regmap);
146
147 ss->pcs[i].pcs.ops = &mtk_pcs_ops;
148 }
149
150 return 0;
151}
152
153struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id)
154{
155 if (!ss->pcs[id].regmap)
156 return NULL;
157
158 return &ss->pcs[id].pcs;
159}