aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/sfc/boards.c
blob: 99e6023732691c08cf6060abff318b2331201b35 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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
152
153
154
155
156
157
158
159
160
161
162
163
164
/****************************************************************************
 * Driver for Solarflare Solarstorm network controllers and boards
 * Copyright 2007 Solarflare Communications Inc.
 *
 * 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"

/* 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;
	if (bl->resubmit)
		mod_timer(&bl->timer, jiffies + BLINK_INTERVAL);
}

static void board_blink(struct efx_nic *efx, bool blink)
{
	struct efx_blinker *blinker = &efx->board_info.blinker;

	/* The rtnl mutex serialises all ethtool ioctls, so
	 * nothing special needs doing here. */
	if (blink) {
		blinker->resubmit = true;
		blinker->state = false;
		setup_timer(&blinker->timer, blink_led_timer,
			    (unsigned long)efx);
		mod_timer(&blinker->timer, jiffies + BLINK_INTERVAL);
	} else {
		blinker->resubmit = false;
		if (blinker->timer.function)
			del_timer_sync(&blinker->timer);
		efx->board_info.set_fault_led(efx, false);
	}
}

/*****************************************************************************
 * Support for the SFE4002
 *
 */
/****************************************************************************/
/* 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;
}

static void sfe4002_fault_led(struct efx_nic *efx, bool state)
{
	xfp_set_led(efx, SFE4002_FAULT_LED, state ? QUAKE_LED_ON :
			QUAKE_LED_OFF);
}

static int sfe4002_init(struct efx_nic *efx)
{
	efx->board_info.init_leds = sfe4002_init_leds;
	efx->board_info.set_fault_led = sfe4002_fault_led;
	efx->board_info.blink = board_blink;
	return 0;
}

/* This will get expanded as board-specific details get moved out of the
 * PHY drivers. */
struct efx_board_data {
	const char *ref_model;
	const char *gen_type;
	int (*init) (struct efx_nic *nic);
};

static int dummy_init(struct efx_nic *nic)
{
	return 0;
}

static struct efx_board_data board_data[] = {
	[EFX_BOARD_INVALID] =
	{NULL,	    NULL,                  dummy_init},
	[EFX_BOARD_SFE4001] =
	{"SFE4001", "10GBASE-T adapter",   sfe4001_init},
	[EFX_BOARD_SFE4002] =
	{"SFE4002", "XFP adapter",         sfe4002_init},
};

int efx_set_board_info(struct efx_nic *efx, u16 revision_info)
{
	int rc = 0;
	struct efx_board_data *data;

	if (BOARD_TYPE(revision_info) >= EFX_BOARD_MAX) {
		EFX_ERR(efx, "squashing unknown board type %d\n",
			BOARD_TYPE(revision_info));
		revision_info = 0;
	}

	if (BOARD_TYPE(revision_info) == 0) {
		efx->board_info.major = 0;
		efx->board_info.minor = 0;
		/* For early boards that don't have revision info. there is
		 * only 1 board for each PHY type, so we can work it out, with
		 * the exception of the PHY-less boards. */
		switch (efx->phy_type) {
		case PHY_TYPE_10XPRESS:
			efx->board_info.type = EFX_BOARD_SFE4001;
			break;
		case PHY_TYPE_XFP:
			efx->board_info.type = EFX_BOARD_SFE4002;
			break;
		default:
			efx->board_info.type = 0;
			break;
		}
	} else {
		efx->board_info.type = BOARD_TYPE(revision_info);
		efx->board_info.major = BOARD_MAJOR(revision_info);
		efx->board_info.minor = BOARD_MINOR(revision_info);
	}

	data = &board_data[efx->board_info.type];

	/* Report the board model number or generic type for recognisable
	 * boards. */
	if (efx->board_info.type != 0)
		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);

	efx->board_info.init = data->init;

	return rc;
}