14765a972SDaniel Golle // SPDX-License-Identifier: GPL-2.0 24765a972SDaniel Golle // Copyright (c) 2018-2019 MediaTek Inc. 34765a972SDaniel Golle /* A library for MediaTek SGMII circuit 44765a972SDaniel Golle * 54765a972SDaniel Golle * Author: Sean Wang <sean.wang@mediatek.com> 64765a972SDaniel Golle * Author: Alexander Couzens <lynxis@fe80.eu> 74765a972SDaniel Golle * Author: Daniel Golle <daniel@makrotopia.org> 84765a972SDaniel Golle * 94765a972SDaniel Golle */ 104765a972SDaniel Golle 114765a972SDaniel Golle #include <linux/mdio.h> 124765a972SDaniel Golle #include <linux/of.h> 134765a972SDaniel Golle #include <linux/pcs/pcs-mtk-lynxi.h> 144765a972SDaniel Golle #include <linux/phylink.h> 154765a972SDaniel Golle #include <linux/regmap.h> 164765a972SDaniel Golle 174765a972SDaniel Golle /* SGMII subsystem config registers */ 184765a972SDaniel Golle /* BMCR (low 16) BMSR (high 16) */ 194765a972SDaniel Golle #define SGMSYS_PCS_CONTROL_1 0x0 204765a972SDaniel Golle #define SGMII_BMCR GENMASK(15, 0) 214765a972SDaniel Golle #define SGMII_BMSR GENMASK(31, 16) 224765a972SDaniel Golle 234765a972SDaniel Golle #define SGMSYS_PCS_DEVICE_ID 0x4 244765a972SDaniel Golle #define SGMII_LYNXI_DEV_ID 0x4d544950 254765a972SDaniel Golle 264765a972SDaniel Golle #define SGMSYS_PCS_ADVERTISE 0x8 274765a972SDaniel Golle #define SGMII_ADVERTISE GENMASK(15, 0) 284765a972SDaniel Golle #define SGMII_LPA GENMASK(31, 16) 294765a972SDaniel Golle 304765a972SDaniel Golle #define SGMSYS_PCS_SCRATCH 0x14 314765a972SDaniel Golle #define SGMII_DEV_VERSION GENMASK(31, 16) 324765a972SDaniel Golle 334765a972SDaniel Golle /* Register to programmable link timer, the unit in 2 * 8ns */ 344765a972SDaniel Golle #define SGMSYS_PCS_LINK_TIMER 0x18 354765a972SDaniel Golle #define SGMII_LINK_TIMER_MASK GENMASK(19, 0) 364765a972SDaniel Golle #define SGMII_LINK_TIMER_VAL(ns) FIELD_PREP(SGMII_LINK_TIMER_MASK, \ 374765a972SDaniel Golle ((ns) / 2 / 8)) 384765a972SDaniel Golle 394765a972SDaniel Golle /* Register to control remote fault */ 404765a972SDaniel Golle #define SGMSYS_SGMII_MODE 0x20 414765a972SDaniel Golle #define SGMII_IF_MODE_SGMII BIT(0) 424765a972SDaniel Golle #define SGMII_SPEED_DUPLEX_AN BIT(1) 434765a972SDaniel Golle #define SGMII_SPEED_MASK GENMASK(3, 2) 444765a972SDaniel Golle #define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0) 454765a972SDaniel Golle #define SGMII_SPEED_100 FIELD_PREP(SGMII_SPEED_MASK, 1) 464765a972SDaniel Golle #define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2) 474765a972SDaniel Golle #define SGMII_DUPLEX_HALF BIT(4) 484765a972SDaniel Golle #define SGMII_REMOTE_FAULT_DIS BIT(8) 494765a972SDaniel Golle 504765a972SDaniel Golle /* Register to reset SGMII design */ 514765a972SDaniel Golle #define SGMSYS_RESERVED_0 0x34 524765a972SDaniel Golle #define SGMII_SW_RESET BIT(0) 534765a972SDaniel Golle 544765a972SDaniel Golle /* Register to set SGMII speed, ANA RG_ Control Signals III */ 554765a972SDaniel Golle #define SGMII_PHY_SPEED_MASK GENMASK(3, 2) 564765a972SDaniel Golle #define SGMII_PHY_SPEED_1_25G FIELD_PREP(SGMII_PHY_SPEED_MASK, 0) 574765a972SDaniel Golle #define SGMII_PHY_SPEED_3_125G FIELD_PREP(SGMII_PHY_SPEED_MASK, 1) 584765a972SDaniel Golle 594765a972SDaniel Golle /* Register to power up QPHY */ 604765a972SDaniel Golle #define SGMSYS_QPHY_PWR_STATE_CTRL 0xe8 614765a972SDaniel Golle #define SGMII_PHYA_PWD BIT(4) 624765a972SDaniel Golle 634765a972SDaniel Golle /* Register to QPHY wrapper control */ 644765a972SDaniel Golle #define SGMSYS_QPHY_WRAP_CTRL 0xec 654765a972SDaniel Golle #define SGMII_PN_SWAP_MASK GENMASK(1, 0) 664765a972SDaniel Golle #define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1)) 674765a972SDaniel Golle 684765a972SDaniel Golle /* struct mtk_pcs_lynxi - This structure holds each sgmii regmap andassociated 694765a972SDaniel Golle * data 704765a972SDaniel Golle * @regmap: The register map pointing at the range used to setup 714765a972SDaniel Golle * SGMII modes 724765a972SDaniel Golle * @dev: Pointer to device owning the PCS 734765a972SDaniel Golle * @ana_rgc3: The offset of register ANA_RGC3 relative to regmap 744765a972SDaniel Golle * @interface: Currently configured interface mode 754765a972SDaniel Golle * @pcs: Phylink PCS structure 764765a972SDaniel Golle * @flags: Flags indicating hardware properties 774765a972SDaniel Golle */ 784765a972SDaniel Golle struct mtk_pcs_lynxi { 794765a972SDaniel Golle struct regmap *regmap; 804765a972SDaniel Golle u32 ana_rgc3; 814765a972SDaniel Golle phy_interface_t interface; 824765a972SDaniel Golle struct phylink_pcs pcs; 834765a972SDaniel Golle u32 flags; 844765a972SDaniel Golle }; 854765a972SDaniel Golle 864765a972SDaniel Golle static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs) 874765a972SDaniel Golle { 884765a972SDaniel Golle return container_of(pcs, struct mtk_pcs_lynxi, pcs); 894765a972SDaniel Golle } 904765a972SDaniel Golle 914765a972SDaniel Golle static void mtk_pcs_lynxi_get_state(struct phylink_pcs *pcs, 924765a972SDaniel Golle struct phylink_link_state *state) 934765a972SDaniel Golle { 944765a972SDaniel Golle struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); 954765a972SDaniel Golle unsigned int bm, adv; 964765a972SDaniel Golle 974765a972SDaniel Golle /* Read the BMSR and LPA */ 984765a972SDaniel Golle regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm); 994765a972SDaniel Golle regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv); 1004765a972SDaniel Golle 1014765a972SDaniel Golle phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm), 1024765a972SDaniel Golle FIELD_GET(SGMII_LPA, adv)); 1034765a972SDaniel Golle } 1044765a972SDaniel Golle 1053b2de56aSRussell King (Oracle) static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int neg_mode, 1064765a972SDaniel Golle phy_interface_t interface, 1074765a972SDaniel Golle const unsigned long *advertising, 1084765a972SDaniel Golle bool permit_pause_to_mac) 1094765a972SDaniel Golle { 1104765a972SDaniel Golle struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); 1113b2de56aSRussell King (Oracle) bool mode_changed = false, changed; 1124765a972SDaniel Golle unsigned int rgc3, sgm_mode, bmcr; 1134765a972SDaniel Golle int advertise, link_timer; 1144765a972SDaniel Golle 1154765a972SDaniel Golle advertise = phylink_mii_c22_pcs_encode_advertisement(interface, 1164765a972SDaniel Golle advertising); 1174765a972SDaniel Golle if (advertise < 0) 1184765a972SDaniel Golle return advertise; 1194765a972SDaniel Golle 1204765a972SDaniel Golle /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and 1214765a972SDaniel Golle * we assume that fixes it's speed at bitrate = line rate (in 1224765a972SDaniel Golle * other words, 1000Mbps or 2500Mbps). 1234765a972SDaniel Golle */ 1243b2de56aSRussell King (Oracle) if (interface == PHY_INTERFACE_MODE_SGMII) 1254765a972SDaniel Golle sgm_mode = SGMII_IF_MODE_SGMII; 1264765a972SDaniel Golle else 1273b2de56aSRussell King (Oracle) sgm_mode = 0; 1283b2de56aSRussell King (Oracle) 1293b2de56aSRussell King (Oracle) if (neg_mode & PHYLINK_PCS_NEG_INBAND) 1303b2de56aSRussell King (Oracle) sgm_mode |= SGMII_REMOTE_FAULT_DIS; 1313b2de56aSRussell King (Oracle) 1323b2de56aSRussell King (Oracle) if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { 1333b2de56aSRussell King (Oracle) if (interface == PHY_INTERFACE_MODE_SGMII) 1343b2de56aSRussell King (Oracle) sgm_mode |= SGMII_SPEED_DUPLEX_AN; 1353b2de56aSRussell King (Oracle) bmcr = BMCR_ANENABLE; 1363b2de56aSRussell King (Oracle) } else { 1374765a972SDaniel Golle bmcr = 0; 1383b2de56aSRussell King (Oracle) } 1394765a972SDaniel Golle 1404765a972SDaniel Golle if (mpcs->interface != interface) { 1414765a972SDaniel Golle link_timer = phylink_get_link_timer_ns(interface); 1424765a972SDaniel Golle if (link_timer < 0) 1434765a972SDaniel Golle return link_timer; 1444765a972SDaniel Golle 1454765a972SDaniel Golle /* PHYA power down */ 1464765a972SDaniel Golle regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 1474765a972SDaniel Golle SGMII_PHYA_PWD); 1484765a972SDaniel Golle 1494765a972SDaniel Golle /* Reset SGMII PCS state */ 1504765a972SDaniel Golle regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0, 1514765a972SDaniel Golle SGMII_SW_RESET); 1524765a972SDaniel Golle 1534765a972SDaniel Golle if (mpcs->flags & MTK_SGMII_FLAG_PN_SWAP) 1544765a972SDaniel Golle regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL, 1554765a972SDaniel Golle SGMII_PN_SWAP_MASK, 1564765a972SDaniel Golle SGMII_PN_SWAP_TX_RX); 1574765a972SDaniel Golle 1584765a972SDaniel Golle if (interface == PHY_INTERFACE_MODE_2500BASEX) 1594765a972SDaniel Golle rgc3 = SGMII_PHY_SPEED_3_125G; 1604765a972SDaniel Golle else 1614765a972SDaniel Golle rgc3 = SGMII_PHY_SPEED_1_25G; 1624765a972SDaniel Golle 1634765a972SDaniel Golle /* Configure the underlying interface speed */ 1644765a972SDaniel Golle regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, 1654765a972SDaniel Golle SGMII_PHY_SPEED_MASK, rgc3); 1664765a972SDaniel Golle 1674765a972SDaniel Golle /* Setup the link timer */ 1684765a972SDaniel Golle regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, 1694765a972SDaniel Golle SGMII_LINK_TIMER_VAL(link_timer)); 1704765a972SDaniel Golle 1714765a972SDaniel Golle mpcs->interface = interface; 1724765a972SDaniel Golle mode_changed = true; 1734765a972SDaniel Golle } 1744765a972SDaniel Golle 1754765a972SDaniel Golle /* Update the advertisement, noting whether it has changed */ 1764765a972SDaniel Golle regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE, 1774765a972SDaniel Golle SGMII_ADVERTISE, advertise, &changed); 1784765a972SDaniel Golle 1794765a972SDaniel Golle /* Update the sgmsys mode register */ 1804765a972SDaniel Golle regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, 1814765a972SDaniel Golle SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN | 1824765a972SDaniel Golle SGMII_IF_MODE_SGMII, sgm_mode); 1834765a972SDaniel Golle 1844765a972SDaniel Golle /* Update the BMCR */ 1854765a972SDaniel Golle regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, 1864765a972SDaniel Golle BMCR_ANENABLE, bmcr); 1874765a972SDaniel Golle 1884765a972SDaniel Golle /* Release PHYA power down state 1894765a972SDaniel Golle * Only removing bit SGMII_PHYA_PWD isn't enough. 1904765a972SDaniel Golle * There are cases when the SGMII_PHYA_PWD register contains 0x9 which 1914765a972SDaniel Golle * prevents SGMII from working. The SGMII still shows link but no traffic 1924765a972SDaniel Golle * can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was 1934765a972SDaniel Golle * taken from a good working state of the SGMII interface. 1944765a972SDaniel Golle * Unknown how much the QPHY needs but it is racy without a sleep. 1954765a972SDaniel Golle * Tested on mt7622 & mt7986. 1964765a972SDaniel Golle */ 1974765a972SDaniel Golle usleep_range(50, 100); 1984765a972SDaniel Golle regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0); 1994765a972SDaniel Golle 2004765a972SDaniel Golle return changed || mode_changed; 2014765a972SDaniel Golle } 2024765a972SDaniel Golle 2034765a972SDaniel Golle static void mtk_pcs_lynxi_restart_an(struct phylink_pcs *pcs) 2044765a972SDaniel Golle { 2054765a972SDaniel Golle struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); 2064765a972SDaniel Golle 2074765a972SDaniel Golle regmap_set_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, BMCR_ANRESTART); 2084765a972SDaniel Golle } 2094765a972SDaniel Golle 2103b2de56aSRussell King (Oracle) static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs, 2113b2de56aSRussell King (Oracle) unsigned int neg_mode, 2124765a972SDaniel Golle phy_interface_t interface, int speed, 2134765a972SDaniel Golle int duplex) 2144765a972SDaniel Golle { 2154765a972SDaniel Golle struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); 2164765a972SDaniel Golle unsigned int sgm_mode; 2174765a972SDaniel Golle 2183b2de56aSRussell King (Oracle) if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED) { 2194765a972SDaniel Golle /* Force the speed and duplex setting */ 2204765a972SDaniel Golle if (speed == SPEED_10) 2214765a972SDaniel Golle sgm_mode = SGMII_SPEED_10; 2224765a972SDaniel Golle else if (speed == SPEED_100) 2234765a972SDaniel Golle sgm_mode = SGMII_SPEED_100; 2244765a972SDaniel Golle else 2254765a972SDaniel Golle sgm_mode = SGMII_SPEED_1000; 2264765a972SDaniel Golle 2274765a972SDaniel Golle if (duplex != DUPLEX_FULL) 2284765a972SDaniel Golle sgm_mode |= SGMII_DUPLEX_HALF; 2294765a972SDaniel Golle 2304765a972SDaniel Golle regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, 2314765a972SDaniel Golle SGMII_DUPLEX_HALF | SGMII_SPEED_MASK, 2324765a972SDaniel Golle sgm_mode); 2334765a972SDaniel Golle } 2344765a972SDaniel Golle } 2354765a972SDaniel Golle 236*90308679SDaniel Golle static void mtk_pcs_lynxi_disable(struct phylink_pcs *pcs) 237*90308679SDaniel Golle { 238*90308679SDaniel Golle struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); 239*90308679SDaniel Golle 240*90308679SDaniel Golle mpcs->interface = PHY_INTERFACE_MODE_NA; 241*90308679SDaniel Golle } 242*90308679SDaniel Golle 2434765a972SDaniel Golle static const struct phylink_pcs_ops mtk_pcs_lynxi_ops = { 2444765a972SDaniel Golle .pcs_get_state = mtk_pcs_lynxi_get_state, 2454765a972SDaniel Golle .pcs_config = mtk_pcs_lynxi_config, 2464765a972SDaniel Golle .pcs_an_restart = mtk_pcs_lynxi_restart_an, 2474765a972SDaniel Golle .pcs_link_up = mtk_pcs_lynxi_link_up, 248*90308679SDaniel Golle .pcs_disable = mtk_pcs_lynxi_disable, 2494765a972SDaniel Golle }; 2504765a972SDaniel Golle 2514765a972SDaniel Golle struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev, 2524765a972SDaniel Golle struct regmap *regmap, u32 ana_rgc3, 2534765a972SDaniel Golle u32 flags) 2544765a972SDaniel Golle { 2554765a972SDaniel Golle struct mtk_pcs_lynxi *mpcs; 2564765a972SDaniel Golle u32 id, ver; 2574765a972SDaniel Golle int ret; 2584765a972SDaniel Golle 2594765a972SDaniel Golle ret = regmap_read(regmap, SGMSYS_PCS_DEVICE_ID, &id); 2604765a972SDaniel Golle if (ret < 0) 2614765a972SDaniel Golle return NULL; 2624765a972SDaniel Golle 2634765a972SDaniel Golle if (id != SGMII_LYNXI_DEV_ID) { 2644765a972SDaniel Golle dev_err(dev, "unknown PCS device id %08x\n", id); 2654765a972SDaniel Golle return NULL; 2664765a972SDaniel Golle } 2674765a972SDaniel Golle 2684765a972SDaniel Golle ret = regmap_read(regmap, SGMSYS_PCS_SCRATCH, &ver); 2694765a972SDaniel Golle if (ret < 0) 2704765a972SDaniel Golle return NULL; 2714765a972SDaniel Golle 2724765a972SDaniel Golle ver = FIELD_GET(SGMII_DEV_VERSION, ver); 2734765a972SDaniel Golle if (ver != 0x1) { 2744765a972SDaniel Golle dev_err(dev, "unknown PCS device version %04x\n", ver); 2754765a972SDaniel Golle return NULL; 2764765a972SDaniel Golle } 2774765a972SDaniel Golle 2784765a972SDaniel Golle dev_dbg(dev, "MediaTek LynxI SGMII PCS (id 0x%08x, ver 0x%04x)\n", id, 2794765a972SDaniel Golle ver); 2804765a972SDaniel Golle 2814765a972SDaniel Golle mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL); 2824765a972SDaniel Golle if (!mpcs) 2834765a972SDaniel Golle return NULL; 2844765a972SDaniel Golle 2854765a972SDaniel Golle mpcs->ana_rgc3 = ana_rgc3; 2864765a972SDaniel Golle mpcs->regmap = regmap; 2874765a972SDaniel Golle mpcs->flags = flags; 2884765a972SDaniel Golle mpcs->pcs.ops = &mtk_pcs_lynxi_ops; 2893b2de56aSRussell King (Oracle) mpcs->pcs.neg_mode = true; 2904765a972SDaniel Golle mpcs->pcs.poll = true; 2914765a972SDaniel Golle mpcs->interface = PHY_INTERFACE_MODE_NA; 2924765a972SDaniel Golle 2934765a972SDaniel Golle return &mpcs->pcs; 2944765a972SDaniel Golle } 2954765a972SDaniel Golle EXPORT_SYMBOL(mtk_pcs_lynxi_create); 2964765a972SDaniel Golle 2974765a972SDaniel Golle void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs) 2984765a972SDaniel Golle { 2994765a972SDaniel Golle if (!pcs) 3004765a972SDaniel Golle return; 3014765a972SDaniel Golle 3024765a972SDaniel Golle kfree(pcs_to_mtk_pcs_lynxi(pcs)); 3034765a972SDaniel Golle } 3044765a972SDaniel Golle EXPORT_SYMBOL(mtk_pcs_lynxi_destroy); 3054765a972SDaniel Golle 3064765a972SDaniel Golle MODULE_LICENSE("GPL"); 307