1// SPDX-License-Identifier: GPL-2.0
2/* $Date: 2005/10/24 23:18:13 $ $RCSfile: mv88e1xxx.c,v $ $Revision: 1.49 $ */
3#include "common.h"
4#include "mv88e1xxx.h"
5#include "cphy.h"
6#include "elmer0.h"
7
8/* MV88E1XXX MDI crossover register values */
9#define CROSSOVER_MDI   0
10#define CROSSOVER_MDIX  1
11#define CROSSOVER_AUTO  3
12
13#define INTR_ENABLE_MASK 0x6CA0
14
15/*
16 * Set the bits given by 'bitval' in PHY register 'reg'.
17 */
18static void mdio_set_bit(struct cphy *cphy, int reg, u32 bitval)
19{
20	u32 val;
21
22	(void) simple_mdio_read(cphy, reg, &val);
23	(void) simple_mdio_write(cphy, reg, val | bitval);
24}
25
26/*
27 * Clear the bits given by 'bitval' in PHY register 'reg'.
28 */
29static void mdio_clear_bit(struct cphy *cphy, int reg, u32 bitval)
30{
31	u32 val;
32
33	(void) simple_mdio_read(cphy, reg, &val);
34	(void) simple_mdio_write(cphy, reg, val & ~bitval);
35}
36
37/*
38 * NAME:   phy_reset
39 *
40 * DESC:   Reset the given PHY's port. NOTE: This is not a global
41 *         chip reset.
42 *
43 * PARAMS: cphy     - Pointer to PHY instance data.
44 *
45 * RETURN:  0 - Successful reset.
46 *         -1 - Timeout.
47 */
48static int mv88e1xxx_reset(struct cphy *cphy, int wait)
49{
50	u32 ctl;
51	int time_out = 1000;
52
53	mdio_set_bit(cphy, MII_BMCR, BMCR_RESET);
54
55	do {
56		(void) simple_mdio_read(cphy, MII_BMCR, &ctl);
57		ctl &= BMCR_RESET;
58		if (ctl)
59			udelay(1);
60	} while (ctl && --time_out);
61
62	return ctl ? -1 : 0;
63}
64
65static int mv88e1xxx_interrupt_enable(struct cphy *cphy)
66{
67	/* Enable PHY interrupts. */
68	(void) simple_mdio_write(cphy, MV88E1XXX_INTERRUPT_ENABLE_REGISTER,
69		   INTR_ENABLE_MASK);
70
71	/* Enable Marvell interrupts through Elmer0. */
72	if (t1_is_asic(cphy->adapter)) {
73		u32 elmer;
74
75		t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
76		elmer |= ELMER0_GP_BIT1;
77		if (is_T2(cphy->adapter))
78		    elmer |= ELMER0_GP_BIT2 | ELMER0_GP_BIT3 | ELMER0_GP_BIT4;
79		t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
80	}
81	return 0;
82}
83
84static int mv88e1xxx_interrupt_disable(struct cphy *cphy)
85{
86	/* Disable all phy interrupts. */
87	(void) simple_mdio_write(cphy, MV88E1XXX_INTERRUPT_ENABLE_REGISTER, 0);
88
89	/* Disable Marvell interrupts through Elmer0. */
90	if (t1_is_asic(cphy->adapter)) {
91		u32 elmer;
92
93		t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
94		elmer &= ~ELMER0_GP_BIT1;
95		if (is_T2(cphy->adapter))
96		    elmer &= ~(ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4);
97		t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
98	}
99	return 0;
100}
101
102static int mv88e1xxx_interrupt_clear(struct cphy *cphy)
103{
104	u32 elmer;
105
106	/* Clear PHY interrupts by reading the register. */
107	(void) simple_mdio_read(cphy,
108			MV88E1XXX_INTERRUPT_STATUS_REGISTER, &elmer);
109
110	/* Clear Marvell interrupts through Elmer0. */
111	if (t1_is_asic(cphy->adapter)) {
112		t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer);
113		elmer |= ELMER0_GP_BIT1;
114		if (is_T2(cphy->adapter))
115		    elmer |= ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4;
116		t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer);
117	}
118	return 0;
119}
120
121/*
122 * Set the PHY speed and duplex.  This also disables auto-negotiation, except
123 * for 1Gb/s, where auto-negotiation is mandatory.
124 */
125static int mv88e1xxx_set_speed_duplex(struct cphy *phy, int speed, int duplex)
126{
127	u32 ctl;
128
129	(void) simple_mdio_read(phy, MII_BMCR, &ctl);
130	if (speed >= 0) {
131		ctl &= ~(BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE);
132		if (speed == SPEED_100)
133			ctl |= BMCR_SPEED100;
134		else if (speed == SPEED_1000)
135			ctl |= BMCR_SPEED1000;
136	}
137	if (duplex >= 0) {
138		ctl &= ~(BMCR_FULLDPLX | BMCR_ANENABLE);
139		if (duplex == DUPLEX_FULL)
140			ctl |= BMCR_FULLDPLX;
141	}
142	if (ctl & BMCR_SPEED1000)  /* auto-negotiation required for 1Gb/s */
143		ctl |= BMCR_ANENABLE;
144	(void) simple_mdio_write(phy, MII_BMCR, ctl);
145	return 0;
146}
147
148static int mv88e1xxx_crossover_set(struct cphy *cphy, int crossover)
149{
150	u32 data32;
151
152	(void) simple_mdio_read(cphy,
153			MV88E1XXX_SPECIFIC_CNTRL_REGISTER, &data32);
154	data32 &= ~V_PSCR_MDI_XOVER_MODE(M_PSCR_MDI_XOVER_MODE);
155	data32 |= V_PSCR_MDI_XOVER_MODE(crossover);
156	(void) simple_mdio_write(cphy,
157			MV88E1XXX_SPECIFIC_CNTRL_REGISTER, data32);
158	return 0;
159}
160
161static int mv88e1xxx_autoneg_enable(struct cphy *cphy)
162{
163	u32 ctl;
164
165	(void) mv88e1xxx_crossover_set(cphy, CROSSOVER_AUTO);
166
167	(void) simple_mdio_read(cphy, MII_BMCR, &ctl);
168	/* restart autoneg for change to take effect */
169	ctl |= BMCR_ANENABLE | BMCR_ANRESTART;
170	(void) simple_mdio_write(cphy, MII_BMCR, ctl);
171	return 0;
172}
173
174static int mv88e1xxx_autoneg_disable(struct cphy *cphy)
175{
176	u32 ctl;
177
178	/*
179	 * Crossover *must* be set to manual in order to disable auto-neg.
180	 * The Alaska FAQs document highlights this point.
181	 */
182	(void) mv88e1xxx_crossover_set(cphy, CROSSOVER_MDI);
183
184	/*
185	 * Must include autoneg reset when disabling auto-neg. This
186	 * is described in the Alaska FAQ document.
187	 */
188	(void) simple_mdio_read(cphy, MII_BMCR, &ctl);
189	ctl &= ~BMCR_ANENABLE;
190	(void) simple_mdio_write(cphy, MII_BMCR, ctl | BMCR_ANRESTART);
191	return 0;
192}
193
194static int mv88e1xxx_autoneg_restart(struct cphy *cphy)
195{
196	mdio_set_bit(cphy, MII_BMCR, BMCR_ANRESTART);
197	return 0;
198}
199
200static int mv88e1xxx_advertise(struct cphy *phy, unsigned int advertise_map)
201{
202	u32 val = 0;
203
204	if (advertise_map &
205	    (ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full)) {
206		(void) simple_mdio_read(phy, MII_GBCR, &val);
207		val &= ~(GBCR_ADV_1000HALF | GBCR_ADV_1000FULL);
208		if (advertise_map & ADVERTISED_1000baseT_Half)
209			val |= GBCR_ADV_1000HALF;
210		if (advertise_map & ADVERTISED_1000baseT_Full)
211			val |= GBCR_ADV_1000FULL;
212	}
213	(void) simple_mdio_write(phy, MII_GBCR, val);
214
215	val = 1;
216	if (advertise_map & ADVERTISED_10baseT_Half)
217		val |= ADVERTISE_10HALF;
218	if (advertise_map & ADVERTISED_10baseT_Full)
219		val |= ADVERTISE_10FULL;
220	if (advertise_map & ADVERTISED_100baseT_Half)
221		val |= ADVERTISE_100HALF;
222	if (advertise_map & ADVERTISED_100baseT_Full)
223		val |= ADVERTISE_100FULL;
224	if (advertise_map & ADVERTISED_PAUSE)
225		val |= ADVERTISE_PAUSE;
226	if (advertise_map & ADVERTISED_ASYM_PAUSE)
227		val |= ADVERTISE_PAUSE_ASYM;
228	(void) simple_mdio_write(phy, MII_ADVERTISE, val);
229	return 0;
230}
231
232static int mv88e1xxx_set_loopback(struct cphy *cphy, int on)
233{
234	if (on)
235		mdio_set_bit(cphy, MII_BMCR, BMCR_LOOPBACK);
236	else
237		mdio_clear_bit(cphy, MII_BMCR, BMCR_LOOPBACK);
238	return 0;
239}
240
241static int mv88e1xxx_get_link_status(struct cphy *cphy, int *link_ok,
242				     int *speed, int *duplex, int *fc)
243{
244	u32 status;
245	int sp = -1, dplx = -1, pause = 0;
246
247	(void) simple_mdio_read(cphy,
248			MV88E1XXX_SPECIFIC_STATUS_REGISTER, &status);
249	if ((status & V_PSSR_STATUS_RESOLVED) != 0) {
250		if (status & V_PSSR_RX_PAUSE)
251			pause |= PAUSE_RX;
252		if (status & V_PSSR_TX_PAUSE)
253			pause |= PAUSE_TX;
254		dplx = (status & V_PSSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
255		sp = G_PSSR_SPEED(status);
256		if (sp == 0)
257			sp = SPEED_10;
258		else if (sp == 1)
259			sp = SPEED_100;
260		else
261			sp = SPEED_1000;
262	}
263	if (link_ok)
264		*link_ok = (status & V_PSSR_LINK) != 0;
265	if (speed)
266		*speed = sp;
267	if (duplex)
268		*duplex = dplx;
269	if (fc)
270		*fc = pause;
271	return 0;
272}
273
274static int mv88e1xxx_downshift_set(struct cphy *cphy, int downshift_enable)
275{
276	u32 val;
277
278	(void) simple_mdio_read(cphy,
279		MV88E1XXX_EXT_PHY_SPECIFIC_CNTRL_REGISTER, &val);
280
281	/*
282	 * Set the downshift counter to 2 so we try to establish Gb link
283	 * twice before downshifting.
284	 */
285	val &= ~(V_DOWNSHIFT_ENABLE | V_DOWNSHIFT_CNT(M_DOWNSHIFT_CNT));
286
287	if (downshift_enable)
288		val |= V_DOWNSHIFT_ENABLE | V_DOWNSHIFT_CNT(2);
289	(void) simple_mdio_write(cphy,
290			MV88E1XXX_EXT_PHY_SPECIFIC_CNTRL_REGISTER, val);
291	return 0;
292}
293
294static int mv88e1xxx_interrupt_handler(struct cphy *cphy)
295{
296	int cphy_cause = 0;
297	u32 status;
298
299	/*
300	 * Loop until cause reads zero. Need to handle bouncing interrupts.
301	 */
302	while (1) {
303		u32 cause;
304
305		(void) simple_mdio_read(cphy,
306				MV88E1XXX_INTERRUPT_STATUS_REGISTER,
307				&cause);
308		cause &= INTR_ENABLE_MASK;
309		if (!cause)
310			break;
311
312		if (cause & MV88E1XXX_INTR_LINK_CHNG) {
313			(void) simple_mdio_read(cphy,
314				MV88E1XXX_SPECIFIC_STATUS_REGISTER, &status);
315
316			if (status & MV88E1XXX_INTR_LINK_CHNG)
317				cphy->state |= PHY_LINK_UP;
318			else {
319				cphy->state &= ~PHY_LINK_UP;
320				if (cphy->state & PHY_AUTONEG_EN)
321					cphy->state &= ~PHY_AUTONEG_RDY;
322				cphy_cause |= cphy_cause_link_change;
323			}
324		}
325
326		if (cause & MV88E1XXX_INTR_AUTONEG_DONE)
327			cphy->state |= PHY_AUTONEG_RDY;
328
329		if ((cphy->state & (PHY_LINK_UP | PHY_AUTONEG_RDY)) ==
330			(PHY_LINK_UP | PHY_AUTONEG_RDY))
331				cphy_cause |= cphy_cause_link_change;
332	}
333	return cphy_cause;
334}
335
336static void mv88e1xxx_destroy(struct cphy *cphy)
337{
338	kfree(cphy);
339}
340
341static const struct cphy_ops mv88e1xxx_ops = {
342	.destroy              = mv88e1xxx_destroy,
343	.reset                = mv88e1xxx_reset,
344	.interrupt_enable     = mv88e1xxx_interrupt_enable,
345	.interrupt_disable    = mv88e1xxx_interrupt_disable,
346	.interrupt_clear      = mv88e1xxx_interrupt_clear,
347	.interrupt_handler    = mv88e1xxx_interrupt_handler,
348	.autoneg_enable       = mv88e1xxx_autoneg_enable,
349	.autoneg_disable      = mv88e1xxx_autoneg_disable,
350	.autoneg_restart      = mv88e1xxx_autoneg_restart,
351	.advertise            = mv88e1xxx_advertise,
352	.set_loopback         = mv88e1xxx_set_loopback,
353	.set_speed_duplex     = mv88e1xxx_set_speed_duplex,
354	.get_link_status      = mv88e1xxx_get_link_status,
355};
356
357static struct cphy *mv88e1xxx_phy_create(struct net_device *dev, int phy_addr,
358					 const struct mdio_ops *mdio_ops)
359{
360	struct adapter *adapter = netdev_priv(dev);
361	struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL);
362
363	if (!cphy)
364		return NULL;
365
366	cphy_init(cphy, dev, phy_addr, &mv88e1xxx_ops, mdio_ops);
367
368	/* Configure particular PHY's to run in a different mode. */
369	if ((board_info(adapter)->caps & SUPPORTED_TP) &&
370	    board_info(adapter)->chip_phy == CHBT_PHY_88E1111) {
371		/*
372		 * Configure the PHY transmitter as class A to reduce EMI.
373		 */
374		(void) simple_mdio_write(cphy,
375				MV88E1XXX_EXTENDED_ADDR_REGISTER, 0xB);
376		(void) simple_mdio_write(cphy,
377				MV88E1XXX_EXTENDED_REGISTER, 0x8004);
378	}
379	(void) mv88e1xxx_downshift_set(cphy, 1);   /* Enable downshift */
380
381	/* LED */
382	if (is_T2(adapter)) {
383		(void) simple_mdio_write(cphy,
384				MV88E1XXX_LED_CONTROL_REGISTER, 0x1);
385	}
386
387	return cphy;
388}
389
390static int mv88e1xxx_phy_reset(adapter_t* adapter)
391{
392	return 0;
393}
394
395const struct gphy t1_mv88e1xxx_ops = {
396	.create = mv88e1xxx_phy_create,
397	.reset =  mv88e1xxx_phy_reset
398};
399