Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v6.2 203 lines 5.6 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2// Copyright (c) 2018-2019 MediaTek Inc. 3 4/* A library for MediaTek SGMII circuit 5 * 6 * Author: Sean Wang <sean.wang@mediatek.com> 7 * 8 */ 9 10#include <linux/mfd/syscon.h> 11#include <linux/of.h> 12#include <linux/phylink.h> 13#include <linux/regmap.h> 14 15#include "mtk_eth_soc.h" 16 17static struct mtk_pcs *pcs_to_mtk_pcs(struct phylink_pcs *pcs) 18{ 19 return container_of(pcs, struct mtk_pcs, pcs); 20} 21 22static void mtk_pcs_get_state(struct phylink_pcs *pcs, 23 struct phylink_link_state *state) 24{ 25 struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); 26 unsigned int bm, adv; 27 28 /* Read the BMSR and LPA */ 29 regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm); 30 regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv); 31 32 phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm), 33 FIELD_GET(SGMII_LPA, adv)); 34} 35 36static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, 37 phy_interface_t interface, 38 const unsigned long *advertising, 39 bool permit_pause_to_mac) 40{ 41 struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); 42 unsigned int rgc3, sgm_mode, bmcr; 43 int advertise, link_timer; 44 bool changed, use_an; 45 46 advertise = phylink_mii_c22_pcs_encode_advertisement(interface, 47 advertising); 48 if (advertise < 0) 49 return advertise; 50 51 link_timer = phylink_get_link_timer_ns(interface); 52 if (link_timer < 0) 53 return link_timer; 54 55 /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and 56 * we assume that fixes it's speed at bitrate = line rate (in 57 * other words, 1000Mbps or 2500Mbps). 58 */ 59 if (interface == PHY_INTERFACE_MODE_SGMII) { 60 sgm_mode = SGMII_IF_MODE_SGMII; 61 if (phylink_autoneg_inband(mode)) { 62 sgm_mode |= SGMII_REMOTE_FAULT_DIS | 63 SGMII_SPEED_DUPLEX_AN; 64 use_an = true; 65 } else { 66 use_an = false; 67 } 68 } else if (phylink_autoneg_inband(mode)) { 69 /* 1000base-X or 2500base-X autoneg */ 70 sgm_mode = SGMII_REMOTE_FAULT_DIS; 71 use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, 72 advertising); 73 } else { 74 /* 1000base-X or 2500base-X without autoneg */ 75 sgm_mode = 0; 76 use_an = false; 77 } 78 79 if (use_an) { 80 /* FIXME: Do we need to set AN_RESTART here? */ 81 bmcr = SGMII_AN_RESTART | SGMII_AN_ENABLE; 82 } else { 83 bmcr = 0; 84 } 85 86 if (mpcs->interface != interface) { 87 /* PHYA power down */ 88 regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 89 SGMII_PHYA_PWD, SGMII_PHYA_PWD); 90 91 if (interface == PHY_INTERFACE_MODE_2500BASEX) 92 rgc3 = RG_PHY_SPEED_3_125G; 93 else 94 rgc3 = 0; 95 96 /* Configure the underlying interface speed */ 97 regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, 98 RG_PHY_SPEED_3_125G, rgc3); 99 100 mpcs->interface = interface; 101 } 102 103 /* Update the advertisement, noting whether it has changed */ 104 regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE, 105 SGMII_ADVERTISE, advertise, &changed); 106 107 /* Setup the link timer and QPHY power up inside SGMIISYS */ 108 regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8); 109 110 /* Update the sgmsys mode register */ 111 regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, 112 SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN | 113 SGMII_IF_MODE_SGMII, sgm_mode); 114 115 /* Update the BMCR */ 116 regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, 117 SGMII_AN_RESTART | SGMII_AN_ENABLE, bmcr); 118 119 /* Release PHYA power down state 120 * Only removing bit SGMII_PHYA_PWD isn't enough. 121 * There are cases when the SGMII_PHYA_PWD register contains 0x9 which 122 * prevents SGMII from working. The SGMII still shows link but no traffic 123 * can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was 124 * taken from a good working state of the SGMII interface. 125 * Unknown how much the QPHY needs but it is racy without a sleep. 126 * Tested on mt7622 & mt7986. 127 */ 128 usleep_range(50, 100); 129 regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0); 130 131 return changed; 132} 133 134static void mtk_pcs_restart_an(struct phylink_pcs *pcs) 135{ 136 struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); 137 138 regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, 139 SGMII_AN_RESTART, SGMII_AN_RESTART); 140} 141 142static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, 143 phy_interface_t interface, int speed, int duplex) 144{ 145 struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); 146 unsigned int sgm_mode; 147 148 if (!phylink_autoneg_inband(mode)) { 149 /* Force the speed and duplex setting */ 150 if (speed == SPEED_10) 151 sgm_mode = SGMII_SPEED_10; 152 else if (speed == SPEED_100) 153 sgm_mode = SGMII_SPEED_100; 154 else 155 sgm_mode = SGMII_SPEED_1000; 156 157 if (duplex != DUPLEX_FULL) 158 sgm_mode |= SGMII_DUPLEX_HALF; 159 160 regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, 161 SGMII_DUPLEX_HALF | SGMII_SPEED_MASK, 162 sgm_mode); 163 } 164} 165 166static const struct phylink_pcs_ops mtk_pcs_ops = { 167 .pcs_get_state = mtk_pcs_get_state, 168 .pcs_config = mtk_pcs_config, 169 .pcs_an_restart = mtk_pcs_restart_an, 170 .pcs_link_up = mtk_pcs_link_up, 171}; 172 173int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3) 174{ 175 struct device_node *np; 176 int i; 177 178 for (i = 0; i < MTK_MAX_DEVS; i++) { 179 np = of_parse_phandle(r, "mediatek,sgmiisys", i); 180 if (!np) 181 break; 182 183 ss->pcs[i].ana_rgc3 = ana_rgc3; 184 ss->pcs[i].regmap = syscon_node_to_regmap(np); 185 of_node_put(np); 186 if (IS_ERR(ss->pcs[i].regmap)) 187 return PTR_ERR(ss->pcs[i].regmap); 188 189 ss->pcs[i].pcs.ops = &mtk_pcs_ops; 190 ss->pcs[i].pcs.poll = true; 191 ss->pcs[i].interface = PHY_INTERFACE_MODE_NA; 192 } 193 194 return 0; 195} 196 197struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id) 198{ 199 if (!ss->pcs[id].regmap) 200 return NULL; 201 202 return &ss->pcs[id].pcs; 203}