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