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

net: dsa: sja1105: Add support for the SGMII port

SJA1105 switches R and S have one SerDes port with an 802.3z
quasi-compatible PCS, hardwired on port 4. The other ports are still
MII/RMII/RGMII. The PCS performs rate adaptation to lower link speeds;
the MAC on this port is hardwired at gigabit. Only full duplex is
supported.

The SGMII port can be configured as part of the static config tables, as
well as through a dedicated SPI address region for its pseudo-clause-22
registers. However it looks like the static configuration is not
able to change some out-of-reset values (like the value of MII_BMCR), so
at the end of the day, having code for it is utterly pointless. We are
just going to use the pseudo-C22 interface.

Because the PCS gets reset when the switch resets, we have to add even
more restoration logic to sja1105_static_config_reload, otherwise the
SGMII port breaks after operations such as enabling PTP timestamping
which require a switch reset.

>From PHYLINK perspective, the switch supports *only* SGMII (it doesn't
support 1000Base-X). It also doesn't expose access to the raw config
word for in-band AN in registers MII_ADV/MII_LPA.
It is able to work in the following modes:
- Forced speed
- SGMII in-band AN slave (speed received from PHY)
- SGMII in-band AN master (acting as a PHY)

The latter mode is not supported by this patch. It is even unclear to me
how that would be described. There is some code for it left in the
patch, but 'an_master' is always passed as false.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Vladimir Oltean and committed by
David S. Miller
ffe10e67 f6e94ff4

+244 -5
+2
drivers/net/dsa/sja1105/sja1105.h
··· 36 36 u64 port_control; 37 37 u64 rgu; 38 38 u64 config; 39 + u64 sgmii; 39 40 u64 rmii_pll1; 40 41 u64 ptp_control; 41 42 u64 ptpclkval; ··· 160 159 XMII_MODE_MII = 0, 161 160 XMII_MODE_RMII = 1, 162 161 XMII_MODE_RGMII = 2, 162 + XMII_MODE_SGMII = 3, 163 163 } sja1105_phy_interface_t; 164 164 165 165 typedef enum {
+4
drivers/net/dsa/sja1105/sja1105_clocking.c
··· 660 660 case XMII_MODE_RGMII: 661 661 rc = sja1105_rgmii_clocking_setup(priv, port, role); 662 662 break; 663 + case XMII_MODE_SGMII: 664 + /* Nothing to do in the CGU for SGMII */ 665 + rc = 0; 666 + break; 663 667 default: 664 668 dev_err(dev, "Invalid interface mode specified: %d\n", 665 669 phy_mode);
+184 -5
drivers/net/dsa/sja1105/sja1105_main.c
··· 22 22 #include <linux/if_ether.h> 23 23 #include <linux/dsa/8021q.h> 24 24 #include "sja1105.h" 25 + #include "sja1105_sgmii.h" 25 26 #include "sja1105_tas.h" 26 27 27 28 static void sja1105_hw_reset(struct gpio_desc *gpio, unsigned int pulse_len, ··· 136 135 return 0; 137 136 } 138 137 138 + static bool sja1105_supports_sgmii(struct sja1105_private *priv, int port) 139 + { 140 + if (priv->info->part_no != SJA1105R_PART_NO && 141 + priv->info->part_no != SJA1105S_PART_NO) 142 + return false; 143 + 144 + if (port != SJA1105_SGMII_PORT) 145 + return false; 146 + 147 + if (dsa_is_unused_port(priv->ds, port)) 148 + return false; 149 + 150 + return true; 151 + } 152 + 139 153 static int sja1105_init_mii_settings(struct sja1105_private *priv, 140 154 struct sja1105_dt_port *ports) 141 155 { ··· 194 178 case PHY_INTERFACE_MODE_RGMII_TXID: 195 179 mii->xmii_mode[i] = XMII_MODE_RGMII; 196 180 break; 181 + case PHY_INTERFACE_MODE_SGMII: 182 + if (!sja1105_supports_sgmii(priv, i)) 183 + return -EINVAL; 184 + mii->xmii_mode[i] = XMII_MODE_SGMII; 185 + break; 197 186 default: 198 187 dev_err(dev, "Unsupported PHY mode %s!\n", 199 188 phy_modes(ports[i].phy_mode)); 200 189 } 201 190 202 - mii->phy_mac[i] = ports[i].role; 191 + /* Even though the SerDes port is able to drive SGMII autoneg 192 + * like a PHY would, from the perspective of the XMII tables, 193 + * the SGMII port should always be put in MAC mode. 194 + */ 195 + if (ports[i].phy_mode == PHY_INTERFACE_MODE_SGMII) 196 + mii->phy_mac[i] = XMII_MAC; 197 + else 198 + mii->phy_mac[i] = ports[i].role; 203 199 } 204 200 return 0; 205 201 } ··· 678 650 return rc; 679 651 } 680 652 653 + static int sja1105_sgmii_read(struct sja1105_private *priv, int pcs_reg) 654 + { 655 + const struct sja1105_regs *regs = priv->info->regs; 656 + u32 val; 657 + int rc; 658 + 659 + rc = sja1105_xfer_u32(priv, SPI_READ, regs->sgmii + pcs_reg, &val, 660 + NULL); 661 + if (rc < 0) 662 + return rc; 663 + 664 + return val; 665 + } 666 + 667 + static int sja1105_sgmii_write(struct sja1105_private *priv, int pcs_reg, 668 + u16 pcs_val) 669 + { 670 + const struct sja1105_regs *regs = priv->info->regs; 671 + u32 val = pcs_val; 672 + int rc; 673 + 674 + rc = sja1105_xfer_u32(priv, SPI_WRITE, regs->sgmii + pcs_reg, &val, 675 + NULL); 676 + if (rc < 0) 677 + return rc; 678 + 679 + return val; 680 + } 681 + 682 + static void sja1105_sgmii_pcs_config(struct sja1105_private *priv, 683 + bool an_enabled, bool an_master) 684 + { 685 + u16 ac = SJA1105_AC_AUTONEG_MODE_SGMII; 686 + 687 + /* DIGITAL_CONTROL_1: Enable vendor-specific MMD1, allow the PHY to 688 + * stop the clock during LPI mode, make the MAC reconfigure 689 + * autonomously after PCS autoneg is done, flush the internal FIFOs. 690 + */ 691 + sja1105_sgmii_write(priv, SJA1105_DC1, SJA1105_DC1_EN_VSMMD1 | 692 + SJA1105_DC1_CLOCK_STOP_EN | 693 + SJA1105_DC1_MAC_AUTO_SW | 694 + SJA1105_DC1_INIT); 695 + /* DIGITAL_CONTROL_2: No polarity inversion for TX and RX lanes */ 696 + sja1105_sgmii_write(priv, SJA1105_DC2, SJA1105_DC2_TX_POL_INV_DISABLE); 697 + /* AUTONEG_CONTROL: Use SGMII autoneg */ 698 + if (an_master) 699 + ac |= SJA1105_AC_PHY_MODE | SJA1105_AC_SGMII_LINK; 700 + sja1105_sgmii_write(priv, SJA1105_AC, ac); 701 + /* BASIC_CONTROL: enable in-band AN now, if requested. Otherwise, 702 + * sja1105_sgmii_pcs_force_speed must be called later for the link 703 + * to become operational. 704 + */ 705 + if (an_enabled) 706 + sja1105_sgmii_write(priv, MII_BMCR, 707 + BMCR_ANENABLE | BMCR_ANRESTART); 708 + } 709 + 710 + static void sja1105_sgmii_pcs_force_speed(struct sja1105_private *priv, 711 + int speed) 712 + { 713 + int pcs_speed; 714 + 715 + switch (speed) { 716 + case SPEED_1000: 717 + pcs_speed = BMCR_SPEED1000; 718 + break; 719 + case SPEED_100: 720 + pcs_speed = BMCR_SPEED100; 721 + break; 722 + case SPEED_10: 723 + pcs_speed = BMCR_SPEED10; 724 + break; 725 + default: 726 + dev_err(priv->ds->dev, "Invalid speed %d\n", speed); 727 + return; 728 + } 729 + sja1105_sgmii_write(priv, MII_BMCR, pcs_speed | BMCR_FULLDPLX); 730 + } 731 + 681 732 /* Convert link speed from SJA1105 to ethtool encoding */ 682 733 static int sja1105_speed[] = { 683 734 [SJA1105_SPEED_AUTO] = SPEED_UNKNOWN, ··· 814 707 * table, since this will be used for the clocking setup, and we no 815 708 * longer need to store it in the static config (already told hardware 816 709 * we want auto during upload phase). 710 + * Actually for the SGMII port, the MAC is fixed at 1 Gbps and 711 + * we need to configure the PCS only (if even that). 817 712 */ 818 - mac[port].speed = speed; 713 + if (sja1105_supports_sgmii(priv, port)) 714 + mac[port].speed = SJA1105_SPEED_1000MBPS; 715 + else 716 + mac[port].speed = speed; 819 717 820 718 /* Write to the dynamic reconfiguration tables */ 821 719 rc = sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, ··· 869 757 case PHY_INTERFACE_MODE_RGMII_RXID: 870 758 case PHY_INTERFACE_MODE_RGMII_TXID: 871 759 return (phy_mode != XMII_MODE_RGMII); 760 + case PHY_INTERFACE_MODE_SGMII: 761 + return (phy_mode != XMII_MODE_SGMII); 872 762 default: 873 763 return true; 874 764 } 875 765 } 876 766 877 767 static void sja1105_mac_config(struct dsa_switch *ds, int port, 878 - unsigned int link_an_mode, 768 + unsigned int mode, 879 769 const struct phylink_link_state *state) 880 770 { 881 771 struct sja1105_private *priv = ds->priv; 772 + bool is_sgmii = sja1105_supports_sgmii(priv, port); 882 773 883 774 if (sja1105_phy_mode_mismatch(priv, port, state->interface)) { 884 775 dev_err(ds->dev, "Changing PHY mode to %s not supported!\n", ··· 889 774 return; 890 775 } 891 776 892 - if (link_an_mode == MLO_AN_INBAND) { 777 + if (phylink_autoneg_inband(mode) && !is_sgmii) { 893 778 dev_err(ds->dev, "In-band AN not supported!\n"); 894 779 return; 895 780 } 781 + 782 + if (is_sgmii) 783 + sja1105_sgmii_pcs_config(priv, phylink_autoneg_inband(mode), 784 + false); 896 785 } 897 786 898 787 static void sja1105_mac_link_down(struct dsa_switch *ds, int port, ··· 916 797 struct sja1105_private *priv = ds->priv; 917 798 918 799 sja1105_adjust_port_config(priv, port, speed); 800 + 801 + if (sja1105_supports_sgmii(priv, port) && !phylink_autoneg_inband(mode)) 802 + sja1105_sgmii_pcs_force_speed(priv, speed); 919 803 920 804 sja1105_inhibit_tx(priv, BIT(port), false); 921 805 } ··· 955 833 phylink_set(mask, 10baseT_Full); 956 834 phylink_set(mask, 100baseT_Full); 957 835 phylink_set(mask, 100baseT1_Full); 958 - if (mii->xmii_mode[port] == XMII_MODE_RGMII) 836 + if (mii->xmii_mode[port] == XMII_MODE_RGMII || 837 + mii->xmii_mode[port] == XMII_MODE_SGMII) 959 838 phylink_set(mask, 1000baseT_Full); 960 839 961 840 bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); 962 841 bitmap_and(state->advertising, state->advertising, mask, 963 842 __ETHTOOL_LINK_MODE_MASK_NBITS); 843 + } 844 + 845 + static int sja1105_mac_pcs_get_state(struct dsa_switch *ds, int port, 846 + struct phylink_link_state *state) 847 + { 848 + struct sja1105_private *priv = ds->priv; 849 + int ais; 850 + 851 + /* Read the vendor-specific AUTONEG_INTR_STATUS register */ 852 + ais = sja1105_sgmii_read(priv, SJA1105_AIS); 853 + if (ais < 0) 854 + return ais; 855 + 856 + switch (SJA1105_AIS_SPEED(ais)) { 857 + case 0: 858 + state->speed = SPEED_10; 859 + break; 860 + case 1: 861 + state->speed = SPEED_100; 862 + break; 863 + case 2: 864 + state->speed = SPEED_1000; 865 + break; 866 + default: 867 + dev_err(ds->dev, "Invalid SGMII PCS speed %lu\n", 868 + SJA1105_AIS_SPEED(ais)); 869 + } 870 + state->duplex = SJA1105_AIS_DUPLEX_MODE(ais); 871 + state->an_complete = SJA1105_AIS_COMPLETE(ais); 872 + state->link = SJA1105_AIS_LINK_STATUS(ais); 873 + 874 + return 0; 964 875 } 965 876 966 877 static int ··· 1522 1367 struct dsa_switch *ds = priv->ds; 1523 1368 s64 t1, t2, t3, t4; 1524 1369 s64 t12, t34; 1370 + u16 bmcr = 0; 1525 1371 int rc, i; 1526 1372 s64 now; 1527 1373 ··· 1539 1383 speed_mbps[i] = sja1105_speed[mac[i].speed]; 1540 1384 mac[i].speed = SJA1105_SPEED_AUTO; 1541 1385 } 1386 + 1387 + if (sja1105_supports_sgmii(priv, SJA1105_SGMII_PORT)) 1388 + bmcr = sja1105_sgmii_read(priv, MII_BMCR); 1542 1389 1543 1390 /* No PTP operations can run right now */ 1544 1391 mutex_lock(&priv->ptp_data.lock); ··· 1591 1432 rc = sja1105_adjust_port_config(priv, i, speed_mbps[i]); 1592 1433 if (rc < 0) 1593 1434 goto out; 1435 + } 1436 + 1437 + if (sja1105_supports_sgmii(priv, SJA1105_SGMII_PORT)) { 1438 + bool an_enabled = !!(bmcr & BMCR_ANENABLE); 1439 + 1440 + sja1105_sgmii_pcs_config(priv, an_enabled, false); 1441 + 1442 + if (!an_enabled) { 1443 + int speed = SPEED_UNKNOWN; 1444 + 1445 + if (bmcr & BMCR_SPEED1000) 1446 + speed = SPEED_1000; 1447 + else if (bmcr & BMCR_SPEED100) 1448 + speed = SPEED_100; 1449 + else if (bmcr & BMCR_SPEED10) 1450 + speed = SPEED_10; 1451 + 1452 + sja1105_sgmii_pcs_force_speed(priv, speed); 1453 + } 1594 1454 } 1595 1455 out: 1596 1456 mutex_unlock(&priv->mgmt_lock); ··· 2176 1998 .teardown = sja1105_teardown, 2177 1999 .set_ageing_time = sja1105_set_ageing_time, 2178 2000 .phylink_validate = sja1105_phylink_validate, 2001 + .phylink_mac_link_state = sja1105_mac_pcs_get_state, 2179 2002 .phylink_mac_config = sja1105_mac_config, 2180 2003 .phylink_mac_link_up = sja1105_mac_link_up, 2181 2004 .phylink_mac_link_down = sja1105_mac_link_down,
+53
drivers/net/dsa/sja1105/sja1105_sgmii.h
··· 1 + /* SPDX-License-Identifier: BSD-3-Clause */ 2 + /* Copyright 2020, NXP Semiconductors 3 + */ 4 + #ifndef _SJA1105_SGMII_H 5 + #define _SJA1105_SGMII_H 6 + 7 + #define SJA1105_SGMII_PORT 4 8 + 9 + /* DIGITAL_CONTROL_1 (address 1f8000h) */ 10 + #define SJA1105_DC1 0x8000 11 + #define SJA1105_DC1_VS_RESET BIT(15) 12 + #define SJA1105_DC1_REMOTE_LOOPBACK BIT(14) 13 + #define SJA1105_DC1_EN_VSMMD1 BIT(13) 14 + #define SJA1105_DC1_POWER_SAVE BIT(11) 15 + #define SJA1105_DC1_CLOCK_STOP_EN BIT(10) 16 + #define SJA1105_DC1_MAC_AUTO_SW BIT(9) 17 + #define SJA1105_DC1_INIT BIT(8) 18 + #define SJA1105_DC1_TX_DISABLE BIT(4) 19 + #define SJA1105_DC1_AUTONEG_TIMER_OVRR BIT(3) 20 + #define SJA1105_DC1_BYP_POWERUP BIT(1) 21 + #define SJA1105_DC1_PHY_MODE_CONTROL BIT(0) 22 + 23 + /* DIGITAL_CONTROL_2 register (address 1f80E1h) */ 24 + #define SJA1105_DC2 0x80e1 25 + #define SJA1105_DC2_TX_POL_INV_DISABLE BIT(4) 26 + #define SJA1105_DC2_RX_POL_INV BIT(0) 27 + 28 + /* DIGITAL_ERROR_CNT register (address 1f80E2h) */ 29 + #define SJA1105_DEC 0x80e2 30 + #define SJA1105_DEC_ICG_EC_ENA BIT(4) 31 + #define SJA1105_DEC_CLEAR_ON_READ BIT(0) 32 + 33 + /* AUTONEG_CONTROL register (address 1f8001h) */ 34 + #define SJA1105_AC 0x8001 35 + #define SJA1105_AC_MII_CONTROL BIT(8) 36 + #define SJA1105_AC_SGMII_LINK BIT(4) 37 + #define SJA1105_AC_PHY_MODE BIT(3) 38 + #define SJA1105_AC_AUTONEG_MODE(x) (((x) << 1) & GENMASK(2, 1)) 39 + #define SJA1105_AC_AUTONEG_MODE_SGMII SJA1105_AC_AUTONEG_MODE(2) 40 + 41 + /* AUTONEG_INTR_STATUS register (address 1f8002h) */ 42 + #define SJA1105_AIS 0x8002 43 + #define SJA1105_AIS_LINK_STATUS(x) (!!((x) & BIT(4))) 44 + #define SJA1105_AIS_SPEED(x) (((x) & GENMASK(3, 2)) >> 2) 45 + #define SJA1105_AIS_DUPLEX_MODE(x) (!!((x) & BIT(1))) 46 + #define SJA1105_AIS_COMPLETE(x) (!!((x) & BIT(0))) 47 + 48 + /* DEBUG_CONTROL register (address 1f8005h) */ 49 + #define SJA1105_DC 0x8005 50 + #define SJA1105_DC_SUPPRESS_LOS BIT(4) 51 + #define SJA1105_DC_RESTART_SYNC BIT(0) 52 + 53 + #endif
+1
drivers/net/dsa/sja1105/sja1105_spi.c
··· 474 474 /* UM10944.pdf, Table 86, ACU Register overview */ 475 475 .pad_mii_tx = {0x100800, 0x100802, 0x100804, 0x100806, 0x100808}, 476 476 .pad_mii_id = {0x100810, 0x100811, 0x100812, 0x100813, 0x100814}, 477 + .sgmii = 0x1F0000, 477 478 .rmii_pll1 = 0x10000A, 478 479 .cgu_idiv = {0x10000B, 0x10000C, 0x10000D, 0x10000E, 0x10000F}, 479 480 .mac = {0x200, 0x202, 0x204, 0x206, 0x208},