1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2022 Bootlin
4 *
5 * Maxime Chevallier <maxime.chevallier@bootlin.com>
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
phylink_pcs_to_tse_pcs(struct phylink_pcs * pcs)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
tse_pcs_read(struct altera_tse_pcs * tse_pcs,int regnum)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
tse_pcs_write(struct altera_tse_pcs * tse_pcs,int regnum,u16 value)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
tse_pcs_reset(struct altera_tse_pcs * tse_pcs)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
alt_tse_pcs_validate(struct phylink_pcs * pcs,unsigned long * supported,const struct phylink_link_state * state)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
alt_tse_pcs_config(struct phylink_pcs * pcs,unsigned int mode,phy_interface_t interface,const unsigned long * advertising,bool permit_pause_to_mac)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
alt_tse_pcs_get_state(struct phylink_pcs * pcs,struct phylink_link_state * state)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
alt_tse_pcs_an_restart(struct phylink_pcs * pcs)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
alt_tse_pcs_create(struct net_device * ndev,void __iomem * pcs_base,int reg_width)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 <maxime.chevallier@bootlin.com>");
176