Skip to content

Commit 4a502cf

Browse files
minimaxwelldavem330
authored andcommitted
net: pcs: add new PCS driver for altera TSE PCS
The Altera Triple Speed Ethernet has a SGMII/1000BaseC PCS that can be integrated in several ways. It can either be part of the TSE MAC's address space, accessed through 32 bits accesses on the mapped mdio device 0, or through a dedicated 16 bits register set. This driver allows using the TSE PCS outside of altera TSE's driver, since it can be used standalone by other MACs. Signed-off-by: Maxime Chevallier <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 5adb0ed commit 4a502cf

File tree

5 files changed

+206
-0
lines changed

5 files changed

+206
-0
lines changed

MAINTAINERS

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,13 @@ L: [email protected]
878878
S: Maintained
879879
F: drivers/net/ethernet/altera/
880880

881+
ALTERA TSE PCS
882+
M: Maxime Chevallier <[email protected]>
883+
884+
S: Supported
885+
F: drivers/net/pcs/pcs-altera-tse.c
886+
F: include/linux/pcs-altera-tse.h
887+
881888
ALTERA UART/JTAG UART SERIAL DRIVERS
882889
M: Tobias Klauser <[email protected]>
883890

drivers/net/pcs/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,10 @@ config PCS_RZN1_MIIC
2626
on RZ/N1 SoCs. This PCS converts MII to RMII/RGMII or can be set in
2727
pass-through mode for MII.
2828

29+
config PCS_ALTERA_TSE
30+
tristate
31+
help
32+
This module provides helper functions for the Altera Triple Speed
33+
Ethernet SGMII PCS, that can be found on the Intel Socfpga family.
34+
2935
endmenu

drivers/net/pcs/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs.o pcs-xpcs-nxp.o
66
obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o
77
obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o
88
obj-$(CONFIG_PCS_RZN1_MIIC) += pcs-rzn1-miic.o
9+
obj-$(CONFIG_PCS_ALTERA_TSE) += pcs-altera-tse.o

drivers/net/pcs/pcs-altera-tse.c

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (C) 2022 Bootlin
4+
*
5+
* Maxime Chevallier <[email protected]>
6+
*/
7+
8+
#include <linux/netdevice.h>
9+
#include <linux/phy.h>
10+
#include <linux/phylink.h>
11+
#include <linux/pcs-altera-tse.h>
12+
13+
/* SGMII PCS register addresses
14+
*/
15+
#define SGMII_PCS_SCRATCH 0x10
16+
#define SGMII_PCS_REV 0x11
17+
#define SGMII_PCS_LINK_TIMER_0 0x12
18+
#define SGMII_PCS_LINK_TIMER_REG(x) (0x12 + (x))
19+
#define SGMII_PCS_LINK_TIMER_1 0x13
20+
#define SGMII_PCS_IF_MODE 0x14
21+
#define PCS_IF_MODE_SGMII_ENA BIT(0)
22+
#define PCS_IF_MODE_USE_SGMII_AN BIT(1)
23+
#define PCS_IF_MODE_SGMI_SPEED_MASK GENMASK(3, 2)
24+
#define PCS_IF_MODE_SGMI_SPEED_10 (0 << 2)
25+
#define PCS_IF_MODE_SGMI_SPEED_100 (1 << 2)
26+
#define PCS_IF_MODE_SGMI_SPEED_1000 (2 << 2)
27+
#define PCS_IF_MODE_SGMI_HALF_DUPLEX BIT(4)
28+
#define PCS_IF_MODE_SGMI_PHY_AN BIT(5)
29+
#define SGMII_PCS_DIS_READ_TO 0x15
30+
#define SGMII_PCS_READ_TO 0x16
31+
#define SGMII_PCS_SW_RESET_TIMEOUT 100 /* usecs */
32+
33+
struct altera_tse_pcs {
34+
struct phylink_pcs pcs;
35+
void __iomem *base;
36+
int reg_width;
37+
};
38+
39+
static struct altera_tse_pcs *phylink_pcs_to_tse_pcs(struct phylink_pcs *pcs)
40+
{
41+
return container_of(pcs, struct altera_tse_pcs, pcs);
42+
}
43+
44+
static u16 tse_pcs_read(struct altera_tse_pcs *tse_pcs, int regnum)
45+
{
46+
if (tse_pcs->reg_width == 4)
47+
return readl(tse_pcs->base + regnum * 4);
48+
else
49+
return readw(tse_pcs->base + regnum * 2);
50+
}
51+
52+
static void tse_pcs_write(struct altera_tse_pcs *tse_pcs, int regnum,
53+
u16 value)
54+
{
55+
if (tse_pcs->reg_width == 4)
56+
writel(value, tse_pcs->base + regnum * 4);
57+
else
58+
writew(value, tse_pcs->base + regnum * 2);
59+
}
60+
61+
static int tse_pcs_reset(struct altera_tse_pcs *tse_pcs)
62+
{
63+
int i = 0;
64+
u16 bmcr;
65+
66+
/* Reset PCS block */
67+
bmcr = tse_pcs_read(tse_pcs, MII_BMCR);
68+
bmcr |= BMCR_RESET;
69+
tse_pcs_write(tse_pcs, MII_BMCR, bmcr);
70+
71+
for (i = 0; i < SGMII_PCS_SW_RESET_TIMEOUT; i++) {
72+
if (!(tse_pcs_read(tse_pcs, MII_BMCR) & BMCR_RESET))
73+
return 0;
74+
udelay(1);
75+
}
76+
77+
return -ETIMEDOUT;
78+
}
79+
80+
static int alt_tse_pcs_validate(struct phylink_pcs *pcs,
81+
unsigned long *supported,
82+
const struct phylink_link_state *state)
83+
{
84+
if (state->interface == PHY_INTERFACE_MODE_SGMII ||
85+
state->interface == PHY_INTERFACE_MODE_1000BASEX)
86+
return 1;
87+
88+
return -EINVAL;
89+
}
90+
91+
static int alt_tse_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
92+
phy_interface_t interface,
93+
const unsigned long *advertising,
94+
bool permit_pause_to_mac)
95+
{
96+
struct altera_tse_pcs *tse_pcs = phylink_pcs_to_tse_pcs(pcs);
97+
u32 ctrl, if_mode;
98+
99+
ctrl = tse_pcs_read(tse_pcs, MII_BMCR);
100+
if_mode = tse_pcs_read(tse_pcs, SGMII_PCS_IF_MODE);
101+
102+
/* Set link timer to 1.6ms, as per the MegaCore Function User Guide */
103+
tse_pcs_write(tse_pcs, SGMII_PCS_LINK_TIMER_0, 0x0D40);
104+
tse_pcs_write(tse_pcs, SGMII_PCS_LINK_TIMER_1, 0x03);
105+
106+
if (interface == PHY_INTERFACE_MODE_SGMII) {
107+
if_mode |= PCS_IF_MODE_USE_SGMII_AN | PCS_IF_MODE_SGMII_ENA;
108+
} else if (interface == PHY_INTERFACE_MODE_1000BASEX) {
109+
if_mode &= ~(PCS_IF_MODE_USE_SGMII_AN | PCS_IF_MODE_SGMII_ENA);
110+
if_mode |= PCS_IF_MODE_SGMI_SPEED_1000;
111+
}
112+
113+
ctrl |= (BMCR_SPEED1000 | BMCR_FULLDPLX | BMCR_ANENABLE);
114+
115+
tse_pcs_write(tse_pcs, MII_BMCR, ctrl);
116+
tse_pcs_write(tse_pcs, SGMII_PCS_IF_MODE, if_mode);
117+
118+
return tse_pcs_reset(tse_pcs);
119+
}
120+
121+
static void alt_tse_pcs_get_state(struct phylink_pcs *pcs,
122+
struct phylink_link_state *state)
123+
{
124+
struct altera_tse_pcs *tse_pcs = phylink_pcs_to_tse_pcs(pcs);
125+
u16 bmsr, lpa;
126+
127+
bmsr = tse_pcs_read(tse_pcs, MII_BMSR);
128+
lpa = tse_pcs_read(tse_pcs, MII_LPA);
129+
130+
phylink_mii_c22_pcs_decode_state(state, bmsr, lpa);
131+
}
132+
133+
static void alt_tse_pcs_an_restart(struct phylink_pcs *pcs)
134+
{
135+
struct altera_tse_pcs *tse_pcs = phylink_pcs_to_tse_pcs(pcs);
136+
u16 bmcr;
137+
138+
bmcr = tse_pcs_read(tse_pcs, MII_BMCR);
139+
bmcr |= BMCR_ANRESTART;
140+
tse_pcs_write(tse_pcs, MII_BMCR, bmcr);
141+
142+
/* This PCS seems to require a soft reset to re-sync the AN logic */
143+
tse_pcs_reset(tse_pcs);
144+
}
145+
146+
static const struct phylink_pcs_ops alt_tse_pcs_ops = {
147+
.pcs_validate = alt_tse_pcs_validate,
148+
.pcs_get_state = alt_tse_pcs_get_state,
149+
.pcs_config = alt_tse_pcs_config,
150+
.pcs_an_restart = alt_tse_pcs_an_restart,
151+
};
152+
153+
struct phylink_pcs *alt_tse_pcs_create(struct net_device *ndev,
154+
void __iomem *pcs_base, int reg_width)
155+
{
156+
struct altera_tse_pcs *tse_pcs;
157+
158+
if (reg_width != 4 && reg_width != 2)
159+
return ERR_PTR(-EINVAL);
160+
161+
tse_pcs = devm_kzalloc(&ndev->dev, sizeof(*tse_pcs), GFP_KERNEL);
162+
if (!tse_pcs)
163+
return ERR_PTR(-ENOMEM);
164+
165+
tse_pcs->pcs.ops = &alt_tse_pcs_ops;
166+
tse_pcs->base = pcs_base;
167+
tse_pcs->reg_width = reg_width;
168+
169+
return &tse_pcs->pcs;
170+
}
171+
EXPORT_SYMBOL_GPL(alt_tse_pcs_create);
172+
173+
MODULE_LICENSE("GPL");
174+
MODULE_DESCRIPTION("Altera TSE PCS driver");
175+
MODULE_AUTHOR("Maxime Chevallier <[email protected]>");

include/linux/pcs-altera-tse.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* Copyright (C) 2022 Bootlin
4+
*
5+
* Maxime Chevallier <[email protected]>
6+
*/
7+
8+
#ifndef __LINUX_PCS_ALTERA_TSE_H
9+
#define __LINUX_PCS_ALTERA_TSE_H
10+
11+
struct phylink_pcs;
12+
struct net_device;
13+
14+
struct phylink_pcs *alt_tse_pcs_create(struct net_device *ndev,
15+
void __iomem *pcs_base, int reg_width);
16+
17+
#endif /* __LINUX_PCS_ALTERA_TSE_H */

0 commit comments

Comments
 (0)