162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PCI Virtual Channel support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013 Red Hat, Inc. All rights reserved. 662306a36Sopenharmony_ci * Author: Alex Williamson <alex.williamson@redhat.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/device.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/pci.h> 1362306a36Sopenharmony_ci#include <linux/pci_regs.h> 1462306a36Sopenharmony_ci#include <linux/types.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "pci.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/** 1962306a36Sopenharmony_ci * pci_vc_save_restore_dwords - Save or restore a series of dwords 2062306a36Sopenharmony_ci * @dev: device 2162306a36Sopenharmony_ci * @pos: starting config space position 2262306a36Sopenharmony_ci * @buf: buffer to save to or restore from 2362306a36Sopenharmony_ci * @dwords: number of dwords to save/restore 2462306a36Sopenharmony_ci * @save: whether to save or restore 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_cistatic void pci_vc_save_restore_dwords(struct pci_dev *dev, int pos, 2762306a36Sopenharmony_ci u32 *buf, int dwords, bool save) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci int i; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci for (i = 0; i < dwords; i++, buf++) { 3262306a36Sopenharmony_ci if (save) 3362306a36Sopenharmony_ci pci_read_config_dword(dev, pos + (i * 4), buf); 3462306a36Sopenharmony_ci else 3562306a36Sopenharmony_ci pci_write_config_dword(dev, pos + (i * 4), *buf); 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/** 4062306a36Sopenharmony_ci * pci_vc_load_arb_table - load and wait for VC arbitration table 4162306a36Sopenharmony_ci * @dev: device 4262306a36Sopenharmony_ci * @pos: starting position of VC capability (VC/VC9/MFVC) 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * Set Load VC Arbitration Table bit requesting hardware to apply the VC 4562306a36Sopenharmony_ci * Arbitration Table (previously loaded). When the VC Arbitration Table 4662306a36Sopenharmony_ci * Status clears, hardware has latched the table into VC arbitration logic. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_cistatic void pci_vc_load_arb_table(struct pci_dev *dev, int pos) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci u16 ctrl; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci pci_read_config_word(dev, pos + PCI_VC_PORT_CTRL, &ctrl); 5362306a36Sopenharmony_ci pci_write_config_word(dev, pos + PCI_VC_PORT_CTRL, 5462306a36Sopenharmony_ci ctrl | PCI_VC_PORT_CTRL_LOAD_TABLE); 5562306a36Sopenharmony_ci if (pci_wait_for_pending(dev, pos + PCI_VC_PORT_STATUS, 5662306a36Sopenharmony_ci PCI_VC_PORT_STATUS_TABLE)) 5762306a36Sopenharmony_ci return; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci pci_err(dev, "VC arbitration table failed to load\n"); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/** 6362306a36Sopenharmony_ci * pci_vc_load_port_arb_table - Load and wait for VC port arbitration table 6462306a36Sopenharmony_ci * @dev: device 6562306a36Sopenharmony_ci * @pos: starting position of VC capability (VC/VC9/MFVC) 6662306a36Sopenharmony_ci * @res: VC resource number, ie. VCn (0-7) 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * Set Load Port Arbitration Table bit requesting hardware to apply the Port 6962306a36Sopenharmony_ci * Arbitration Table (previously loaded). When the Port Arbitration Table 7062306a36Sopenharmony_ci * Status clears, hardware has latched the table into port arbitration logic. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_cistatic void pci_vc_load_port_arb_table(struct pci_dev *dev, int pos, int res) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci int ctrl_pos, status_pos; 7562306a36Sopenharmony_ci u32 ctrl; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci ctrl_pos = pos + PCI_VC_RES_CTRL + (res * PCI_CAP_VC_PER_VC_SIZEOF); 7862306a36Sopenharmony_ci status_pos = pos + PCI_VC_RES_STATUS + (res * PCI_CAP_VC_PER_VC_SIZEOF); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci pci_read_config_dword(dev, ctrl_pos, &ctrl); 8162306a36Sopenharmony_ci pci_write_config_dword(dev, ctrl_pos, 8262306a36Sopenharmony_ci ctrl | PCI_VC_RES_CTRL_LOAD_TABLE); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (pci_wait_for_pending(dev, status_pos, PCI_VC_RES_STATUS_TABLE)) 8562306a36Sopenharmony_ci return; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci pci_err(dev, "VC%d port arbitration table failed to load\n", res); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/** 9162306a36Sopenharmony_ci * pci_vc_enable - Enable virtual channel 9262306a36Sopenharmony_ci * @dev: device 9362306a36Sopenharmony_ci * @pos: starting position of VC capability (VC/VC9/MFVC) 9462306a36Sopenharmony_ci * @res: VC res number, ie. VCn (0-7) 9562306a36Sopenharmony_ci * 9662306a36Sopenharmony_ci * A VC is enabled by setting the enable bit in matching resource control 9762306a36Sopenharmony_ci * registers on both sides of a link. We therefore need to find the opposite 9862306a36Sopenharmony_ci * end of the link. To keep this simple we enable from the downstream device. 9962306a36Sopenharmony_ci * RC devices do not have an upstream device, nor does it seem that VC9 do 10062306a36Sopenharmony_ci * (spec is unclear). Once we find the upstream device, match the VC ID to 10162306a36Sopenharmony_ci * get the correct resource, disable and enable on both ends. 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_cistatic void pci_vc_enable(struct pci_dev *dev, int pos, int res) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci int ctrl_pos, status_pos, id, pos2, evcc, i, ctrl_pos2, status_pos2; 10662306a36Sopenharmony_ci u32 ctrl, header, cap1, ctrl2; 10762306a36Sopenharmony_ci struct pci_dev *link = NULL; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* Enable VCs from the downstream device */ 11062306a36Sopenharmony_ci if (!pci_is_pcie(dev) || !pcie_downstream_port(dev)) 11162306a36Sopenharmony_ci return; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci ctrl_pos = pos + PCI_VC_RES_CTRL + (res * PCI_CAP_VC_PER_VC_SIZEOF); 11462306a36Sopenharmony_ci status_pos = pos + PCI_VC_RES_STATUS + (res * PCI_CAP_VC_PER_VC_SIZEOF); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci pci_read_config_dword(dev, ctrl_pos, &ctrl); 11762306a36Sopenharmony_ci id = ctrl & PCI_VC_RES_CTRL_ID; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci pci_read_config_dword(dev, pos, &header); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* If there is no opposite end of the link, skip to enable */ 12262306a36Sopenharmony_ci if (PCI_EXT_CAP_ID(header) == PCI_EXT_CAP_ID_VC9 || 12362306a36Sopenharmony_ci pci_is_root_bus(dev->bus)) 12462306a36Sopenharmony_ci goto enable; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci pos2 = pci_find_ext_capability(dev->bus->self, PCI_EXT_CAP_ID_VC); 12762306a36Sopenharmony_ci if (!pos2) 12862306a36Sopenharmony_ci goto enable; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci pci_read_config_dword(dev->bus->self, pos2 + PCI_VC_PORT_CAP1, &cap1); 13162306a36Sopenharmony_ci evcc = cap1 & PCI_VC_CAP1_EVCC; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* VC0 is hardwired enabled, so we can start with 1 */ 13462306a36Sopenharmony_ci for (i = 1; i < evcc + 1; i++) { 13562306a36Sopenharmony_ci ctrl_pos2 = pos2 + PCI_VC_RES_CTRL + 13662306a36Sopenharmony_ci (i * PCI_CAP_VC_PER_VC_SIZEOF); 13762306a36Sopenharmony_ci status_pos2 = pos2 + PCI_VC_RES_STATUS + 13862306a36Sopenharmony_ci (i * PCI_CAP_VC_PER_VC_SIZEOF); 13962306a36Sopenharmony_ci pci_read_config_dword(dev->bus->self, ctrl_pos2, &ctrl2); 14062306a36Sopenharmony_ci if ((ctrl2 & PCI_VC_RES_CTRL_ID) == id) { 14162306a36Sopenharmony_ci link = dev->bus->self; 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (!link) 14762306a36Sopenharmony_ci goto enable; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* Disable if enabled */ 15062306a36Sopenharmony_ci if (ctrl2 & PCI_VC_RES_CTRL_ENABLE) { 15162306a36Sopenharmony_ci ctrl2 &= ~PCI_VC_RES_CTRL_ENABLE; 15262306a36Sopenharmony_ci pci_write_config_dword(link, ctrl_pos2, ctrl2); 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* Enable on both ends */ 15662306a36Sopenharmony_ci ctrl2 |= PCI_VC_RES_CTRL_ENABLE; 15762306a36Sopenharmony_ci pci_write_config_dword(link, ctrl_pos2, ctrl2); 15862306a36Sopenharmony_cienable: 15962306a36Sopenharmony_ci ctrl |= PCI_VC_RES_CTRL_ENABLE; 16062306a36Sopenharmony_ci pci_write_config_dword(dev, ctrl_pos, ctrl); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (!pci_wait_for_pending(dev, status_pos, PCI_VC_RES_STATUS_NEGO)) 16362306a36Sopenharmony_ci pci_err(dev, "VC%d negotiation stuck pending\n", id); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (link && !pci_wait_for_pending(link, status_pos2, 16662306a36Sopenharmony_ci PCI_VC_RES_STATUS_NEGO)) 16762306a36Sopenharmony_ci pci_err(link, "VC%d negotiation stuck pending\n", id); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/** 17162306a36Sopenharmony_ci * pci_vc_do_save_buffer - Size, save, or restore VC state 17262306a36Sopenharmony_ci * @dev: device 17362306a36Sopenharmony_ci * @pos: starting position of VC capability (VC/VC9/MFVC) 17462306a36Sopenharmony_ci * @save_state: buffer for save/restore 17562306a36Sopenharmony_ci * @save: if provided a buffer, this indicates what to do with it 17662306a36Sopenharmony_ci * 17762306a36Sopenharmony_ci * Walking Virtual Channel config space to size, save, or restore it 17862306a36Sopenharmony_ci * is complicated, so we do it all from one function to reduce code and 17962306a36Sopenharmony_ci * guarantee ordering matches in the buffer. When called with NULL 18062306a36Sopenharmony_ci * @save_state, return the size of the necessary save buffer. When called 18162306a36Sopenharmony_ci * with a non-NULL @save_state, @save determines whether we save to the 18262306a36Sopenharmony_ci * buffer or restore from it. 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_cistatic int pci_vc_do_save_buffer(struct pci_dev *dev, int pos, 18562306a36Sopenharmony_ci struct pci_cap_saved_state *save_state, 18662306a36Sopenharmony_ci bool save) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci u32 cap1; 18962306a36Sopenharmony_ci char evcc, lpevcc, parb_size; 19062306a36Sopenharmony_ci int i, len = 0; 19162306a36Sopenharmony_ci u8 *buf = save_state ? (u8 *)save_state->cap.data : NULL; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* Sanity check buffer size for save/restore */ 19462306a36Sopenharmony_ci if (buf && save_state->cap.size != 19562306a36Sopenharmony_ci pci_vc_do_save_buffer(dev, pos, NULL, save)) { 19662306a36Sopenharmony_ci pci_err(dev, "VC save buffer size does not match @0x%x\n", pos); 19762306a36Sopenharmony_ci return -ENOMEM; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci pci_read_config_dword(dev, pos + PCI_VC_PORT_CAP1, &cap1); 20162306a36Sopenharmony_ci /* Extended VC Count (not counting VC0) */ 20262306a36Sopenharmony_ci evcc = cap1 & PCI_VC_CAP1_EVCC; 20362306a36Sopenharmony_ci /* Low Priority Extended VC Count (not counting VC0) */ 20462306a36Sopenharmony_ci lpevcc = (cap1 & PCI_VC_CAP1_LPEVCC) >> 4; 20562306a36Sopenharmony_ci /* Port Arbitration Table Entry Size (bits) */ 20662306a36Sopenharmony_ci parb_size = 1 << ((cap1 & PCI_VC_CAP1_ARB_SIZE) >> 10); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* 20962306a36Sopenharmony_ci * Port VC Control Register contains VC Arbitration Select, which 21062306a36Sopenharmony_ci * cannot be modified when more than one LPVC is in operation. We 21162306a36Sopenharmony_ci * therefore save/restore it first, as only VC0 should be enabled 21262306a36Sopenharmony_ci * after device reset. 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_ci if (buf) { 21562306a36Sopenharmony_ci if (save) 21662306a36Sopenharmony_ci pci_read_config_word(dev, pos + PCI_VC_PORT_CTRL, 21762306a36Sopenharmony_ci (u16 *)buf); 21862306a36Sopenharmony_ci else 21962306a36Sopenharmony_ci pci_write_config_word(dev, pos + PCI_VC_PORT_CTRL, 22062306a36Sopenharmony_ci *(u16 *)buf); 22162306a36Sopenharmony_ci buf += 4; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci len += 4; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* 22662306a36Sopenharmony_ci * If we have any Low Priority VCs and a VC Arbitration Table Offset 22762306a36Sopenharmony_ci * in Port VC Capability Register 2 then save/restore it next. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ci if (lpevcc) { 23062306a36Sopenharmony_ci u32 cap2; 23162306a36Sopenharmony_ci int vcarb_offset; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci pci_read_config_dword(dev, pos + PCI_VC_PORT_CAP2, &cap2); 23462306a36Sopenharmony_ci vcarb_offset = ((cap2 & PCI_VC_CAP2_ARB_OFF) >> 24) * 16; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (vcarb_offset) { 23762306a36Sopenharmony_ci int size, vcarb_phases = 0; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (cap2 & PCI_VC_CAP2_128_PHASE) 24062306a36Sopenharmony_ci vcarb_phases = 128; 24162306a36Sopenharmony_ci else if (cap2 & PCI_VC_CAP2_64_PHASE) 24262306a36Sopenharmony_ci vcarb_phases = 64; 24362306a36Sopenharmony_ci else if (cap2 & PCI_VC_CAP2_32_PHASE) 24462306a36Sopenharmony_ci vcarb_phases = 32; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* Fixed 4 bits per phase per lpevcc (plus VC0) */ 24762306a36Sopenharmony_ci size = ((lpevcc + 1) * vcarb_phases * 4) / 8; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (size && buf) { 25062306a36Sopenharmony_ci pci_vc_save_restore_dwords(dev, 25162306a36Sopenharmony_ci pos + vcarb_offset, 25262306a36Sopenharmony_ci (u32 *)buf, 25362306a36Sopenharmony_ci size / 4, save); 25462306a36Sopenharmony_ci /* 25562306a36Sopenharmony_ci * On restore, we need to signal hardware to 25662306a36Sopenharmony_ci * re-load the VC Arbitration Table. 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ci if (!save) 25962306a36Sopenharmony_ci pci_vc_load_arb_table(dev, pos); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci buf += size; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci len += size; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* 26862306a36Sopenharmony_ci * In addition to each VC Resource Control Register, we may have a 26962306a36Sopenharmony_ci * Port Arbitration Table attached to each VC. The Port Arbitration 27062306a36Sopenharmony_ci * Table Offset in each VC Resource Capability Register tells us if 27162306a36Sopenharmony_ci * it exists. The entry size is global from the Port VC Capability 27262306a36Sopenharmony_ci * Register1 above. The number of phases is determined per VC. 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_ci for (i = 0; i < evcc + 1; i++) { 27562306a36Sopenharmony_ci u32 cap; 27662306a36Sopenharmony_ci int parb_offset; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci pci_read_config_dword(dev, pos + PCI_VC_RES_CAP + 27962306a36Sopenharmony_ci (i * PCI_CAP_VC_PER_VC_SIZEOF), &cap); 28062306a36Sopenharmony_ci parb_offset = ((cap & PCI_VC_RES_CAP_ARB_OFF) >> 24) * 16; 28162306a36Sopenharmony_ci if (parb_offset) { 28262306a36Sopenharmony_ci int size, parb_phases = 0; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (cap & PCI_VC_RES_CAP_256_PHASE) 28562306a36Sopenharmony_ci parb_phases = 256; 28662306a36Sopenharmony_ci else if (cap & (PCI_VC_RES_CAP_128_PHASE | 28762306a36Sopenharmony_ci PCI_VC_RES_CAP_128_PHASE_TB)) 28862306a36Sopenharmony_ci parb_phases = 128; 28962306a36Sopenharmony_ci else if (cap & PCI_VC_RES_CAP_64_PHASE) 29062306a36Sopenharmony_ci parb_phases = 64; 29162306a36Sopenharmony_ci else if (cap & PCI_VC_RES_CAP_32_PHASE) 29262306a36Sopenharmony_ci parb_phases = 32; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci size = (parb_size * parb_phases) / 8; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (size && buf) { 29762306a36Sopenharmony_ci pci_vc_save_restore_dwords(dev, 29862306a36Sopenharmony_ci pos + parb_offset, 29962306a36Sopenharmony_ci (u32 *)buf, 30062306a36Sopenharmony_ci size / 4, save); 30162306a36Sopenharmony_ci buf += size; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci len += size; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* VC Resource Control Register */ 30762306a36Sopenharmony_ci if (buf) { 30862306a36Sopenharmony_ci int ctrl_pos = pos + PCI_VC_RES_CTRL + 30962306a36Sopenharmony_ci (i * PCI_CAP_VC_PER_VC_SIZEOF); 31062306a36Sopenharmony_ci if (save) 31162306a36Sopenharmony_ci pci_read_config_dword(dev, ctrl_pos, 31262306a36Sopenharmony_ci (u32 *)buf); 31362306a36Sopenharmony_ci else { 31462306a36Sopenharmony_ci u32 tmp, ctrl = *(u32 *)buf; 31562306a36Sopenharmony_ci /* 31662306a36Sopenharmony_ci * For an FLR case, the VC config may remain. 31762306a36Sopenharmony_ci * Preserve enable bit, restore the rest. 31862306a36Sopenharmony_ci */ 31962306a36Sopenharmony_ci pci_read_config_dword(dev, ctrl_pos, &tmp); 32062306a36Sopenharmony_ci tmp &= PCI_VC_RES_CTRL_ENABLE; 32162306a36Sopenharmony_ci tmp |= ctrl & ~PCI_VC_RES_CTRL_ENABLE; 32262306a36Sopenharmony_ci pci_write_config_dword(dev, ctrl_pos, tmp); 32362306a36Sopenharmony_ci /* Load port arbitration table if used */ 32462306a36Sopenharmony_ci if (ctrl & PCI_VC_RES_CTRL_ARB_SELECT) 32562306a36Sopenharmony_ci pci_vc_load_port_arb_table(dev, pos, i); 32662306a36Sopenharmony_ci /* Re-enable if needed */ 32762306a36Sopenharmony_ci if ((ctrl ^ tmp) & PCI_VC_RES_CTRL_ENABLE) 32862306a36Sopenharmony_ci pci_vc_enable(dev, pos, i); 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci buf += 4; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci len += 4; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci return buf ? 0 : len; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic struct { 33962306a36Sopenharmony_ci u16 id; 34062306a36Sopenharmony_ci const char *name; 34162306a36Sopenharmony_ci} vc_caps[] = { { PCI_EXT_CAP_ID_MFVC, "MFVC" }, 34262306a36Sopenharmony_ci { PCI_EXT_CAP_ID_VC, "VC" }, 34362306a36Sopenharmony_ci { PCI_EXT_CAP_ID_VC9, "VC9" } }; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci/** 34662306a36Sopenharmony_ci * pci_save_vc_state - Save VC state to pre-allocate save buffer 34762306a36Sopenharmony_ci * @dev: device 34862306a36Sopenharmony_ci * 34962306a36Sopenharmony_ci * For each type of VC capability, VC/VC9/MFVC, find the capability and 35062306a36Sopenharmony_ci * save it to the pre-allocated save buffer. 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_ciint pci_save_vc_state(struct pci_dev *dev) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci int i; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vc_caps); i++) { 35762306a36Sopenharmony_ci int pos, ret; 35862306a36Sopenharmony_ci struct pci_cap_saved_state *save_state; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci pos = pci_find_ext_capability(dev, vc_caps[i].id); 36162306a36Sopenharmony_ci if (!pos) 36262306a36Sopenharmony_ci continue; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci save_state = pci_find_saved_ext_cap(dev, vc_caps[i].id); 36562306a36Sopenharmony_ci if (!save_state) { 36662306a36Sopenharmony_ci pci_err(dev, "%s buffer not found in %s\n", 36762306a36Sopenharmony_ci vc_caps[i].name, __func__); 36862306a36Sopenharmony_ci return -ENOMEM; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci ret = pci_vc_do_save_buffer(dev, pos, save_state, true); 37262306a36Sopenharmony_ci if (ret) { 37362306a36Sopenharmony_ci pci_err(dev, "%s save unsuccessful %s\n", 37462306a36Sopenharmony_ci vc_caps[i].name, __func__); 37562306a36Sopenharmony_ci return ret; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci/** 38362306a36Sopenharmony_ci * pci_restore_vc_state - Restore VC state from save buffer 38462306a36Sopenharmony_ci * @dev: device 38562306a36Sopenharmony_ci * 38662306a36Sopenharmony_ci * For each type of VC capability, VC/VC9/MFVC, find the capability and 38762306a36Sopenharmony_ci * restore it from the previously saved buffer. 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_civoid pci_restore_vc_state(struct pci_dev *dev) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci int i; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vc_caps); i++) { 39462306a36Sopenharmony_ci int pos; 39562306a36Sopenharmony_ci struct pci_cap_saved_state *save_state; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci pos = pci_find_ext_capability(dev, vc_caps[i].id); 39862306a36Sopenharmony_ci save_state = pci_find_saved_ext_cap(dev, vc_caps[i].id); 39962306a36Sopenharmony_ci if (!save_state || !pos) 40062306a36Sopenharmony_ci continue; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci pci_vc_do_save_buffer(dev, pos, save_state, false); 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci/** 40762306a36Sopenharmony_ci * pci_allocate_vc_save_buffers - Allocate save buffers for VC caps 40862306a36Sopenharmony_ci * @dev: device 40962306a36Sopenharmony_ci * 41062306a36Sopenharmony_ci * For each type of VC capability, VC/VC9/MFVC, find the capability, size 41162306a36Sopenharmony_ci * it, and allocate a buffer for save/restore. 41262306a36Sopenharmony_ci */ 41362306a36Sopenharmony_civoid pci_allocate_vc_save_buffers(struct pci_dev *dev) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci int i; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vc_caps); i++) { 41862306a36Sopenharmony_ci int len, pos = pci_find_ext_capability(dev, vc_caps[i].id); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (!pos) 42162306a36Sopenharmony_ci continue; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci len = pci_vc_do_save_buffer(dev, pos, NULL, false); 42462306a36Sopenharmony_ci if (pci_add_ext_cap_save_buffer(dev, vc_caps[i].id, len)) 42562306a36Sopenharmony_ci pci_err(dev, "unable to preallocate %s save buffer\n", 42662306a36Sopenharmony_ci vc_caps[i].name); 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci} 429