1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2018-2019 MediaTek Inc.
3 /* A library for MediaTek SGMII circuit
5 * Author: Sean Wang <sean.wang@mediatek.com>
6 * Author: Alexander Couzens <lynxis@fe80.eu>
7 * Author: Daniel Golle <daniel@makrotopia.org>
11 #include <linux/mdio.h>
13 #include <linux/pcs/pcs-mtk-lynxi.h>
14 #include <linux/phylink.h>
15 #include <linux/regmap.h>
17 /* SGMII subsystem config registers */
18 /* BMCR (low 16) BMSR (high 16) */
19 #define SGMSYS_PCS_CONTROL_1 0x0
20 #define SGMII_BMCR GENMASK(15, 0)
21 #define SGMII_BMSR GENMASK(31, 16)
23 #define SGMSYS_PCS_DEVICE_ID 0x4
24 #define SGMII_LYNXI_DEV_ID 0x4d544950
26 #define SGMSYS_PCS_ADVERTISE 0x8
27 #define SGMII_ADVERTISE GENMASK(15, 0)
28 #define SGMII_LPA GENMASK(31, 16)
30 #define SGMSYS_PCS_SCRATCH 0x14
31 #define SGMII_DEV_VERSION GENMASK(31, 16)
33 /* Register to programmable link timer, the unit in 2 * 8ns */
34 #define SGMSYS_PCS_LINK_TIMER 0x18
35 #define SGMII_LINK_TIMER_MASK GENMASK(19, 0)
36 #define SGMII_LINK_TIMER_VAL(ns) FIELD_PREP(SGMII_LINK_TIMER_MASK, \
39 /* Register to control remote fault */
40 #define SGMSYS_SGMII_MODE 0x20
41 #define SGMII_IF_MODE_SGMII BIT(0)
42 #define SGMII_SPEED_DUPLEX_AN BIT(1)
43 #define SGMII_SPEED_MASK GENMASK(3, 2)
44 #define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0)
45 #define SGMII_SPEED_100 FIELD_PREP(SGMII_SPEED_MASK, 1)
46 #define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2)
47 #define SGMII_DUPLEX_HALF BIT(4)
48 #define SGMII_REMOTE_FAULT_DIS BIT(8)
50 /* Register to reset SGMII design */
51 #define SGMSYS_RESERVED_0 0x34
52 #define SGMII_SW_RESET BIT(0)
54 /* Register to set SGMII speed, ANA RG_ Control Signals III */
55 #define SGMII_PHY_SPEED_MASK GENMASK(3, 2)
56 #define SGMII_PHY_SPEED_1_25G FIELD_PREP(SGMII_PHY_SPEED_MASK, 0)
57 #define SGMII_PHY_SPEED_3_125G FIELD_PREP(SGMII_PHY_SPEED_MASK, 1)
59 /* Register to power up QPHY */
60 #define SGMSYS_QPHY_PWR_STATE_CTRL 0xe8
61 #define SGMII_PHYA_PWD BIT(4)
63 /* Register to QPHY wrapper control */
64 #define SGMSYS_QPHY_WRAP_CTRL 0xec
65 #define SGMII_PN_SWAP_MASK GENMASK(1, 0)
66 #define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1))
68 /* struct mtk_pcs_lynxi - This structure holds each sgmii regmap andassociated
70 * @regmap: The register map pointing at the range used to setup
72 * @dev: Pointer to device owning the PCS
73 * @ana_rgc3: The offset of register ANA_RGC3 relative to regmap
74 * @interface: Currently configured interface mode
75 * @pcs: Phylink PCS structure
76 * @flags: Flags indicating hardware properties
78 struct mtk_pcs_lynxi
{
79 struct regmap
*regmap
;
81 phy_interface_t interface
;
82 struct phylink_pcs pcs
;
86 static struct mtk_pcs_lynxi
*pcs_to_mtk_pcs_lynxi(struct phylink_pcs
*pcs
)
88 return container_of(pcs
, struct mtk_pcs_lynxi
, pcs
);
91 static void mtk_pcs_lynxi_get_state(struct phylink_pcs
*pcs
,
92 struct phylink_link_state
*state
)
94 struct mtk_pcs_lynxi
*mpcs
= pcs_to_mtk_pcs_lynxi(pcs
);
97 /* Read the BMSR and LPA */
98 regmap_read(mpcs
->regmap
, SGMSYS_PCS_CONTROL_1
, &bm
);
99 regmap_read(mpcs
->regmap
, SGMSYS_PCS_ADVERTISE
, &adv
);
101 phylink_mii_c22_pcs_decode_state(state
, FIELD_GET(SGMII_BMSR
, bm
),
102 FIELD_GET(SGMII_LPA
, adv
));
105 static int mtk_pcs_lynxi_config(struct phylink_pcs
*pcs
, unsigned int neg_mode
,
106 phy_interface_t interface
,
107 const unsigned long *advertising
,
108 bool permit_pause_to_mac
)
110 struct mtk_pcs_lynxi
*mpcs
= pcs_to_mtk_pcs_lynxi(pcs
);
111 bool mode_changed
= false, changed
;
112 unsigned int rgc3
, sgm_mode
, bmcr
;
113 int advertise
, link_timer
;
115 advertise
= phylink_mii_c22_pcs_encode_advertisement(interface
,
120 /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and
121 * we assume that fixes it's speed at bitrate = line rate (in
122 * other words, 1000Mbps or 2500Mbps).
124 if (interface
== PHY_INTERFACE_MODE_SGMII
)
125 sgm_mode
= SGMII_IF_MODE_SGMII
;
129 if (neg_mode
& PHYLINK_PCS_NEG_INBAND
)
130 sgm_mode
|= SGMII_REMOTE_FAULT_DIS
;
132 if (neg_mode
== PHYLINK_PCS_NEG_INBAND_ENABLED
) {
133 if (interface
== PHY_INTERFACE_MODE_SGMII
)
134 sgm_mode
|= SGMII_SPEED_DUPLEX_AN
;
135 bmcr
= BMCR_ANENABLE
;
140 if (mpcs
->interface
!= interface
) {
141 link_timer
= phylink_get_link_timer_ns(interface
);
145 /* PHYA power down */
146 regmap_set_bits(mpcs
->regmap
, SGMSYS_QPHY_PWR_STATE_CTRL
,
149 /* Reset SGMII PCS state */
150 regmap_set_bits(mpcs
->regmap
, SGMSYS_RESERVED_0
,
153 if (mpcs
->flags
& MTK_SGMII_FLAG_PN_SWAP
)
154 regmap_update_bits(mpcs
->regmap
, SGMSYS_QPHY_WRAP_CTRL
,
156 SGMII_PN_SWAP_TX_RX
);
158 if (interface
== PHY_INTERFACE_MODE_2500BASEX
)
159 rgc3
= SGMII_PHY_SPEED_3_125G
;
161 rgc3
= SGMII_PHY_SPEED_1_25G
;
163 /* Configure the underlying interface speed */
164 regmap_update_bits(mpcs
->regmap
, mpcs
->ana_rgc3
,
165 SGMII_PHY_SPEED_MASK
, rgc3
);
167 /* Setup the link timer */
168 regmap_write(mpcs
->regmap
, SGMSYS_PCS_LINK_TIMER
,
169 SGMII_LINK_TIMER_VAL(link_timer
));
171 mpcs
->interface
= interface
;
175 /* Update the advertisement, noting whether it has changed */
176 regmap_update_bits_check(mpcs
->regmap
, SGMSYS_PCS_ADVERTISE
,
177 SGMII_ADVERTISE
, advertise
, &changed
);
179 /* Update the sgmsys mode register */
180 regmap_update_bits(mpcs
->regmap
, SGMSYS_SGMII_MODE
,
181 SGMII_REMOTE_FAULT_DIS
| SGMII_SPEED_DUPLEX_AN
|
182 SGMII_IF_MODE_SGMII
, sgm_mode
);
184 /* Update the BMCR */
185 regmap_update_bits(mpcs
->regmap
, SGMSYS_PCS_CONTROL_1
,
186 BMCR_ANENABLE
, bmcr
);
188 /* Release PHYA power down state
189 * Only removing bit SGMII_PHYA_PWD isn't enough.
190 * There are cases when the SGMII_PHYA_PWD register contains 0x9 which
191 * prevents SGMII from working. The SGMII still shows link but no traffic
192 * can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was
193 * taken from a good working state of the SGMII interface.
194 * Unknown how much the QPHY needs but it is racy without a sleep.
195 * Tested on mt7622 & mt7986.
197 usleep_range(50, 100);
198 regmap_write(mpcs
->regmap
, SGMSYS_QPHY_PWR_STATE_CTRL
, 0);
200 return changed
|| mode_changed
;
203 static void mtk_pcs_lynxi_restart_an(struct phylink_pcs
*pcs
)
205 struct mtk_pcs_lynxi
*mpcs
= pcs_to_mtk_pcs_lynxi(pcs
);
207 regmap_set_bits(mpcs
->regmap
, SGMSYS_PCS_CONTROL_1
, BMCR_ANRESTART
);
210 static void mtk_pcs_lynxi_link_up(struct phylink_pcs
*pcs
,
211 unsigned int neg_mode
,
212 phy_interface_t interface
, int speed
,
215 struct mtk_pcs_lynxi
*mpcs
= pcs_to_mtk_pcs_lynxi(pcs
);
216 unsigned int sgm_mode
;
218 if (neg_mode
!= PHYLINK_PCS_NEG_INBAND_ENABLED
) {
219 /* Force the speed and duplex setting */
220 if (speed
== SPEED_10
)
221 sgm_mode
= SGMII_SPEED_10
;
222 else if (speed
== SPEED_100
)
223 sgm_mode
= SGMII_SPEED_100
;
225 sgm_mode
= SGMII_SPEED_1000
;
227 if (duplex
!= DUPLEX_FULL
)
228 sgm_mode
|= SGMII_DUPLEX_HALF
;
230 regmap_update_bits(mpcs
->regmap
, SGMSYS_SGMII_MODE
,
231 SGMII_DUPLEX_HALF
| SGMII_SPEED_MASK
,
236 static void mtk_pcs_lynxi_disable(struct phylink_pcs
*pcs
)
238 struct mtk_pcs_lynxi
*mpcs
= pcs_to_mtk_pcs_lynxi(pcs
);
240 mpcs
->interface
= PHY_INTERFACE_MODE_NA
;
243 static const struct phylink_pcs_ops mtk_pcs_lynxi_ops
= {
244 .pcs_get_state
= mtk_pcs_lynxi_get_state
,
245 .pcs_config
= mtk_pcs_lynxi_config
,
246 .pcs_an_restart
= mtk_pcs_lynxi_restart_an
,
247 .pcs_link_up
= mtk_pcs_lynxi_link_up
,
248 .pcs_disable
= mtk_pcs_lynxi_disable
,
251 struct phylink_pcs
*mtk_pcs_lynxi_create(struct device
*dev
,
252 struct regmap
*regmap
, u32 ana_rgc3
,
255 struct mtk_pcs_lynxi
*mpcs
;
259 ret
= regmap_read(regmap
, SGMSYS_PCS_DEVICE_ID
, &id
);
263 if (id
!= SGMII_LYNXI_DEV_ID
) {
264 dev_err(dev
, "unknown PCS device id %08x\n", id
);
268 ret
= regmap_read(regmap
, SGMSYS_PCS_SCRATCH
, &ver
);
272 ver
= FIELD_GET(SGMII_DEV_VERSION
, ver
);
274 dev_err(dev
, "unknown PCS device version %04x\n", ver
);
278 dev_dbg(dev
, "MediaTek LynxI SGMII PCS (id 0x%08x, ver 0x%04x)\n", id
,
281 mpcs
= kzalloc(sizeof(*mpcs
), GFP_KERNEL
);
285 mpcs
->ana_rgc3
= ana_rgc3
;
286 mpcs
->regmap
= regmap
;
288 mpcs
->pcs
.ops
= &mtk_pcs_lynxi_ops
;
289 mpcs
->pcs
.neg_mode
= true;
290 mpcs
->pcs
.poll
= true;
291 mpcs
->interface
= PHY_INTERFACE_MODE_NA
;
295 EXPORT_SYMBOL(mtk_pcs_lynxi_create
);
297 void mtk_pcs_lynxi_destroy(struct phylink_pcs
*pcs
)
302 kfree(pcs_to_mtk_pcs_lynxi(pcs
));
304 EXPORT_SYMBOL(mtk_pcs_lynxi_destroy
);
306 MODULE_DESCRIPTION("MediaTek SGMII library for LynxI");
307 MODULE_LICENSE("GPL");