|
Packit Service |
db8df9 |
/*
|
|
Packit Service |
db8df9 |
* Intel(R) Enclosure LED Utilities
|
|
Packit Service |
db8df9 |
* Copyright (C) 2019-2020 Intel Corporation.
|
|
Packit Service |
db8df9 |
*
|
|
Packit Service |
db8df9 |
* This program is free software; you can redistribute it and/or modify it
|
|
Packit Service |
db8df9 |
* under the terms and conditions of the GNU General Public License,
|
|
Packit Service |
db8df9 |
* version 2, as published by the Free Software Foundation.
|
|
Packit Service |
db8df9 |
*
|
|
Packit Service |
db8df9 |
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
Packit Service |
db8df9 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
Packit Service |
db8df9 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
Packit Service |
db8df9 |
* more details.
|
|
Packit Service |
db8df9 |
*
|
|
Packit Service |
db8df9 |
* You should have received a copy of the GNU General Public License along with
|
|
Packit Service |
db8df9 |
* this program; if not, write to the Free Software Foundation, Inc.,
|
|
Packit Service |
db8df9 |
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
|
Packit Service |
db8df9 |
*
|
|
Packit Service |
db8df9 |
*/
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
#include <errno.h>
|
|
Packit Service |
db8df9 |
#include <stdio.h>
|
|
Packit Service |
db8df9 |
#include <string.h>
|
|
Packit Service |
db8df9 |
#include <pci/pci.h>
|
|
Packit Service |
db8df9 |
#include <time.h>
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
#include "config.h"
|
|
Packit Service |
db8df9 |
#include "cntrl.h"
|
|
Packit Service |
db8df9 |
#include "npem.h"
|
|
Packit Service |
db8df9 |
#include "utils.h"
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
#define PCI_EXT_CAP_ID_NPEM 0x29 /* Native PCIe Enclosure Management */
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
#define PCI_NPEM_CAP_REG 0x04 /* NPEM Capability Register */
|
|
Packit Service |
db8df9 |
#define PCI_NPEM_CTRL_REG 0x08 /* NPEM Control Register */
|
|
Packit Service |
db8df9 |
#define PCI_NPEM_STATUS_REG 0x0C /* NPEM Status Register */
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
/* NPEM Capable/Enable */
|
|
Packit Service |
db8df9 |
#define PCI_NPEM_CAP 0x001
|
|
Packit Service |
db8df9 |
/* NPEM OK Capable/Control */
|
|
Packit Service |
db8df9 |
#define PCI_NPEM_OK_CAP 0x004
|
|
Packit Service |
db8df9 |
/* NPEM Locate Capable/Control */
|
|
Packit Service |
db8df9 |
#define PCI_NPEM_LOCATE_CAP 0x008
|
|
Packit Service |
db8df9 |
/* NPEM Fail Capable/Control */
|
|
Packit Service |
db8df9 |
#define PCI_NPEM_FAIL_CAP 0x010
|
|
Packit Service |
db8df9 |
/* NPEM Rebuild Capable/Control */
|
|
Packit Service |
db8df9 |
#define PCI_NPEM_REBUILD_CAP 0x020
|
|
Packit Service |
db8df9 |
/* NPEM Predicted Failure Analysis Capable/Control */
|
|
Packit Service |
db8df9 |
#define PCI_NPEM_PFA_CAP 0x040
|
|
Packit Service |
db8df9 |
/* NPEM Hot Spare Capable/Control */
|
|
Packit Service |
db8df9 |
#define PCI_NPEM_HOT_SPARE_CAP 0x080
|
|
Packit Service |
db8df9 |
/* NPEM in a Critical Array Capable/Control */
|
|
Packit Service |
db8df9 |
#define PCI_NPEM_CRA_CAP 0x100
|
|
Packit Service |
db8df9 |
/* NPEM in a Failed Array Capable/Control */
|
|
Packit Service |
db8df9 |
#define PCI_NPEM_FA_CAP 0x200
|
|
Packit Service |
db8df9 |
/* NPEM reserved and enclosure specific */
|
|
Packit Service |
db8df9 |
#define PCI_NPEM_RESERVED ~0xfff
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
#define PCI_NPEM_STATUS_CC 0x01 /* NPEM Command Completed */
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
const int ibpi_to_npem_capability[] = {
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_NORMAL] = PCI_NPEM_OK_CAP,
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_ONESHOT_NORMAL] = PCI_NPEM_OK_CAP,
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_DEGRADED] = PCI_NPEM_CRA_CAP,
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_HOTSPARE] = PCI_NPEM_HOT_SPARE_CAP,
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_REBUILD] = PCI_NPEM_REBUILD_CAP,
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_FAILED_ARRAY] = PCI_NPEM_FA_CAP,
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_PFA] = PCI_NPEM_PFA_CAP,
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_FAILED_DRIVE] = PCI_NPEM_FAIL_CAP,
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_LOCATE] = PCI_NPEM_LOCATE_CAP,
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_LOCATE_OFF] = PCI_NPEM_OK_CAP,
|
|
Packit Service |
db8df9 |
};
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
static struct pci_access *get_pci_access()
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
struct pci_access *pacc;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
pacc = pci_alloc();
|
|
Packit Service |
db8df9 |
pci_init(pacc);
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
return pacc;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
static struct pci_dev *get_pci_dev(struct pci_access *pacc, const char *path)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
unsigned int domain, bus, dev, fn;
|
|
Packit Service |
db8df9 |
char *p = strrchr(path, '/');
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
if (!p)
|
|
Packit Service |
db8df9 |
return NULL;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
if (sscanf(p + 1, "%x:%x:%x.%x", &domain, &bus, &dev, &fn) != 4)
|
|
Packit Service |
db8df9 |
return NULL;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
return pci_get_dev(pacc, domain, bus, dev, fn);
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
static struct pci_cap *get_npem_cap(struct pci_dev *pdev)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
return pci_find_cap(pdev, PCI_EXT_CAP_ID_NPEM, PCI_CAP_EXTENDED);
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
static u32 read_npem_register(struct pci_dev *pdev, int reg)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
u32 val = 0;
|
|
Packit Service |
db8df9 |
struct pci_cap *pcap = get_npem_cap(pdev);
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
if (!pcap)
|
|
Packit Service |
db8df9 |
return val;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
return pci_read_long(pdev, pcap->addr + reg);
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
static int write_npem_register(struct pci_dev *pdev, int reg, u32 val)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
struct pci_cap *pcap = get_npem_cap(pdev);
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
if (!pcap)
|
|
Packit Service |
db8df9 |
return val;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
return pci_write_long(pdev, pcap->addr + reg, val);
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
int is_npem_capable(const char *path)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
u8 val;
|
|
Packit Service |
db8df9 |
struct pci_access *pacc = get_pci_access();
|
|
Packit Service |
db8df9 |
struct pci_dev *pdev;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
if (!pacc)
|
|
Packit Service |
db8df9 |
return 0;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
pdev = get_pci_dev(pacc, path);
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
if (!pdev) {
|
|
Packit Service |
db8df9 |
pci_cleanup(pacc);
|
|
Packit Service |
db8df9 |
return 0;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
val = read_npem_register(pdev, PCI_NPEM_CAP_REG);
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
pci_free_dev(pdev);
|
|
Packit Service |
db8df9 |
pci_cleanup(pacc);
|
|
Packit Service |
db8df9 |
return (val & PCI_NPEM_CAP);
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
static int npem_wait_command(struct pci_dev *pdev)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
/*
|
|
Packit Service |
db8df9 |
* Software must wait for an NPEM command to complete before issuing
|
|
Packit Service |
db8df9 |
* the next NPEM command. However, if this bit is not set within
|
|
Packit Service |
db8df9 |
* 1 second limit on command execution, software is permitted to repeat
|
|
Packit Service |
db8df9 |
* the NPEM command or issue the next NPEM command.
|
|
Packit Service |
db8df9 |
* PCIe r4.0 sec 7.9.20.4
|
|
Packit Service |
db8df9 |
*
|
|
Packit Service |
db8df9 |
* Poll the status register until the Command Completed bit becomes set
|
|
Packit Service |
db8df9 |
* or timeout is reached.
|
|
Packit Service |
db8df9 |
*/
|
|
Packit Service |
db8df9 |
time_t start, end;
|
|
Packit Service |
db8df9 |
u32 reg;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
time(&start;;
|
|
Packit Service |
db8df9 |
end = start;
|
|
Packit Service |
db8df9 |
while (difftime(start, end) < 1) {
|
|
Packit Service |
db8df9 |
reg = read_npem_register(pdev, PCI_NPEM_STATUS_REG);
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
if (reg & PCI_NPEM_STATUS_CC) {
|
|
Packit Service |
db8df9 |
/* status register type is RW1C */
|
|
Packit Service |
db8df9 |
write_npem_register(pdev, PCI_NPEM_STATUS_REG,
|
|
Packit Service |
db8df9 |
PCI_NPEM_STATUS_CC);
|
|
Packit Service |
db8df9 |
return 0;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
time(&end;;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
return 1;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
int npem_write(struct block_device *device, enum ibpi_pattern ibpi)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
struct cntrl_device *npem_cntrl = device->cntrl;
|
|
Packit Service |
db8df9 |
struct pci_access *pacc = NULL;
|
|
Packit Service |
db8df9 |
struct pci_dev *pdev = NULL;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
u32 reg;
|
|
Packit Service |
db8df9 |
u32 val;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
int err = 0;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
if (ibpi == device->ibpi_prev)
|
|
Packit Service |
db8df9 |
return 0;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
if ((ibpi < IBPI_PATTERN_NORMAL) || (ibpi > IBPI_PATTERN_LOCATE_OFF)) {
|
|
Packit Service |
db8df9 |
err = -EINVAL;
|
|
Packit Service |
db8df9 |
goto exit;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
pacc = get_pci_access();
|
|
Packit Service |
db8df9 |
if (!pacc) {
|
|
Packit Service |
db8df9 |
log_error("NPEM: Unable to initialize pci access for %s\n",
|
|
Packit Service |
db8df9 |
npem_cntrl->sysfs_path);
|
|
Packit Service |
db8df9 |
err = -ENOMEM;
|
|
Packit Service |
db8df9 |
goto exit;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
pdev = get_pci_dev(pacc, npem_cntrl->sysfs_path);
|
|
Packit Service |
db8df9 |
if (!pdev) {
|
|
Packit Service |
db8df9 |
log_error("NPEM: Unable to get pci device for %s\n",
|
|
Packit Service |
db8df9 |
npem_cntrl->sysfs_path);
|
|
Packit Service |
db8df9 |
err = -ENXIO;
|
|
Packit Service |
db8df9 |
goto exit;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
reg = read_npem_register(pdev, PCI_NPEM_CAP_REG);
|
|
Packit Service |
db8df9 |
if ((reg & ibpi_to_npem_capability[ibpi]) == 0) {
|
|
Packit Service |
db8df9 |
log_debug("NPEM: Controller %s doesn't support %s pattern\n",
|
|
Packit Service |
db8df9 |
npem_cntrl->sysfs_path, ibpi_str[ibpi]);
|
|
Packit Service |
db8df9 |
ibpi = IBPI_PATTERN_NORMAL;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
reg = read_npem_register(pdev, PCI_NPEM_CTRL_REG);
|
|
Packit Service |
db8df9 |
val = (reg & PCI_NPEM_RESERVED);
|
|
Packit Service |
db8df9 |
val = (val | PCI_NPEM_CAP | ibpi_to_npem_capability[ibpi]);
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
write_npem_register(pdev, PCI_NPEM_CTRL_REG, val);
|
|
Packit Service |
db8df9 |
if (npem_wait_command(pdev)) {
|
|
Packit Service |
db8df9 |
log_error("NPEM: Write timeout for %s\n",
|
|
Packit Service |
db8df9 |
npem_cntrl->sysfs_path);
|
|
Packit Service |
db8df9 |
err = -EAGAIN;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
exit:
|
|
Packit Service |
db8df9 |
if (pdev)
|
|
Packit Service |
db8df9 |
pci_free_dev(pdev);
|
|
Packit Service |
db8df9 |
if (pacc)
|
|
Packit Service |
db8df9 |
pci_cleanup(pacc);
|
|
Packit Service |
db8df9 |
return err;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
char *npem_get_path(const char *cntrl_path)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
return str_dup(cntrl_path);
|
|
Packit Service |
db8df9 |
}
|