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