Linux Audio

Check our new training course

Yocto distribution development and maintenance

Need a Yocto distribution for your embedded project?
Loading...
Note: File does not exist in v5.9.
  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 void mtk_pcs_lynxi_get_state(struct phylink_pcs *pcs,
 92				    struct phylink_link_state *state)
 93{
 94	struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
 95	unsigned int bm, adv;
 96
 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);
100
101	phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm),
102					 FIELD_GET(SGMII_LPA, adv));
103}
104
105static 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)
109{
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;
114
115	advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
116							     advertising);
117	if (advertise < 0)
118		return advertise;
119
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).
123	 */
124	if (interface == PHY_INTERFACE_MODE_SGMII)
125		sgm_mode = SGMII_IF_MODE_SGMII;
126	else
127		sgm_mode = 0;
128
129	if (neg_mode & PHYLINK_PCS_NEG_INBAND)
130		sgm_mode |= SGMII_REMOTE_FAULT_DIS;
131
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;
136	} else {
137		bmcr = 0;
138	}
139
140	if (mpcs->interface != interface) {
141		link_timer = phylink_get_link_timer_ns(interface);
142		if (link_timer < 0)
143			return link_timer;
144
145		/* PHYA power down */
146		regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
147				SGMII_PHYA_PWD);
148
149		/* Reset SGMII PCS state */
150		regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0,
151				SGMII_SW_RESET);
152
153		if (mpcs->flags & MTK_SGMII_FLAG_PN_SWAP)
154			regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL,
155					   SGMII_PN_SWAP_MASK,
156					   SGMII_PN_SWAP_TX_RX);
157
158		if (interface == PHY_INTERFACE_MODE_2500BASEX)
159			rgc3 = SGMII_PHY_SPEED_3_125G;
160		else
161			rgc3 = SGMII_PHY_SPEED_1_25G;
162
163		/* Configure the underlying interface speed */
164		regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
165				   SGMII_PHY_SPEED_MASK, rgc3);
166
167		/* Setup the link timer */
168		regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
169			     SGMII_LINK_TIMER_VAL(link_timer));
170
171		mpcs->interface = interface;
172		mode_changed = true;
173	}
174
175	/* Update the advertisement, noting whether it has changed */
176	regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE,
177				 SGMII_ADVERTISE, advertise, &changed);
178
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);
183
184	/* Update the BMCR */
185	regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
186			   BMCR_ANENABLE, bmcr);
187
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.
196	 */
197	usleep_range(50, 100);
198	regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
199
200	return changed || mode_changed;
201}
202
203static void mtk_pcs_lynxi_restart_an(struct phylink_pcs *pcs)
204{
205	struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
206
207	regmap_set_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, BMCR_ANRESTART);
208}
209
210static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs,
211				  unsigned int neg_mode,
212				  phy_interface_t interface, int speed,
213				  int duplex)
214{
215	struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
216	unsigned int sgm_mode;
217
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;
224		else
225			sgm_mode = SGMII_SPEED_1000;
226
227		if (duplex != DUPLEX_FULL)
228			sgm_mode |= SGMII_DUPLEX_HALF;
229
230		regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
231				   SGMII_DUPLEX_HALF | SGMII_SPEED_MASK,
232				   sgm_mode);
233	}
234}
235
236static void mtk_pcs_lynxi_disable(struct phylink_pcs *pcs)
237{
238	struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
239
240	mpcs->interface = PHY_INTERFACE_MODE_NA;
241}
242
243static 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,
249};
250
251struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
252					 struct regmap *regmap, u32 ana_rgc3,
253					 u32 flags)
254{
255	struct mtk_pcs_lynxi *mpcs;
256	u32 id, ver;
257	int ret;
258
259	ret = regmap_read(regmap, SGMSYS_PCS_DEVICE_ID, &id);
260	if (ret < 0)
261		return NULL;
262
263	if (id != SGMII_LYNXI_DEV_ID) {
264		dev_err(dev, "unknown PCS device id %08x\n", id);
265		return NULL;
266	}
267
268	ret = regmap_read(regmap, SGMSYS_PCS_SCRATCH, &ver);
269	if (ret < 0)
270		return NULL;
271
272	ver = FIELD_GET(SGMII_DEV_VERSION, ver);
273	if (ver != 0x1) {
274		dev_err(dev, "unknown PCS device version %04x\n", ver);
275		return NULL;
276	}
277
278	dev_dbg(dev, "MediaTek LynxI SGMII PCS (id 0x%08x, ver 0x%04x)\n", id,
279		ver);
280
281	mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL);
282	if (!mpcs)
283		return NULL;
284
285	mpcs->ana_rgc3 = ana_rgc3;
286	mpcs->regmap = regmap;
287	mpcs->flags = flags;
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;
292
293	return &mpcs->pcs;
294}
295EXPORT_SYMBOL(mtk_pcs_lynxi_create);
296
297void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs)
298{
299	if (!pcs)
300		return;
301
302	kfree(pcs_to_mtk_pcs_lynxi(pcs));
303}
304EXPORT_SYMBOL(mtk_pcs_lynxi_destroy);
305
306MODULE_LICENSE("GPL");