1/*****************************************************************************
2 *                                                                           *
3 * File: mv88x201x.c                                                         *
4 * $Revision: 1.12 $                                                         *
5 * $Date: 2005/04/15 19:27:14 $                                              *
6 * Description:                                                              *
7 *  Marvell PHY (mv88x201x) functionality.                                   *
8 *  part of the Chelsio 10Gb Ethernet Driver.                                *
9 *                                                                           *
10 * This program is free software; you can redistribute it and/or modify      *
11 * it under the terms of the GNU General Public License, version 2, as       *
12 * published by the Free Software Foundation.                                *
13 *                                                                           *
14 * You should have received a copy of the GNU General Public License along   *
15 * with this program; if not, see <http://www.gnu.org/licenses/>.            *
16 *                                                                           *
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED    *
18 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF      *
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.                     *
20 *                                                                           *
21 * http://www.chelsio.com                                                    *
22 *                                                                           *
23 * Copyright (c) 2003 - 2005 Chelsio Communications, Inc.                    *
24 * All rights reserved.                                                      *
25 *                                                                           *
26 * Maintainers: maintainers@chelsio.com                                      *
27 *                                                                           *
28 * Authors: Dimitrios Michailidis   <dm@chelsio.com>                         *
29 *          Tina Yang               <tainay@chelsio.com>                     *
30 *          Felix Marti             <felix@chelsio.com>                      *
31 *          Scott Bardone           <sbardone@chelsio.com>                   *
32 *          Kurt Ottaway            <kottaway@chelsio.com>                   *
33 *          Frank DiMambro          <frank@chelsio.com>                      *
34 *                                                                           *
35 * History:                                                                  *
36 *                                                                           *
37 ****************************************************************************/
38
39#include "cphy.h"
40#include "elmer0.h"
41
42/*
43 * The 88x2010 Rev C. requires some link status registers * to be read
44 * twice in order to get the right values. Future * revisions will fix
45 * this problem and then this macro * can disappear.
46 */
47#define MV88x2010_LINK_STATUS_BUGS    1
48
49static int led_init(struct cphy *cphy)
50{
51	/* Setup the LED registers so we can turn on/off.
52	 * Writing these bits maps control to another
53	 * register. mmd(0x1) addr(0x7)
54	 */
55	cphy_mdio_write(cphy, MDIO_MMD_PCS, 0x8304, 0xdddd);
56	return 0;
57}
58
59static int led_link(struct cphy *cphy, u32 do_enable)
60{
61	u32 led = 0;
62#define LINK_ENABLE_BIT 0x1
63
64	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, &led);
65
66	if (do_enable & LINK_ENABLE_BIT) {
67		led |= LINK_ENABLE_BIT;
68		cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, led);
69	} else {
70		led &= ~LINK_ENABLE_BIT;
71		cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, led);
72	}
73	return 0;
74}
75
76/* Port Reset */
77static int mv88x201x_reset(struct cphy *cphy, int wait)
78{
79	/* This can be done through registers.  It is not required since
80	 * a full chip reset is used.
81	 */
82	return 0;
83}
84
85static int mv88x201x_interrupt_enable(struct cphy *cphy)
86{
87	/* Enable PHY LASI interrupts. */
88	cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL,
89			MDIO_PMA_LASI_LSALARM);
90
91	/* Enable Marvell interrupts through Elmer0. */
92	if (t1_is_asic(cphy->adapter)) {
93		u32 elmer;
94
95		t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
96		elmer |= ELMER0_GP_BIT6;
97		t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
98	}
99	return 0;
100}
101
102static int mv88x201x_interrupt_disable(struct cphy *cphy)
103{
104	/* Disable PHY LASI interrupts. */
105	cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, 0x0);
106
107	/* Disable Marvell interrupts through Elmer0. */
108	if (t1_is_asic(cphy->adapter)) {
109		u32 elmer;
110
111		t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
112		elmer &= ~ELMER0_GP_BIT6;
113		t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
114	}
115	return 0;
116}
117
118static int mv88x201x_interrupt_clear(struct cphy *cphy)
119{
120	u32 elmer;
121	u32 val;
122
123#ifdef MV88x2010_LINK_STATUS_BUGS
124	/* Required to read twice before clear takes affect. */
125	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_RXSTAT, &val);
126	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_TXSTAT, &val);
127	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT, &val);
128
129	/* Read this register after the others above it else
130	 * the register doesn't clear correctly.
131	 */
132	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val);
133#endif
134
135	/* Clear link status. */
136	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val);
137	/* Clear PHY LASI interrupts. */
138	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT, &val);
139
140#ifdef MV88x2010_LINK_STATUS_BUGS
141	/* Do it again. */
142	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_RXSTAT, &val);
143	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_TXSTAT, &val);
144#endif
145
146	/* Clear Marvell interrupts through Elmer0. */
147	if (t1_is_asic(cphy->adapter)) {
148		t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer);
149		elmer |= ELMER0_GP_BIT6;
150		t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer);
151	}
152	return 0;
153}
154
155static int mv88x201x_interrupt_handler(struct cphy *cphy)
156{
157	/* Clear interrupts */
158	mv88x201x_interrupt_clear(cphy);
159
160	/* We have only enabled link change interrupts and so
161	 * cphy_cause must be a link change interrupt.
162	 */
163	return cphy_cause_link_change;
164}
165
166static int mv88x201x_set_loopback(struct cphy *cphy, int on)
167{
168	return 0;
169}
170
171static int mv88x201x_get_link_status(struct cphy *cphy, int *link_ok,
172				     int *speed, int *duplex, int *fc)
173{
174	u32 val = 0;
175
176	if (link_ok) {
177		/* Read link status. */
178		cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val);
179		val &= MDIO_STAT1_LSTATUS;
180		*link_ok = (val == MDIO_STAT1_LSTATUS);
181		/* Turn on/off Link LED */
182		led_link(cphy, *link_ok);
183	}
184	if (speed)
185		*speed = SPEED_10000;
186	if (duplex)
187		*duplex = DUPLEX_FULL;
188	if (fc)
189		*fc = PAUSE_RX | PAUSE_TX;
190	return 0;
191}
192
193static void mv88x201x_destroy(struct cphy *cphy)
194{
195	kfree(cphy);
196}
197
198static struct cphy_ops mv88x201x_ops = {
199	.destroy           = mv88x201x_destroy,
200	.reset             = mv88x201x_reset,
201	.interrupt_enable  = mv88x201x_interrupt_enable,
202	.interrupt_disable = mv88x201x_interrupt_disable,
203	.interrupt_clear   = mv88x201x_interrupt_clear,
204	.interrupt_handler = mv88x201x_interrupt_handler,
205	.get_link_status   = mv88x201x_get_link_status,
206	.set_loopback      = mv88x201x_set_loopback,
207	.mmds              = (MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS |
208			      MDIO_DEVS_PHYXS | MDIO_DEVS_WIS),
209};
210
211static struct cphy *mv88x201x_phy_create(struct net_device *dev, int phy_addr,
212					 const struct mdio_ops *mdio_ops)
213{
214	u32 val;
215	struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL);
216
217	if (!cphy)
218		return NULL;
219
220	cphy_init(cphy, dev, phy_addr, &mv88x201x_ops, mdio_ops);
221
222	/* Commands the PHY to enable XFP's clock. */
223	cphy_mdio_read(cphy, MDIO_MMD_PCS, 0x8300, &val);
224	cphy_mdio_write(cphy, MDIO_MMD_PCS, 0x8300, val | 1);
225
226	/* Clear link status. Required because of a bug in the PHY.  */
227	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT2, &val);
228	cphy_mdio_read(cphy, MDIO_MMD_PCS, MDIO_STAT2, &val);
229
230	/* Allows for Link,Ack LED turn on/off */
231	led_init(cphy);
232	return cphy;
233}
234
235/* Chip Reset */
236static int mv88x201x_phy_reset(adapter_t *adapter)
237{
238	u32 val;
239
240	t1_tpi_read(adapter, A_ELMER0_GPO, &val);
241	val &= ~4;
242	t1_tpi_write(adapter, A_ELMER0_GPO, val);
243	msleep(100);
244
245	t1_tpi_write(adapter, A_ELMER0_GPO, val | 4);
246	msleep(1000);
247
248	/* Now lets enable the Laser. Delay 100us */
249	t1_tpi_read(adapter, A_ELMER0_GPO, &val);
250	val |= 0x8000;
251	t1_tpi_write(adapter, A_ELMER0_GPO, val);
252	udelay(100);
253	return 0;
254}
255
256const struct gphy t1_mv88x201x_ops = {
257	.create = mv88x201x_phy_create,
258	.reset = mv88x201x_phy_reset
259};
260