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: Michael Albaugh <michael.albaugh@qlogic.com> Signed-off-by: Roland Dreier <rolandd@cisco.com>
This commit is contained in:
parent
a024291b36
commit
82466f00ec
5 changed files with 149 additions and 1 deletions
|
@ -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
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue