Commit 82466f00 authored by Michael Albaugh's avatar Michael Albaugh Committed by Roland Dreier

IB/ipath: Support blinking LEDs with an led_override file

When we want to find an InfiniPath HCA in a rack of nodes, it is often
expeditious to blink the status LEDs via a userspace /sys file.

A write-only led_override "file" is published per device. Writes to
this file are interpreted as (string form) numbers, and the resulting
value sent to ipath_set_led_override(). The upper eight bits are
interpretted as a 4.4 fixed-point "frequency in Hertz", and the bottom
two 4-bit values are alternately (D0..3, then D4..7) used by the
board-specific LED-setting function to override the normal state.
Signed-off-by: default avatarMichael Albaugh <michael.albaugh@qlogic.com>
Signed-off-by: default avatarRoland Dreier <rolandd@cisco.com>
parent a024291b
......@@ -1846,6 +1846,87 @@ void ipath_write_kreg_port(const struct ipath_devdata *dd, ipath_kreg regno,
ipath_write_kreg(dd, where, value);
}
/*
* Following deal with the "obviously simple" task of overriding the state
* of the LEDS, which normally indicate link physical and logical status.
* The complications arise in dealing with different hardware mappings
* and the board-dependent routine being called from interrupts.
* and then there's the requirement to _flash_ them.
*/
#define LED_OVER_FREQ_SHIFT 8
#define LED_OVER_FREQ_MASK (0xFF<<LED_OVER_FREQ_SHIFT)
/* Below is "non-zero" to force override, but both actual LEDs are off */
#define LED_OVER_BOTH_OFF (8)
void ipath_run_led_override(unsigned long opaque)
{
struct ipath_devdata *dd = (struct ipath_devdata *)opaque;
int timeoff;
int pidx;
u64 lstate, ltstate, val;
if (!(dd->ipath_flags & IPATH_INITTED))
return;
pidx = dd->ipath_led_override_phase++ & 1;
dd->ipath_led_override = dd->ipath_led_override_vals[pidx];
timeoff = dd->ipath_led_override_timeoff;
/*
* below potentially restores the LED values per current status,
* should also possibly setup the traffic-blink register,
* but leave that to per-chip functions.
*/
val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_ibcstatus);
ltstate = (val >> INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) &
INFINIPATH_IBCS_LINKTRAININGSTATE_MASK;
lstate = (val >> INFINIPATH_IBCS_LINKSTATE_SHIFT) &
INFINIPATH_IBCS_LINKSTATE_MASK;
dd->ipath_f_setextled(dd, lstate, ltstate);
mod_timer(&dd->ipath_led_override_timer, jiffies + timeoff);
}
void ipath_set_led_override(struct ipath_devdata *dd, unsigned int val)
{
int timeoff, freq;
if (!(dd->ipath_flags & IPATH_INITTED))
return;
/* First check if we are blinking. If not, use 1HZ polling */
timeoff = HZ;
freq = (val & LED_OVER_FREQ_MASK) >> LED_OVER_FREQ_SHIFT;
if (freq) {
/* For blink, set each phase from one nybble of val */
dd->ipath_led_override_vals[0] = val & 0xF;
dd->ipath_led_override_vals[1] = (val >> 4) & 0xF;
timeoff = (HZ << 4)/freq;
} else {
/* Non-blink set both phases the same. */
dd->ipath_led_override_vals[0] = val & 0xF;
dd->ipath_led_override_vals[1] = val & 0xF;
}
dd->ipath_led_override_timeoff = timeoff;
/*
* If the timer has not already been started, do so. Use a "quick"
* timeout so the function will be called soon, to look at our request.
*/
if (atomic_inc_return(&dd->ipath_led_override_timer_active) == 1) {
/* Need to start timer */
init_timer(&dd->ipath_led_override_timer);
dd->ipath_led_override_timer.function =
ipath_run_led_override;
dd->ipath_led_override_timer.data = (unsigned long) dd;
dd->ipath_led_override_timer.expires = jiffies + 1;
add_timer(&dd->ipath_led_override_timer);
} else {
atomic_dec(&dd->ipath_led_override_timer_active);
}
}
/**
* ipath_shutdown_device - shut down a device
* @dd: the infinipath device
......@@ -1909,7 +1990,6 @@ void ipath_shutdown_device(struct ipath_devdata *dd)
* Turn the LEDs off explictly for the same reason.
*/
dd->ipath_f_quiet_serdes(dd);
dd->ipath_f_setextled(dd, 0, 0);
if (dd->ipath_stats_timer_active) {
del_timer_sync(&dd->ipath_stats_timer);
......@@ -2085,6 +2165,16 @@ int ipath_reset_device(int unit)
goto bail;
}
if (atomic_read(&dd->ipath_led_override_timer_active)) {
/* Need to stop LED timer, _then_ shut off LEDs */
del_timer_sync(&dd->ipath_led_override_timer);
atomic_set(&dd->ipath_led_override_timer_active, 0);
}
/* Shut off LEDs after we are sure timer is not running */
dd->ipath_led_override = LED_OVER_BOTH_OFF;
dd->ipath_f_setextled(dd, 0, 0);
dev_info(&dd->pcidev->dev, "Reset on unit %u requested\n", unit);
if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_PRESENT)) {
......
......@@ -1065,6 +1065,16 @@ static void ipath_setup_ht_setextled(struct ipath_devdata *dd,
if (ipath_diag_inuse)
return;
/* Allow override of LED display for, e.g. Locating system in rack */
if (dd->ipath_led_override) {
ltst = (dd->ipath_led_override & IPATH_LED_PHYS)
? INFINIPATH_IBCS_LT_STATE_LINKUP
: INFINIPATH_IBCS_LT_STATE_DISABLED;
lst = (dd->ipath_led_override & IPATH_LED_LOG)
? INFINIPATH_IBCS_L_STATE_ACTIVE
: INFINIPATH_IBCS_L_STATE_DOWN;
}
/*
* start by setting both LED control bits to off, then turn
* on the appropriate bit(s).
......
......@@ -797,6 +797,16 @@ static void ipath_setup_pe_setextled(struct ipath_devdata *dd, u64 lst,
if (ipath_diag_inuse)
return;
/* Allow override of LED display for, e.g. Locating system in rack */
if (dd->ipath_led_override) {
ltst = (dd->ipath_led_override & IPATH_LED_PHYS)
? INFINIPATH_IBCS_LT_STATE_LINKUP
: INFINIPATH_IBCS_LT_STATE_DISABLED;
lst = (dd->ipath_led_override & IPATH_LED_LOG)
? INFINIPATH_IBCS_L_STATE_ACTIVE
: INFINIPATH_IBCS_L_STATE_DOWN;
}
extctl = dd->ipath_extctrl & ~(INFINIPATH_EXTC_LED1PRIPORT_ON |
INFINIPATH_EXTC_LED2PRIPORT_ON);
......
......@@ -575,6 +575,16 @@ struct ipath_devdata {
u16 ipath_gpio_scl_num;
u64 ipath_gpio_sda;
u64 ipath_gpio_scl;
/* used to override LED behavior */
u8 ipath_led_override; /* Substituted for normal value, if non-zero */
u16 ipath_led_override_timeoff; /* delta to next timer event */
u8 ipath_led_override_vals[2]; /* Alternates per blink-frame */
u8 ipath_led_override_phase; /* Just counts, LSB picks from vals[] */
atomic_t ipath_led_override_timer_active;
/* Used to flash LEDs in override mode */
struct timer_list ipath_led_override_timer;
};
/* Private data for file operations */
......@@ -716,6 +726,15 @@ void ipath_get_eeprom_info(struct ipath_devdata *);
u64 ipath_snap_cntr(struct ipath_devdata *, ipath_creg);
void ipath_disarm_senderrbufs(struct ipath_devdata *, int);
/*
* Set LED override, only the two LSBs have "public" meaning, but
* any non-zero value substitutes them for the Link and LinkTrain
* LED states.
*/
#define IPATH_LED_PHYS 1 /* Physical (linktraining) GREEN LED */
#define IPATH_LED_LOG 2 /* Logical (link) YELLOW LED */
void ipath_set_led_override(struct ipath_devdata *dd, unsigned int val);
/*
* number of words used for protocol header if not set by ipath_userinit();
*/
......
......@@ -596,6 +596,23 @@ static ssize_t store_rx_pol_inv(struct device *dev,
return ret;
}
static ssize_t store_led_override(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct ipath_devdata *dd = dev_get_drvdata(dev);
int ret;
u16 val;
ret = ipath_parse_ushort(buf, &val);
if (ret > 0)
ipath_set_led_override(dd, val);
else
ipath_dev_err(dd, "attempt to set invalid LED override\n");
return ret;
}
static DRIVER_ATTR(num_units, S_IRUGO, show_num_units, NULL);
static DRIVER_ATTR(version, S_IRUGO, show_version, NULL);
......@@ -625,6 +642,7 @@ static DEVICE_ATTR(status_str, S_IRUGO, show_status_str, NULL);
static DEVICE_ATTR(boardversion, S_IRUGO, show_boardversion, NULL);
static DEVICE_ATTR(unit, S_IRUGO, show_unit, NULL);
static DEVICE_ATTR(rx_pol_inv, S_IWUSR, NULL, store_rx_pol_inv);
static DEVICE_ATTR(led_override, S_IWUSR, NULL, store_led_override);
static struct attribute *dev_attributes[] = {
&dev_attr_guid.attr,
......@@ -641,6 +659,7 @@ static struct attribute *dev_attributes[] = {
&dev_attr_unit.attr,
&dev_attr_enabled.attr,
&dev_attr_rx_pol_inv.attr,
&dev_attr_led_override.attr,
NULL
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment