Commit | Line | Data |
---|---|---|
8ceee660 BH |
1 | /**************************************************************************** |
2 | * Driver for Solarflare Solarstorm network controllers and boards | |
3 | * Copyright 2007 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 | #include "net_driver.h" | |
11 | #include "phy.h" | |
12 | #include "boards.h" | |
13 | #include "efx.h" | |
3e133c44 | 14 | #include "workarounds.h" |
8ceee660 BH |
15 | |
16 | /* Macros for unpacking the board revision */ | |
17 | /* The revision info is in host byte order. */ | |
18 | #define BOARD_TYPE(_rev) (_rev >> 8) | |
19 | #define BOARD_MAJOR(_rev) ((_rev >> 4) & 0xf) | |
20 | #define BOARD_MINOR(_rev) (_rev & 0xf) | |
21 | ||
22 | /* Blink support. If the PHY has no auto-blink mode so we hang it off a timer */ | |
23 | #define BLINK_INTERVAL (HZ/2) | |
24 | ||
25 | static void blink_led_timer(unsigned long context) | |
26 | { | |
27 | struct efx_nic *efx = (struct efx_nic *)context; | |
28 | struct efx_blinker *bl = &efx->board_info.blinker; | |
29 | efx->board_info.set_fault_led(efx, bl->state); | |
30 | bl->state = !bl->state; | |
4cc58bde BH |
31 | if (bl->resubmit) |
32 | mod_timer(&bl->timer, jiffies + BLINK_INTERVAL); | |
8ceee660 BH |
33 | } |
34 | ||
dc8cfa55 | 35 | static void board_blink(struct efx_nic *efx, bool blink) |
8ceee660 BH |
36 | { |
37 | struct efx_blinker *blinker = &efx->board_info.blinker; | |
38 | ||
39 | /* The rtnl mutex serialises all ethtool ioctls, so | |
40 | * nothing special needs doing here. */ | |
41 | if (blink) { | |
dc8cfa55 BH |
42 | blinker->resubmit = true; |
43 | blinker->state = false; | |
8ceee660 BH |
44 | setup_timer(&blinker->timer, blink_led_timer, |
45 | (unsigned long)efx); | |
4cc58bde | 46 | mod_timer(&blinker->timer, jiffies + BLINK_INTERVAL); |
8ceee660 | 47 | } else { |
dc8cfa55 | 48 | blinker->resubmit = false; |
8ceee660 BH |
49 | if (blinker->timer.function) |
50 | del_timer_sync(&blinker->timer); | |
dc8cfa55 | 51 | efx->board_info.set_fault_led(efx, false); |
8ceee660 BH |
52 | } |
53 | } | |
54 | ||
3e133c44 BH |
55 | /***************************************************************************** |
56 | * Support for LM87 sensor chip used on several boards | |
57 | */ | |
58 | #define LM87_REG_ALARMS1 0x41 | |
59 | #define LM87_REG_ALARMS2 0x42 | |
60 | #define LM87_IN_LIMITS(nr, _min, _max) \ | |
61 | 0x2B + (nr) * 2, _max, 0x2C + (nr) * 2, _min | |
62 | #define LM87_AIN_LIMITS(nr, _min, _max) \ | |
63 | 0x3B + (nr), _max, 0x1A + (nr), _min | |
64 | #define LM87_TEMP_INT_LIMITS(_min, _max) \ | |
65 | 0x39, _max, 0x3A, _min | |
66 | #define LM87_TEMP_EXT1_LIMITS(_min, _max) \ | |
67 | 0x37, _max, 0x38, _min | |
68 | ||
69 | #define LM87_ALARM_TEMP_INT 0x10 | |
70 | #define LM87_ALARM_TEMP_EXT1 0x20 | |
71 | ||
72 | #if defined(CONFIG_SENSORS_LM87) || defined(CONFIG_SENSORS_LM87_MODULE) | |
73 | ||
74 | static int efx_init_lm87(struct efx_nic *efx, struct i2c_board_info *info, | |
75 | const u8 *reg_values) | |
76 | { | |
77 | struct i2c_client *client = i2c_new_device(&efx->i2c_adap, info); | |
78 | int rc; | |
79 | ||
80 | if (!client) | |
81 | return -EIO; | |
82 | ||
83 | while (*reg_values) { | |
84 | u8 reg = *reg_values++; | |
85 | u8 value = *reg_values++; | |
86 | rc = i2c_smbus_write_byte_data(client, reg, value); | |
87 | if (rc) | |
88 | goto err; | |
89 | } | |
90 | ||
91 | efx->board_info.hwmon_client = client; | |
92 | return 0; | |
93 | ||
94 | err: | |
95 | i2c_unregister_device(client); | |
96 | return rc; | |
97 | } | |
98 | ||
99 | static void efx_fini_lm87(struct efx_nic *efx) | |
100 | { | |
101 | i2c_unregister_device(efx->board_info.hwmon_client); | |
102 | } | |
103 | ||
104 | static int efx_check_lm87(struct efx_nic *efx, unsigned mask) | |
105 | { | |
106 | struct i2c_client *client = efx->board_info.hwmon_client; | |
107 | s32 alarms1, alarms2; | |
108 | ||
109 | /* If link is up then do not monitor temperature */ | |
110 | if (EFX_WORKAROUND_7884(efx) && efx->link_up) | |
111 | return 0; | |
112 | ||
113 | alarms1 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS1); | |
114 | alarms2 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS2); | |
115 | if (alarms1 < 0) | |
116 | return alarms1; | |
117 | if (alarms2 < 0) | |
118 | return alarms2; | |
119 | alarms1 &= mask; | |
120 | alarms2 &= mask >> 8; | |
121 | if (alarms1 || alarms2) { | |
122 | EFX_ERR(efx, | |
123 | "LM87 detected a hardware failure (status %02x:%02x)" | |
124 | "%s%s\n", | |
125 | alarms1, alarms2, | |
126 | (alarms1 & LM87_ALARM_TEMP_INT) ? " INTERNAL" : "", | |
127 | (alarms1 & LM87_ALARM_TEMP_EXT1) ? " EXTERNAL" : ""); | |
128 | return -ERANGE; | |
129 | } | |
130 | ||
131 | return 0; | |
132 | } | |
133 | ||
134 | #else /* !CONFIG_SENSORS_LM87 */ | |
135 | ||
136 | static inline int | |
137 | efx_init_lm87(struct efx_nic *efx, struct i2c_board_info *info, | |
138 | const u8 *reg_values) | |
139 | { | |
140 | return 0; | |
141 | } | |
142 | static inline void efx_fini_lm87(struct efx_nic *efx) | |
143 | { | |
144 | } | |
145 | static inline int efx_check_lm87(struct efx_nic *efx, unsigned mask) | |
146 | { | |
147 | return 0; | |
148 | } | |
149 | ||
150 | #endif /* CONFIG_SENSORS_LM87 */ | |
151 | ||
8ceee660 BH |
152 | /***************************************************************************** |
153 | * Support for the SFE4002 | |
154 | * | |
155 | */ | |
3e133c44 BH |
156 | static u8 sfe4002_lm87_channel = 0x03; /* use AIN not FAN inputs */ |
157 | ||
158 | static const u8 sfe4002_lm87_regs[] = { | |
159 | LM87_IN_LIMITS(0, 0x83, 0x91), /* 2.5V: 1.8V +/- 5% */ | |
160 | LM87_IN_LIMITS(1, 0x51, 0x5a), /* Vccp1: 1.2V +/- 5% */ | |
161 | LM87_IN_LIMITS(2, 0xb6, 0xca), /* 3.3V: 3.3V +/- 5% */ | |
162 | LM87_IN_LIMITS(3, 0xb0, 0xc9), /* 5V: 4.6-5.2V */ | |
163 | LM87_IN_LIMITS(4, 0xb0, 0xe0), /* 12V: 11-14V */ | |
164 | LM87_IN_LIMITS(5, 0x44, 0x4b), /* Vccp2: 1.0V +/- 5% */ | |
165 | LM87_AIN_LIMITS(0, 0xa0, 0xb2), /* AIN1: 1.66V +/- 5% */ | |
166 | LM87_AIN_LIMITS(1, 0x91, 0xa1), /* AIN2: 1.5V +/- 5% */ | |
167 | LM87_TEMP_INT_LIMITS(10, 60), /* board */ | |
168 | LM87_TEMP_EXT1_LIMITS(10, 70), /* Falcon */ | |
169 | 0 | |
170 | }; | |
171 | ||
172 | static struct i2c_board_info sfe4002_hwmon_info = { | |
173 | I2C_BOARD_INFO("lm87", 0x2e), | |
174 | .platform_data = &sfe4002_lm87_channel, | |
175 | .irq = -1, | |
176 | }; | |
177 | ||
8ceee660 BH |
178 | /****************************************************************************/ |
179 | /* LED allocations. Note that on rev A0 boards the schematic and the reality | |
180 | * differ: red and green are swapped. Below is the fixed (A1) layout (there | |
181 | * are only 3 A0 boards in existence, so no real reason to make this | |
182 | * conditional). | |
183 | */ | |
184 | #define SFE4002_FAULT_LED (2) /* Red */ | |
185 | #define SFE4002_RX_LED (0) /* Green */ | |
186 | #define SFE4002_TX_LED (1) /* Amber */ | |
187 | ||
188 | static int sfe4002_init_leds(struct efx_nic *efx) | |
189 | { | |
190 | /* Set the TX and RX LEDs to reflect status and activity, and the | |
191 | * fault LED off */ | |
192 | xfp_set_led(efx, SFE4002_TX_LED, | |
193 | QUAKE_LED_TXLINK | QUAKE_LED_LINK_ACTSTAT); | |
194 | xfp_set_led(efx, SFE4002_RX_LED, | |
195 | QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACTSTAT); | |
196 | xfp_set_led(efx, SFE4002_FAULT_LED, QUAKE_LED_OFF); | |
197 | efx->board_info.blinker.led_num = SFE4002_FAULT_LED; | |
198 | return 0; | |
199 | } | |
200 | ||
dc8cfa55 | 201 | static void sfe4002_fault_led(struct efx_nic *efx, bool state) |
8ceee660 BH |
202 | { |
203 | xfp_set_led(efx, SFE4002_FAULT_LED, state ? QUAKE_LED_ON : | |
204 | QUAKE_LED_OFF); | |
205 | } | |
206 | ||
3e133c44 BH |
207 | static int sfe4002_check_hw(struct efx_nic *efx) |
208 | { | |
209 | /* A0 board rev. 4002s report a temperature fault the whole time | |
210 | * (bad sensor) so we mask it out. */ | |
211 | unsigned alarm_mask = | |
212 | (efx->board_info.major == 0 && efx->board_info.minor == 0) ? | |
213 | ~LM87_ALARM_TEMP_EXT1 : ~0; | |
214 | ||
215 | return efx_check_lm87(efx, alarm_mask); | |
216 | } | |
217 | ||
8ceee660 BH |
218 | static int sfe4002_init(struct efx_nic *efx) |
219 | { | |
3e133c44 BH |
220 | int rc = efx_init_lm87(efx, &sfe4002_hwmon_info, sfe4002_lm87_regs); |
221 | if (rc) | |
222 | return rc; | |
223 | efx->board_info.monitor = sfe4002_check_hw; | |
8ceee660 BH |
224 | efx->board_info.init_leds = sfe4002_init_leds; |
225 | efx->board_info.set_fault_led = sfe4002_fault_led; | |
226 | efx->board_info.blink = board_blink; | |
3e133c44 | 227 | efx->board_info.fini = efx_fini_lm87; |
8ceee660 BH |
228 | return 0; |
229 | } | |
230 | ||
231 | /* This will get expanded as board-specific details get moved out of the | |
232 | * PHY drivers. */ | |
233 | struct efx_board_data { | |
234 | const char *ref_model; | |
235 | const char *gen_type; | |
236 | int (*init) (struct efx_nic *nic); | |
237 | }; | |
238 | ||
239 | static int dummy_init(struct efx_nic *nic) | |
240 | { | |
241 | return 0; | |
242 | } | |
243 | ||
244 | static struct efx_board_data board_data[] = { | |
245 | [EFX_BOARD_INVALID] = | |
246 | {NULL, NULL, dummy_init}, | |
247 | [EFX_BOARD_SFE4001] = | |
37b5a603 | 248 | {"SFE4001", "10GBASE-T adapter", sfe4001_init}, |
8ceee660 BH |
249 | [EFX_BOARD_SFE4002] = |
250 | {"SFE4002", "XFP adapter", sfe4002_init}, | |
251 | }; | |
252 | ||
253 | int efx_set_board_info(struct efx_nic *efx, u16 revision_info) | |
254 | { | |
255 | int rc = 0; | |
256 | struct efx_board_data *data; | |
257 | ||
258 | if (BOARD_TYPE(revision_info) >= EFX_BOARD_MAX) { | |
259 | EFX_ERR(efx, "squashing unknown board type %d\n", | |
260 | BOARD_TYPE(revision_info)); | |
261 | revision_info = 0; | |
262 | } | |
263 | ||
264 | if (BOARD_TYPE(revision_info) == 0) { | |
265 | efx->board_info.major = 0; | |
266 | efx->board_info.minor = 0; | |
267 | /* For early boards that don't have revision info. there is | |
268 | * only 1 board for each PHY type, so we can work it out, with | |
269 | * the exception of the PHY-less boards. */ | |
270 | switch (efx->phy_type) { | |
271 | case PHY_TYPE_10XPRESS: | |
272 | efx->board_info.type = EFX_BOARD_SFE4001; | |
273 | break; | |
274 | case PHY_TYPE_XFP: | |
275 | efx->board_info.type = EFX_BOARD_SFE4002; | |
276 | break; | |
277 | default: | |
278 | efx->board_info.type = 0; | |
279 | break; | |
280 | } | |
281 | } else { | |
282 | efx->board_info.type = BOARD_TYPE(revision_info); | |
283 | efx->board_info.major = BOARD_MAJOR(revision_info); | |
284 | efx->board_info.minor = BOARD_MINOR(revision_info); | |
285 | } | |
286 | ||
287 | data = &board_data[efx->board_info.type]; | |
288 | ||
289 | /* Report the board model number or generic type for recognisable | |
290 | * boards. */ | |
291 | if (efx->board_info.type != 0) | |
292 | EFX_INFO(efx, "board is %s rev %c%d\n", | |
293 | (efx->pci_dev->subsystem_vendor == EFX_VENDID_SFC) | |
294 | ? data->ref_model : data->gen_type, | |
295 | 'A' + efx->board_info.major, efx->board_info.minor); | |
296 | ||
297 | efx->board_info.init = data->init; | |
298 | ||
299 | return rc; | |
300 | } |