xref: /openbmc/linux/drivers/net/dsa/b53/b53_serdes.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1*59da9885SKrzysztof Kozlowski // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
20e01491dSFlorian Fainelli /*
30e01491dSFlorian Fainelli  * Northstar Plus switch SerDes/SGMII PHY main logic
40e01491dSFlorian Fainelli  *
50e01491dSFlorian Fainelli  * Copyright (C) 2018 Florian Fainelli <f.fainelli@gmail.com>
60e01491dSFlorian Fainelli  */
70e01491dSFlorian Fainelli 
80e01491dSFlorian Fainelli #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
90e01491dSFlorian Fainelli 
100e01491dSFlorian Fainelli #include <linux/delay.h>
110e01491dSFlorian Fainelli #include <linux/kernel.h>
120e01491dSFlorian Fainelli #include <linux/phy.h>
130e01491dSFlorian Fainelli #include <linux/phylink.h>
140e01491dSFlorian Fainelli #include <net/dsa.h>
150e01491dSFlorian Fainelli 
160e01491dSFlorian Fainelli #include "b53_priv.h"
170e01491dSFlorian Fainelli #include "b53_serdes.h"
180e01491dSFlorian Fainelli #include "b53_regs.h"
190e01491dSFlorian Fainelli 
pcs_to_b53_pcs(struct phylink_pcs * pcs)2079396934SRussell King (Oracle) static inline struct b53_pcs *pcs_to_b53_pcs(struct phylink_pcs *pcs)
2179396934SRussell King (Oracle) {
2279396934SRussell King (Oracle) 	return container_of(pcs, struct b53_pcs, pcs);
2379396934SRussell King (Oracle) }
2479396934SRussell King (Oracle) 
b53_serdes_write_blk(struct b53_device * dev,u8 offset,u16 block,u16 value)250e01491dSFlorian Fainelli static void b53_serdes_write_blk(struct b53_device *dev, u8 offset, u16 block,
260e01491dSFlorian Fainelli 				 u16 value)
270e01491dSFlorian Fainelli {
280e01491dSFlorian Fainelli 	b53_write16(dev, B53_SERDES_PAGE, B53_SERDES_BLKADDR, block);
290e01491dSFlorian Fainelli 	b53_write16(dev, B53_SERDES_PAGE, offset, value);
300e01491dSFlorian Fainelli }
310e01491dSFlorian Fainelli 
b53_serdes_read_blk(struct b53_device * dev,u8 offset,u16 block)320e01491dSFlorian Fainelli static u16 b53_serdes_read_blk(struct b53_device *dev, u8 offset, u16 block)
330e01491dSFlorian Fainelli {
340e01491dSFlorian Fainelli 	u16 value;
350e01491dSFlorian Fainelli 
360e01491dSFlorian Fainelli 	b53_write16(dev, B53_SERDES_PAGE, B53_SERDES_BLKADDR, block);
370e01491dSFlorian Fainelli 	b53_read16(dev, B53_SERDES_PAGE, offset, &value);
380e01491dSFlorian Fainelli 
390e01491dSFlorian Fainelli 	return value;
400e01491dSFlorian Fainelli }
410e01491dSFlorian Fainelli 
b53_serdes_set_lane(struct b53_device * dev,u8 lane)420e01491dSFlorian Fainelli static void b53_serdes_set_lane(struct b53_device *dev, u8 lane)
430e01491dSFlorian Fainelli {
440e01491dSFlorian Fainelli 	if (dev->serdes_lane == lane)
450e01491dSFlorian Fainelli 		return;
460e01491dSFlorian Fainelli 
470e01491dSFlorian Fainelli 	WARN_ON(lane > 1);
480e01491dSFlorian Fainelli 
490e01491dSFlorian Fainelli 	b53_serdes_write_blk(dev, B53_SERDES_LANE,
500e01491dSFlorian Fainelli 			     SERDES_XGXSBLK0_BLOCKADDRESS, lane);
510e01491dSFlorian Fainelli 	dev->serdes_lane = lane;
520e01491dSFlorian Fainelli }
530e01491dSFlorian Fainelli 
b53_serdes_write(struct b53_device * dev,u8 lane,u8 offset,u16 block,u16 value)540e01491dSFlorian Fainelli static void b53_serdes_write(struct b53_device *dev, u8 lane,
550e01491dSFlorian Fainelli 			     u8 offset, u16 block, u16 value)
560e01491dSFlorian Fainelli {
570e01491dSFlorian Fainelli 	b53_serdes_set_lane(dev, lane);
580e01491dSFlorian Fainelli 	b53_serdes_write_blk(dev, offset, block, value);
590e01491dSFlorian Fainelli }
600e01491dSFlorian Fainelli 
b53_serdes_read(struct b53_device * dev,u8 lane,u8 offset,u16 block)610e01491dSFlorian Fainelli static u16 b53_serdes_read(struct b53_device *dev, u8 lane,
620e01491dSFlorian Fainelli 			   u8 offset, u16 block)
630e01491dSFlorian Fainelli {
640e01491dSFlorian Fainelli 	b53_serdes_set_lane(dev, lane);
650e01491dSFlorian Fainelli 	return b53_serdes_read_blk(dev, offset, block);
660e01491dSFlorian Fainelli }
670e01491dSFlorian Fainelli 
b53_serdes_config(struct phylink_pcs * pcs,unsigned int neg_mode,phy_interface_t interface,const unsigned long * advertising,bool permit_pause_to_mac)68772c476dSRussell King (Oracle) static int b53_serdes_config(struct phylink_pcs *pcs, unsigned int neg_mode,
6979396934SRussell King (Oracle) 			     phy_interface_t interface,
7079396934SRussell King (Oracle) 			     const unsigned long *advertising,
7179396934SRussell King (Oracle) 			     bool permit_pause_to_mac)
720e01491dSFlorian Fainelli {
7379396934SRussell King (Oracle) 	struct b53_device *dev = pcs_to_b53_pcs(pcs)->dev;
7479396934SRussell King (Oracle) 	u8 lane = pcs_to_b53_pcs(pcs)->lane;
750e01491dSFlorian Fainelli 	u16 reg;
760e01491dSFlorian Fainelli 
770e01491dSFlorian Fainelli 	reg = b53_serdes_read(dev, lane, B53_SERDES_DIGITAL_CONTROL(1),
780e01491dSFlorian Fainelli 			      SERDES_DIGITAL_BLK);
7979396934SRussell King (Oracle) 	if (interface == PHY_INTERFACE_MODE_1000BASEX)
800e01491dSFlorian Fainelli 		reg |= FIBER_MODE_1000X;
810e01491dSFlorian Fainelli 	else
820e01491dSFlorian Fainelli 		reg &= ~FIBER_MODE_1000X;
830e01491dSFlorian Fainelli 	b53_serdes_write(dev, lane, B53_SERDES_DIGITAL_CONTROL(1),
840e01491dSFlorian Fainelli 			 SERDES_DIGITAL_BLK, reg);
8579396934SRussell King (Oracle) 
8679396934SRussell King (Oracle) 	return 0;
870e01491dSFlorian Fainelli }
880e01491dSFlorian Fainelli 
b53_serdes_an_restart(struct phylink_pcs * pcs)8979396934SRussell King (Oracle) static void b53_serdes_an_restart(struct phylink_pcs *pcs)
900e01491dSFlorian Fainelli {
9179396934SRussell King (Oracle) 	struct b53_device *dev = pcs_to_b53_pcs(pcs)->dev;
9279396934SRussell King (Oracle) 	u8 lane = pcs_to_b53_pcs(pcs)->lane;
930e01491dSFlorian Fainelli 	u16 reg;
940e01491dSFlorian Fainelli 
950e01491dSFlorian Fainelli 	reg = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
960e01491dSFlorian Fainelli 			      SERDES_MII_BLK);
970e01491dSFlorian Fainelli 	reg |= BMCR_ANRESTART;
980e01491dSFlorian Fainelli 	b53_serdes_write(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
990e01491dSFlorian Fainelli 			 SERDES_MII_BLK, reg);
1000e01491dSFlorian Fainelli }
1010e01491dSFlorian Fainelli 
b53_serdes_get_state(struct phylink_pcs * pcs,struct phylink_link_state * state)10279396934SRussell King (Oracle) static void b53_serdes_get_state(struct phylink_pcs *pcs,
1030e01491dSFlorian Fainelli 				  struct phylink_link_state *state)
1040e01491dSFlorian Fainelli {
10579396934SRussell King (Oracle) 	struct b53_device *dev = pcs_to_b53_pcs(pcs)->dev;
10679396934SRussell King (Oracle) 	u8 lane = pcs_to_b53_pcs(pcs)->lane;
107e24cf6b3SFlorian Fainelli 	u16 dig, bmsr;
1080e01491dSFlorian Fainelli 
1090e01491dSFlorian Fainelli 	dig = b53_serdes_read(dev, lane, B53_SERDES_DIGITAL_STATUS,
1100e01491dSFlorian Fainelli 			      SERDES_DIGITAL_BLK);
1110e01491dSFlorian Fainelli 	bmsr = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_BMSR),
1120e01491dSFlorian Fainelli 			       SERDES_MII_BLK);
1130e01491dSFlorian Fainelli 
1140e01491dSFlorian Fainelli 	switch ((dig >> SPEED_STATUS_SHIFT) & SPEED_STATUS_MASK) {
1150e01491dSFlorian Fainelli 	case SPEED_STATUS_10:
1160e01491dSFlorian Fainelli 		state->speed = SPEED_10;
1170e01491dSFlorian Fainelli 		break;
1180e01491dSFlorian Fainelli 	case SPEED_STATUS_100:
1190e01491dSFlorian Fainelli 		state->speed = SPEED_100;
1200e01491dSFlorian Fainelli 		break;
1210e01491dSFlorian Fainelli 	case SPEED_STATUS_1000:
1220e01491dSFlorian Fainelli 		state->speed = SPEED_1000;
1230e01491dSFlorian Fainelli 		break;
1240e01491dSFlorian Fainelli 	default:
1250e01491dSFlorian Fainelli 	case SPEED_STATUS_2500:
1260e01491dSFlorian Fainelli 		state->speed = SPEED_2500;
1270e01491dSFlorian Fainelli 		break;
1280e01491dSFlorian Fainelli 	}
1290e01491dSFlorian Fainelli 
1300e01491dSFlorian Fainelli 	state->duplex = dig & DUPLEX_STATUS ? DUPLEX_FULL : DUPLEX_HALF;
1310e01491dSFlorian Fainelli 	state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE);
1320e01491dSFlorian Fainelli 	state->link = !!(dig & LINK_STATUS);
1330e01491dSFlorian Fainelli 	if (dig & PAUSE_RESOLUTION_RX_SIDE)
1340e01491dSFlorian Fainelli 		state->pause |= MLO_PAUSE_RX;
1350e01491dSFlorian Fainelli 	if (dig & PAUSE_RESOLUTION_TX_SIDE)
1360e01491dSFlorian Fainelli 		state->pause |= MLO_PAUSE_TX;
1370e01491dSFlorian Fainelli }
1380e01491dSFlorian Fainelli 
b53_serdes_link_set(struct b53_device * dev,int port,unsigned int mode,phy_interface_t interface,bool link_up)1390e01491dSFlorian Fainelli void b53_serdes_link_set(struct b53_device *dev, int port, unsigned int mode,
1400e01491dSFlorian Fainelli 			 phy_interface_t interface, bool link_up)
1410e01491dSFlorian Fainelli {
1420e01491dSFlorian Fainelli 	u8 lane = b53_serdes_map_lane(dev, port);
1430e01491dSFlorian Fainelli 	u16 reg;
1440e01491dSFlorian Fainelli 
1450e01491dSFlorian Fainelli 	if (lane == B53_INVALID_LANE)
1460e01491dSFlorian Fainelli 		return;
1470e01491dSFlorian Fainelli 
1480e01491dSFlorian Fainelli 	reg = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
1490e01491dSFlorian Fainelli 			      SERDES_MII_BLK);
1500e01491dSFlorian Fainelli 	if (link_up)
1510e01491dSFlorian Fainelli 		reg &= ~BMCR_PDOWN;
1520e01491dSFlorian Fainelli 	else
1530e01491dSFlorian Fainelli 		reg |= BMCR_PDOWN;
1540e01491dSFlorian Fainelli 	b53_serdes_write(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
1550e01491dSFlorian Fainelli 			 SERDES_MII_BLK, reg);
1560e01491dSFlorian Fainelli }
1570e01491dSFlorian Fainelli EXPORT_SYMBOL(b53_serdes_link_set);
1580e01491dSFlorian Fainelli 
15979396934SRussell King (Oracle) static const struct phylink_pcs_ops b53_pcs_ops = {
16079396934SRussell King (Oracle) 	.pcs_get_state = b53_serdes_get_state,
16179396934SRussell King (Oracle) 	.pcs_config = b53_serdes_config,
16279396934SRussell King (Oracle) 	.pcs_an_restart = b53_serdes_an_restart,
16379396934SRussell King (Oracle) };
16479396934SRussell King (Oracle) 
b53_serdes_phylink_get_caps(struct b53_device * dev,int port,struct phylink_config * config)165dda1c257SRussell King (Oracle) void b53_serdes_phylink_get_caps(struct b53_device *dev, int port,
166dda1c257SRussell King (Oracle) 				 struct phylink_config *config)
167dda1c257SRussell King (Oracle) {
168dda1c257SRussell King (Oracle) 	u8 lane = b53_serdes_map_lane(dev, port);
169dda1c257SRussell King (Oracle) 
170dda1c257SRussell King (Oracle) 	if (lane == B53_INVALID_LANE)
171dda1c257SRussell King (Oracle) 		return;
172dda1c257SRussell King (Oracle) 
173dda1c257SRussell King (Oracle) 	switch (lane) {
174dda1c257SRussell King (Oracle) 	case 0:
175dda1c257SRussell King (Oracle) 		/* It appears lane 0 supports 2500base-X and 1000base-X */
176dda1c257SRussell King (Oracle) 		__set_bit(PHY_INTERFACE_MODE_2500BASEX,
177dda1c257SRussell King (Oracle) 			  config->supported_interfaces);
178dda1c257SRussell King (Oracle) 		config->mac_capabilities |= MAC_2500FD;
179dda1c257SRussell King (Oracle) 		fallthrough;
180dda1c257SRussell King (Oracle) 	case 1:
181dda1c257SRussell King (Oracle) 		/* It appears lane 1 only supports 1000base-X and SGMII */
182dda1c257SRussell King (Oracle) 		__set_bit(PHY_INTERFACE_MODE_1000BASEX,
183dda1c257SRussell King (Oracle) 			  config->supported_interfaces);
184dda1c257SRussell King (Oracle) 		__set_bit(PHY_INTERFACE_MODE_SGMII,
185dda1c257SRussell King (Oracle) 			  config->supported_interfaces);
186dda1c257SRussell King (Oracle) 		config->mac_capabilities |= MAC_1000FD;
187dda1c257SRussell King (Oracle) 		break;
188dda1c257SRussell King (Oracle) 	default:
189dda1c257SRussell King (Oracle) 		break;
190dda1c257SRussell King (Oracle) 	}
191dda1c257SRussell King (Oracle) }
192dda1c257SRussell King (Oracle) EXPORT_SYMBOL(b53_serdes_phylink_get_caps);
193dda1c257SRussell King (Oracle) 
b53_serdes_phylink_mac_select_pcs(struct b53_device * dev,int port,phy_interface_t interface)19479396934SRussell King (Oracle) struct phylink_pcs *b53_serdes_phylink_mac_select_pcs(struct b53_device *dev,
19579396934SRussell King (Oracle) 						      int port,
19679396934SRussell King (Oracle) 						      phy_interface_t interface)
19779396934SRussell King (Oracle) {
19879396934SRussell King (Oracle) 	u8 lane = b53_serdes_map_lane(dev, port);
19979396934SRussell King (Oracle) 
20079396934SRussell King (Oracle) 	if (lane == B53_INVALID_LANE || lane >= B53_N_PCS ||
20179396934SRussell King (Oracle) 	    !dev->pcs[lane].dev)
20279396934SRussell King (Oracle) 		return NULL;
20379396934SRussell King (Oracle) 
20479396934SRussell King (Oracle) 	if (!phy_interface_mode_is_8023z(interface) &&
20579396934SRussell King (Oracle) 	    interface != PHY_INTERFACE_MODE_SGMII)
20679396934SRussell King (Oracle) 		return NULL;
20779396934SRussell King (Oracle) 
20879396934SRussell King (Oracle) 	return &dev->pcs[lane].pcs;
20979396934SRussell King (Oracle) }
21079396934SRussell King (Oracle) EXPORT_SYMBOL(b53_serdes_phylink_mac_select_pcs);
21179396934SRussell King (Oracle) 
b53_serdes_init(struct b53_device * dev,int port)2120e01491dSFlorian Fainelli int b53_serdes_init(struct b53_device *dev, int port)
2130e01491dSFlorian Fainelli {
2140e01491dSFlorian Fainelli 	u8 lane = b53_serdes_map_lane(dev, port);
21579396934SRussell King (Oracle) 	struct b53_pcs *pcs;
2160e01491dSFlorian Fainelli 	u16 id0, msb, lsb;
2170e01491dSFlorian Fainelli 
2180e01491dSFlorian Fainelli 	if (lane == B53_INVALID_LANE)
2190e01491dSFlorian Fainelli 		return -EINVAL;
2200e01491dSFlorian Fainelli 
2210e01491dSFlorian Fainelli 	id0 = b53_serdes_read(dev, lane, B53_SERDES_ID0, SERDES_ID0);
2220e01491dSFlorian Fainelli 	msb = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_PHYSID1),
2230e01491dSFlorian Fainelli 			      SERDES_MII_BLK);
2240e01491dSFlorian Fainelli 	lsb = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_PHYSID2),
2250e01491dSFlorian Fainelli 			      SERDES_MII_BLK);
2260e01491dSFlorian Fainelli 	if (id0 == 0 || id0 == 0xffff) {
2270e01491dSFlorian Fainelli 		dev_err(dev->dev, "SerDes not initialized, check settings\n");
2280e01491dSFlorian Fainelli 		return -ENODEV;
2290e01491dSFlorian Fainelli 	}
2300e01491dSFlorian Fainelli 
2310e01491dSFlorian Fainelli 	dev_info(dev->dev,
2320e01491dSFlorian Fainelli 		 "SerDes lane %d, model: %d, rev %c%d (OUI: 0x%08x)\n",
2330e01491dSFlorian Fainelli 		 lane, id0 & SERDES_ID0_MODEL_MASK,
2340e01491dSFlorian Fainelli 		 (id0 >> SERDES_ID0_REV_LETTER_SHIFT) + 0x41,
2350e01491dSFlorian Fainelli 		 (id0 >> SERDES_ID0_REV_NUM_SHIFT) & SERDES_ID0_REV_NUM_MASK,
2360e01491dSFlorian Fainelli 		 (u32)msb << 16 | lsb);
2370e01491dSFlorian Fainelli 
23879396934SRussell King (Oracle) 	pcs = &dev->pcs[lane];
23979396934SRussell King (Oracle) 	pcs->dev = dev;
24079396934SRussell King (Oracle) 	pcs->lane = lane;
24179396934SRussell King (Oracle) 	pcs->pcs.ops = &b53_pcs_ops;
242772c476dSRussell King (Oracle) 	pcs->pcs.neg_mode = true;
24379396934SRussell King (Oracle) 
2440e01491dSFlorian Fainelli 	return 0;
2450e01491dSFlorian Fainelli }
2460e01491dSFlorian Fainelli EXPORT_SYMBOL(b53_serdes_init);
2470e01491dSFlorian Fainelli 
2480e01491dSFlorian Fainelli MODULE_AUTHOR("Florian Fainelli <f.fainelli@gmail.com>");
2490e01491dSFlorian Fainelli MODULE_DESCRIPTION("B53 Switch SerDes driver");
2500e01491dSFlorian Fainelli MODULE_LICENSE("Dual BSD/GPL");
251