Skip to content

Commit 0e01491

Browse files
ffainellidavem330
authored andcommitted
net: dsa: b53: Add SerDes support
Add support for the Northstar Plus SerDes which is accessed through a special page of the switch. Since this is something that most people probably will not want to use, make it a configurable option with a default on ARCH_BCM_NSP where it is the most useful currently. The SerDes supports both SGMII and 1000baseX modes for both lanes, and 2500baseX for one of the lanes, and is internally looking like a seemingly standard MII PHY, except for the few bits that got repurposed. Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent a8e8b98 commit 0e01491

File tree

7 files changed

+490
-0
lines changed

7 files changed

+490
-0
lines changed

drivers/net/dsa/b53/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,10 @@ config B53_SRAB_DRIVER
3535
help
3636
Select to enable support for memory-mapped Switch Register Access
3737
Bridge Registers (SRAB) like it is found on the BCM53010
38+
39+
config B53_SERDES
40+
tristate "B53 SerDes support"
41+
depends on B53
42+
default ARCH_BCM_NSP
43+
help
44+
Select to enable support for SerDes on e.g: Northstar Plus SoCs.

drivers/net/dsa/b53/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ obj-$(CONFIG_B53_SPI_DRIVER) += b53_spi.o
55
obj-$(CONFIG_B53_MDIO_DRIVER) += b53_mdio.o
66
obj-$(CONFIG_B53_MMAP_DRIVER) += b53_mmap.o
77
obj-$(CONFIG_B53_SRAB_DRIVER) += b53_srab.o
8+
obj-$(CONFIG_B53_SERDES) += b53_serdes.o

drivers/net/dsa/b53/b53_common.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,8 @@ static int b53_reset_switch(struct b53_device *priv)
765765
memset(priv->vlans, 0, sizeof(*priv->vlans) * priv->num_vlans);
766766
memset(priv->ports, 0, sizeof(*priv->ports) * priv->num_ports);
767767

768+
priv->serdes_lane = B53_INVALID_LANE;
769+
768770
return b53_switch_reset(priv);
769771
}
770772

@@ -1128,6 +1130,9 @@ void b53_phylink_validate(struct dsa_switch *ds, int port,
11281130
struct b53_device *dev = ds->priv;
11291131
__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
11301132

1133+
if (dev->ops->serdes_phylink_validate)
1134+
dev->ops->serdes_phylink_validate(dev, port, mask, state);
1135+
11311136
/* Allow all the expected bits */
11321137
phylink_set(mask, Autoneg);
11331138
phylink_set_port_modes(mask);
@@ -1164,8 +1169,13 @@ EXPORT_SYMBOL(b53_phylink_validate);
11641169
int b53_phylink_mac_link_state(struct dsa_switch *ds, int port,
11651170
struct phylink_link_state *state)
11661171
{
1172+
struct b53_device *dev = ds->priv;
11671173
int ret = -EOPNOTSUPP;
11681174

1175+
if (phy_interface_mode_is_8023z(state->interface) &&
1176+
dev->ops->serdes_link_state)
1177+
ret = dev->ops->serdes_link_state(dev, port, state);
1178+
11691179
return ret;
11701180
}
11711181
EXPORT_SYMBOL(b53_phylink_mac_link_state);
@@ -1184,11 +1194,19 @@ void b53_phylink_mac_config(struct dsa_switch *ds, int port,
11841194
state->duplex, state->pause);
11851195
return;
11861196
}
1197+
1198+
if (phy_interface_mode_is_8023z(state->interface) &&
1199+
dev->ops->serdes_config)
1200+
dev->ops->serdes_config(dev, port, mode, state);
11871201
}
11881202
EXPORT_SYMBOL(b53_phylink_mac_config);
11891203

11901204
void b53_phylink_mac_an_restart(struct dsa_switch *ds, int port)
11911205
{
1206+
struct b53_device *dev = ds->priv;
1207+
1208+
if (dev->ops->serdes_an_restart)
1209+
dev->ops->serdes_an_restart(dev, port);
11921210
}
11931211
EXPORT_SYMBOL(b53_phylink_mac_an_restart);
11941212

@@ -1205,6 +1223,10 @@ void b53_phylink_mac_link_down(struct dsa_switch *ds, int port,
12051223
b53_force_link(dev, port, false);
12061224
return;
12071225
}
1226+
1227+
if (phy_interface_mode_is_8023z(interface) &&
1228+
dev->ops->serdes_link_set)
1229+
dev->ops->serdes_link_set(dev, port, mode, interface, false);
12081230
}
12091231
EXPORT_SYMBOL(b53_phylink_mac_link_down);
12101232

@@ -1222,6 +1244,10 @@ void b53_phylink_mac_link_up(struct dsa_switch *ds, int port,
12221244
b53_force_link(dev, port, true);
12231245
return;
12241246
}
1247+
1248+
if (phy_interface_mode_is_8023z(interface) &&
1249+
dev->ops->serdes_link_set)
1250+
dev->ops->serdes_link_set(dev, port, mode, interface, true);
12251251
}
12261252
EXPORT_SYMBOL(b53_phylink_mac_link_up);
12271253

drivers/net/dsa/b53/b53_priv.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
struct b53_device;
3131
struct net_device;
32+
struct phylink_link_state;
3233

3334
struct b53_io_ops {
3435
int (*read8)(struct b53_device *dev, u8 page, u8 reg, u8 *value);
@@ -45,8 +46,23 @@ struct b53_io_ops {
4546
int (*phy_write16)(struct b53_device *dev, int addr, int reg, u16 value);
4647
int (*irq_enable)(struct b53_device *dev, int port);
4748
void (*irq_disable)(struct b53_device *dev, int port);
49+
u8 (*serdes_map_lane)(struct b53_device *dev, int port);
50+
int (*serdes_link_state)(struct b53_device *dev, int port,
51+
struct phylink_link_state *state);
52+
void (*serdes_config)(struct b53_device *dev, int port,
53+
unsigned int mode,
54+
const struct phylink_link_state *state);
55+
void (*serdes_an_restart)(struct b53_device *dev, int port);
56+
void (*serdes_link_set)(struct b53_device *dev, int port,
57+
unsigned int mode, phy_interface_t interface,
58+
bool link_up);
59+
void (*serdes_phylink_validate)(struct b53_device *dev, int port,
60+
unsigned long *supported,
61+
struct phylink_link_state *state);
4862
};
4963

64+
#define B53_INVALID_LANE 0xff
65+
5066
enum {
5167
BCM5325_DEVICE_ID = 0x25,
5268
BCM5365_DEVICE_ID = 0x65,
@@ -109,6 +125,7 @@ struct b53_device {
109125
/* connect specific data */
110126
u8 current_page;
111127
struct device *dev;
128+
u8 serdes_lane;
112129

113130
/* Master MDIO bus we got probed from */
114131
struct mii_bus *bus;

drivers/net/dsa/b53/b53_serdes.c

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
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+
20+
static void b53_serdes_write_blk(struct b53_device *dev, u8 offset, u16 block,
21+
u16 value)
22+
{
23+
b53_write16(dev, B53_SERDES_PAGE, B53_SERDES_BLKADDR, block);
24+
b53_write16(dev, B53_SERDES_PAGE, offset, value);
25+
}
26+
27+
static u16 b53_serdes_read_blk(struct b53_device *dev, u8 offset, u16 block)
28+
{
29+
u16 value;
30+
31+
b53_write16(dev, B53_SERDES_PAGE, B53_SERDES_BLKADDR, block);
32+
b53_read16(dev, B53_SERDES_PAGE, offset, &value);
33+
34+
return value;
35+
}
36+
37+
static void b53_serdes_set_lane(struct b53_device *dev, u8 lane)
38+
{
39+
if (dev->serdes_lane == lane)
40+
return;
41+
42+
WARN_ON(lane > 1);
43+
44+
b53_serdes_write_blk(dev, B53_SERDES_LANE,
45+
SERDES_XGXSBLK0_BLOCKADDRESS, lane);
46+
dev->serdes_lane = lane;
47+
}
48+
49+
static void b53_serdes_write(struct b53_device *dev, u8 lane,
50+
u8 offset, u16 block, u16 value)
51+
{
52+
b53_serdes_set_lane(dev, lane);
53+
b53_serdes_write_blk(dev, offset, block, value);
54+
}
55+
56+
static u16 b53_serdes_read(struct b53_device *dev, u8 lane,
57+
u8 offset, u16 block)
58+
{
59+
b53_serdes_set_lane(dev, lane);
60+
return b53_serdes_read_blk(dev, offset, block);
61+
}
62+
63+
void b53_serdes_config(struct b53_device *dev, int port, unsigned int mode,
64+
const struct phylink_link_state *state)
65+
{
66+
u8 lane = b53_serdes_map_lane(dev, port);
67+
u16 reg;
68+
69+
if (lane == B53_INVALID_LANE)
70+
return;
71+
72+
reg = b53_serdes_read(dev, lane, B53_SERDES_DIGITAL_CONTROL(1),
73+
SERDES_DIGITAL_BLK);
74+
if (state->interface == PHY_INTERFACE_MODE_1000BASEX)
75+
reg |= FIBER_MODE_1000X;
76+
else
77+
reg &= ~FIBER_MODE_1000X;
78+
b53_serdes_write(dev, lane, B53_SERDES_DIGITAL_CONTROL(1),
79+
SERDES_DIGITAL_BLK, reg);
80+
}
81+
EXPORT_SYMBOL(b53_serdes_config);
82+
83+
void b53_serdes_an_restart(struct b53_device *dev, int port)
84+
{
85+
u8 lane = b53_serdes_map_lane(dev, port);
86+
u16 reg;
87+
88+
if (lane == B53_INVALID_LANE)
89+
return;
90+
91+
reg = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
92+
SERDES_MII_BLK);
93+
reg |= BMCR_ANRESTART;
94+
b53_serdes_write(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
95+
SERDES_MII_BLK, reg);
96+
}
97+
EXPORT_SYMBOL(b53_serdes_an_restart);
98+
99+
int b53_serdes_link_state(struct b53_device *dev, int port,
100+
struct phylink_link_state *state)
101+
{
102+
u8 lane = b53_serdes_map_lane(dev, port);
103+
u16 dig, bmcr, bmsr;
104+
105+
if (lane == B53_INVALID_LANE)
106+
return 1;
107+
108+
dig = b53_serdes_read(dev, lane, B53_SERDES_DIGITAL_STATUS,
109+
SERDES_DIGITAL_BLK);
110+
bmcr = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
111+
SERDES_MII_BLK);
112+
bmsr = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_BMSR),
113+
SERDES_MII_BLK);
114+
115+
switch ((dig >> SPEED_STATUS_SHIFT) & SPEED_STATUS_MASK) {
116+
case SPEED_STATUS_10:
117+
state->speed = SPEED_10;
118+
break;
119+
case SPEED_STATUS_100:
120+
state->speed = SPEED_100;
121+
break;
122+
case SPEED_STATUS_1000:
123+
state->speed = SPEED_1000;
124+
break;
125+
default:
126+
case SPEED_STATUS_2500:
127+
state->speed = SPEED_2500;
128+
break;
129+
}
130+
131+
state->duplex = dig & DUPLEX_STATUS ? DUPLEX_FULL : DUPLEX_HALF;
132+
state->an_enabled = !!(bmcr & BMCR_ANENABLE);
133+
state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE);
134+
state->link = !!(dig & LINK_STATUS);
135+
if (dig & PAUSE_RESOLUTION_RX_SIDE)
136+
state->pause |= MLO_PAUSE_RX;
137+
if (dig & PAUSE_RESOLUTION_TX_SIDE)
138+
state->pause |= MLO_PAUSE_TX;
139+
140+
return 0;
141+
}
142+
EXPORT_SYMBOL(b53_serdes_link_state);
143+
144+
void b53_serdes_link_set(struct b53_device *dev, int port, unsigned int mode,
145+
phy_interface_t interface, bool link_up)
146+
{
147+
u8 lane = b53_serdes_map_lane(dev, port);
148+
u16 reg;
149+
150+
if (lane == B53_INVALID_LANE)
151+
return;
152+
153+
reg = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
154+
SERDES_MII_BLK);
155+
if (link_up)
156+
reg &= ~BMCR_PDOWN;
157+
else
158+
reg |= BMCR_PDOWN;
159+
b53_serdes_write(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
160+
SERDES_MII_BLK, reg);
161+
}
162+
EXPORT_SYMBOL(b53_serdes_link_set);
163+
164+
void b53_serdes_phylink_validate(struct b53_device *dev, int port,
165+
unsigned long *supported,
166+
struct phylink_link_state *state)
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+
phylink_set(supported, 2500baseX_Full);
176+
/* fallthrough */
177+
case 1:
178+
phylink_set(supported, 1000baseX_Full);
179+
break;
180+
default:
181+
break;
182+
}
183+
}
184+
EXPORT_SYMBOL(b53_serdes_phylink_validate);
185+
186+
int b53_serdes_init(struct b53_device *dev, int port)
187+
{
188+
u8 lane = b53_serdes_map_lane(dev, port);
189+
u16 id0, msb, lsb;
190+
191+
if (lane == B53_INVALID_LANE)
192+
return -EINVAL;
193+
194+
id0 = b53_serdes_read(dev, lane, B53_SERDES_ID0, SERDES_ID0);
195+
msb = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_PHYSID1),
196+
SERDES_MII_BLK);
197+
lsb = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_PHYSID2),
198+
SERDES_MII_BLK);
199+
if (id0 == 0 || id0 == 0xffff) {
200+
dev_err(dev->dev, "SerDes not initialized, check settings\n");
201+
return -ENODEV;
202+
}
203+
204+
dev_info(dev->dev,
205+
"SerDes lane %d, model: %d, rev %c%d (OUI: 0x%08x)\n",
206+
lane, id0 & SERDES_ID0_MODEL_MASK,
207+
(id0 >> SERDES_ID0_REV_LETTER_SHIFT) + 0x41,
208+
(id0 >> SERDES_ID0_REV_NUM_SHIFT) & SERDES_ID0_REV_NUM_MASK,
209+
(u32)msb << 16 | lsb);
210+
211+
return 0;
212+
}
213+
EXPORT_SYMBOL(b53_serdes_init);
214+
215+
MODULE_AUTHOR("Florian Fainelli <f.fainelli@gmail.com>");
216+
MODULE_DESCRIPTION("B53 Switch SerDes driver");
217+
MODULE_LICENSE("Dual BSD/GPL");

0 commit comments

Comments
 (0)