cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

b53_serdes.c (6547B)


      1// SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause
      2/*
      3 * Northstar Plus switch SerDes/SGMII PHY main logic
      4 *
      5 * Copyright (C) 2018 Florian Fainelli <f.fainelli@gmail.com>
      6 */
      7
      8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
      9
     10#include <linux/delay.h>
     11#include <linux/kernel.h>
     12#include <linux/phy.h>
     13#include <linux/phylink.h>
     14#include <net/dsa.h>
     15
     16#include "b53_priv.h"
     17#include "b53_serdes.h"
     18#include "b53_regs.h"
     19
     20static inline struct b53_pcs *pcs_to_b53_pcs(struct phylink_pcs *pcs)
     21{
     22	return container_of(pcs, struct b53_pcs, pcs);
     23}
     24
     25static void b53_serdes_write_blk(struct b53_device *dev, u8 offset, u16 block,
     26				 u16 value)
     27{
     28	b53_write16(dev, B53_SERDES_PAGE, B53_SERDES_BLKADDR, block);
     29	b53_write16(dev, B53_SERDES_PAGE, offset, value);
     30}
     31
     32static u16 b53_serdes_read_blk(struct b53_device *dev, u8 offset, u16 block)
     33{
     34	u16 value;
     35
     36	b53_write16(dev, B53_SERDES_PAGE, B53_SERDES_BLKADDR, block);
     37	b53_read16(dev, B53_SERDES_PAGE, offset, &value);
     38
     39	return value;
     40}
     41
     42static void b53_serdes_set_lane(struct b53_device *dev, u8 lane)
     43{
     44	if (dev->serdes_lane == lane)
     45		return;
     46
     47	WARN_ON(lane > 1);
     48
     49	b53_serdes_write_blk(dev, B53_SERDES_LANE,
     50			     SERDES_XGXSBLK0_BLOCKADDRESS, lane);
     51	dev->serdes_lane = lane;
     52}
     53
     54static void b53_serdes_write(struct b53_device *dev, u8 lane,
     55			     u8 offset, u16 block, u16 value)
     56{
     57	b53_serdes_set_lane(dev, lane);
     58	b53_serdes_write_blk(dev, offset, block, value);
     59}
     60
     61static u16 b53_serdes_read(struct b53_device *dev, u8 lane,
     62			   u8 offset, u16 block)
     63{
     64	b53_serdes_set_lane(dev, lane);
     65	return b53_serdes_read_blk(dev, offset, block);
     66}
     67
     68static int b53_serdes_config(struct phylink_pcs *pcs, unsigned int mode,
     69			     phy_interface_t interface,
     70			     const unsigned long *advertising,
     71			     bool permit_pause_to_mac)
     72{
     73	struct b53_device *dev = pcs_to_b53_pcs(pcs)->dev;
     74	u8 lane = pcs_to_b53_pcs(pcs)->lane;
     75	u16 reg;
     76
     77	reg = b53_serdes_read(dev, lane, B53_SERDES_DIGITAL_CONTROL(1),
     78			      SERDES_DIGITAL_BLK);
     79	if (interface == PHY_INTERFACE_MODE_1000BASEX)
     80		reg |= FIBER_MODE_1000X;
     81	else
     82		reg &= ~FIBER_MODE_1000X;
     83	b53_serdes_write(dev, lane, B53_SERDES_DIGITAL_CONTROL(1),
     84			 SERDES_DIGITAL_BLK, reg);
     85
     86	return 0;
     87}
     88
     89static void b53_serdes_an_restart(struct phylink_pcs *pcs)
     90{
     91	struct b53_device *dev = pcs_to_b53_pcs(pcs)->dev;
     92	u8 lane = pcs_to_b53_pcs(pcs)->lane;
     93	u16 reg;
     94
     95	reg = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
     96			      SERDES_MII_BLK);
     97	reg |= BMCR_ANRESTART;
     98	b53_serdes_write(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
     99			 SERDES_MII_BLK, reg);
    100}
    101
    102static void b53_serdes_get_state(struct phylink_pcs *pcs,
    103				  struct phylink_link_state *state)
    104{
    105	struct b53_device *dev = pcs_to_b53_pcs(pcs)->dev;
    106	u8 lane = pcs_to_b53_pcs(pcs)->lane;
    107	u16 dig, bmsr;
    108
    109	dig = b53_serdes_read(dev, lane, B53_SERDES_DIGITAL_STATUS,
    110			      SERDES_DIGITAL_BLK);
    111	bmsr = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_BMSR),
    112			       SERDES_MII_BLK);
    113
    114	switch ((dig >> SPEED_STATUS_SHIFT) & SPEED_STATUS_MASK) {
    115	case SPEED_STATUS_10:
    116		state->speed = SPEED_10;
    117		break;
    118	case SPEED_STATUS_100:
    119		state->speed = SPEED_100;
    120		break;
    121	case SPEED_STATUS_1000:
    122		state->speed = SPEED_1000;
    123		break;
    124	default:
    125	case SPEED_STATUS_2500:
    126		state->speed = SPEED_2500;
    127		break;
    128	}
    129
    130	state->duplex = dig & DUPLEX_STATUS ? DUPLEX_FULL : DUPLEX_HALF;
    131	state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE);
    132	state->link = !!(dig & LINK_STATUS);
    133	if (dig & PAUSE_RESOLUTION_RX_SIDE)
    134		state->pause |= MLO_PAUSE_RX;
    135	if (dig & PAUSE_RESOLUTION_TX_SIDE)
    136		state->pause |= MLO_PAUSE_TX;
    137}
    138
    139void b53_serdes_link_set(struct b53_device *dev, int port, unsigned int mode,
    140			 phy_interface_t interface, bool link_up)
    141{
    142	u8 lane = b53_serdes_map_lane(dev, port);
    143	u16 reg;
    144
    145	if (lane == B53_INVALID_LANE)
    146		return;
    147
    148	reg = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
    149			      SERDES_MII_BLK);
    150	if (link_up)
    151		reg &= ~BMCR_PDOWN;
    152	else
    153		reg |= BMCR_PDOWN;
    154	b53_serdes_write(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
    155			 SERDES_MII_BLK, reg);
    156}
    157EXPORT_SYMBOL(b53_serdes_link_set);
    158
    159static const struct phylink_pcs_ops b53_pcs_ops = {
    160	.pcs_get_state = b53_serdes_get_state,
    161	.pcs_config = b53_serdes_config,
    162	.pcs_an_restart = b53_serdes_an_restart,
    163};
    164
    165void b53_serdes_phylink_get_caps(struct b53_device *dev, int port,
    166				 struct phylink_config *config)
    167{
    168	u8 lane = b53_serdes_map_lane(dev, port);
    169
    170	if (lane == B53_INVALID_LANE)
    171		return;
    172
    173	switch (lane) {
    174	case 0:
    175		/* It appears lane 0 supports 2500base-X and 1000base-X */
    176		__set_bit(PHY_INTERFACE_MODE_2500BASEX,
    177			  config->supported_interfaces);
    178		config->mac_capabilities |= MAC_2500FD;
    179		fallthrough;
    180	case 1:
    181		/* It appears lane 1 only supports 1000base-X and SGMII */
    182		__set_bit(PHY_INTERFACE_MODE_1000BASEX,
    183			  config->supported_interfaces);
    184		__set_bit(PHY_INTERFACE_MODE_SGMII,
    185			  config->supported_interfaces);
    186		config->mac_capabilities |= MAC_1000FD;
    187		break;
    188	default:
    189		break;
    190	}
    191}
    192EXPORT_SYMBOL(b53_serdes_phylink_get_caps);
    193
    194struct phylink_pcs *b53_serdes_phylink_mac_select_pcs(struct b53_device *dev,
    195						      int port,
    196						      phy_interface_t interface)
    197{
    198	u8 lane = b53_serdes_map_lane(dev, port);
    199
    200	if (lane == B53_INVALID_LANE || lane >= B53_N_PCS ||
    201	    !dev->pcs[lane].dev)
    202		return NULL;
    203
    204	if (!phy_interface_mode_is_8023z(interface) &&
    205	    interface != PHY_INTERFACE_MODE_SGMII)
    206		return NULL;
    207
    208	return &dev->pcs[lane].pcs;
    209}
    210EXPORT_SYMBOL(b53_serdes_phylink_mac_select_pcs);
    211
    212int b53_serdes_init(struct b53_device *dev, int port)
    213{
    214	u8 lane = b53_serdes_map_lane(dev, port);
    215	struct b53_pcs *pcs;
    216	u16 id0, msb, lsb;
    217
    218	if (lane == B53_INVALID_LANE)
    219		return -EINVAL;
    220
    221	id0 = b53_serdes_read(dev, lane, B53_SERDES_ID0, SERDES_ID0);
    222	msb = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_PHYSID1),
    223			      SERDES_MII_BLK);
    224	lsb = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_PHYSID2),
    225			      SERDES_MII_BLK);
    226	if (id0 == 0 || id0 == 0xffff) {
    227		dev_err(dev->dev, "SerDes not initialized, check settings\n");
    228		return -ENODEV;
    229	}
    230
    231	dev_info(dev->dev,
    232		 "SerDes lane %d, model: %d, rev %c%d (OUI: 0x%08x)\n",
    233		 lane, id0 & SERDES_ID0_MODEL_MASK,
    234		 (id0 >> SERDES_ID0_REV_LETTER_SHIFT) + 0x41,
    235		 (id0 >> SERDES_ID0_REV_NUM_SHIFT) & SERDES_ID0_REV_NUM_MASK,
    236		 (u32)msb << 16 | lsb);
    237
    238	pcs = &dev->pcs[lane];
    239	pcs->dev = dev;
    240	pcs->lane = lane;
    241	pcs->pcs.ops = &b53_pcs_ops;
    242
    243	return 0;
    244}
    245EXPORT_SYMBOL(b53_serdes_init);
    246
    247MODULE_AUTHOR("Florian Fainelli <f.fainelli@gmail.com>");
    248MODULE_DESCRIPTION("B53 Switch SerDes driver");
    249MODULE_LICENSE("Dual BSD/GPL");