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