You need to sign in or sign up before continuing.
boards.c 7.7 KB
Newer Older
1 2
/****************************************************************************
 * Driver for Solarflare Solarstorm network controllers and boards
B
Ben Hutchings 已提交
3
 * Copyright 2007-2008 Solarflare Communications Inc.
4 5 6 7 8 9 10 11 12 13
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation, incorporated herein by reference.
 */

#include "net_driver.h"
#include "phy.h"
#include "boards.h"
#include "efx.h"
14
#include "workarounds.h"
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

/* Macros for unpacking the board revision */
/* The revision info is in host byte order. */
#define BOARD_TYPE(_rev) (_rev >> 8)
#define BOARD_MAJOR(_rev) ((_rev >> 4) & 0xf)
#define BOARD_MINOR(_rev) (_rev & 0xf)

/* Blink support. If the PHY has no auto-blink mode so we hang it off a timer */
#define BLINK_INTERVAL (HZ/2)

static void blink_led_timer(unsigned long context)
{
	struct efx_nic *efx = (struct efx_nic *)context;
	struct efx_blinker *bl = &efx->board_info.blinker;
	efx->board_info.set_fault_led(efx, bl->state);
	bl->state = !bl->state;
31 32
	if (bl->resubmit)
		mod_timer(&bl->timer, jiffies + BLINK_INTERVAL);
33 34
}

35
static void board_blink(struct efx_nic *efx, bool blink)
36 37 38 39 40 41
{
	struct efx_blinker *blinker = &efx->board_info.blinker;

	/* The rtnl mutex serialises all ethtool ioctls, so
	 * nothing special needs doing here. */
	if (blink) {
42 43
		blinker->resubmit = true;
		blinker->state = false;
44 45
		setup_timer(&blinker->timer, blink_led_timer,
			    (unsigned long)efx);
46
		mod_timer(&blinker->timer, jiffies + BLINK_INTERVAL);
47
	} else {
48
		blinker->resubmit = false;
49 50
		if (blinker->timer.function)
			del_timer_sync(&blinker->timer);
51
		efx->board_info.set_fault_led(efx, false);
52 53 54
	}
}

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
/*****************************************************************************
 * Support for LM87 sensor chip used on several boards
 */
#define LM87_REG_ALARMS1		0x41
#define LM87_REG_ALARMS2		0x42
#define LM87_IN_LIMITS(nr, _min, _max)			\
	0x2B + (nr) * 2, _max, 0x2C + (nr) * 2, _min
#define LM87_AIN_LIMITS(nr, _min, _max)			\
	0x3B + (nr), _max, 0x1A + (nr), _min
#define LM87_TEMP_INT_LIMITS(_min, _max)		\
	0x39, _max, 0x3A, _min
#define LM87_TEMP_EXT1_LIMITS(_min, _max)		\
	0x37, _max, 0x38, _min

#define LM87_ALARM_TEMP_INT		0x10
#define LM87_ALARM_TEMP_EXT1		0x20

#if defined(CONFIG_SENSORS_LM87) || defined(CONFIG_SENSORS_LM87_MODULE)

static int efx_init_lm87(struct efx_nic *efx, struct i2c_board_info *info,
			 const u8 *reg_values)
{
	struct i2c_client *client = i2c_new_device(&efx->i2c_adap, info);
	int rc;

	if (!client)
		return -EIO;

	while (*reg_values) {
		u8 reg = *reg_values++;
		u8 value = *reg_values++;
		rc = i2c_smbus_write_byte_data(client, reg, value);
		if (rc)
			goto err;
	}

	efx->board_info.hwmon_client = client;
	return 0;

err:
	i2c_unregister_device(client);
	return rc;
}

static void efx_fini_lm87(struct efx_nic *efx)
{
	i2c_unregister_device(efx->board_info.hwmon_client);
}

static int efx_check_lm87(struct efx_nic *efx, unsigned mask)
{
	struct i2c_client *client = efx->board_info.hwmon_client;
	s32 alarms1, alarms2;

	/* If link is up then do not monitor temperature */
	if (EFX_WORKAROUND_7884(efx) && efx->link_up)
		return 0;

	alarms1 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS1);
	alarms2 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS2);
	if (alarms1 < 0)
		return alarms1;
	if (alarms2 < 0)
		return alarms2;
	alarms1 &= mask;
	alarms2 &= mask >> 8;
	if (alarms1 || alarms2) {
		EFX_ERR(efx,
			"LM87 detected a hardware failure (status %02x:%02x)"
			"%s%s\n",
			alarms1, alarms2,
			(alarms1 & LM87_ALARM_TEMP_INT) ? " INTERNAL" : "",
			(alarms1 & LM87_ALARM_TEMP_EXT1) ? " EXTERNAL" : "");
		return -ERANGE;
	}

	return 0;
}

#else /* !CONFIG_SENSORS_LM87 */

static inline int
efx_init_lm87(struct efx_nic *efx, struct i2c_board_info *info,
	      const u8 *reg_values)
{
	return 0;
}
static inline void efx_fini_lm87(struct efx_nic *efx)
{
}
static inline int efx_check_lm87(struct efx_nic *efx, unsigned mask)
{
	return 0;
}

#endif /* CONFIG_SENSORS_LM87 */

152 153 154 155
/*****************************************************************************
 * Support for the SFE4002
 *
 */
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
static u8 sfe4002_lm87_channel = 0x03; /* use AIN not FAN inputs */

static const u8 sfe4002_lm87_regs[] = {
	LM87_IN_LIMITS(0, 0x83, 0x91),		/* 2.5V:  1.8V +/- 5% */
	LM87_IN_LIMITS(1, 0x51, 0x5a),		/* Vccp1: 1.2V +/- 5% */
	LM87_IN_LIMITS(2, 0xb6, 0xca),		/* 3.3V:  3.3V +/- 5% */
	LM87_IN_LIMITS(3, 0xb0, 0xc9),		/* 5V:    4.6-5.2V */
	LM87_IN_LIMITS(4, 0xb0, 0xe0),		/* 12V:   11-14V */
	LM87_IN_LIMITS(5, 0x44, 0x4b),		/* Vccp2: 1.0V +/- 5% */
	LM87_AIN_LIMITS(0, 0xa0, 0xb2),		/* AIN1:  1.66V +/- 5% */
	LM87_AIN_LIMITS(1, 0x91, 0xa1),		/* AIN2:  1.5V +/- 5% */
	LM87_TEMP_INT_LIMITS(10, 60),		/* board */
	LM87_TEMP_EXT1_LIMITS(10, 70),		/* Falcon */
	0
};

static struct i2c_board_info sfe4002_hwmon_info = {
	I2C_BOARD_INFO("lm87", 0x2e),
	.platform_data	= &sfe4002_lm87_channel,
	.irq		= -1,
};

178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
/****************************************************************************/
/* LED allocations. Note that on rev A0 boards the schematic and the reality
 * differ: red and green are swapped. Below is the fixed (A1) layout (there
 * are only 3 A0 boards in existence, so no real reason to make this
 * conditional).
 */
#define SFE4002_FAULT_LED (2)	/* Red */
#define SFE4002_RX_LED    (0)	/* Green */
#define SFE4002_TX_LED    (1)	/* Amber */

static int sfe4002_init_leds(struct efx_nic *efx)
{
	/* Set the TX and RX LEDs to reflect status and activity, and the
	 * fault LED off */
	xfp_set_led(efx, SFE4002_TX_LED,
		    QUAKE_LED_TXLINK | QUAKE_LED_LINK_ACTSTAT);
	xfp_set_led(efx, SFE4002_RX_LED,
		    QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACTSTAT);
	xfp_set_led(efx, SFE4002_FAULT_LED, QUAKE_LED_OFF);
	efx->board_info.blinker.led_num = SFE4002_FAULT_LED;
	return 0;
}

201
static void sfe4002_fault_led(struct efx_nic *efx, bool state)
202 203 204 205 206
{
	xfp_set_led(efx, SFE4002_FAULT_LED, state ? QUAKE_LED_ON :
			QUAKE_LED_OFF);
}

207 208 209 210 211 212 213 214 215 216 217
static int sfe4002_check_hw(struct efx_nic *efx)
{
	/* A0 board rev. 4002s report a temperature fault the whole time
	 * (bad sensor) so we mask it out. */
	unsigned alarm_mask =
		(efx->board_info.major == 0 && efx->board_info.minor == 0) ?
		~LM87_ALARM_TEMP_EXT1 : ~0;

	return efx_check_lm87(efx, alarm_mask);
}

218 219
static int sfe4002_init(struct efx_nic *efx)
{
220 221 222 223
	int rc = efx_init_lm87(efx, &sfe4002_hwmon_info, sfe4002_lm87_regs);
	if (rc)
		return rc;
	efx->board_info.monitor = sfe4002_check_hw;
224 225 226
	efx->board_info.init_leds = sfe4002_init_leds;
	efx->board_info.set_fault_led = sfe4002_fault_led;
	efx->board_info.blink = board_blink;
227
	efx->board_info.fini = efx_fini_lm87;
228 229 230 231 232 233
	return 0;
}

/* This will get expanded as board-specific details get moved out of the
 * PHY drivers. */
struct efx_board_data {
B
Ben Hutchings 已提交
234
	enum efx_board_type type;
235 236 237 238 239 240 241
	const char *ref_model;
	const char *gen_type;
	int (*init) (struct efx_nic *nic);
};


static struct efx_board_data board_data[] = {
B
Ben Hutchings 已提交
242 243
	{ EFX_BOARD_SFE4001, "SFE4001", "10GBASE-T adapter", sfe4001_init },
	{ EFX_BOARD_SFE4002, "SFE4002", "XFP adapter", sfe4002_init },
B
Ben Hutchings 已提交
244 245
	{ EFX_BOARD_SFN4111T, "SFN4111T", "100/1000/10GBASE-T adapter",
	  sfn4111t_init },
246 247
};

B
Ben Hutchings 已提交
248
void efx_set_board_info(struct efx_nic *efx, u16 revision_info)
249
{
B
Ben Hutchings 已提交
250 251
	struct efx_board_data *data = NULL;
	int i;
252

B
Ben Hutchings 已提交
253 254 255
	efx->board_info.type = BOARD_TYPE(revision_info);
	efx->board_info.major = BOARD_MAJOR(revision_info);
	efx->board_info.minor = BOARD_MINOR(revision_info);
256

B
Ben Hutchings 已提交
257 258 259
	for (i = 0; i < ARRAY_SIZE(board_data); i++)
		if (board_data[i].type == efx->board_info.type)
			data = &board_data[i];
260

B
Ben Hutchings 已提交
261
	if (data) {
262 263 264 265
		EFX_INFO(efx, "board is %s rev %c%d\n",
			 (efx->pci_dev->subsystem_vendor == EFX_VENDID_SFC)
			 ? data->ref_model : data->gen_type,
			 'A' + efx->board_info.major, efx->board_info.minor);
B
Ben Hutchings 已提交
266 267 268 269
		efx->board_info.init = data->init;
	} else {
		EFX_ERR(efx, "unknown board type %d\n", efx->board_info.type);
	}
270
}