Merge branch 'pci/enumeration'

- Consolidate _HPP & _HPX code in pci-acpi.h and remove unnecessary
    struct hotplug_program_ops (Krzysztof Wilczynski)

  - Fixup PCIe device types to remove the need for dev->has_secondary_link
    (Mika Westerberg)

* pci/enumeration:
  PCI: Get rid of dev->has_secondary_link flag
  PCI: Make pcie_downstream_port() available outside of access.c
  PCI/ACPI: Remove unnecessary struct hotplug_program_ops
  PCI/ACPI: Move _HPP & _HPX functions to pci-acpi.c
  PCI/ACPI: Rename _HPX structs from hpp_* to hpx_*
This commit is contained in:
Bjorn Helgaas 2019-09-23 16:10:08 -05:00
commit a10a1f60c7
10 changed files with 434 additions and 445 deletions

View File

@ -336,15 +336,6 @@ static inline int pcie_cap_version(const struct pci_dev *dev)
return pcie_caps_reg(dev) & PCI_EXP_FLAGS_VERS;
}
static bool pcie_downstream_port(const struct pci_dev *dev)
{
int type = pci_pcie_type(dev);
return type == PCI_EXP_TYPE_ROOT_PORT ||
type == PCI_EXP_TYPE_DOWNSTREAM ||
type == PCI_EXP_TYPE_PCIE_BRIDGE;
}
bool pcie_cap_has_lnkctl(const struct pci_dev *dev)
{
int type = pci_pcie_type(dev);

View File

@ -117,8 +117,58 @@ phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle)
return (phys_addr_t)mcfg_addr;
}
/* _HPX PCI Setting Record (Type 0); same as _HPP */
struct hpx_type0 {
u32 revision; /* Not present in _HPP */
u8 cache_line_size; /* Not applicable to PCIe */
u8 latency_timer; /* Not applicable to PCIe */
u8 enable_serr;
u8 enable_perr;
};
static struct hpx_type0 pci_default_type0 = {
.revision = 1,
.cache_line_size = 8,
.latency_timer = 0x40,
.enable_serr = 0,
.enable_perr = 0,
};
static void program_hpx_type0(struct pci_dev *dev, struct hpx_type0 *hpx)
{
u16 pci_cmd, pci_bctl;
if (!hpx)
hpx = &pci_default_type0;
if (hpx->revision > 1) {
pci_warn(dev, "PCI settings rev %d not supported; using defaults\n",
hpx->revision);
hpx = &pci_default_type0;
}
pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, hpx->cache_line_size);
pci_write_config_byte(dev, PCI_LATENCY_TIMER, hpx->latency_timer);
pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
if (hpx->enable_serr)
pci_cmd |= PCI_COMMAND_SERR;
if (hpx->enable_perr)
pci_cmd |= PCI_COMMAND_PARITY;
pci_write_config_word(dev, PCI_COMMAND, pci_cmd);
/* Program bridge control value */
if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER,
hpx->latency_timer);
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl);
if (hpx->enable_perr)
pci_bctl |= PCI_BRIDGE_CTL_PARITY;
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl);
}
}
static acpi_status decode_type0_hpx_record(union acpi_object *record,
struct hpp_type0 *hpx0)
struct hpx_type0 *hpx0)
{
int i;
union acpi_object *fields = record->package.elements;
@ -145,8 +195,30 @@ static acpi_status decode_type0_hpx_record(union acpi_object *record,
return AE_OK;
}
/* _HPX PCI-X Setting Record (Type 1) */
struct hpx_type1 {
u32 revision;
u8 max_mem_read;
u8 avg_max_split;
u16 tot_max_split;
};
static void program_hpx_type1(struct pci_dev *dev, struct hpx_type1 *hpx)
{
int pos;
if (!hpx)
return;
pos = pci_find_capability(dev, PCI_CAP_ID_PCIX);
if (!pos)
return;
pci_warn(dev, "PCI-X settings not supported\n");
}
static acpi_status decode_type1_hpx_record(union acpi_object *record,
struct hpp_type1 *hpx1)
struct hpx_type1 *hpx1)
{
int i;
union acpi_object *fields = record->package.elements;
@ -172,8 +244,130 @@ static acpi_status decode_type1_hpx_record(union acpi_object *record,
return AE_OK;
}
static bool pcie_root_rcb_set(struct pci_dev *dev)
{
struct pci_dev *rp = pcie_find_root_port(dev);
u16 lnkctl;
if (!rp)
return false;
pcie_capability_read_word(rp, PCI_EXP_LNKCTL, &lnkctl);
if (lnkctl & PCI_EXP_LNKCTL_RCB)
return true;
return false;
}
/* _HPX PCI Express Setting Record (Type 2) */
struct hpx_type2 {
u32 revision;
u32 unc_err_mask_and;
u32 unc_err_mask_or;
u32 unc_err_sever_and;
u32 unc_err_sever_or;
u32 cor_err_mask_and;
u32 cor_err_mask_or;
u32 adv_err_cap_and;
u32 adv_err_cap_or;
u16 pci_exp_devctl_and;
u16 pci_exp_devctl_or;
u16 pci_exp_lnkctl_and;
u16 pci_exp_lnkctl_or;
u32 sec_unc_err_sever_and;
u32 sec_unc_err_sever_or;
u32 sec_unc_err_mask_and;
u32 sec_unc_err_mask_or;
};
static void program_hpx_type2(struct pci_dev *dev, struct hpx_type2 *hpx)
{
int pos;
u32 reg32;
if (!hpx)
return;
if (!pci_is_pcie(dev))
return;
if (hpx->revision > 1) {
pci_warn(dev, "PCIe settings rev %d not supported\n",
hpx->revision);
return;
}
/*
* Don't allow _HPX to change MPS or MRRS settings. We manage
* those to make sure they're consistent with the rest of the
* platform.
*/
hpx->pci_exp_devctl_and |= PCI_EXP_DEVCTL_PAYLOAD |
PCI_EXP_DEVCTL_READRQ;
hpx->pci_exp_devctl_or &= ~(PCI_EXP_DEVCTL_PAYLOAD |
PCI_EXP_DEVCTL_READRQ);
/* Initialize Device Control Register */
pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
~hpx->pci_exp_devctl_and, hpx->pci_exp_devctl_or);
/* Initialize Link Control Register */
if (pcie_cap_has_lnkctl(dev)) {
/*
* If the Root Port supports Read Completion Boundary of
* 128, set RCB to 128. Otherwise, clear it.
*/
hpx->pci_exp_lnkctl_and |= PCI_EXP_LNKCTL_RCB;
hpx->pci_exp_lnkctl_or &= ~PCI_EXP_LNKCTL_RCB;
if (pcie_root_rcb_set(dev))
hpx->pci_exp_lnkctl_or |= PCI_EXP_LNKCTL_RCB;
pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL,
~hpx->pci_exp_lnkctl_and, hpx->pci_exp_lnkctl_or);
}
/* Find Advanced Error Reporting Enhanced Capability */
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
if (!pos)
return;
/* Initialize Uncorrectable Error Mask Register */
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &reg32);
reg32 = (reg32 & hpx->unc_err_mask_and) | hpx->unc_err_mask_or;
pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, reg32);
/* Initialize Uncorrectable Error Severity Register */
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &reg32);
reg32 = (reg32 & hpx->unc_err_sever_and) | hpx->unc_err_sever_or;
pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, reg32);
/* Initialize Correctable Error Mask Register */
pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &reg32);
reg32 = (reg32 & hpx->cor_err_mask_and) | hpx->cor_err_mask_or;
pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg32);
/* Initialize Advanced Error Capabilities and Control Register */
pci_read_config_dword(dev, pos + PCI_ERR_CAP, &reg32);
reg32 = (reg32 & hpx->adv_err_cap_and) | hpx->adv_err_cap_or;
/* Don't enable ECRC generation or checking if unsupported */
if (!(reg32 & PCI_ERR_CAP_ECRC_GENC))
reg32 &= ~PCI_ERR_CAP_ECRC_GENE;
if (!(reg32 & PCI_ERR_CAP_ECRC_CHKC))
reg32 &= ~PCI_ERR_CAP_ECRC_CHKE;
pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32);
/*
* FIXME: The following two registers are not supported yet.
*
* o Secondary Uncorrectable Error Severity Register
* o Secondary Uncorrectable Error Mask Register
*/
}
static acpi_status decode_type2_hpx_record(union acpi_object *record,
struct hpp_type2 *hpx2)
struct hpx_type2 *hpx2)
{
int i;
union acpi_object *fields = record->package.elements;
@ -212,6 +406,164 @@ static acpi_status decode_type2_hpx_record(union acpi_object *record,
return AE_OK;
}
/* _HPX PCI Express Setting Record (Type 3) */
struct hpx_type3 {
u16 device_type;
u16 function_type;
u16 config_space_location;
u16 pci_exp_cap_id;
u16 pci_exp_cap_ver;
u16 pci_exp_vendor_id;
u16 dvsec_id;
u16 dvsec_rev;
u16 match_offset;
u32 match_mask_and;
u32 match_value;
u16 reg_offset;
u32 reg_mask_and;
u32 reg_mask_or;
};
enum hpx_type3_dev_type {
HPX_TYPE_ENDPOINT = BIT(0),
HPX_TYPE_LEG_END = BIT(1),
HPX_TYPE_RC_END = BIT(2),
HPX_TYPE_RC_EC = BIT(3),
HPX_TYPE_ROOT_PORT = BIT(4),
HPX_TYPE_UPSTREAM = BIT(5),
HPX_TYPE_DOWNSTREAM = BIT(6),
HPX_TYPE_PCI_BRIDGE = BIT(7),
HPX_TYPE_PCIE_BRIDGE = BIT(8),
};
static u16 hpx3_device_type(struct pci_dev *dev)
{
u16 pcie_type = pci_pcie_type(dev);
const int pcie_to_hpx3_type[] = {
[PCI_EXP_TYPE_ENDPOINT] = HPX_TYPE_ENDPOINT,
[PCI_EXP_TYPE_LEG_END] = HPX_TYPE_LEG_END,
[PCI_EXP_TYPE_RC_END] = HPX_TYPE_RC_END,
[PCI_EXP_TYPE_RC_EC] = HPX_TYPE_RC_EC,
[PCI_EXP_TYPE_ROOT_PORT] = HPX_TYPE_ROOT_PORT,
[PCI_EXP_TYPE_UPSTREAM] = HPX_TYPE_UPSTREAM,
[PCI_EXP_TYPE_DOWNSTREAM] = HPX_TYPE_DOWNSTREAM,
[PCI_EXP_TYPE_PCI_BRIDGE] = HPX_TYPE_PCI_BRIDGE,
[PCI_EXP_TYPE_PCIE_BRIDGE] = HPX_TYPE_PCIE_BRIDGE,
};
if (pcie_type >= ARRAY_SIZE(pcie_to_hpx3_type))
return 0;
return pcie_to_hpx3_type[pcie_type];
}
enum hpx_type3_fn_type {
HPX_FN_NORMAL = BIT(0),
HPX_FN_SRIOV_PHYS = BIT(1),
HPX_FN_SRIOV_VIRT = BIT(2),
};
static u8 hpx3_function_type(struct pci_dev *dev)
{
if (dev->is_virtfn)
return HPX_FN_SRIOV_VIRT;
else if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV) > 0)
return HPX_FN_SRIOV_PHYS;
else
return HPX_FN_NORMAL;
}
static bool hpx3_cap_ver_matches(u8 pcie_cap_id, u8 hpx3_cap_id)
{
u8 cap_ver = hpx3_cap_id & 0xf;
if ((hpx3_cap_id & BIT(4)) && cap_ver >= pcie_cap_id)
return true;
else if (cap_ver == pcie_cap_id)
return true;
return false;
}
enum hpx_type3_cfg_loc {
HPX_CFG_PCICFG = 0,
HPX_CFG_PCIE_CAP = 1,
HPX_CFG_PCIE_CAP_EXT = 2,
HPX_CFG_VEND_CAP = 3,
HPX_CFG_DVSEC = 4,
HPX_CFG_MAX,
};
static void program_hpx_type3_register(struct pci_dev *dev,
const struct hpx_type3 *reg)
{
u32 match_reg, write_reg, header, orig_value;
u16 pos;
if (!(hpx3_device_type(dev) & reg->device_type))
return;
if (!(hpx3_function_type(dev) & reg->function_type))
return;
switch (reg->config_space_location) {
case HPX_CFG_PCICFG:
pos = 0;
break;
case HPX_CFG_PCIE_CAP:
pos = pci_find_capability(dev, reg->pci_exp_cap_id);
if (pos == 0)
return;
break;
case HPX_CFG_PCIE_CAP_EXT:
pos = pci_find_ext_capability(dev, reg->pci_exp_cap_id);
if (pos == 0)
return;
pci_read_config_dword(dev, pos, &header);
if (!hpx3_cap_ver_matches(PCI_EXT_CAP_VER(header),
reg->pci_exp_cap_ver))
return;
break;
case HPX_CFG_VEND_CAP: /* Fall through */
case HPX_CFG_DVSEC: /* Fall through */
default:
pci_warn(dev, "Encountered _HPX type 3 with unsupported config space location");
return;
}
pci_read_config_dword(dev, pos + reg->match_offset, &match_reg);
if ((match_reg & reg->match_mask_and) != reg->match_value)
return;
pci_read_config_dword(dev, pos + reg->reg_offset, &write_reg);
orig_value = write_reg;
write_reg &= reg->reg_mask_and;
write_reg |= reg->reg_mask_or;
if (orig_value == write_reg)
return;
pci_write_config_dword(dev, pos + reg->reg_offset, write_reg);
pci_dbg(dev, "Applied _HPX3 at [0x%x]: 0x%08x -> 0x%08x",
pos, orig_value, write_reg);
}
static void program_hpx_type3(struct pci_dev *dev, struct hpx_type3 *hpx)
{
if (!hpx)
return;
if (!pci_is_pcie(dev))
return;
program_hpx_type3_register(dev, hpx);
}
static void parse_hpx3_register(struct hpx_type3 *hpx3_reg,
union acpi_object *reg_fields)
{
@ -232,8 +584,7 @@ static void parse_hpx3_register(struct hpx_type3 *hpx3_reg,
}
static acpi_status program_type3_hpx_record(struct pci_dev *dev,
union acpi_object *record,
const struct hotplug_program_ops *hp_ops)
union acpi_object *record)
{
union acpi_object *fields = record->package.elements;
u32 desc_count, expected_length, revision;
@ -257,7 +608,7 @@ static acpi_status program_type3_hpx_record(struct pci_dev *dev,
for (i = 0; i < desc_count; i++) {
reg_fields = fields + 3 + i * 14;
parse_hpx3_register(&hpx3, reg_fields);
hp_ops->program_type3(dev, &hpx3);
program_hpx_type3(dev, &hpx3);
}
break;
@ -270,15 +621,14 @@ static acpi_status program_type3_hpx_record(struct pci_dev *dev,
return AE_OK;
}
static acpi_status acpi_run_hpx(struct pci_dev *dev, acpi_handle handle,
const struct hotplug_program_ops *hp_ops)
static acpi_status acpi_run_hpx(struct pci_dev *dev, acpi_handle handle)
{
acpi_status status;
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
union acpi_object *package, *record, *fields;
struct hpp_type0 hpx0;
struct hpp_type1 hpx1;
struct hpp_type2 hpx2;
struct hpx_type0 hpx0;
struct hpx_type1 hpx1;
struct hpx_type2 hpx2;
u32 type;
int i;
@ -313,24 +663,24 @@ static acpi_status acpi_run_hpx(struct pci_dev *dev, acpi_handle handle,
status = decode_type0_hpx_record(record, &hpx0);
if (ACPI_FAILURE(status))
goto exit;
hp_ops->program_type0(dev, &hpx0);
program_hpx_type0(dev, &hpx0);
break;
case 1:
memset(&hpx1, 0, sizeof(hpx1));
status = decode_type1_hpx_record(record, &hpx1);
if (ACPI_FAILURE(status))
goto exit;
hp_ops->program_type1(dev, &hpx1);
program_hpx_type1(dev, &hpx1);
break;
case 2:
memset(&hpx2, 0, sizeof(hpx2));
status = decode_type2_hpx_record(record, &hpx2);
if (ACPI_FAILURE(status))
goto exit;
hp_ops->program_type2(dev, &hpx2);
program_hpx_type2(dev, &hpx2);
break;
case 3:
status = program_type3_hpx_record(dev, record, hp_ops);
status = program_type3_hpx_record(dev, record);
if (ACPI_FAILURE(status))
goto exit;
break;
@ -346,16 +696,15 @@ static acpi_status acpi_run_hpx(struct pci_dev *dev, acpi_handle handle,
return status;
}
static acpi_status acpi_run_hpp(struct pci_dev *dev, acpi_handle handle,
const struct hotplug_program_ops *hp_ops)
static acpi_status acpi_run_hpp(struct pci_dev *dev, acpi_handle handle)
{
acpi_status status;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *package, *fields;
struct hpp_type0 hpp0;
struct hpx_type0 hpx0;
int i;
memset(&hpp0, 0, sizeof(hpp0));
memset(&hpx0, 0, sizeof(hpx0));
status = acpi_evaluate_object(handle, "_HPP", NULL, &buffer);
if (ACPI_FAILURE(status))
@ -376,26 +725,24 @@ static acpi_status acpi_run_hpp(struct pci_dev *dev, acpi_handle handle,
}
}
hpp0.revision = 1;
hpp0.cache_line_size = fields[0].integer.value;
hpp0.latency_timer = fields[1].integer.value;
hpp0.enable_serr = fields[2].integer.value;
hpp0.enable_perr = fields[3].integer.value;
hpx0.revision = 1;
hpx0.cache_line_size = fields[0].integer.value;
hpx0.latency_timer = fields[1].integer.value;
hpx0.enable_serr = fields[2].integer.value;
hpx0.enable_perr = fields[3].integer.value;
hp_ops->program_type0(dev, &hpp0);
program_hpx_type0(dev, &hpx0);
exit:
kfree(buffer.pointer);
return status;
}
/* pci_get_hp_params
/* pci_acpi_program_hp_params
*
* @dev - the pci_dev for which we want parameters
* @hpp - allocated by the caller
*/
int pci_acpi_program_hp_params(struct pci_dev *dev,
const struct hotplug_program_ops *hp_ops)
int pci_acpi_program_hp_params(struct pci_dev *dev)
{
acpi_status status;
acpi_handle handle, phandle;
@ -418,10 +765,10 @@ int pci_acpi_program_hp_params(struct pci_dev *dev,
* this pci dev.
*/
while (handle) {
status = acpi_run_hpx(dev, handle, hp_ops);
status = acpi_run_hpx(dev, handle);
if (ACPI_SUCCESS(status))
return 0;
status = acpi_run_hpp(dev, handle, hp_ops);
status = acpi_run_hpp(dev, handle);
if (ACPI_SUCCESS(status))
return 0;
if (acpi_is_root_bridge(handle))

View File

@ -3576,7 +3576,7 @@ int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask)
}
/* Ensure upstream ports don't block AtomicOps on egress */
if (!bridge->has_secondary_link) {
if (pci_pcie_type(bridge) == PCI_EXP_TYPE_UPSTREAM) {
pcie_capability_read_dword(bridge, PCI_EXP_DEVCTL2,
&ctl2);
if (ctl2 & PCI_EXP_DEVCTL2_ATOMIC_EGRESS_BLOCK)

View File

@ -125,6 +125,15 @@ static inline bool pci_power_manageable(struct pci_dev *pci_dev)
return !pci_has_subordinate(pci_dev) || pci_dev->bridge_d3;
}
static inline bool pcie_downstream_port(const struct pci_dev *dev)
{
int type = pci_pcie_type(dev);
return type == PCI_EXP_TYPE_ROOT_PORT ||
type == PCI_EXP_TYPE_DOWNSTREAM ||
type == PCI_EXP_TYPE_PCIE_BRIDGE;
}
int pci_vpd_init(struct pci_dev *dev);
void pci_vpd_release(struct pci_dev *dev);
void pcie_vpd_create_sysfs_dev_files(struct pci_dev *dev);
@ -650,4 +659,13 @@ static inline void pci_aer_clear_fatal_status(struct pci_dev *dev) { }
static inline void pci_aer_clear_device_status(struct pci_dev *dev) { }
#endif
#ifdef CONFIG_ACPI
int pci_acpi_program_hp_params(struct pci_dev *dev);
#else
static inline int pci_acpi_program_hp_params(struct pci_dev *dev)
{
return -ENODEV;
}
#endif
#endif /* DRIVERS_PCI_H */

View File

@ -912,10 +912,10 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
/*
* We allocate pcie_link_state for the component on the upstream
* end of a Link, so there's nothing to do unless this device has a
* Link on its secondary side.
* end of a Link, so there's nothing to do unless this device is
* downstream port.
*/
if (!pdev->has_secondary_link)
if (!pcie_downstream_port(pdev))
return;
/* VIA has a strange chipset, root port is under a bridge */
@ -1069,7 +1069,7 @@ static int __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem)
if (!pci_is_pcie(pdev))
return 0;
if (pdev->has_secondary_link)
if (pcie_downstream_port(pdev))
parent = pdev;
if (!parent || !parent->link_state)
return -EINVAL;

View File

@ -166,7 +166,7 @@ static pci_ers_result_t reset_link(struct pci_dev *dev, u32 service)
driver = pcie_port_find_service(dev, service);
if (driver && driver->reset_link) {
status = driver->reset_link(dev);
} else if (dev->has_secondary_link) {
} else if (pcie_downstream_port(dev)) {
status = default_reset_link(dev);
} else {
pci_printk(KERN_DEBUG, dev, "no link-reset support at upstream device %s\n",

View File

@ -1431,26 +1431,38 @@ void set_pcie_port_type(struct pci_dev *pdev)
pci_read_config_word(pdev, pos + PCI_EXP_DEVCAP, &reg16);
pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD;
parent = pci_upstream_bridge(pdev);
if (!parent)
return;
/*
* A Root Port or a PCI-to-PCIe bridge is always the upstream end
* of a Link. No PCIe component has two Links. Two Links are
* connected by a Switch that has a Port on each Link and internal
* logic to connect the two Ports.
* Some systems do not identify their upstream/downstream ports
* correctly so detect impossible configurations here and correct
* the port type accordingly.
*/
type = pci_pcie_type(pdev);
if (type == PCI_EXP_TYPE_ROOT_PORT ||
type == PCI_EXP_TYPE_PCIE_BRIDGE)
pdev->has_secondary_link = 1;
else if (type == PCI_EXP_TYPE_UPSTREAM ||
type == PCI_EXP_TYPE_DOWNSTREAM) {
parent = pci_upstream_bridge(pdev);
if (type == PCI_EXP_TYPE_DOWNSTREAM) {
/*
* Usually there's an upstream device (Root Port or Switch
* Downstream Port), but we can't assume one exists.
* If pdev claims to be downstream port but the parent
* device is also downstream port assume pdev is actually
* upstream port.
*/
if (parent && !parent->has_secondary_link)
pdev->has_secondary_link = 1;
if (pcie_downstream_port(parent)) {
pci_info(pdev, "claims to be downstream port but is acting as upstream port, correcting type\n");
pdev->pcie_flags_reg &= ~PCI_EXP_FLAGS_TYPE;
pdev->pcie_flags_reg |= PCI_EXP_TYPE_UPSTREAM;
}
} else if (type == PCI_EXP_TYPE_UPSTREAM) {
/*
* If pdev claims to be upstream port but the parent
* device is also upstream port assume pdev is actually
* downstream port.
*/
if (pci_pcie_type(parent) == PCI_EXP_TYPE_UPSTREAM) {
pci_info(pdev, "claims to be upstream port but is acting as downstream port, correcting type\n");
pdev->pcie_flags_reg &= ~PCI_EXP_FLAGS_TYPE;
pdev->pcie_flags_reg |= PCI_EXP_TYPE_DOWNSTREAM;
}
}
}
@ -1920,275 +1932,6 @@ static void pci_configure_mps(struct pci_dev *dev)
p_mps, mps, mpss);
}
static struct hpp_type0 pci_default_type0 = {
.revision = 1,
.cache_line_size = 8,
.latency_timer = 0x40,
.enable_serr = 0,
.enable_perr = 0,
};
static void program_hpp_type0(struct pci_dev *dev, struct hpp_type0 *hpp)
{
u16 pci_cmd, pci_bctl;
if (!hpp)
hpp = &pci_default_type0;
if (hpp->revision > 1) {
pci_warn(dev, "PCI settings rev %d not supported; using defaults\n",
hpp->revision);
hpp = &pci_default_type0;
}
pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, hpp->cache_line_size);
pci_write_config_byte(dev, PCI_LATENCY_TIMER, hpp->latency_timer);
pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
if (hpp->enable_serr)
pci_cmd |= PCI_COMMAND_SERR;
if (hpp->enable_perr)
pci_cmd |= PCI_COMMAND_PARITY;
pci_write_config_word(dev, PCI_COMMAND, pci_cmd);
/* Program bridge control value */
if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER,
hpp->latency_timer);
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl);
if (hpp->enable_perr)
pci_bctl |= PCI_BRIDGE_CTL_PARITY;
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl);
}
}
static void program_hpp_type1(struct pci_dev *dev, struct hpp_type1 *hpp)
{
int pos;
if (!hpp)
return;
pos = pci_find_capability(dev, PCI_CAP_ID_PCIX);
if (!pos)
return;
pci_warn(dev, "PCI-X settings not supported\n");
}
static bool pcie_root_rcb_set(struct pci_dev *dev)
{
struct pci_dev *rp = pcie_find_root_port(dev);
u16 lnkctl;
if (!rp)
return false;
pcie_capability_read_word(rp, PCI_EXP_LNKCTL, &lnkctl);
if (lnkctl & PCI_EXP_LNKCTL_RCB)
return true;
return false;
}
static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
{
int pos;
u32 reg32;
if (!hpp)
return;
if (!pci_is_pcie(dev))
return;
if (hpp->revision > 1) {
pci_warn(dev, "PCIe settings rev %d not supported\n",
hpp->revision);
return;
}
/*
* Don't allow _HPX to change MPS or MRRS settings. We manage
* those to make sure they're consistent with the rest of the
* platform.
*/
hpp->pci_exp_devctl_and |= PCI_EXP_DEVCTL_PAYLOAD |
PCI_EXP_DEVCTL_READRQ;
hpp->pci_exp_devctl_or &= ~(PCI_EXP_DEVCTL_PAYLOAD |
PCI_EXP_DEVCTL_READRQ);
/* Initialize Device Control Register */
pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
~hpp->pci_exp_devctl_and, hpp->pci_exp_devctl_or);
/* Initialize Link Control Register */
if (pcie_cap_has_lnkctl(dev)) {
/*
* If the Root Port supports Read Completion Boundary of
* 128, set RCB to 128. Otherwise, clear it.
*/
hpp->pci_exp_lnkctl_and |= PCI_EXP_LNKCTL_RCB;
hpp->pci_exp_lnkctl_or &= ~PCI_EXP_LNKCTL_RCB;
if (pcie_root_rcb_set(dev))
hpp->pci_exp_lnkctl_or |= PCI_EXP_LNKCTL_RCB;
pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL,
~hpp->pci_exp_lnkctl_and, hpp->pci_exp_lnkctl_or);
}
/* Find Advanced Error Reporting Enhanced Capability */
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
if (!pos)
return;
/* Initialize Uncorrectable Error Mask Register */
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &reg32);
reg32 = (reg32 & hpp->unc_err_mask_and) | hpp->unc_err_mask_or;
pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, reg32);
/* Initialize Uncorrectable Error Severity Register */
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &reg32);
reg32 = (reg32 & hpp->unc_err_sever_and) | hpp->unc_err_sever_or;
pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, reg32);
/* Initialize Correctable Error Mask Register */
pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &reg32);
reg32 = (reg32 & hpp->cor_err_mask_and) | hpp->cor_err_mask_or;
pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg32);
/* Initialize Advanced Error Capabilities and Control Register */
pci_read_config_dword(dev, pos + PCI_ERR_CAP, &reg32);
reg32 = (reg32 & hpp->adv_err_cap_and) | hpp->adv_err_cap_or;
/* Don't enable ECRC generation or checking if unsupported */
if (!(reg32 & PCI_ERR_CAP_ECRC_GENC))
reg32 &= ~PCI_ERR_CAP_ECRC_GENE;
if (!(reg32 & PCI_ERR_CAP_ECRC_CHKC))
reg32 &= ~PCI_ERR_CAP_ECRC_CHKE;
pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32);
/*
* FIXME: The following two registers are not supported yet.
*
* o Secondary Uncorrectable Error Severity Register
* o Secondary Uncorrectable Error Mask Register
*/
}
static u16 hpx3_device_type(struct pci_dev *dev)
{
u16 pcie_type = pci_pcie_type(dev);
const int pcie_to_hpx3_type[] = {
[PCI_EXP_TYPE_ENDPOINT] = HPX_TYPE_ENDPOINT,
[PCI_EXP_TYPE_LEG_END] = HPX_TYPE_LEG_END,
[PCI_EXP_TYPE_RC_END] = HPX_TYPE_RC_END,
[PCI_EXP_TYPE_RC_EC] = HPX_TYPE_RC_EC,
[PCI_EXP_TYPE_ROOT_PORT] = HPX_TYPE_ROOT_PORT,
[PCI_EXP_TYPE_UPSTREAM] = HPX_TYPE_UPSTREAM,
[PCI_EXP_TYPE_DOWNSTREAM] = HPX_TYPE_DOWNSTREAM,
[PCI_EXP_TYPE_PCI_BRIDGE] = HPX_TYPE_PCI_BRIDGE,
[PCI_EXP_TYPE_PCIE_BRIDGE] = HPX_TYPE_PCIE_BRIDGE,
};
if (pcie_type >= ARRAY_SIZE(pcie_to_hpx3_type))
return 0;
return pcie_to_hpx3_type[pcie_type];
}
static u8 hpx3_function_type(struct pci_dev *dev)
{
if (dev->is_virtfn)
return HPX_FN_SRIOV_VIRT;
else if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV) > 0)
return HPX_FN_SRIOV_PHYS;
else
return HPX_FN_NORMAL;
}
static bool hpx3_cap_ver_matches(u8 pcie_cap_id, u8 hpx3_cap_id)
{
u8 cap_ver = hpx3_cap_id & 0xf;
if ((hpx3_cap_id & BIT(4)) && cap_ver >= pcie_cap_id)
return true;
else if (cap_ver == pcie_cap_id)
return true;
return false;
}
static void program_hpx_type3_register(struct pci_dev *dev,
const struct hpx_type3 *reg)
{
u32 match_reg, write_reg, header, orig_value;
u16 pos;
if (!(hpx3_device_type(dev) & reg->device_type))
return;
if (!(hpx3_function_type(dev) & reg->function_type))
return;
switch (reg->config_space_location) {
case HPX_CFG_PCICFG:
pos = 0;
break;
case HPX_CFG_PCIE_CAP:
pos = pci_find_capability(dev, reg->pci_exp_cap_id);
if (pos == 0)
return;
break;
case HPX_CFG_PCIE_CAP_EXT:
pos = pci_find_ext_capability(dev, reg->pci_exp_cap_id);
if (pos == 0)
return;
pci_read_config_dword(dev, pos, &header);
if (!hpx3_cap_ver_matches(PCI_EXT_CAP_VER(header),
reg->pci_exp_cap_ver))
return;
break;
case HPX_CFG_VEND_CAP: /* Fall through */
case HPX_CFG_DVSEC: /* Fall through */
default:
pci_warn(dev, "Encountered _HPX type 3 with unsupported config space location");
return;
}
pci_read_config_dword(dev, pos + reg->match_offset, &match_reg);
if ((match_reg & reg->match_mask_and) != reg->match_value)
return;
pci_read_config_dword(dev, pos + reg->reg_offset, &write_reg);
orig_value = write_reg;
write_reg &= reg->reg_mask_and;
write_reg |= reg->reg_mask_or;
if (orig_value == write_reg)
return;
pci_write_config_dword(dev, pos + reg->reg_offset, write_reg);
pci_dbg(dev, "Applied _HPX3 at [0x%x]: 0x%08x -> 0x%08x",
pos, orig_value, write_reg);
}
static void program_hpx_type3(struct pci_dev *dev, struct hpx_type3 *hpx3)
{
if (!hpx3)
return;
if (!pci_is_pcie(dev))
return;
program_hpx_type3_register(dev, hpx3);
}
int pci_configure_extended_tags(struct pci_dev *dev, void *ign)
{
struct pci_host_bridge *host;
@ -2369,13 +2112,6 @@ static void pci_configure_serr(struct pci_dev *dev)
static void pci_configure_device(struct pci_dev *dev)
{
static const struct hotplug_program_ops hp_ops = {
.program_type0 = program_hpp_type0,
.program_type1 = program_hpp_type1,
.program_type2 = program_hpp_type2,
.program_type3 = program_hpx_type3,
};
pci_configure_mps(dev);
pci_configure_extended_tags(dev, NULL);
pci_configure_relaxed_ordering(dev);
@ -2383,7 +2119,7 @@ static void pci_configure_device(struct pci_dev *dev)
pci_configure_eetlp_prefix(dev);
pci_configure_serr(dev);
pci_acpi_program_hp_params(dev, &hp_ops);
pci_acpi_program_hp_params(dev);
}
static void pci_release_capabilities(struct pci_dev *dev)
@ -2764,12 +2500,8 @@ static int only_one_child(struct pci_bus *bus)
* A PCIe Downstream Port normally leads to a Link with only Device
* 0 on it (PCIe spec r3.1, sec 7.3.1). As an optimization, scan
* only for Device 0 in that situation.
*
* Checking has_secondary_link is a hack to identify Downstream
* Ports because sometimes Switches are configured such that the
* PCIe Port Type labels are backwards.
*/
if (bridge && pci_is_pcie(bridge) && bridge->has_secondary_link)
if (bridge && pci_is_pcie(bridge) && pcie_downstream_port(bridge))
return 1;
return 0;

View File

@ -13,6 +13,8 @@
#include <linux/pci_regs.h>
#include <linux/types.h>
#include "pci.h"
/**
* pci_vc_save_restore_dwords - Save or restore a series of dwords
* @dev: device
@ -105,7 +107,7 @@ static void pci_vc_enable(struct pci_dev *dev, int pos, int res)
struct pci_dev *link = NULL;
/* Enable VCs from the downstream device */
if (!dev->has_secondary_link)
if (!pci_is_pcie(dev) || !pcie_downstream_port(dev))
return;
ctrl_pos = pos + PCI_VC_RES_CTRL + (res * PCI_CAP_VC_PER_VC_SIZEOF);

View File

@ -419,7 +419,6 @@ struct pci_dev {
unsigned int broken_intx_masking:1; /* INTx masking can't be used */
unsigned int io_window_1k:1; /* Intel bridge 1K I/O windows */
unsigned int irq_managed:1;
unsigned int has_secondary_link:1;
unsigned int non_compliant_bars:1; /* Broken BARs; ignore them */
unsigned int is_probed:1; /* Device probing in progress */
unsigned int link_active_reporting:1;/* Device capable of reporting link active */

View File

@ -86,114 +86,14 @@ void pci_hp_deregister(struct hotplug_slot *slot);
#define pci_hp_initialize(slot, bus, nr, name) \
__pci_hp_initialize(slot, bus, nr, name, THIS_MODULE, KBUILD_MODNAME)
/* PCI Setting Record (Type 0) */
struct hpp_type0 {
u32 revision;
u8 cache_line_size;
u8 latency_timer;
u8 enable_serr;
u8 enable_perr;
};
/* PCI-X Setting Record (Type 1) */
struct hpp_type1 {
u32 revision;
u8 max_mem_read;
u8 avg_max_split;
u16 tot_max_split;
};
/* PCI Express Setting Record (Type 2) */
struct hpp_type2 {
u32 revision;
u32 unc_err_mask_and;
u32 unc_err_mask_or;
u32 unc_err_sever_and;
u32 unc_err_sever_or;
u32 cor_err_mask_and;
u32 cor_err_mask_or;
u32 adv_err_cap_and;
u32 adv_err_cap_or;
u16 pci_exp_devctl_and;
u16 pci_exp_devctl_or;
u16 pci_exp_lnkctl_and;
u16 pci_exp_lnkctl_or;
u32 sec_unc_err_sever_and;
u32 sec_unc_err_sever_or;
u32 sec_unc_err_mask_and;
u32 sec_unc_err_mask_or;
};
/*
* _HPX PCI Express Setting Record (Type 3)
*/
struct hpx_type3 {
u16 device_type;
u16 function_type;
u16 config_space_location;
u16 pci_exp_cap_id;
u16 pci_exp_cap_ver;
u16 pci_exp_vendor_id;
u16 dvsec_id;
u16 dvsec_rev;
u16 match_offset;
u32 match_mask_and;
u32 match_value;
u16 reg_offset;
u32 reg_mask_and;
u32 reg_mask_or;
};
struct hotplug_program_ops {
void (*program_type0)(struct pci_dev *dev, struct hpp_type0 *hpp);
void (*program_type1)(struct pci_dev *dev, struct hpp_type1 *hpp);
void (*program_type2)(struct pci_dev *dev, struct hpp_type2 *hpp);
void (*program_type3)(struct pci_dev *dev, struct hpx_type3 *hpp);
};
enum hpx_type3_dev_type {
HPX_TYPE_ENDPOINT = BIT(0),
HPX_TYPE_LEG_END = BIT(1),
HPX_TYPE_RC_END = BIT(2),
HPX_TYPE_RC_EC = BIT(3),
HPX_TYPE_ROOT_PORT = BIT(4),
HPX_TYPE_UPSTREAM = BIT(5),
HPX_TYPE_DOWNSTREAM = BIT(6),
HPX_TYPE_PCI_BRIDGE = BIT(7),
HPX_TYPE_PCIE_BRIDGE = BIT(8),
};
enum hpx_type3_fn_type {
HPX_FN_NORMAL = BIT(0),
HPX_FN_SRIOV_PHYS = BIT(1),
HPX_FN_SRIOV_VIRT = BIT(2),
};
enum hpx_type3_cfg_loc {
HPX_CFG_PCICFG = 0,
HPX_CFG_PCIE_CAP = 1,
HPX_CFG_PCIE_CAP_EXT = 2,
HPX_CFG_VEND_CAP = 3,
HPX_CFG_DVSEC = 4,
HPX_CFG_MAX,
};
#ifdef CONFIG_ACPI
#include <linux/acpi.h>
int pci_acpi_program_hp_params(struct pci_dev *dev,
const struct hotplug_program_ops *hp_ops);
bool pciehp_is_native(struct pci_dev *bridge);
int acpi_get_hp_hw_control_from_firmware(struct pci_dev *bridge);
bool shpchp_is_native(struct pci_dev *bridge);
int acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle);
int acpi_pci_detect_ejectable(acpi_handle handle);
#else
static inline int pci_acpi_program_hp_params(struct pci_dev *dev,
const struct hotplug_program_ops *hp_ops)
{
return -ENODEV;
}
static inline int acpi_get_hp_hw_control_from_firmware(struct pci_dev *bridge)
{
return 0;