mirror of
https://github.com/FEX-Emu/linux.git
synced 2025-01-07 10:03:24 +00:00
Merge branch 'pci/vc' into next
* pci/vc: PCI: Rename PCI_VC_PORT_REG1/2 to PCI_VC_PORT_CAP1/2 PCI: Add Virtual Channel to save/restore support PCI: Add support for save/restore of extended capabilities PCI: Add pci_wait_for_pending() (refactor pci_wait_for_pending_transaction())
This commit is contained in:
commit
608235a307
@ -4,7 +4,7 @@
|
||||
|
||||
obj-y += access.o bus.o probe.o host-bridge.o remove.o pci.o \
|
||||
pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \
|
||||
irq.o vpd.o setup-bus.o
|
||||
irq.o vpd.o setup-bus.o vc.o
|
||||
obj-$(CONFIG_PROC_FS) += proc.o
|
||||
obj-$(CONFIG_SYSFS) += slot.o
|
||||
|
||||
|
@ -430,6 +430,32 @@ pci_find_parent_resource(const struct pci_dev *dev, struct resource *res)
|
||||
return best;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_wait_for_pending - wait for @mask bit(s) to clear in status word @pos
|
||||
* @dev: the PCI device to operate on
|
||||
* @pos: config space offset of status word
|
||||
* @mask: mask of bit(s) to care about in status word
|
||||
*
|
||||
* Return 1 when mask bit(s) in status word clear, 0 otherwise.
|
||||
*/
|
||||
int pci_wait_for_pending(struct pci_dev *dev, int pos, u16 mask)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Wait for Transaction Pending bit clean */
|
||||
for (i = 0; i < 4; i++) {
|
||||
u16 status;
|
||||
if (i)
|
||||
msleep((1 << (i - 1)) * 100);
|
||||
|
||||
pci_read_config_word(dev, pos, &status);
|
||||
if (!(status & mask))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_restore_bars - restore a devices BAR values (e.g. after wake-up)
|
||||
* @dev: PCI device to have its BARs restored
|
||||
@ -835,18 +861,28 @@ EXPORT_SYMBOL(pci_choose_state);
|
||||
#define PCI_EXP_SAVE_REGS 7
|
||||
|
||||
|
||||
static struct pci_cap_saved_state *pci_find_saved_cap(
|
||||
struct pci_dev *pci_dev, char cap)
|
||||
static struct pci_cap_saved_state *_pci_find_saved_cap(struct pci_dev *pci_dev,
|
||||
u16 cap, bool extended)
|
||||
{
|
||||
struct pci_cap_saved_state *tmp;
|
||||
|
||||
hlist_for_each_entry(tmp, &pci_dev->saved_cap_space, next) {
|
||||
if (tmp->cap.cap_nr == cap)
|
||||
if (tmp->cap.cap_extended == extended && tmp->cap.cap_nr == cap)
|
||||
return tmp;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct pci_cap_saved_state *pci_find_saved_cap(struct pci_dev *dev, char cap)
|
||||
{
|
||||
return _pci_find_saved_cap(dev, cap, false);
|
||||
}
|
||||
|
||||
struct pci_cap_saved_state *pci_find_saved_ext_cap(struct pci_dev *dev, u16 cap)
|
||||
{
|
||||
return _pci_find_saved_cap(dev, cap, true);
|
||||
}
|
||||
|
||||
static int pci_save_pcie_state(struct pci_dev *dev)
|
||||
{
|
||||
int i = 0;
|
||||
@ -948,6 +984,8 @@ pci_save_state(struct pci_dev *dev)
|
||||
return i;
|
||||
if ((i = pci_save_pcix_state(dev)) != 0)
|
||||
return i;
|
||||
if ((i = pci_save_vc_state(dev)) != 0)
|
||||
return i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1010,6 +1048,7 @@ void pci_restore_state(struct pci_dev *dev)
|
||||
/* PCI Express register must be restored first */
|
||||
pci_restore_pcie_state(dev);
|
||||
pci_restore_ats_state(dev);
|
||||
pci_restore_vc_state(dev);
|
||||
|
||||
pci_restore_config_space(dev);
|
||||
|
||||
@ -1087,7 +1126,7 @@ int pci_load_saved_state(struct pci_dev *dev, struct pci_saved_state *state)
|
||||
while (cap->size) {
|
||||
struct pci_cap_saved_state *tmp;
|
||||
|
||||
tmp = pci_find_saved_cap(dev, cap->cap_nr);
|
||||
tmp = _pci_find_saved_cap(dev, cap->cap_nr, cap->cap_extended);
|
||||
if (!tmp || tmp->cap.size != cap->size)
|
||||
return -EINVAL;
|
||||
|
||||
@ -2021,18 +2060,24 @@ static void pci_add_saved_cap(struct pci_dev *pci_dev,
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_add_cap_save_buffer - allocate buffer for saving given capability registers
|
||||
* _pci_add_cap_save_buffer - allocate buffer for saving given
|
||||
* capability registers
|
||||
* @dev: the PCI device
|
||||
* @cap: the capability to allocate the buffer for
|
||||
* @extended: Standard or Extended capability ID
|
||||
* @size: requested size of the buffer
|
||||
*/
|
||||
static int pci_add_cap_save_buffer(
|
||||
struct pci_dev *dev, char cap, unsigned int size)
|
||||
static int _pci_add_cap_save_buffer(struct pci_dev *dev, u16 cap,
|
||||
bool extended, unsigned int size)
|
||||
{
|
||||
int pos;
|
||||
struct pci_cap_saved_state *save_state;
|
||||
|
||||
pos = pci_find_capability(dev, cap);
|
||||
if (extended)
|
||||
pos = pci_find_ext_capability(dev, cap);
|
||||
else
|
||||
pos = pci_find_capability(dev, cap);
|
||||
|
||||
if (pos <= 0)
|
||||
return 0;
|
||||
|
||||
@ -2041,12 +2086,23 @@ static int pci_add_cap_save_buffer(
|
||||
return -ENOMEM;
|
||||
|
||||
save_state->cap.cap_nr = cap;
|
||||
save_state->cap.cap_extended = extended;
|
||||
save_state->cap.size = size;
|
||||
pci_add_saved_cap(dev, save_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pci_add_cap_save_buffer(struct pci_dev *dev, char cap, unsigned int size)
|
||||
{
|
||||
return _pci_add_cap_save_buffer(dev, cap, false, size);
|
||||
}
|
||||
|
||||
int pci_add_ext_cap_save_buffer(struct pci_dev *dev, u16 cap, unsigned int size)
|
||||
{
|
||||
return _pci_add_cap_save_buffer(dev, cap, true, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_allocate_cap_save_buffers - allocate buffers for saving capabilities
|
||||
* @dev: the PCI device
|
||||
@ -2065,6 +2121,8 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev)
|
||||
if (error)
|
||||
dev_err(&dev->dev,
|
||||
"unable to preallocate PCI-X save buffer\n");
|
||||
|
||||
pci_allocate_vc_save_buffers(dev);
|
||||
}
|
||||
|
||||
void pci_free_cap_save_buffers(struct pci_dev *dev)
|
||||
@ -3204,20 +3262,10 @@ EXPORT_SYMBOL(pci_set_dma_seg_boundary);
|
||||
*/
|
||||
int pci_wait_for_pending_transaction(struct pci_dev *dev)
|
||||
{
|
||||
int i;
|
||||
u16 status;
|
||||
if (!pci_is_pcie(dev))
|
||||
return 1;
|
||||
|
||||
/* Wait for Transaction Pending bit clean */
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (i)
|
||||
msleep((1 << (i - 1)) * 100);
|
||||
|
||||
pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &status);
|
||||
if (!(status & PCI_EXP_DEVSTA_TRPND))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return pci_wait_for_pending(dev, PCI_EXP_DEVSTA, PCI_EXP_DEVSTA_TRPND);
|
||||
}
|
||||
EXPORT_SYMBOL(pci_wait_for_pending_transaction);
|
||||
|
||||
@ -3244,10 +3292,8 @@ static int pcie_flr(struct pci_dev *dev, int probe)
|
||||
|
||||
static int pci_af_flr(struct pci_dev *dev, int probe)
|
||||
{
|
||||
int i;
|
||||
int pos;
|
||||
u8 cap;
|
||||
u8 status;
|
||||
|
||||
pos = pci_find_capability(dev, PCI_CAP_ID_AF);
|
||||
if (!pos)
|
||||
@ -3261,14 +3307,8 @@ static int pci_af_flr(struct pci_dev *dev, int probe)
|
||||
return 0;
|
||||
|
||||
/* Wait for Transaction Pending bit clean */
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (i)
|
||||
msleep((1 << (i - 1)) * 100);
|
||||
|
||||
pci_read_config_byte(dev, pos + PCI_AF_STATUS, &status);
|
||||
if (!(status & PCI_AF_STATUS_TP))
|
||||
goto clear;
|
||||
}
|
||||
if (pci_wait_for_pending(dev, PCI_AF_STATUS, PCI_AF_STATUS_TP))
|
||||
goto clear;
|
||||
|
||||
dev_err(&dev->dev, "transaction is not cleared; "
|
||||
"proceeding with reset anyway\n");
|
||||
|
434
drivers/pci/vc.c
Normal file
434
drivers/pci/vc.c
Normal file
@ -0,0 +1,434 @@
|
||||
/*
|
||||
* PCI Virtual Channel support
|
||||
*
|
||||
* Copyright (C) 2013 Red Hat, Inc. All rights reserved.
|
||||
* Author: Alex Williamson <alex.williamson@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_regs.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/**
|
||||
* pci_vc_save_restore_dwords - Save or restore a series of dwords
|
||||
* @dev: device
|
||||
* @pos: starting config space position
|
||||
* @buf: buffer to save to or restore from
|
||||
* @dwords: number of dwords to save/restore
|
||||
* @save: whether to save or restore
|
||||
*/
|
||||
static void pci_vc_save_restore_dwords(struct pci_dev *dev, int pos,
|
||||
u32 *buf, int dwords, bool save)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dwords; i++, buf++) {
|
||||
if (save)
|
||||
pci_read_config_dword(dev, pos + (i * 4), buf);
|
||||
else
|
||||
pci_write_config_dword(dev, pos + (i * 4), *buf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_vc_load_arb_table - load and wait for VC arbitration table
|
||||
* @dev: device
|
||||
* @pos: starting position of VC capability (VC/VC9/MFVC)
|
||||
*
|
||||
* Set Load VC Arbitration Table bit requesting hardware to apply the VC
|
||||
* Arbitration Table (previously loaded). When the VC Arbitration Table
|
||||
* Status clears, hardware has latched the table into VC arbitration logic.
|
||||
*/
|
||||
static void pci_vc_load_arb_table(struct pci_dev *dev, int pos)
|
||||
{
|
||||
u16 ctrl;
|
||||
|
||||
pci_read_config_word(dev, pos + PCI_VC_PORT_CTRL, &ctrl);
|
||||
pci_write_config_word(dev, pos + PCI_VC_PORT_CTRL,
|
||||
ctrl | PCI_VC_PORT_CTRL_LOAD_TABLE);
|
||||
if (pci_wait_for_pending(dev, pos + PCI_VC_PORT_STATUS,
|
||||
PCI_VC_PORT_STATUS_TABLE))
|
||||
return;
|
||||
|
||||
dev_err(&dev->dev, "VC arbitration table failed to load\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_vc_load_port_arb_table - Load and wait for VC port arbitration table
|
||||
* @dev: device
|
||||
* @pos: starting position of VC capability (VC/VC9/MFVC)
|
||||
* @res: VC resource number, ie. VCn (0-7)
|
||||
*
|
||||
* Set Load Port Arbitration Table bit requesting hardware to apply the Port
|
||||
* Arbitration Table (previously loaded). When the Port Arbitration Table
|
||||
* Status clears, hardware has latched the table into port arbitration logic.
|
||||
*/
|
||||
static void pci_vc_load_port_arb_table(struct pci_dev *dev, int pos, int res)
|
||||
{
|
||||
int ctrl_pos, status_pos;
|
||||
u32 ctrl;
|
||||
|
||||
ctrl_pos = pos + PCI_VC_RES_CTRL + (res * PCI_CAP_VC_PER_VC_SIZEOF);
|
||||
status_pos = pos + PCI_VC_RES_STATUS + (res * PCI_CAP_VC_PER_VC_SIZEOF);
|
||||
|
||||
pci_read_config_dword(dev, ctrl_pos, &ctrl);
|
||||
pci_write_config_dword(dev, ctrl_pos,
|
||||
ctrl | PCI_VC_RES_CTRL_LOAD_TABLE);
|
||||
|
||||
if (pci_wait_for_pending(dev, status_pos, PCI_VC_RES_STATUS_TABLE))
|
||||
return;
|
||||
|
||||
dev_err(&dev->dev, "VC%d port arbitration table failed to load\n", res);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_vc_enable - Enable virtual channel
|
||||
* @dev: device
|
||||
* @pos: starting position of VC capability (VC/VC9/MFVC)
|
||||
* @res: VC res number, ie. VCn (0-7)
|
||||
*
|
||||
* A VC is enabled by setting the enable bit in matching resource control
|
||||
* registers on both sides of a link. We therefore need to find the opposite
|
||||
* end of the link. To keep this simple we enable from the downstream device.
|
||||
* RC devices do not have an upstream device, nor does it seem that VC9 do
|
||||
* (spec is unclear). Once we find the upstream device, match the VC ID to
|
||||
* get the correct resource, disable and enable on both ends.
|
||||
*/
|
||||
static void pci_vc_enable(struct pci_dev *dev, int pos, int res)
|
||||
{
|
||||
int ctrl_pos, status_pos, id, pos2, evcc, i, ctrl_pos2, status_pos2;
|
||||
u32 ctrl, header, cap1, ctrl2;
|
||||
struct pci_dev *link = NULL;
|
||||
|
||||
/* Enable VCs from the downstream device */
|
||||
if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
|
||||
pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM)
|
||||
return;
|
||||
|
||||
ctrl_pos = pos + PCI_VC_RES_CTRL + (res * PCI_CAP_VC_PER_VC_SIZEOF);
|
||||
status_pos = pos + PCI_VC_RES_STATUS + (res * PCI_CAP_VC_PER_VC_SIZEOF);
|
||||
|
||||
pci_read_config_dword(dev, ctrl_pos, &ctrl);
|
||||
id = ctrl & PCI_VC_RES_CTRL_ID;
|
||||
|
||||
pci_read_config_dword(dev, pos, &header);
|
||||
|
||||
/* If there is no opposite end of the link, skip to enable */
|
||||
if (PCI_EXT_CAP_ID(header) == PCI_EXT_CAP_ID_VC9 ||
|
||||
pci_is_root_bus(dev->bus))
|
||||
goto enable;
|
||||
|
||||
pos2 = pci_find_ext_capability(dev->bus->self, PCI_EXT_CAP_ID_VC);
|
||||
if (!pos2)
|
||||
goto enable;
|
||||
|
||||
pci_read_config_dword(dev->bus->self, pos2 + PCI_VC_PORT_CAP1, &cap1);
|
||||
evcc = cap1 & PCI_VC_CAP1_EVCC;
|
||||
|
||||
/* VC0 is hardwired enabled, so we can start with 1 */
|
||||
for (i = 1; i < evcc + 1; i++) {
|
||||
ctrl_pos2 = pos2 + PCI_VC_RES_CTRL +
|
||||
(i * PCI_CAP_VC_PER_VC_SIZEOF);
|
||||
status_pos2 = pos2 + PCI_VC_RES_STATUS +
|
||||
(i * PCI_CAP_VC_PER_VC_SIZEOF);
|
||||
pci_read_config_dword(dev->bus->self, ctrl_pos2, &ctrl2);
|
||||
if ((ctrl2 & PCI_VC_RES_CTRL_ID) == id) {
|
||||
link = dev->bus->self;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!link)
|
||||
goto enable;
|
||||
|
||||
/* Disable if enabled */
|
||||
if (ctrl2 & PCI_VC_RES_CTRL_ENABLE) {
|
||||
ctrl2 &= ~PCI_VC_RES_CTRL_ENABLE;
|
||||
pci_write_config_dword(link, ctrl_pos2, ctrl2);
|
||||
}
|
||||
|
||||
/* Enable on both ends */
|
||||
ctrl2 |= PCI_VC_RES_CTRL_ENABLE;
|
||||
pci_write_config_dword(link, ctrl_pos2, ctrl2);
|
||||
enable:
|
||||
ctrl |= PCI_VC_RES_CTRL_ENABLE;
|
||||
pci_write_config_dword(dev, ctrl_pos, ctrl);
|
||||
|
||||
if (!pci_wait_for_pending(dev, status_pos, PCI_VC_RES_STATUS_NEGO))
|
||||
dev_err(&dev->dev, "VC%d negotiation stuck pending\n", id);
|
||||
|
||||
if (link && !pci_wait_for_pending(link, status_pos2,
|
||||
PCI_VC_RES_STATUS_NEGO))
|
||||
dev_err(&link->dev, "VC%d negotiation stuck pending\n", id);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_vc_do_save_buffer - Size, save, or restore VC state
|
||||
* @dev: device
|
||||
* @pos: starting position of VC capability (VC/VC9/MFVC)
|
||||
* @save_state: buffer for save/restore
|
||||
* @name: for error message
|
||||
* @save: if provided a buffer, this indicates what to do with it
|
||||
*
|
||||
* Walking Virtual Channel config space to size, save, or restore it
|
||||
* is complicated, so we do it all from one function to reduce code and
|
||||
* guarantee ordering matches in the buffer. When called with NULL
|
||||
* @save_state, return the size of the necessary save buffer. When called
|
||||
* with a non-NULL @save_state, @save determines whether we save to the
|
||||
* buffer or restore from it.
|
||||
*/
|
||||
static int pci_vc_do_save_buffer(struct pci_dev *dev, int pos,
|
||||
struct pci_cap_saved_state *save_state,
|
||||
bool save)
|
||||
{
|
||||
u32 cap1;
|
||||
char evcc, lpevcc, parb_size;
|
||||
int i, len = 0;
|
||||
u8 *buf = save_state ? (u8 *)save_state->cap.data : NULL;
|
||||
|
||||
/* Sanity check buffer size for save/restore */
|
||||
if (buf && save_state->cap.size !=
|
||||
pci_vc_do_save_buffer(dev, pos, NULL, save)) {
|
||||
dev_err(&dev->dev,
|
||||
"VC save buffer size does not match @0x%x\n", pos);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pci_read_config_dword(dev, pos + PCI_VC_PORT_CAP1, &cap1);
|
||||
/* Extended VC Count (not counting VC0) */
|
||||
evcc = cap1 & PCI_VC_CAP1_EVCC;
|
||||
/* Low Priority Extended VC Count (not counting VC0) */
|
||||
lpevcc = (cap1 & PCI_VC_CAP1_LPEVCC) >> 4;
|
||||
/* Port Arbitration Table Entry Size (bits) */
|
||||
parb_size = 1 << ((cap1 & PCI_VC_CAP1_ARB_SIZE) >> 10);
|
||||
|
||||
/*
|
||||
* Port VC Control Register contains VC Arbitration Select, which
|
||||
* cannot be modified when more than one LPVC is in operation. We
|
||||
* therefore save/restore it first, as only VC0 should be enabled
|
||||
* after device reset.
|
||||
*/
|
||||
if (buf) {
|
||||
if (save)
|
||||
pci_read_config_word(dev, pos + PCI_VC_PORT_CTRL,
|
||||
(u16 *)buf);
|
||||
else
|
||||
pci_write_config_word(dev, pos + PCI_VC_PORT_CTRL,
|
||||
*(u16 *)buf);
|
||||
buf += 2;
|
||||
}
|
||||
len += 2;
|
||||
|
||||
/*
|
||||
* If we have any Low Priority VCs and a VC Arbitration Table Offset
|
||||
* in Port VC Capability Register 2 then save/restore it next.
|
||||
*/
|
||||
if (lpevcc) {
|
||||
u32 cap2;
|
||||
int vcarb_offset;
|
||||
|
||||
pci_read_config_dword(dev, pos + PCI_VC_PORT_CAP2, &cap2);
|
||||
vcarb_offset = ((cap2 & PCI_VC_CAP2_ARB_OFF) >> 24) * 16;
|
||||
|
||||
if (vcarb_offset) {
|
||||
int size, vcarb_phases = 0;
|
||||
|
||||
if (cap2 & PCI_VC_CAP2_128_PHASE)
|
||||
vcarb_phases = 128;
|
||||
else if (cap2 & PCI_VC_CAP2_64_PHASE)
|
||||
vcarb_phases = 64;
|
||||
else if (cap2 & PCI_VC_CAP2_32_PHASE)
|
||||
vcarb_phases = 32;
|
||||
|
||||
/* Fixed 4 bits per phase per lpevcc (plus VC0) */
|
||||
size = ((lpevcc + 1) * vcarb_phases * 4) / 8;
|
||||
|
||||
if (size && buf) {
|
||||
pci_vc_save_restore_dwords(dev,
|
||||
pos + vcarb_offset,
|
||||
(u32 *)buf,
|
||||
size / 4, save);
|
||||
/*
|
||||
* On restore, we need to signal hardware to
|
||||
* re-load the VC Arbitration Table.
|
||||
*/
|
||||
if (!save)
|
||||
pci_vc_load_arb_table(dev, pos);
|
||||
|
||||
buf += size;
|
||||
}
|
||||
len += size;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* In addition to each VC Resource Control Register, we may have a
|
||||
* Port Arbitration Table attached to each VC. The Port Arbitration
|
||||
* Table Offset in each VC Resource Capability Register tells us if
|
||||
* it exists. The entry size is global from the Port VC Capability
|
||||
* Register1 above. The number of phases is determined per VC.
|
||||
*/
|
||||
for (i = 0; i < evcc + 1; i++) {
|
||||
u32 cap;
|
||||
int parb_offset;
|
||||
|
||||
pci_read_config_dword(dev, pos + PCI_VC_RES_CAP +
|
||||
(i * PCI_CAP_VC_PER_VC_SIZEOF), &cap);
|
||||
parb_offset = ((cap & PCI_VC_RES_CAP_ARB_OFF) >> 24) * 16;
|
||||
if (parb_offset) {
|
||||
int size, parb_phases = 0;
|
||||
|
||||
if (cap & PCI_VC_RES_CAP_256_PHASE)
|
||||
parb_phases = 256;
|
||||
else if (cap & (PCI_VC_RES_CAP_128_PHASE |
|
||||
PCI_VC_RES_CAP_128_PHASE_TB))
|
||||
parb_phases = 128;
|
||||
else if (cap & PCI_VC_RES_CAP_64_PHASE)
|
||||
parb_phases = 64;
|
||||
else if (cap & PCI_VC_RES_CAP_32_PHASE)
|
||||
parb_phases = 32;
|
||||
|
||||
size = (parb_size * parb_phases) / 8;
|
||||
|
||||
if (size && buf) {
|
||||
pci_vc_save_restore_dwords(dev,
|
||||
pos + parb_offset,
|
||||
(u32 *)buf,
|
||||
size / 4, save);
|
||||
buf += size;
|
||||
}
|
||||
len += size;
|
||||
}
|
||||
|
||||
/* VC Resource Control Register */
|
||||
if (buf) {
|
||||
int ctrl_pos = pos + PCI_VC_RES_CTRL +
|
||||
(i * PCI_CAP_VC_PER_VC_SIZEOF);
|
||||
if (save)
|
||||
pci_read_config_dword(dev, ctrl_pos,
|
||||
(u32 *)buf);
|
||||
else {
|
||||
u32 tmp, ctrl = *(u32 *)buf;
|
||||
/*
|
||||
* For an FLR case, the VC config may remain.
|
||||
* Preserve enable bit, restore the rest.
|
||||
*/
|
||||
pci_read_config_dword(dev, ctrl_pos, &tmp);
|
||||
tmp &= PCI_VC_RES_CTRL_ENABLE;
|
||||
tmp |= ctrl & ~PCI_VC_RES_CTRL_ENABLE;
|
||||
pci_write_config_dword(dev, ctrl_pos, tmp);
|
||||
/* Load port arbitration table if used */
|
||||
if (ctrl & PCI_VC_RES_CTRL_ARB_SELECT)
|
||||
pci_vc_load_port_arb_table(dev, pos, i);
|
||||
/* Re-enable if needed */
|
||||
if ((ctrl ^ tmp) & PCI_VC_RES_CTRL_ENABLE)
|
||||
pci_vc_enable(dev, pos, i);
|
||||
}
|
||||
buf += 4;
|
||||
}
|
||||
len += 4;
|
||||
}
|
||||
|
||||
return buf ? 0 : len;
|
||||
}
|
||||
|
||||
static struct {
|
||||
u16 id;
|
||||
const char *name;
|
||||
} vc_caps[] = { { PCI_EXT_CAP_ID_MFVC, "MFVC" },
|
||||
{ PCI_EXT_CAP_ID_VC, "VC" },
|
||||
{ PCI_EXT_CAP_ID_VC9, "VC9" } };
|
||||
|
||||
/**
|
||||
* pci_save_vc_state - Save VC state to pre-allocate save buffer
|
||||
* @dev: device
|
||||
*
|
||||
* For each type of VC capability, VC/VC9/MFVC, find the capability and
|
||||
* save it to the pre-allocated save buffer.
|
||||
*/
|
||||
int pci_save_vc_state(struct pci_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(vc_caps); i++) {
|
||||
int pos, ret;
|
||||
struct pci_cap_saved_state *save_state;
|
||||
|
||||
pos = pci_find_ext_capability(dev, vc_caps[i].id);
|
||||
if (!pos)
|
||||
continue;
|
||||
|
||||
save_state = pci_find_saved_ext_cap(dev, vc_caps[i].id);
|
||||
if (!save_state) {
|
||||
dev_err(&dev->dev, "%s buffer not found in %s\n",
|
||||
vc_caps[i].name, __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = pci_vc_do_save_buffer(dev, pos, save_state, true);
|
||||
if (ret) {
|
||||
dev_err(&dev->dev, "%s save unsuccessful %s\n",
|
||||
vc_caps[i].name, __func__);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_restore_vc_state - Restore VC state from save buffer
|
||||
* @dev: device
|
||||
*
|
||||
* For each type of VC capability, VC/VC9/MFVC, find the capability and
|
||||
* restore it from the previously saved buffer.
|
||||
*/
|
||||
void pci_restore_vc_state(struct pci_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(vc_caps); i++) {
|
||||
int pos;
|
||||
struct pci_cap_saved_state *save_state;
|
||||
|
||||
pos = pci_find_ext_capability(dev, vc_caps[i].id);
|
||||
save_state = pci_find_saved_ext_cap(dev, vc_caps[i].id);
|
||||
if (!save_state || !pos)
|
||||
continue;
|
||||
|
||||
pci_vc_do_save_buffer(dev, pos, save_state, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_allocate_vc_save_buffers - Allocate save buffers for VC caps
|
||||
* @dev: device
|
||||
*
|
||||
* For each type of VC capability, VC/VC9/MFVC, find the capability, size
|
||||
* it, and allocate a buffer for save/restore.
|
||||
*/
|
||||
|
||||
void pci_allocate_vc_save_buffers(struct pci_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(vc_caps); i++) {
|
||||
int len, pos = pci_find_ext_capability(dev, vc_caps[i].id);
|
||||
|
||||
if (!pos)
|
||||
continue;
|
||||
|
||||
len = pci_vc_do_save_buffer(dev, pos, NULL, false);
|
||||
if (pci_add_ext_cap_save_buffer(dev, vc_caps[i].id, len))
|
||||
dev_err(&dev->dev,
|
||||
"unable to preallocate %s save buffer\n",
|
||||
vc_caps[i].name);
|
||||
}
|
||||
}
|
@ -975,20 +975,20 @@ static int vfio_vc_cap_len(struct vfio_pci_device *vdev, u16 pos)
|
||||
int ret, evcc, phases, vc_arb;
|
||||
int len = PCI_CAP_VC_BASE_SIZEOF;
|
||||
|
||||
ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_REG1, &tmp);
|
||||
ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_CAP1, &tmp);
|
||||
if (ret)
|
||||
return pcibios_err_to_errno(ret);
|
||||
|
||||
evcc = tmp & PCI_VC_REG1_EVCC; /* extended vc count */
|
||||
ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_REG2, &tmp);
|
||||
evcc = tmp & PCI_VC_CAP1_EVCC; /* extended vc count */
|
||||
ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_CAP2, &tmp);
|
||||
if (ret)
|
||||
return pcibios_err_to_errno(ret);
|
||||
|
||||
if (tmp & PCI_VC_REG2_128_PHASE)
|
||||
if (tmp & PCI_VC_CAP2_128_PHASE)
|
||||
phases = 128;
|
||||
else if (tmp & PCI_VC_REG2_64_PHASE)
|
||||
else if (tmp & PCI_VC_CAP2_64_PHASE)
|
||||
phases = 64;
|
||||
else if (tmp & PCI_VC_REG2_32_PHASE)
|
||||
else if (tmp & PCI_VC_CAP2_32_PHASE)
|
||||
phases = 32;
|
||||
else
|
||||
phases = 0;
|
||||
|
@ -224,7 +224,8 @@ enum pci_bus_speed {
|
||||
};
|
||||
|
||||
struct pci_cap_saved_data {
|
||||
char cap_nr;
|
||||
u16 cap_nr;
|
||||
bool cap_extended;
|
||||
unsigned int size;
|
||||
u32 data[0];
|
||||
};
|
||||
@ -938,6 +939,7 @@ bool pci_check_and_unmask_intx(struct pci_dev *dev);
|
||||
void pci_msi_off(struct pci_dev *dev);
|
||||
int pci_set_dma_max_seg_size(struct pci_dev *dev, unsigned int size);
|
||||
int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask);
|
||||
int pci_wait_for_pending(struct pci_dev *dev, int pos, u16 mask);
|
||||
int pci_wait_for_pending_transaction(struct pci_dev *dev);
|
||||
int pcix_get_max_mmrbc(struct pci_dev *dev);
|
||||
int pcix_get_mmrbc(struct pci_dev *dev);
|
||||
@ -976,6 +978,12 @@ struct pci_saved_state *pci_store_saved_state(struct pci_dev *dev);
|
||||
int pci_load_saved_state(struct pci_dev *dev, struct pci_saved_state *state);
|
||||
int pci_load_and_free_saved_state(struct pci_dev *dev,
|
||||
struct pci_saved_state **state);
|
||||
struct pci_cap_saved_state *pci_find_saved_cap(struct pci_dev *dev, char cap);
|
||||
struct pci_cap_saved_state *pci_find_saved_ext_cap(struct pci_dev *dev,
|
||||
u16 cap);
|
||||
int pci_add_cap_save_buffer(struct pci_dev *dev, char cap, unsigned int size);
|
||||
int pci_add_ext_cap_save_buffer(struct pci_dev *dev,
|
||||
u16 cap, unsigned int size);
|
||||
int __pci_complete_power_transition(struct pci_dev *dev, pci_power_t state);
|
||||
int pci_set_power_state(struct pci_dev *dev, pci_power_t state);
|
||||
pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state);
|
||||
@ -997,6 +1005,11 @@ static inline int pci_enable_wake(struct pci_dev *dev, pci_power_t state,
|
||||
return __pci_enable_wake(dev, state, false, enable);
|
||||
}
|
||||
|
||||
/* PCI Virtual Channel */
|
||||
int pci_save_vc_state(struct pci_dev *dev);
|
||||
void pci_restore_vc_state(struct pci_dev *dev);
|
||||
void pci_allocate_vc_save_buffers(struct pci_dev *dev);
|
||||
|
||||
#define PCI_EXP_IDO_REQUEST (1<<0)
|
||||
#define PCI_EXP_IDO_COMPLETION (1<<1)
|
||||
void pci_enable_ido(struct pci_dev *dev, unsigned long type);
|
||||
|
@ -685,17 +685,34 @@
|
||||
#define PCI_ERR_ROOT_ERR_SRC 52 /* Error Source Identification */
|
||||
|
||||
/* Virtual Channel */
|
||||
#define PCI_VC_PORT_REG1 4
|
||||
#define PCI_VC_REG1_EVCC 0x7 /* extended VC count */
|
||||
#define PCI_VC_PORT_REG2 8
|
||||
#define PCI_VC_REG2_32_PHASE 0x2
|
||||
#define PCI_VC_REG2_64_PHASE 0x4
|
||||
#define PCI_VC_REG2_128_PHASE 0x8
|
||||
#define PCI_VC_PORT_CAP1 4
|
||||
#define PCI_VC_CAP1_EVCC 0x00000007 /* extended VC count */
|
||||
#define PCI_VC_CAP1_LPEVCC 0x00000070 /* low prio extended VC count */
|
||||
#define PCI_VC_CAP1_ARB_SIZE 0x00000c00
|
||||
#define PCI_VC_PORT_CAP2 8
|
||||
#define PCI_VC_CAP2_32_PHASE 0x00000002
|
||||
#define PCI_VC_CAP2_64_PHASE 0x00000004
|
||||
#define PCI_VC_CAP2_128_PHASE 0x00000008
|
||||
#define PCI_VC_CAP2_ARB_OFF 0xff000000
|
||||
#define PCI_VC_PORT_CTRL 12
|
||||
#define PCI_VC_PORT_CTRL_LOAD_TABLE 0x00000001
|
||||
#define PCI_VC_PORT_STATUS 14
|
||||
#define PCI_VC_PORT_STATUS_TABLE 0x00000001
|
||||
#define PCI_VC_RES_CAP 16
|
||||
#define PCI_VC_RES_CAP_32_PHASE 0x00000002
|
||||
#define PCI_VC_RES_CAP_64_PHASE 0x00000004
|
||||
#define PCI_VC_RES_CAP_128_PHASE 0x00000008
|
||||
#define PCI_VC_RES_CAP_128_PHASE_TB 0x00000010
|
||||
#define PCI_VC_RES_CAP_256_PHASE 0x00000020
|
||||
#define PCI_VC_RES_CAP_ARB_OFF 0xff000000
|
||||
#define PCI_VC_RES_CTRL 20
|
||||
#define PCI_VC_RES_CTRL_LOAD_TABLE 0x00010000
|
||||
#define PCI_VC_RES_CTRL_ARB_SELECT 0x000e0000
|
||||
#define PCI_VC_RES_CTRL_ID 0x07000000
|
||||
#define PCI_VC_RES_CTRL_ENABLE 0x80000000
|
||||
#define PCI_VC_RES_STATUS 26
|
||||
#define PCI_VC_RES_STATUS_TABLE 0x00000001
|
||||
#define PCI_VC_RES_STATUS_NEGO 0x00000002
|
||||
#define PCI_CAP_VC_BASE_SIZEOF 0x10
|
||||
#define PCI_CAP_VC_PER_VC_SIZEOF 0x0C
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user