brintos

brintos / linux-shallow public Read only

0
0
Text · 8.7 KiB · 4f63abe Raw
308 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (c) 2018-2019 MediaTek Inc.3/* A library for MediaTek SGMII circuit4 *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		0x020#define SGMII_BMCR			GENMASK(15, 0)21#define SGMII_BMSR			GENMASK(31, 16)22 23#define SGMSYS_PCS_DEVICE_ID		0x424#define SGMII_LYNXI_DEV_ID		0x4d54495025 26#define SGMSYS_PCS_ADVERTISE		0x827#define SGMII_ADVERTISE			GENMASK(15, 0)28#define SGMII_LPA			GENMASK(31, 16)29 30#define SGMSYS_PCS_SCRATCH		0x1431#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		0x1835#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		0x2041#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		0x3452#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	0xe861#define	SGMII_PHYA_PWD			BIT(4)62 63/* Register to QPHY wrapper control */64#define SGMSYS_QPHY_WRAP_CTRL		0xec65#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 andassociated69 *                         data70 * @regmap:                The register map pointing at the range used to setup71 *                         SGMII modes72 * @dev:                   Pointer to device owning the PCS73 * @ana_rgc3:              The offset of register ANA_RGC3 relative to regmap74 * @interface:             Currently configured interface mode75 * @pcs:                   Phylink PCS structure76 * @flags:                 Flags indicating hardware properties77 */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, and121	 * we assume that fixes it's speed at bitrate = line rate (in122	 * other words, 1000Mbps or 2500Mbps).123	 */124	if (interface == PHY_INTERFACE_MODE_SGMII)125		sgm_mode = SGMII_IF_MODE_SGMII;126	else127		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		else161			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 state189	 * Only removing bit SGMII_PHYA_PWD isn't enough.190	 * There are cases when the SGMII_PHYA_PWD register contains 0x9 which191	 * prevents SGMII from working. The SGMII still shows link but no traffic192	 * can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was193	 * 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		else225			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_DESCRIPTION("MediaTek SGMII library for LynxI");307MODULE_LICENSE("GPL");308