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-rc2 185 lines 4.8 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 if (interface == PHY_INTERFACE_MODE_2500BASEX) 47 rgc3 = RG_PHY_SPEED_3_125G; 48 else 49 rgc3 = 0; 50 51 advertise = phylink_mii_c22_pcs_encode_advertisement(interface, 52 advertising); 53 if (advertise < 0) 54 return advertise; 55 56 link_timer = phylink_get_link_timer_ns(interface); 57 if (link_timer < 0) 58 return link_timer; 59 60 /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and 61 * we assume that fixes it's speed at bitrate = line rate (in 62 * other words, 1000Mbps or 2500Mbps). 63 */ 64 if (interface == PHY_INTERFACE_MODE_SGMII) { 65 sgm_mode = SGMII_IF_MODE_SGMII; 66 if (phylink_autoneg_inband(mode)) { 67 sgm_mode |= SGMII_REMOTE_FAULT_DIS | 68 SGMII_SPEED_DUPLEX_AN; 69 use_an = true; 70 } else { 71 use_an = false; 72 } 73 } else if (phylink_autoneg_inband(mode)) { 74 /* 1000base-X or 2500base-X autoneg */ 75 sgm_mode = SGMII_REMOTE_FAULT_DIS; 76 use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, 77 advertising); 78 } else { 79 /* 1000base-X or 2500base-X without autoneg */ 80 sgm_mode = 0; 81 use_an = false; 82 } 83 84 if (use_an) { 85 /* FIXME: Do we need to set AN_RESTART here? */ 86 bmcr = SGMII_AN_RESTART | SGMII_AN_ENABLE; 87 } else { 88 bmcr = 0; 89 } 90 91 /* Configure the underlying interface speed */ 92 regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, 93 RG_PHY_SPEED_3_125G, rgc3); 94 95 /* Update the advertisement, noting whether it has changed */ 96 regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE, 97 SGMII_ADVERTISE, advertise, &changed); 98 99 /* Setup the link timer and QPHY power up inside SGMIISYS */ 100 regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8); 101 102 /* Update the sgmsys mode register */ 103 regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, 104 SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN | 105 SGMII_IF_MODE_SGMII, sgm_mode); 106 107 /* Update the BMCR */ 108 regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, 109 SGMII_AN_RESTART | SGMII_AN_ENABLE, bmcr); 110 111 /* Release PHYA power down state */ 112 regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 113 SGMII_PHYA_PWD, 0); 114 115 return changed; 116} 117 118static void mtk_pcs_restart_an(struct phylink_pcs *pcs) 119{ 120 struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); 121 122 regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, 123 SGMII_AN_RESTART, SGMII_AN_RESTART); 124} 125 126static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, 127 phy_interface_t interface, int speed, int duplex) 128{ 129 struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); 130 unsigned int sgm_mode; 131 132 if (!phylink_autoneg_inband(mode)) { 133 /* Force the speed and duplex setting */ 134 if (speed == SPEED_10) 135 sgm_mode = SGMII_SPEED_10; 136 else if (speed == SPEED_100) 137 sgm_mode = SGMII_SPEED_100; 138 else 139 sgm_mode = SGMII_SPEED_1000; 140 141 if (duplex == DUPLEX_FULL) 142 sgm_mode |= SGMII_DUPLEX_FULL; 143 144 regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, 145 SGMII_DUPLEX_FULL | SGMII_SPEED_MASK, 146 sgm_mode); 147 } 148} 149 150static const struct phylink_pcs_ops mtk_pcs_ops = { 151 .pcs_get_state = mtk_pcs_get_state, 152 .pcs_config = mtk_pcs_config, 153 .pcs_an_restart = mtk_pcs_restart_an, 154 .pcs_link_up = mtk_pcs_link_up, 155}; 156 157int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3) 158{ 159 struct device_node *np; 160 int i; 161 162 for (i = 0; i < MTK_MAX_DEVS; i++) { 163 np = of_parse_phandle(r, "mediatek,sgmiisys", i); 164 if (!np) 165 break; 166 167 ss->pcs[i].ana_rgc3 = ana_rgc3; 168 ss->pcs[i].regmap = syscon_node_to_regmap(np); 169 of_node_put(np); 170 if (IS_ERR(ss->pcs[i].regmap)) 171 return PTR_ERR(ss->pcs[i].regmap); 172 173 ss->pcs[i].pcs.ops = &mtk_pcs_ops; 174 } 175 176 return 0; 177} 178 179struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id) 180{ 181 if (!ss->pcs[id].regmap) 182 return NULL; 183 184 return &ss->pcs[id].pcs; 185}