Commit 3ea6f739 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/hotplug'

  - Fix RPA and RPA DLPAR refcount issues (Tyrel Datwyler)

  - Stop exporting pci_get_hp_params() (Alexandru Gagniuc)

  - Simplify _HPP, _HPX parsing (Alexandru Gagniuc)

  - Add support for _HPX Type 3 settings (Alexandru Gagniuc)

  - Tell firmware we support _HPX Type 3 via _OSC (Alexandru Gagniuc)

* pci/hotplug:
  PCI/ACPI: Advertise _HPX Type 3 support via _OSC
  PCI/ACPI: Implement _HPX Type 3 Setting Record
  PCI/ACPI: Remove the need for 'struct hotplug_params'
  PCI/ACPI: Do not export pci_get_hp_params()
  PCI: rpaphp: Get/put device node reference during slot alloc/dealloc
  PCI: rpadlpar: Fix leaked device_node references in add/remove paths
parents 178901bf ba11edc6
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -145,6 +145,7 @@ static struct pci_osc_bit_struct pci_osc_support_bit[] = {
	{ OSC_PCI_CLOCK_PM_SUPPORT, "ClockPM" },
	{ OSC_PCI_SEGMENT_GROUPS_SUPPORT, "Segments" },
	{ OSC_PCI_MSI_SUPPORT, "MSI" },
	{ OSC_PCI_HPX_TYPE_3_SUPPORT, "HPX-Type3" },
};

static struct pci_osc_bit_struct pci_osc_control_bit[] = {
@@ -446,6 +447,7 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm,
	 * PCI domains, so we indicate this in _OSC support capabilities.
	 */
	support = OSC_PCI_SEGMENT_GROUPS_SUPPORT;
	support |= OSC_PCI_HPX_TYPE_3_SUPPORT;
	if (pci_ext_cfg_avail())
		support |= OSC_PCI_EXT_CONFIG_SUPPORT;
	if (pcie_aspm_support_enabled())
+4 −0
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ static struct device_node *find_vio_slot_node(char *drc_name)
		if (rc == 0)
			break;
	}
	of_node_put(parent);

	return dn;
}
@@ -71,6 +72,7 @@ static struct device_node *find_php_slot_pci_node(char *drc_name,
	return np;
}

/* Returns a device_node with its reference count incremented */
static struct device_node *find_dlpar_node(char *drc_name, int *node_type)
{
	struct device_node *dn;
@@ -306,6 +308,7 @@ int dlpar_add_slot(char *drc_name)
			rc = dlpar_add_phb(drc_name, dn);
			break;
	}
	of_node_put(dn);

	printk(KERN_INFO "%s: slot %s added\n", DLPAR_MODULE_NAME, drc_name);
exit:
@@ -439,6 +442,7 @@ int dlpar_remove_slot(char *drc_name)
			rc = dlpar_remove_pci_slot(drc_name, dn);
			break;
	}
	of_node_put(dn);
	vm_unmap_aliases();

	printk(KERN_INFO "%s: slot %s removed\n", DLPAR_MODULE_NAME, drc_name);
+2 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
/* free up the memory used by a slot */
void dealloc_slot_struct(struct slot *slot)
{
	of_node_put(slot->dn);
	kfree(slot->name);
	kfree(slot);
}
@@ -36,7 +37,7 @@ struct slot *alloc_slot_struct(struct device_node *dn,
	slot->name = kstrdup(drc_name, GFP_KERNEL);
	if (!slot->name)
		goto error_slot;
	slot->dn = dn;
	slot->dn = of_node_get(dn);
	slot->index = drc_index;
	slot->power_domain = power_domain;
	slot->hotplug_slot.ops = &rpaphp_hotplug_slot_ops;
+121 −51
Original line number Diff line number Diff line
@@ -119,7 +119,7 @@ phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle)
}

static acpi_status decode_type0_hpx_record(union acpi_object *record,
					   struct hotplug_params *hpx)
					   struct hpp_type0 *hpx0)
{
	int i;
	union acpi_object *fields = record->package.elements;
@@ -132,12 +132,11 @@ static acpi_status decode_type0_hpx_record(union acpi_object *record,
		for (i = 2; i < 6; i++)
			if (fields[i].type != ACPI_TYPE_INTEGER)
				return AE_ERROR;
		hpx->t0 = &hpx->type0_data;
		hpx->t0->revision        = revision;
		hpx->t0->cache_line_size = fields[2].integer.value;
		hpx->t0->latency_timer   = fields[3].integer.value;
		hpx->t0->enable_serr     = fields[4].integer.value;
		hpx->t0->enable_perr     = fields[5].integer.value;
		hpx0->revision        = revision;
		hpx0->cache_line_size = fields[2].integer.value;
		hpx0->latency_timer   = fields[3].integer.value;
		hpx0->enable_serr     = fields[4].integer.value;
		hpx0->enable_perr     = fields[5].integer.value;
		break;
	default:
		printk(KERN_WARNING
@@ -149,7 +148,7 @@ static acpi_status decode_type0_hpx_record(union acpi_object *record,
}

static acpi_status decode_type1_hpx_record(union acpi_object *record,
					   struct hotplug_params *hpx)
					   struct hpp_type1 *hpx1)
{
	int i;
	union acpi_object *fields = record->package.elements;
@@ -162,11 +161,10 @@ static acpi_status decode_type1_hpx_record(union acpi_object *record,
		for (i = 2; i < 5; i++)
			if (fields[i].type != ACPI_TYPE_INTEGER)
				return AE_ERROR;
		hpx->t1 = &hpx->type1_data;
		hpx->t1->revision      = revision;
		hpx->t1->max_mem_read  = fields[2].integer.value;
		hpx->t1->avg_max_split = fields[3].integer.value;
		hpx->t1->tot_max_split = fields[4].integer.value;
		hpx1->revision      = revision;
		hpx1->max_mem_read  = fields[2].integer.value;
		hpx1->avg_max_split = fields[3].integer.value;
		hpx1->tot_max_split = fields[4].integer.value;
		break;
	default:
		printk(KERN_WARNING
@@ -178,7 +176,7 @@ static acpi_status decode_type1_hpx_record(union acpi_object *record,
}

static acpi_status decode_type2_hpx_record(union acpi_object *record,
					   struct hotplug_params *hpx)
					   struct hpp_type2 *hpx2)
{
	int i;
	union acpi_object *fields = record->package.elements;
@@ -191,24 +189,23 @@ static acpi_status decode_type2_hpx_record(union acpi_object *record,
		for (i = 2; i < 18; i++)
			if (fields[i].type != ACPI_TYPE_INTEGER)
				return AE_ERROR;
		hpx->t2 = &hpx->type2_data;
		hpx->t2->revision      = revision;
		hpx->t2->unc_err_mask_and      = fields[2].integer.value;
		hpx->t2->unc_err_mask_or       = fields[3].integer.value;
		hpx->t2->unc_err_sever_and     = fields[4].integer.value;
		hpx->t2->unc_err_sever_or      = fields[5].integer.value;
		hpx->t2->cor_err_mask_and      = fields[6].integer.value;
		hpx->t2->cor_err_mask_or       = fields[7].integer.value;
		hpx->t2->adv_err_cap_and       = fields[8].integer.value;
		hpx->t2->adv_err_cap_or        = fields[9].integer.value;
		hpx->t2->pci_exp_devctl_and    = fields[10].integer.value;
		hpx->t2->pci_exp_devctl_or     = fields[11].integer.value;
		hpx->t2->pci_exp_lnkctl_and    = fields[12].integer.value;
		hpx->t2->pci_exp_lnkctl_or     = fields[13].integer.value;
		hpx->t2->sec_unc_err_sever_and = fields[14].integer.value;
		hpx->t2->sec_unc_err_sever_or  = fields[15].integer.value;
		hpx->t2->sec_unc_err_mask_and  = fields[16].integer.value;
		hpx->t2->sec_unc_err_mask_or   = fields[17].integer.value;
		hpx2->revision      = revision;
		hpx2->unc_err_mask_and      = fields[2].integer.value;
		hpx2->unc_err_mask_or       = fields[3].integer.value;
		hpx2->unc_err_sever_and     = fields[4].integer.value;
		hpx2->unc_err_sever_or      = fields[5].integer.value;
		hpx2->cor_err_mask_and      = fields[6].integer.value;
		hpx2->cor_err_mask_or       = fields[7].integer.value;
		hpx2->adv_err_cap_and       = fields[8].integer.value;
		hpx2->adv_err_cap_or        = fields[9].integer.value;
		hpx2->pci_exp_devctl_and    = fields[10].integer.value;
		hpx2->pci_exp_devctl_or     = fields[11].integer.value;
		hpx2->pci_exp_lnkctl_and    = fields[12].integer.value;
		hpx2->pci_exp_lnkctl_or     = fields[13].integer.value;
		hpx2->sec_unc_err_sever_and = fields[14].integer.value;
		hpx2->sec_unc_err_sever_or  = fields[15].integer.value;
		hpx2->sec_unc_err_mask_and  = fields[16].integer.value;
		hpx2->sec_unc_err_mask_or   = fields[17].integer.value;
		break;
	default:
		printk(KERN_WARNING
@@ -219,17 +216,76 @@ static acpi_status decode_type2_hpx_record(union acpi_object *record,
	return AE_OK;
}

static acpi_status acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx)
static void parse_hpx3_register(struct hpx_type3 *hpx3_reg,
				union acpi_object *reg_fields)
{
	hpx3_reg->device_type            = reg_fields[0].integer.value;
	hpx3_reg->function_type          = reg_fields[1].integer.value;
	hpx3_reg->config_space_location  = reg_fields[2].integer.value;
	hpx3_reg->pci_exp_cap_id         = reg_fields[3].integer.value;
	hpx3_reg->pci_exp_cap_ver        = reg_fields[4].integer.value;
	hpx3_reg->pci_exp_vendor_id      = reg_fields[5].integer.value;
	hpx3_reg->dvsec_id               = reg_fields[6].integer.value;
	hpx3_reg->dvsec_rev              = reg_fields[7].integer.value;
	hpx3_reg->match_offset           = reg_fields[8].integer.value;
	hpx3_reg->match_mask_and         = reg_fields[9].integer.value;
	hpx3_reg->match_value            = reg_fields[10].integer.value;
	hpx3_reg->reg_offset             = reg_fields[11].integer.value;
	hpx3_reg->reg_mask_and           = reg_fields[12].integer.value;
	hpx3_reg->reg_mask_or            = reg_fields[13].integer.value;
}

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 *fields = record->package.elements;
	u32 desc_count, expected_length, revision;
	union acpi_object *reg_fields;
	struct hpx_type3 hpx3;
	int i;

	revision = fields[1].integer.value;
	switch (revision) {
	case 1:
		desc_count = fields[2].integer.value;
		expected_length = 3 + desc_count * 14;

		if (record->package.count != expected_length)
			return AE_ERROR;

		for (i = 2; i < expected_length; i++)
			if (fields[i].type != ACPI_TYPE_INTEGER)
				return AE_ERROR;

		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);
		}

		break;
	default:
		printk(KERN_WARNING
			"%s: Type 3 Revision %d record not supported\n",
			__func__, revision);
		return AE_ERROR;
	}
	return AE_OK;
}

static acpi_status acpi_run_hpx(struct pci_dev *dev, acpi_handle handle,
				const struct hotplug_program_ops *hp_ops)
{
	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;
	u32 type;
	int i;

	/* Clear the return buffer with zeros */
	memset(hpx, 0, sizeof(struct hotplug_params));

	status = acpi_evaluate_object(handle, "_HPX", NULL, &buffer);
	if (ACPI_FAILURE(status))
		return status;
@@ -257,17 +313,28 @@ static acpi_status acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx)
		type = fields[0].integer.value;
		switch (type) {
		case 0:
			status = decode_type0_hpx_record(record, hpx);
			memset(&hpx0, 0, sizeof(hpx0));
			status = decode_type0_hpx_record(record, &hpx0);
			if (ACPI_FAILURE(status))
				goto exit;
			hp_ops->program_type0(dev, &hpx0);
			break;
		case 1:
			status = decode_type1_hpx_record(record, hpx);
			memset(&hpx1, 0, sizeof(hpx1));
			status = decode_type1_hpx_record(record, &hpx1);
			if (ACPI_FAILURE(status))
				goto exit;
			hp_ops->program_type1(dev, &hpx1);
			break;
		case 2:
			status = decode_type2_hpx_record(record, hpx);
			memset(&hpx2, 0, sizeof(hpx2));
			status = decode_type2_hpx_record(record, &hpx2);
			if (ACPI_FAILURE(status))
				goto exit;
			hp_ops->program_type2(dev, &hpx2);
			break;
		case 3:
			status = program_type3_hpx_record(dev, record, hp_ops);
			if (ACPI_FAILURE(status))
				goto exit;
			break;
@@ -283,14 +350,16 @@ static acpi_status acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx)
	return status;
}

static acpi_status acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp)
static acpi_status acpi_run_hpp(struct pci_dev *dev, acpi_handle handle,
				const struct hotplug_program_ops *hp_ops)
{
	acpi_status status;
	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object *package, *fields;
	struct hpp_type0 hpp0;
	int i;

	memset(hpp, 0, sizeof(struct hotplug_params));
	memset(&hpp0, 0, sizeof(hpp0));

	status = acpi_evaluate_object(handle, "_HPP", NULL, &buffer);
	if (ACPI_FAILURE(status))
@@ -311,12 +380,13 @@ static acpi_status acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp)
		}
	}

	hpp->t0 = &hpp->type0_data;
	hpp->t0->revision        = 1;
	hpp->t0->cache_line_size = fields[0].integer.value;
	hpp->t0->latency_timer   = fields[1].integer.value;
	hpp->t0->enable_serr     = fields[2].integer.value;
	hpp->t0->enable_perr     = fields[3].integer.value;
	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;

	hp_ops->program_type0(dev, &hpp0);

exit:
	kfree(buffer.pointer);
@@ -328,7 +398,8 @@ static acpi_status acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp)
 * @dev - the pci_dev for which we want parameters
 * @hpp - allocated by the caller
 */
int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp)
int pci_acpi_program_hp_params(struct pci_dev *dev,
			       const struct hotplug_program_ops *hp_ops)
{
	acpi_status status;
	acpi_handle handle, phandle;
@@ -351,10 +422,10 @@ int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp)
	 * this pci dev.
	 */
	while (handle) {
		status = acpi_run_hpx(handle, hpp);
		status = acpi_run_hpx(dev, handle, hp_ops);
		if (ACPI_SUCCESS(status))
			return 0;
		status = acpi_run_hpp(handle, hpp);
		status = acpi_run_hpp(dev, handle, hp_ops);
		if (ACPI_SUCCESS(status))
			return 0;
		if (acpi_is_root_bridge(handle))
@@ -366,7 +437,6 @@ int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp)
	}
	return -ENODEV;
}
EXPORT_SYMBOL_GPL(pci_get_hp_params);

/**
 * pciehp_is_native - Check whether a hotplug port is handled by the OS
+120 −10
Original line number Diff line number Diff line
@@ -2077,6 +2077,119 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
	 */
}

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;
@@ -2257,8 +2370,12 @@ static void pci_configure_serr(struct pci_dev *dev)

static void pci_configure_device(struct pci_dev *dev)
{
	struct hotplug_params hpp;
	int ret;
	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);
@@ -2267,14 +2384,7 @@ static void pci_configure_device(struct pci_dev *dev)
	pci_configure_eetlp_prefix(dev);
	pci_configure_serr(dev);

	memset(&hpp, 0, sizeof(hpp));
	ret = pci_get_hp_params(dev, &hpp);
	if (ret)
		return;

	program_hpp_type2(dev, hpp.t2);
	program_hpp_type1(dev, hpp.t1);
	program_hpp_type0(dev, hpp.t0);
	pci_acpi_program_hp_params(dev, &hp_ops);
}

static void pci_release_capabilities(struct pci_dev *dev)
Loading