Commit | Line | Data |
---|---|---|
8ceee660 BH |
1 | /**************************************************************************** |
2 | * Driver for Solarflare Solarstorm network controllers and boards | |
3 | * Copyright 2006-2008 Solarflare Communications Inc. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License version 2 as published | |
7 | * by the Free Software Foundation, incorporated herein by reference. | |
8 | */ | |
9 | /* | |
10 | * Driver for XFP optical PHYs (plus some support specific to the Quake 2032) | |
11 | * See www.amcc.com for details (search for qt2032) | |
12 | */ | |
13 | ||
14 | #include <linux/timer.h> | |
15 | #include <linux/delay.h> | |
16 | #include "efx.h" | |
17 | #include "gmii.h" | |
18 | #include "mdio_10g.h" | |
19 | #include "xenpack.h" | |
20 | #include "phy.h" | |
21 | #include "mac.h" | |
22 | ||
23 | #define XFP_REQUIRED_DEVS (MDIO_MMDREG_DEVS0_PCS | \ | |
24 | MDIO_MMDREG_DEVS0_PMAPMD | \ | |
25 | MDIO_MMDREG_DEVS0_PHYXS) | |
26 | ||
3273c2e8 BH |
27 | #define XFP_LOOPBACKS ((1 << LOOPBACK_PCS) | \ |
28 | (1 << LOOPBACK_PMAPMD) | \ | |
29 | (1 << LOOPBACK_NETWORK)) | |
30 | ||
8ceee660 BH |
31 | /****************************************************************************/ |
32 | /* Quake-specific MDIO registers */ | |
33 | #define MDIO_QUAKE_LED0_REG (0xD006) | |
34 | ||
35 | void xfp_set_led(struct efx_nic *p, int led, int mode) | |
36 | { | |
37 | int addr = MDIO_QUAKE_LED0_REG + led; | |
38 | mdio_clause45_write(p, p->mii.phy_id, MDIO_MMD_PMAPMD, addr, | |
39 | mode); | |
40 | } | |
41 | ||
3273c2e8 BH |
42 | struct xfp_phy_data { |
43 | int tx_disabled; | |
44 | }; | |
45 | ||
8ceee660 BH |
46 | #define XFP_MAX_RESET_TIME 500 |
47 | #define XFP_RESET_WAIT 10 | |
48 | ||
49 | /* Reset the PHYXS MMD. This is documented (for the Quake PHY) as doing | |
50 | * a complete soft reset. | |
51 | */ | |
52 | static int xfp_reset_phy(struct efx_nic *efx) | |
53 | { | |
54 | int rc; | |
55 | ||
56 | rc = mdio_clause45_reset_mmd(efx, MDIO_MMD_PHYXS, | |
57 | XFP_MAX_RESET_TIME / XFP_RESET_WAIT, | |
58 | XFP_RESET_WAIT); | |
59 | if (rc < 0) | |
60 | goto fail; | |
61 | ||
62 | /* Wait 250ms for the PHY to complete bootup */ | |
63 | msleep(250); | |
64 | ||
65 | /* Check that all the MMDs we expect are present and responding. We | |
66 | * expect faults on some if the link is down, but not on the PHY XS */ | |
67 | rc = mdio_clause45_check_mmds(efx, XFP_REQUIRED_DEVS, | |
68 | MDIO_MMDREG_DEVS0_PHYXS); | |
69 | if (rc < 0) | |
70 | goto fail; | |
71 | ||
72 | efx->board_info.init_leds(efx); | |
73 | ||
74 | return rc; | |
75 | ||
76 | fail: | |
77 | EFX_ERR(efx, "XFP: reset timed out!\n"); | |
78 | return rc; | |
79 | } | |
80 | ||
81 | static int xfp_phy_init(struct efx_nic *efx) | |
82 | { | |
3273c2e8 | 83 | struct xfp_phy_data *phy_data; |
8ceee660 BH |
84 | u32 devid = mdio_clause45_read_id(efx, MDIO_MMD_PHYXS); |
85 | int rc; | |
86 | ||
3273c2e8 | 87 | phy_data = kzalloc(sizeof(struct xfp_phy_data), GFP_KERNEL); |
d3208b5e | 88 | efx->phy_data = phy_data; |
3273c2e8 | 89 | |
8ceee660 BH |
90 | EFX_INFO(efx, "XFP: PHY ID reg %x (OUI %x model %x revision" |
91 | " %x)\n", devid, MDIO_ID_OUI(devid), MDIO_ID_MODEL(devid), | |
92 | MDIO_ID_REV(devid)); | |
93 | ||
3273c2e8 BH |
94 | phy_data->tx_disabled = efx->tx_disabled; |
95 | ||
8ceee660 BH |
96 | rc = xfp_reset_phy(efx); |
97 | ||
98 | EFX_INFO(efx, "XFP: PHY init %s.\n", | |
99 | rc ? "failed" : "successful"); | |
3273c2e8 BH |
100 | if (rc < 0) |
101 | goto fail; | |
8ceee660 | 102 | |
3273c2e8 BH |
103 | return 0; |
104 | ||
105 | fail: | |
106 | kfree(efx->phy_data); | |
107 | efx->phy_data = NULL; | |
8ceee660 BH |
108 | return rc; |
109 | } | |
110 | ||
111 | static void xfp_phy_clear_interrupt(struct efx_nic *efx) | |
112 | { | |
113 | xenpack_clear_lasi_irqs(efx); | |
114 | } | |
115 | ||
116 | static int xfp_link_ok(struct efx_nic *efx) | |
117 | { | |
118 | return mdio_clause45_links_ok(efx, XFP_REQUIRED_DEVS); | |
119 | } | |
120 | ||
121 | static int xfp_phy_check_hw(struct efx_nic *efx) | |
122 | { | |
123 | int rc = 0; | |
124 | int link_up = xfp_link_ok(efx); | |
125 | /* Simulate a PHY event if link state has changed */ | |
126 | if (link_up != efx->link_up) | |
127 | falcon_xmac_sim_phy_event(efx); | |
128 | ||
129 | return rc; | |
130 | } | |
131 | ||
132 | static void xfp_phy_reconfigure(struct efx_nic *efx) | |
133 | { | |
3273c2e8 BH |
134 | struct xfp_phy_data *phy_data = efx->phy_data; |
135 | ||
136 | /* Reset the PHY when moving from tx off to tx on */ | |
137 | if (phy_data->tx_disabled && !efx->tx_disabled) | |
138 | xfp_reset_phy(efx); | |
139 | ||
140 | mdio_clause45_transmit_disable(efx); | |
141 | mdio_clause45_phy_reconfigure(efx); | |
142 | ||
143 | phy_data->tx_disabled = efx->tx_disabled; | |
8ceee660 BH |
144 | efx->link_up = xfp_link_ok(efx); |
145 | efx->link_options = GM_LPA_10000FULL; | |
146 | } | |
147 | ||
148 | ||
149 | static void xfp_phy_fini(struct efx_nic *efx) | |
150 | { | |
151 | /* Clobber the LED if it was blinking */ | |
152 | efx->board_info.blink(efx, 0); | |
3273c2e8 BH |
153 | |
154 | /* Free the context block */ | |
155 | kfree(efx->phy_data); | |
156 | efx->phy_data = NULL; | |
8ceee660 BH |
157 | } |
158 | ||
159 | struct efx_phy_operations falcon_xfp_phy_ops = { | |
160 | .init = xfp_phy_init, | |
161 | .reconfigure = xfp_phy_reconfigure, | |
162 | .check_hw = xfp_phy_check_hw, | |
163 | .fini = xfp_phy_fini, | |
164 | .clear_interrupt = xfp_phy_clear_interrupt, | |
165 | .reset_xaui = efx_port_dummy_op_void, | |
166 | .mmds = XFP_REQUIRED_DEVS, | |
3273c2e8 | 167 | .loopbacks = XFP_LOOPBACKS, |
8ceee660 | 168 | }; |