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_LINK_TIMER_0 0x12
16 #define SGMII_PCS_LINK_TIMER_1 0x13
17 #define SGMII_PCS_IF_MODE 0x14
18 #define PCS_IF_MODE_SGMII_ENA BIT(0)
19 #define PCS_IF_MODE_USE_SGMII_AN BIT(1)
20 #define PCS_IF_MODE_SGMI_HALF_DUPLEX BIT(4)
21 #define PCS_IF_MODE_SGMI_PHY_AN BIT(5)
22 #define SGMII_PCS_SW_RESET_TIMEOUT 100 /* usecs */
23
24 struct altera_tse_pcs {
25 struct phylink_pcs pcs;
26 void __iomem *base;
27 int reg_width;
28 };
29
phylink_pcs_to_tse_pcs(struct phylink_pcs * pcs)30 static struct altera_tse_pcs *phylink_pcs_to_tse_pcs(struct phylink_pcs *pcs)
31 {
32 return container_of(pcs, struct altera_tse_pcs, pcs);
33 }
34
tse_pcs_read(struct altera_tse_pcs * tse_pcs,int regnum)35 static u16 tse_pcs_read(struct altera_tse_pcs *tse_pcs, int regnum)
36 {
37 if (tse_pcs->reg_width == 4)
38 return readl(tse_pcs->base + regnum * 4);
39 else
40 return readw(tse_pcs->base + regnum * 2);
41 }
42
tse_pcs_write(struct altera_tse_pcs * tse_pcs,int regnum,u16 value)43 static void tse_pcs_write(struct altera_tse_pcs *tse_pcs, int regnum,
44 u16 value)
45 {
46 if (tse_pcs->reg_width == 4)
47 writel(value, tse_pcs->base + regnum * 4);
48 else
49 writew(value, tse_pcs->base + regnum * 2);
50 }
51
tse_pcs_reset(struct altera_tse_pcs * tse_pcs)52 static int tse_pcs_reset(struct altera_tse_pcs *tse_pcs)
53 {
54 u16 bmcr;
55
56 /* Reset PCS block */
57 bmcr = tse_pcs_read(tse_pcs, MII_BMCR);
58 bmcr |= BMCR_RESET;
59 tse_pcs_write(tse_pcs, MII_BMCR, bmcr);
60
61 return read_poll_timeout(tse_pcs_read, bmcr, (bmcr & BMCR_RESET),
62 10, SGMII_PCS_SW_RESET_TIMEOUT, 1,
63 tse_pcs, MII_BMCR);
64 }
65
alt_tse_pcs_validate(struct phylink_pcs * pcs,unsigned long * supported,const struct phylink_link_state * state)66 static int alt_tse_pcs_validate(struct phylink_pcs *pcs,
67 unsigned long *supported,
68 const struct phylink_link_state *state)
69 {
70 if (state->interface == PHY_INTERFACE_MODE_SGMII ||
71 state->interface == PHY_INTERFACE_MODE_1000BASEX)
72 return 1;
73
74 return -EINVAL;
75 }
76
alt_tse_pcs_config(struct phylink_pcs * pcs,unsigned int mode,phy_interface_t interface,const unsigned long * advertising,bool permit_pause_to_mac)77 static int alt_tse_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
78 phy_interface_t interface,
79 const unsigned long *advertising,
80 bool permit_pause_to_mac)
81 {
82 struct altera_tse_pcs *tse_pcs = phylink_pcs_to_tse_pcs(pcs);
83 u32 ctrl, if_mode;
84
85 ctrl = tse_pcs_read(tse_pcs, MII_BMCR);
86 if_mode = tse_pcs_read(tse_pcs, SGMII_PCS_IF_MODE);
87
88 /* Set link timer to 1.6ms, as per the MegaCore Function User Guide */
89 tse_pcs_write(tse_pcs, SGMII_PCS_LINK_TIMER_0, 0x0D40);
90 tse_pcs_write(tse_pcs, SGMII_PCS_LINK_TIMER_1, 0x03);
91
92 if (interface == PHY_INTERFACE_MODE_SGMII) {
93 if_mode |= PCS_IF_MODE_USE_SGMII_AN | PCS_IF_MODE_SGMII_ENA;
94 } else if (interface == PHY_INTERFACE_MODE_1000BASEX) {
95 if_mode &= ~(PCS_IF_MODE_USE_SGMII_AN | PCS_IF_MODE_SGMII_ENA);
96 }
97
98 ctrl |= (BMCR_SPEED1000 | BMCR_FULLDPLX | BMCR_ANENABLE);
99
100 tse_pcs_write(tse_pcs, MII_BMCR, ctrl);
101 tse_pcs_write(tse_pcs, SGMII_PCS_IF_MODE, if_mode);
102
103 return tse_pcs_reset(tse_pcs);
104 }
105
alt_tse_pcs_get_state(struct phylink_pcs * pcs,struct phylink_link_state * state)106 static void alt_tse_pcs_get_state(struct phylink_pcs *pcs,
107 struct phylink_link_state *state)
108 {
109 struct altera_tse_pcs *tse_pcs = phylink_pcs_to_tse_pcs(pcs);
110 u16 bmsr, lpa;
111
112 bmsr = tse_pcs_read(tse_pcs, MII_BMSR);
113 lpa = tse_pcs_read(tse_pcs, MII_LPA);
114
115 phylink_mii_c22_pcs_decode_state(state, bmsr, lpa);
116 }
117
alt_tse_pcs_an_restart(struct phylink_pcs * pcs)118 static void alt_tse_pcs_an_restart(struct phylink_pcs *pcs)
119 {
120 struct altera_tse_pcs *tse_pcs = phylink_pcs_to_tse_pcs(pcs);
121 u16 bmcr;
122
123 bmcr = tse_pcs_read(tse_pcs, MII_BMCR);
124 bmcr |= BMCR_ANRESTART;
125 tse_pcs_write(tse_pcs, MII_BMCR, bmcr);
126
127 /* This PCS seems to require a soft reset to re-sync the AN logic */
128 tse_pcs_reset(tse_pcs);
129 }
130
131 static const struct phylink_pcs_ops alt_tse_pcs_ops = {
132 .pcs_validate = alt_tse_pcs_validate,
133 .pcs_get_state = alt_tse_pcs_get_state,
134 .pcs_config = alt_tse_pcs_config,
135 .pcs_an_restart = alt_tse_pcs_an_restart,
136 };
137
alt_tse_pcs_create(struct net_device * ndev,void __iomem * pcs_base,int reg_width)138 struct phylink_pcs *alt_tse_pcs_create(struct net_device *ndev,
139 void __iomem *pcs_base, int reg_width)
140 {
141 struct altera_tse_pcs *tse_pcs;
142
143 if (reg_width != 4 && reg_width != 2)
144 return ERR_PTR(-EINVAL);
145
146 tse_pcs = devm_kzalloc(&ndev->dev, sizeof(*tse_pcs), GFP_KERNEL);
147 if (!tse_pcs)
148 return ERR_PTR(-ENOMEM);
149
150 tse_pcs->pcs.ops = &alt_tse_pcs_ops;
151 tse_pcs->base = pcs_base;
152 tse_pcs->reg_width = reg_width;
153
154 return &tse_pcs->pcs;
155 }
156 EXPORT_SYMBOL_GPL(alt_tse_pcs_create);
157
158 MODULE_LICENSE("GPL");
159 MODULE_DESCRIPTION("Altera TSE PCS driver");
160 MODULE_AUTHOR("Maxime Chevallier <maxime.chevallier@bootlin.com>");
161