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