File Index
Files and directories
Sign in
FILE

pcs-mtk-lynxi.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/pcs/pcs-mtk-lynxi.c C
  1// SPDX-License-Identifier: GPL-2.0
  2// Copyright (c) 2018-2019 MediaTek Inc.
  3/* A library for MediaTek SGMII circuit
  4 *
  5 * Author: Sean Wang <sean.wang@mediatek.com>
  6 * Author: Alexander Couzens <lynxis@fe80.eu>
  7 * Author: Daniel Golle <daniel@makrotopia.org>
  8 *
  9 */
 10
 11#include <linux/mdio.h>
 12#include <linux/of.h>
 13#include <linux/pcs/pcs-mtk-lynxi.h>
 14#include <linux/phylink.h>
 15#include <linux/regmap.h>
 16
 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)
 22
 23#define SGMSYS_PCS_DEVICE_ID		0x4
 24#define SGMII_LYNXI_DEV_ID		0x4d544950
 25
 26#define SGMSYS_PCS_ADVERTISE		0x8
 27#define SGMII_ADVERTISE			GENMASK(15, 0)
 28#define SGMII_LPA			GENMASK(31, 16)
 29
 30#define SGMSYS_PCS_SCRATCH		0x14
 31#define SGMII_DEV_VERSION		GENMASK(31, 16)
 32
 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, \
 37						   ((ns) / 2 / 8))
 38
 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)
 49
 50/* Register to reset SGMII design */
 51#define SGMSYS_RESERVED_0		0x34
 52#define SGMII_SW_RESET			BIT(0)
 53
 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)
 58
 59/* Register to power up QPHY */
 60#define SGMSYS_QPHY_PWR_STATE_CTRL	0xe8
 61#define	SGMII_PHYA_PWD			BIT(4)
 62
 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))
 67
 68/* struct mtk_pcs_lynxi -  This structure holds each sgmii regmap andassociated
 69 *                         data
 70 * @regmap:                The register map pointing at the range used to setup
 71 *                         SGMII modes
 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
 77 */
 78struct mtk_pcs_lynxi {
 79	struct regmap		*regmap;
 80	u32			ana_rgc3;
 81	phy_interface_t		interface;
 82	struct			phylink_pcs pcs;
 83	u32			flags;
 84};
 85
 86static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs)
 87{
 88	return container_of(pcs, struct mtk_pcs_lynxi, pcs);
 89}
 90
 91static unsigned int mtk_pcs_lynxi_inband_caps(struct phylink_pcs *pcs,
 92					      phy_interface_t interface)
 93{
 94	switch (interface) {
 95	case PHY_INTERFACE_MODE_1000BASEX:
 96	case PHY_INTERFACE_MODE_2500BASEX:
 97	case PHY_INTERFACE_MODE_SGMII:
 98		return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
 99
100	default:
101		return 0;
102	}
103}
104
105static void mtk_pcs_lynxi_get_state(struct phylink_pcs *pcs,
106				    unsigned int neg_mode,
107				    struct phylink_link_state *state)
108{
109	struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
110	unsigned int bm, adv;
111
112	/* Read the BMSR and LPA */
113	regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm);
114	regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
115
116	phylink_mii_c22_pcs_decode_state(state, neg_mode,
117					 FIELD_GET(SGMII_BMSR, bm),
118					 FIELD_GET(SGMII_LPA, adv));
119}
120
121static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int neg_mode,
122				phy_interface_t interface,
123				const unsigned long *advertising,
124				bool permit_pause_to_mac)
125{
126	struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
127	bool mode_changed = false, changed;
128	unsigned int rgc3, sgm_mode, bmcr;
129	int advertise, link_timer;
130
131	advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
132							     advertising);
133	if (advertise < 0)
134		return advertise;
135
136	/* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and
137	 * we assume that fixes it's speed at bitrate = line rate (in
138	 * other words, 1000Mbps or 2500Mbps).
139	 */
140	if (interface == PHY_INTERFACE_MODE_SGMII)
141		sgm_mode = SGMII_IF_MODE_SGMII;
142	else
143		sgm_mode = 0;
144
145	if (neg_mode & PHYLINK_PCS_NEG_INBAND)
146		sgm_mode |= SGMII_REMOTE_FAULT_DIS;
147
148	if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
149		if (interface == PHY_INTERFACE_MODE_SGMII)
150			sgm_mode |= SGMII_SPEED_DUPLEX_AN;
151		bmcr = BMCR_ANENABLE;
152	} else {
153		bmcr = 0;
154	}
155
156	if (mpcs->interface != interface) {
157		link_timer = phylink_get_link_timer_ns(interface);
158		if (link_timer < 0)
159			return link_timer;
160
161		/* PHYA power down */
162		regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
163				SGMII_PHYA_PWD);
164
165		/* Reset SGMII PCS state */
166		regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0,
167				SGMII_SW_RESET);
168
169		if (mpcs->flags & MTK_SGMII_FLAG_PN_SWAP)
170			regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL,
171					   SGMII_PN_SWAP_MASK,
172					   SGMII_PN_SWAP_TX_RX);
173
174		if (interface == PHY_INTERFACE_MODE_2500BASEX)
175			rgc3 = SGMII_PHY_SPEED_3_125G;
176		else
177			rgc3 = SGMII_PHY_SPEED_1_25G;
178
179		/* Configure the underlying interface speed */
180		regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
181				   SGMII_PHY_SPEED_MASK, rgc3);
182
183		/* Setup the link timer */
184		regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
185			     SGMII_LINK_TIMER_VAL(link_timer));
186
187		mpcs->interface = interface;
188		mode_changed = true;
189	}
190
191	/* Update the advertisement, noting whether it has changed */
192	regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE,
193				 SGMII_ADVERTISE, advertise, &changed);
194
195	/* Update the sgmsys mode register */
196	regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
197			   SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN |
198			   SGMII_IF_MODE_SGMII, sgm_mode);
199
200	/* Update the BMCR */
201	regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
202			   BMCR_ANENABLE, bmcr);
203
204	/* Release PHYA power down state
205	 * Only removing bit SGMII_PHYA_PWD isn't enough.
206	 * There are cases when the SGMII_PHYA_PWD register contains 0x9 which
207	 * prevents SGMII from working. The SGMII still shows link but no traffic
208	 * can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was
209	 * taken from a good working state of the SGMII interface.
210	 * Unknown how much the QPHY needs but it is racy without a sleep.
211	 * Tested on mt7622 & mt7986.
212	 */
213	usleep_range(50, 100);
214	regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
215
216	return changed || mode_changed;
217}
218
219static void mtk_pcs_lynxi_restart_an(struct phylink_pcs *pcs)
220{
221	struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
222
223	regmap_set_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, BMCR_ANRESTART);
224}
225
226static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs,
227				  unsigned int neg_mode,
228				  phy_interface_t interface, int speed,
229				  int duplex)
230{
231	struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
232	unsigned int sgm_mode;
233
234	if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED) {
235		/* Force the speed and duplex setting */
236		if (speed == SPEED_10)
237			sgm_mode = SGMII_SPEED_10;
238		else if (speed == SPEED_100)
239			sgm_mode = SGMII_SPEED_100;
240		else
241			sgm_mode = SGMII_SPEED_1000;
242
243		if (duplex != DUPLEX_FULL)
244			sgm_mode |= SGMII_DUPLEX_HALF;
245
246		regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
247				   SGMII_DUPLEX_HALF | SGMII_SPEED_MASK,
248				   sgm_mode);
249	}
250}
251
252static void mtk_pcs_lynxi_disable(struct phylink_pcs *pcs)
253{
254	struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
255
256	mpcs->interface = PHY_INTERFACE_MODE_NA;
257}
258
259static const struct phylink_pcs_ops mtk_pcs_lynxi_ops = {
260	.pcs_inband_caps = mtk_pcs_lynxi_inband_caps,
261	.pcs_get_state = mtk_pcs_lynxi_get_state,
262	.pcs_config = mtk_pcs_lynxi_config,
263	.pcs_an_restart = mtk_pcs_lynxi_restart_an,
264	.pcs_link_up = mtk_pcs_lynxi_link_up,
265	.pcs_disable = mtk_pcs_lynxi_disable,
266};
267
268struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
269					 struct regmap *regmap, u32 ana_rgc3,
270					 u32 flags)
271{
272	struct mtk_pcs_lynxi *mpcs;
273	u32 id, ver;
274	int ret;
275
276	ret = regmap_read(regmap, SGMSYS_PCS_DEVICE_ID, &id);
277	if (ret < 0)
278		return NULL;
279
280	if (id != SGMII_LYNXI_DEV_ID) {
281		dev_err(dev, "unknown PCS device id %08x\n", id);
282		return NULL;
283	}
284
285	ret = regmap_read(regmap, SGMSYS_PCS_SCRATCH, &ver);
286	if (ret < 0)
287		return NULL;
288
289	ver = FIELD_GET(SGMII_DEV_VERSION, ver);
290	if (ver != 0x1) {
291		dev_err(dev, "unknown PCS device version %04x\n", ver);
292		return NULL;
293	}
294
295	dev_dbg(dev, "MediaTek LynxI SGMII PCS (id 0x%08x, ver 0x%04x)\n", id,
296		ver);
297
298	mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL);
299	if (!mpcs)
300		return NULL;
301
302	mpcs->ana_rgc3 = ana_rgc3;
303	mpcs->regmap = regmap;
304	mpcs->flags = flags;
305	mpcs->pcs.ops = &mtk_pcs_lynxi_ops;
306	mpcs->pcs.poll = true;
307	mpcs->interface = PHY_INTERFACE_MODE_NA;
308
309	__set_bit(PHY_INTERFACE_MODE_SGMII, mpcs->pcs.supported_interfaces);
310	__set_bit(PHY_INTERFACE_MODE_1000BASEX, mpcs->pcs.supported_interfaces);
311	__set_bit(PHY_INTERFACE_MODE_2500BASEX, mpcs->pcs.supported_interfaces);
312
313	return &mpcs->pcs;
314}
315EXPORT_SYMBOL(mtk_pcs_lynxi_create);
316
317void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs)
318{
319	if (!pcs)
320		return;
321
322	kfree(pcs_to_mtk_pcs_lynxi(pcs));
323}
324EXPORT_SYMBOL(mtk_pcs_lynxi_destroy);
325
326MODULE_DESCRIPTION("MediaTek SGMII library for LynxI");
327MODULE_LICENSE("GPL");
~/ Read-only file preview.