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