scsi: qla2xxx: Add beacon LED config sysfs interface
This patch provides an interface to do the following (using MBC 0x3B): - Displays (in hex) the LED config words for all three LEDs. - Programs the config words for one LED or for all three LEDs. The sysfs node defined is named beacon_config. First, to allow driver to gain LED control, do this: # echo 1 > /sys/class/scsi_host/host#/beacon Then, to display config words for all three LEDs do this: # cat /sys/class/scsi_host/host#/beacon_config To set config words for all three LEDs do this: # echo 3 xxxx yyyy zzzz > /sys/class/scsi_host/host#/beacon_config Or, to set config word for a specific single LED n do this: # echo n xxxx > /sys/class/scsi_host/host#/beacon_config where n is the LED number (0, 1, 2) Finally, to restore LED control back to firmware, do this: # echo 0 > /sys/class/scsi_host/host#/beacon Link: https://lore.kernel.org/r/20200212214436.25532-2-hmadhani@marvell.com Signed-off-by: Joe Carnuccio <joe.carnuccio@cavium.com> Signed-off-by: Himanshu Madhani <hmadhani@marvell.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Родитель
1bf630fddd
Коммит
07553b1e83
|
@ -1323,6 +1323,79 @@ qla2x00_beacon_store(struct device *dev, struct device_attribute *attr,
|
|||
return count;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
qla2x00_beacon_config_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
|
||||
struct qla_hw_data *ha = vha->hw;
|
||||
uint16_t led[3] = { 0 };
|
||||
|
||||
if (!IS_QLA2031(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
|
||||
return -EPERM;
|
||||
|
||||
if (ql26xx_led_config(vha, 0, led))
|
||||
return scnprintf(buf, PAGE_SIZE, "\n");
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%#04hx %#04hx %#04hx\n",
|
||||
led[0], led[1], led[2]);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
qla2x00_beacon_config_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
|
||||
struct qla_hw_data *ha = vha->hw;
|
||||
uint16_t options = BIT_0;
|
||||
uint16_t led[3] = { 0 };
|
||||
uint16_t word[4];
|
||||
int n;
|
||||
|
||||
if (!IS_QLA2031(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
|
||||
return -EPERM;
|
||||
|
||||
n = sscanf(buf, "%hx %hx %hx %hx", word+0, word+1, word+2, word+3);
|
||||
if (n == 4) {
|
||||
if (word[0] == 3) {
|
||||
options |= BIT_3|BIT_2|BIT_1;
|
||||
led[0] = word[1];
|
||||
led[1] = word[2];
|
||||
led[2] = word[3];
|
||||
goto write;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (n == 2) {
|
||||
/* check led index */
|
||||
if (word[0] == 0) {
|
||||
options |= BIT_2;
|
||||
led[0] = word[1];
|
||||
goto write;
|
||||
}
|
||||
if (word[0] == 1) {
|
||||
options |= BIT_3;
|
||||
led[1] = word[1];
|
||||
goto write;
|
||||
}
|
||||
if (word[0] == 2) {
|
||||
options |= BIT_1;
|
||||
led[2] = word[1];
|
||||
goto write;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
|
||||
write:
|
||||
if (ql26xx_led_config(vha, options, led))
|
||||
return -EFAULT;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
qla2x00_optrom_bios_version_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
|
@ -2264,6 +2337,8 @@ static DEVICE_ATTR(zio_timer, S_IRUGO | S_IWUSR, qla2x00_zio_timer_show,
|
|||
qla2x00_zio_timer_store);
|
||||
static DEVICE_ATTR(beacon, S_IRUGO | S_IWUSR, qla2x00_beacon_show,
|
||||
qla2x00_beacon_store);
|
||||
static DEVICE_ATTR(beacon_config, 0644, qla2x00_beacon_config_show,
|
||||
qla2x00_beacon_config_store);
|
||||
static DEVICE_ATTR(optrom_bios_version, S_IRUGO,
|
||||
qla2x00_optrom_bios_version_show, NULL);
|
||||
static DEVICE_ATTR(optrom_efi_version, S_IRUGO,
|
||||
|
@ -2327,6 +2402,7 @@ struct device_attribute *qla2x00_host_attrs[] = {
|
|||
&dev_attr_zio,
|
||||
&dev_attr_zio_timer,
|
||||
&dev_attr_beacon,
|
||||
&dev_attr_beacon_config,
|
||||
&dev_attr_optrom_bios_version,
|
||||
&dev_attr_optrom_efi_version,
|
||||
&dev_attr_optrom_fcode_version,
|
||||
|
|
|
@ -1134,6 +1134,7 @@ static inline bool qla2xxx_is_valid_mbs(unsigned int mbs)
|
|||
#define MBC_GET_FIRMWARE_OPTION 0x28 /* Get Firmware Options. */
|
||||
#define MBC_GET_MEM_OFFLOAD_CNTRL_STAT 0x34 /* Memory Offload ctrl/Stat*/
|
||||
#define MBC_SET_FIRMWARE_OPTION 0x38 /* Set Firmware Options. */
|
||||
#define MBC_SET_GET_FC_LED_CONFIG 0x3b /* Set/Get FC LED config */
|
||||
#define MBC_LOOP_PORT_BYPASS 0x40 /* Loop Port Bypass. */
|
||||
#define MBC_LOOP_PORT_ENABLE 0x41 /* Loop Port Enable. */
|
||||
#define MBC_GET_RESOURCE_COUNTS 0x42 /* Get Resource Counts. */
|
||||
|
|
|
@ -844,6 +844,7 @@ extern void qla82xx_clear_pending_mbx(scsi_qla_host_t *);
|
|||
extern int qla82xx_read_temperature(scsi_qla_host_t *);
|
||||
extern int qla8044_read_temperature(scsi_qla_host_t *);
|
||||
extern int qla2x00_read_sfp_dev(struct scsi_qla_host *, char *, int);
|
||||
extern int ql26xx_led_config(scsi_qla_host_t *, uint16_t, uint16_t *);
|
||||
|
||||
/* BSG related functions */
|
||||
extern int qla24xx_bsg_request(struct bsg_job *);
|
||||
|
|
|
@ -6688,3 +6688,60 @@ int qla2xxx_read_remote_register(scsi_qla_host_t *vha, uint32_t addr,
|
|||
|
||||
return rval;
|
||||
}
|
||||
|
||||
int
|
||||
ql26xx_led_config(scsi_qla_host_t *vha, uint16_t options, uint16_t *led)
|
||||
{
|
||||
struct qla_hw_data *ha = vha->hw;
|
||||
mbx_cmd_t mc;
|
||||
mbx_cmd_t *mcp = &mc;
|
||||
int rval;
|
||||
|
||||
if (!IS_QLA2031(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
|
||||
return QLA_FUNCTION_FAILED;
|
||||
|
||||
ql_dbg(ql_dbg_mbx, vha, 0x7070, "Entered %s (options=%x).\n",
|
||||
__func__, options);
|
||||
|
||||
mcp->mb[0] = MBC_SET_GET_FC_LED_CONFIG;
|
||||
mcp->mb[1] = options;
|
||||
mcp->out_mb = MBX_1|MBX_0;
|
||||
mcp->in_mb = MBX_1|MBX_0;
|
||||
if (options & BIT_0) {
|
||||
if (options & BIT_1) {
|
||||
mcp->mb[2] = led[2];
|
||||
mcp->out_mb |= MBX_2;
|
||||
}
|
||||
if (options & BIT_2) {
|
||||
mcp->mb[3] = led[0];
|
||||
mcp->out_mb |= MBX_3;
|
||||
}
|
||||
if (options & BIT_3) {
|
||||
mcp->mb[4] = led[1];
|
||||
mcp->out_mb |= MBX_4;
|
||||
}
|
||||
} else {
|
||||
mcp->in_mb |= MBX_4|MBX_3|MBX_2;
|
||||
}
|
||||
mcp->tov = MBX_TOV_SECONDS;
|
||||
mcp->flags = 0;
|
||||
rval = qla2x00_mailbox_command(vha, mcp);
|
||||
if (rval) {
|
||||
ql_dbg(ql_dbg_mbx, vha, 0x7071, "Failed %s %x (mb=%x,%x)\n",
|
||||
__func__, rval, mcp->mb[0], mcp->mb[1]);
|
||||
return rval;
|
||||
}
|
||||
|
||||
if (options & BIT_0) {
|
||||
ha->beacon_blink_led = 0;
|
||||
ql_dbg(ql_dbg_mbx, vha, 0x7072, "Done %s\n", __func__);
|
||||
} else {
|
||||
led[2] = mcp->mb[2];
|
||||
led[0] = mcp->mb[3];
|
||||
led[1] = mcp->mb[4];
|
||||
ql_dbg(ql_dbg_mbx, vha, 0x7073, "Done %s (led=%x,%x,%x)\n",
|
||||
__func__, led[0], led[1], led[2]);
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче