Blame src/npem.c

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
}