2007-05-08 10:58:35 +08:00
|
|
|
/*
|
|
|
|
* Copyright 2006 Jake Moilanen <moilanen@austin.ibm.com>, IBM Corp.
|
|
|
|
* Copyright 2006-2007 Michael Ellerman, IBM Corp.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; version 2 of the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/irq.h>
|
|
|
|
#include <linux/msi.h>
|
|
|
|
|
|
|
|
#include <asm/rtas.h>
|
|
|
|
#include <asm/hw_irq.h>
|
|
|
|
#include <asm/ppc-pci.h>
|
2014-07-16 10:02:43 +08:00
|
|
|
#include <asm/machdep.h>
|
2007-05-08 10:58:35 +08:00
|
|
|
|
|
|
|
static int query_token, change_token;
|
|
|
|
|
|
|
|
#define RTAS_QUERY_FN 0
|
|
|
|
#define RTAS_CHANGE_FN 1
|
|
|
|
#define RTAS_RESET_FN 2
|
|
|
|
#define RTAS_CHANGE_MSI_FN 3
|
|
|
|
#define RTAS_CHANGE_MSIX_FN 4
|
2013-05-03 19:30:59 +08:00
|
|
|
#define RTAS_CHANGE_32MSI_FN 5
|
2007-05-08 10:58:35 +08:00
|
|
|
|
|
|
|
/* RTAS Helpers */
|
|
|
|
|
|
|
|
static int rtas_change_msi(struct pci_dn *pdn, u32 func, u32 num_irqs)
|
|
|
|
{
|
|
|
|
u32 addr, seq_num, rtas_ret[3];
|
|
|
|
unsigned long buid;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
addr = rtas_config_addr(pdn->busno, pdn->devfn, 0);
|
|
|
|
buid = pdn->phb->buid;
|
|
|
|
|
|
|
|
seq_num = 1;
|
|
|
|
do {
|
2013-05-03 19:30:59 +08:00
|
|
|
if (func == RTAS_CHANGE_MSI_FN || func == RTAS_CHANGE_MSIX_FN ||
|
|
|
|
func == RTAS_CHANGE_32MSI_FN)
|
2007-05-08 10:58:35 +08:00
|
|
|
rc = rtas_call(change_token, 6, 4, rtas_ret, addr,
|
|
|
|
BUID_HI(buid), BUID_LO(buid),
|
|
|
|
func, num_irqs, seq_num);
|
|
|
|
else
|
|
|
|
rc = rtas_call(change_token, 6, 3, rtas_ret, addr,
|
|
|
|
BUID_HI(buid), BUID_LO(buid),
|
|
|
|
func, num_irqs, seq_num);
|
|
|
|
|
|
|
|
seq_num = rtas_ret[1];
|
|
|
|
} while (rtas_busy_delay(rc));
|
|
|
|
|
2007-09-20 14:36:50 +08:00
|
|
|
/*
|
2009-01-23 04:54:33 +08:00
|
|
|
* If the RTAS call succeeded, return the number of irqs allocated.
|
|
|
|
* If not, make sure we return a negative error code.
|
2007-09-20 14:36:50 +08:00
|
|
|
*/
|
2009-01-23 04:54:33 +08:00
|
|
|
if (rc == 0)
|
|
|
|
rc = rtas_ret[0];
|
|
|
|
else if (rc > 0)
|
|
|
|
rc = -rc;
|
2007-05-08 10:58:35 +08:00
|
|
|
|
2007-09-20 14:36:50 +08:00
|
|
|
pr_debug("rtas_msi: ibm,change_msi(func=%d,num=%d), got %d rc = %d\n",
|
|
|
|
func, num_irqs, rtas_ret[0], rc);
|
2007-05-08 10:58:35 +08:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rtas_disable_msi(struct pci_dev *pdev)
|
|
|
|
{
|
|
|
|
struct pci_dn *pdn;
|
|
|
|
|
2013-05-22 06:58:21 +08:00
|
|
|
pdn = pci_get_pdn(pdev);
|
2007-05-08 10:58:35 +08:00
|
|
|
if (!pdn)
|
|
|
|
return;
|
|
|
|
|
2011-03-03 23:41:02 +08:00
|
|
|
/*
|
|
|
|
* disabling MSI with the explicit interface also disables MSI-X
|
|
|
|
*/
|
|
|
|
if (rtas_change_msi(pdn, RTAS_CHANGE_MSI_FN, 0) != 0) {
|
|
|
|
/*
|
|
|
|
* may have failed because explicit interface is not
|
|
|
|
* present
|
|
|
|
*/
|
|
|
|
if (rtas_change_msi(pdn, RTAS_CHANGE_FN, 0) != 0) {
|
|
|
|
pr_debug("rtas_msi: Setting MSIs to 0 failed!\n");
|
|
|
|
}
|
|
|
|
}
|
2007-05-08 10:58:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int rtas_query_irq_number(struct pci_dn *pdn, int offset)
|
|
|
|
{
|
|
|
|
u32 addr, rtas_ret[2];
|
|
|
|
unsigned long buid;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
addr = rtas_config_addr(pdn->busno, pdn->devfn, 0);
|
|
|
|
buid = pdn->phb->buid;
|
|
|
|
|
|
|
|
do {
|
|
|
|
rc = rtas_call(query_token, 4, 3, rtas_ret, addr,
|
|
|
|
BUID_HI(buid), BUID_LO(buid), offset);
|
|
|
|
} while (rtas_busy_delay(rc));
|
|
|
|
|
|
|
|
if (rc) {
|
|
|
|
pr_debug("rtas_msi: error (%d) querying source number\n", rc);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rtas_ret[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rtas_teardown_msi_irqs(struct pci_dev *pdev)
|
|
|
|
{
|
|
|
|
struct msi_desc *entry;
|
|
|
|
|
|
|
|
list_for_each_entry(entry, &pdev->msi_list, list) {
|
|
|
|
if (entry->irq == NO_IRQ)
|
|
|
|
continue;
|
|
|
|
|
2011-03-25 23:45:20 +08:00
|
|
|
irq_set_msi_desc(entry->irq, NULL);
|
2007-05-08 10:58:35 +08:00
|
|
|
irq_dispose_mapping(entry->irq);
|
|
|
|
}
|
|
|
|
|
|
|
|
rtas_disable_msi(pdev);
|
|
|
|
}
|
|
|
|
|
2009-01-23 04:54:31 +08:00
|
|
|
static int check_req(struct pci_dev *pdev, int nvec, char *prop_name)
|
2007-05-08 10:58:35 +08:00
|
|
|
{
|
|
|
|
struct device_node *dn;
|
|
|
|
struct pci_dn *pdn;
|
2013-12-12 12:59:40 +08:00
|
|
|
const __be32 *p;
|
|
|
|
u32 req_msi;
|
2007-05-08 10:58:35 +08:00
|
|
|
|
2013-05-22 06:58:21 +08:00
|
|
|
pdn = pci_get_pdn(pdev);
|
2007-05-08 10:58:35 +08:00
|
|
|
if (!pdn)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
dn = pdn->node;
|
|
|
|
|
2013-12-12 12:59:40 +08:00
|
|
|
p = of_get_property(dn, prop_name, NULL);
|
|
|
|
if (!p) {
|
2009-01-23 04:54:31 +08:00
|
|
|
pr_debug("rtas_msi: No %s on %s\n", prop_name, dn->full_name);
|
2007-05-08 10:58:35 +08:00
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2013-12-12 12:59:40 +08:00
|
|
|
req_msi = be32_to_cpup(p);
|
|
|
|
if (req_msi < nvec) {
|
2009-01-23 04:54:31 +08:00
|
|
|
pr_debug("rtas_msi: %s requests < %d MSIs\n", prop_name, nvec);
|
2009-02-17 08:18:49 +08:00
|
|
|
|
2013-12-12 12:59:40 +08:00
|
|
|
if (req_msi == 0) /* Be paranoid */
|
2009-02-17 08:18:49 +08:00
|
|
|
return -ENOSPC;
|
|
|
|
|
2013-12-12 12:59:40 +08:00
|
|
|
return req_msi;
|
2007-05-08 10:58:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-01-23 04:54:31 +08:00
|
|
|
static int check_req_msi(struct pci_dev *pdev, int nvec)
|
|
|
|
{
|
|
|
|
return check_req(pdev, nvec, "ibm,req#msi");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int check_req_msix(struct pci_dev *pdev, int nvec)
|
|
|
|
{
|
|
|
|
return check_req(pdev, nvec, "ibm,req#msi-x");
|
|
|
|
}
|
|
|
|
|
powerpc/pseries: Implement a quota system for MSIs
There are hardware limitations on the number of available MSIs,
which firmware expresses using a property named "ibm,pe-total-#msi".
This property tells us how many MSIs are available for devices below
the point in the PCI tree where we find the property.
For old firmwares which don't have the property, we assume there are
8 MSIs available per "partitionable endpoint" (PE). The PE can be
found using existing EEH code, which uses the methods described in
PAPR. For our purposes we want the parent of the node that's
identified using this method.
When a driver requests n MSIs for a device, we first establish where
the "ibm,pe-total-#msi" property above that device is, or we find the
PE if the property is not found. In both cases we call this node
the "pe_dn".
We then count all non-bridge devices below the pe_dn, to establish
how many devices in total may need MSIs. The quota is then simply the
total available divided by the number of devices, if the request is
less than or equal to the quota, the request is fine and we're done.
If the request is greater than the quota, we try to determine if there
are any "spare" MSIs which we can give to this device. Spare MSIs are
found by looking for other devices which can never use their full
quota, because their "req#msi(-x)" property is less than the quota.
If we find any spare, we divide the spares by the number of devices
that could request more than their quota. This ensures the spare
MSIs are spread evenly amongst all over-quota requestors.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2009-02-17 08:21:56 +08:00
|
|
|
/* Quota calculation */
|
|
|
|
|
|
|
|
static struct device_node *find_pe_total_msi(struct pci_dev *dev, int *total)
|
|
|
|
{
|
|
|
|
struct device_node *dn;
|
2013-12-12 12:59:40 +08:00
|
|
|
const __be32 *p;
|
powerpc/pseries: Implement a quota system for MSIs
There are hardware limitations on the number of available MSIs,
which firmware expresses using a property named "ibm,pe-total-#msi".
This property tells us how many MSIs are available for devices below
the point in the PCI tree where we find the property.
For old firmwares which don't have the property, we assume there are
8 MSIs available per "partitionable endpoint" (PE). The PE can be
found using existing EEH code, which uses the methods described in
PAPR. For our purposes we want the parent of the node that's
identified using this method.
When a driver requests n MSIs for a device, we first establish where
the "ibm,pe-total-#msi" property above that device is, or we find the
PE if the property is not found. In both cases we call this node
the "pe_dn".
We then count all non-bridge devices below the pe_dn, to establish
how many devices in total may need MSIs. The quota is then simply the
total available divided by the number of devices, if the request is
less than or equal to the quota, the request is fine and we're done.
If the request is greater than the quota, we try to determine if there
are any "spare" MSIs which we can give to this device. Spare MSIs are
found by looking for other devices which can never use their full
quota, because their "req#msi(-x)" property is less than the quota.
If we find any spare, we divide the spares by the number of devices
that could request more than their quota. This ensures the spare
MSIs are spread evenly amongst all over-quota requestors.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2009-02-17 08:21:56 +08:00
|
|
|
|
|
|
|
dn = of_node_get(pci_device_to_OF_node(dev));
|
|
|
|
while (dn) {
|
|
|
|
p = of_get_property(dn, "ibm,pe-total-#msi", NULL);
|
|
|
|
if (p) {
|
|
|
|
pr_debug("rtas_msi: found prop on dn %s\n",
|
|
|
|
dn->full_name);
|
2013-12-12 12:59:40 +08:00
|
|
|
*total = be32_to_cpup(p);
|
powerpc/pseries: Implement a quota system for MSIs
There are hardware limitations on the number of available MSIs,
which firmware expresses using a property named "ibm,pe-total-#msi".
This property tells us how many MSIs are available for devices below
the point in the PCI tree where we find the property.
For old firmwares which don't have the property, we assume there are
8 MSIs available per "partitionable endpoint" (PE). The PE can be
found using existing EEH code, which uses the methods described in
PAPR. For our purposes we want the parent of the node that's
identified using this method.
When a driver requests n MSIs for a device, we first establish where
the "ibm,pe-total-#msi" property above that device is, or we find the
PE if the property is not found. In both cases we call this node
the "pe_dn".
We then count all non-bridge devices below the pe_dn, to establish
how many devices in total may need MSIs. The quota is then simply the
total available divided by the number of devices, if the request is
less than or equal to the quota, the request is fine and we're done.
If the request is greater than the quota, we try to determine if there
are any "spare" MSIs which we can give to this device. Spare MSIs are
found by looking for other devices which can never use their full
quota, because their "req#msi(-x)" property is less than the quota.
If we find any spare, we divide the spares by the number of devices
that could request more than their quota. This ensures the spare
MSIs are spread evenly amongst all over-quota requestors.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2009-02-17 08:21:56 +08:00
|
|
|
return dn;
|
|
|
|
}
|
|
|
|
|
|
|
|
dn = of_get_next_parent(dn);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct device_node *find_pe_dn(struct pci_dev *dev, int *total)
|
|
|
|
{
|
|
|
|
struct device_node *dn;
|
2012-09-08 06:44:13 +08:00
|
|
|
struct eeh_dev *edev;
|
powerpc/pseries: Implement a quota system for MSIs
There are hardware limitations on the number of available MSIs,
which firmware expresses using a property named "ibm,pe-total-#msi".
This property tells us how many MSIs are available for devices below
the point in the PCI tree where we find the property.
For old firmwares which don't have the property, we assume there are
8 MSIs available per "partitionable endpoint" (PE). The PE can be
found using existing EEH code, which uses the methods described in
PAPR. For our purposes we want the parent of the node that's
identified using this method.
When a driver requests n MSIs for a device, we first establish where
the "ibm,pe-total-#msi" property above that device is, or we find the
PE if the property is not found. In both cases we call this node
the "pe_dn".
We then count all non-bridge devices below the pe_dn, to establish
how many devices in total may need MSIs. The quota is then simply the
total available divided by the number of devices, if the request is
less than or equal to the quota, the request is fine and we're done.
If the request is greater than the quota, we try to determine if there
are any "spare" MSIs which we can give to this device. Spare MSIs are
found by looking for other devices which can never use their full
quota, because their "req#msi(-x)" property is less than the quota.
If we find any spare, we divide the spares by the number of devices
that could request more than their quota. This ensures the spare
MSIs are spread evenly amongst all over-quota requestors.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2009-02-17 08:21:56 +08:00
|
|
|
|
|
|
|
/* Found our PE and assume 8 at that point. */
|
|
|
|
|
|
|
|
dn = pci_device_to_OF_node(dev);
|
|
|
|
if (!dn)
|
|
|
|
return NULL;
|
|
|
|
|
2012-09-08 06:44:13 +08:00
|
|
|
/* Get the top level device in the PE */
|
|
|
|
edev = of_node_to_eeh_dev(dn);
|
2012-11-23 10:25:39 +08:00
|
|
|
if (edev->pe)
|
|
|
|
edev = list_first_entry(&edev->pe->edevs, struct eeh_dev, list);
|
2012-09-08 06:44:13 +08:00
|
|
|
dn = eeh_dev_to_of_node(edev);
|
powerpc/pseries: Implement a quota system for MSIs
There are hardware limitations on the number of available MSIs,
which firmware expresses using a property named "ibm,pe-total-#msi".
This property tells us how many MSIs are available for devices below
the point in the PCI tree where we find the property.
For old firmwares which don't have the property, we assume there are
8 MSIs available per "partitionable endpoint" (PE). The PE can be
found using existing EEH code, which uses the methods described in
PAPR. For our purposes we want the parent of the node that's
identified using this method.
When a driver requests n MSIs for a device, we first establish where
the "ibm,pe-total-#msi" property above that device is, or we find the
PE if the property is not found. In both cases we call this node
the "pe_dn".
We then count all non-bridge devices below the pe_dn, to establish
how many devices in total may need MSIs. The quota is then simply the
total available divided by the number of devices, if the request is
less than or equal to the quota, the request is fine and we're done.
If the request is greater than the quota, we try to determine if there
are any "spare" MSIs which we can give to this device. Spare MSIs are
found by looking for other devices which can never use their full
quota, because their "req#msi(-x)" property is less than the quota.
If we find any spare, we divide the spares by the number of devices
that could request more than their quota. This ensures the spare
MSIs are spread evenly amongst all over-quota requestors.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2009-02-17 08:21:56 +08:00
|
|
|
if (!dn)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* We actually want the parent */
|
|
|
|
dn = of_get_parent(dn);
|
|
|
|
if (!dn)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Hardcode of 8 for old firmwares */
|
|
|
|
*total = 8;
|
|
|
|
pr_debug("rtas_msi: using PE dn %s\n", dn->full_name);
|
|
|
|
|
|
|
|
return dn;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct msi_counts {
|
|
|
|
struct device_node *requestor;
|
|
|
|
int num_devices;
|
|
|
|
int request;
|
|
|
|
int quota;
|
|
|
|
int spare;
|
|
|
|
int over_quota;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void *count_non_bridge_devices(struct device_node *dn, void *data)
|
|
|
|
{
|
|
|
|
struct msi_counts *counts = data;
|
2013-12-12 12:59:40 +08:00
|
|
|
const __be32 *p;
|
powerpc/pseries: Implement a quota system for MSIs
There are hardware limitations on the number of available MSIs,
which firmware expresses using a property named "ibm,pe-total-#msi".
This property tells us how many MSIs are available for devices below
the point in the PCI tree where we find the property.
For old firmwares which don't have the property, we assume there are
8 MSIs available per "partitionable endpoint" (PE). The PE can be
found using existing EEH code, which uses the methods described in
PAPR. For our purposes we want the parent of the node that's
identified using this method.
When a driver requests n MSIs for a device, we first establish where
the "ibm,pe-total-#msi" property above that device is, or we find the
PE if the property is not found. In both cases we call this node
the "pe_dn".
We then count all non-bridge devices below the pe_dn, to establish
how many devices in total may need MSIs. The quota is then simply the
total available divided by the number of devices, if the request is
less than or equal to the quota, the request is fine and we're done.
If the request is greater than the quota, we try to determine if there
are any "spare" MSIs which we can give to this device. Spare MSIs are
found by looking for other devices which can never use their full
quota, because their "req#msi(-x)" property is less than the quota.
If we find any spare, we divide the spares by the number of devices
that could request more than their quota. This ensures the spare
MSIs are spread evenly amongst all over-quota requestors.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2009-02-17 08:21:56 +08:00
|
|
|
u32 class;
|
|
|
|
|
|
|
|
pr_debug("rtas_msi: counting %s\n", dn->full_name);
|
|
|
|
|
|
|
|
p = of_get_property(dn, "class-code", NULL);
|
2013-12-12 12:59:40 +08:00
|
|
|
class = p ? be32_to_cpup(p) : 0;
|
powerpc/pseries: Implement a quota system for MSIs
There are hardware limitations on the number of available MSIs,
which firmware expresses using a property named "ibm,pe-total-#msi".
This property tells us how many MSIs are available for devices below
the point in the PCI tree where we find the property.
For old firmwares which don't have the property, we assume there are
8 MSIs available per "partitionable endpoint" (PE). The PE can be
found using existing EEH code, which uses the methods described in
PAPR. For our purposes we want the parent of the node that's
identified using this method.
When a driver requests n MSIs for a device, we first establish where
the "ibm,pe-total-#msi" property above that device is, or we find the
PE if the property is not found. In both cases we call this node
the "pe_dn".
We then count all non-bridge devices below the pe_dn, to establish
how many devices in total may need MSIs. The quota is then simply the
total available divided by the number of devices, if the request is
less than or equal to the quota, the request is fine and we're done.
If the request is greater than the quota, we try to determine if there
are any "spare" MSIs which we can give to this device. Spare MSIs are
found by looking for other devices which can never use their full
quota, because their "req#msi(-x)" property is less than the quota.
If we find any spare, we divide the spares by the number of devices
that could request more than their quota. This ensures the spare
MSIs are spread evenly amongst all over-quota requestors.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2009-02-17 08:21:56 +08:00
|
|
|
|
|
|
|
if ((class >> 8) != PCI_CLASS_BRIDGE_PCI)
|
|
|
|
counts->num_devices++;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *count_spare_msis(struct device_node *dn, void *data)
|
|
|
|
{
|
|
|
|
struct msi_counts *counts = data;
|
2013-12-12 12:59:40 +08:00
|
|
|
const __be32 *p;
|
powerpc/pseries: Implement a quota system for MSIs
There are hardware limitations on the number of available MSIs,
which firmware expresses using a property named "ibm,pe-total-#msi".
This property tells us how many MSIs are available for devices below
the point in the PCI tree where we find the property.
For old firmwares which don't have the property, we assume there are
8 MSIs available per "partitionable endpoint" (PE). The PE can be
found using existing EEH code, which uses the methods described in
PAPR. For our purposes we want the parent of the node that's
identified using this method.
When a driver requests n MSIs for a device, we first establish where
the "ibm,pe-total-#msi" property above that device is, or we find the
PE if the property is not found. In both cases we call this node
the "pe_dn".
We then count all non-bridge devices below the pe_dn, to establish
how many devices in total may need MSIs. The quota is then simply the
total available divided by the number of devices, if the request is
less than or equal to the quota, the request is fine and we're done.
If the request is greater than the quota, we try to determine if there
are any "spare" MSIs which we can give to this device. Spare MSIs are
found by looking for other devices which can never use their full
quota, because their "req#msi(-x)" property is less than the quota.
If we find any spare, we divide the spares by the number of devices
that could request more than their quota. This ensures the spare
MSIs are spread evenly amongst all over-quota requestors.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2009-02-17 08:21:56 +08:00
|
|
|
int req;
|
|
|
|
|
|
|
|
if (dn == counts->requestor)
|
|
|
|
req = counts->request;
|
|
|
|
else {
|
|
|
|
/* We don't know if a driver will try to use MSI or MSI-X,
|
|
|
|
* so we just have to punt and use the larger of the two. */
|
|
|
|
req = 0;
|
|
|
|
p = of_get_property(dn, "ibm,req#msi", NULL);
|
|
|
|
if (p)
|
2013-12-12 12:59:40 +08:00
|
|
|
req = be32_to_cpup(p);
|
powerpc/pseries: Implement a quota system for MSIs
There are hardware limitations on the number of available MSIs,
which firmware expresses using a property named "ibm,pe-total-#msi".
This property tells us how many MSIs are available for devices below
the point in the PCI tree where we find the property.
For old firmwares which don't have the property, we assume there are
8 MSIs available per "partitionable endpoint" (PE). The PE can be
found using existing EEH code, which uses the methods described in
PAPR. For our purposes we want the parent of the node that's
identified using this method.
When a driver requests n MSIs for a device, we first establish where
the "ibm,pe-total-#msi" property above that device is, or we find the
PE if the property is not found. In both cases we call this node
the "pe_dn".
We then count all non-bridge devices below the pe_dn, to establish
how many devices in total may need MSIs. The quota is then simply the
total available divided by the number of devices, if the request is
less than or equal to the quota, the request is fine and we're done.
If the request is greater than the quota, we try to determine if there
are any "spare" MSIs which we can give to this device. Spare MSIs are
found by looking for other devices which can never use their full
quota, because their "req#msi(-x)" property is less than the quota.
If we find any spare, we divide the spares by the number of devices
that could request more than their quota. This ensures the spare
MSIs are spread evenly amongst all over-quota requestors.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2009-02-17 08:21:56 +08:00
|
|
|
|
|
|
|
p = of_get_property(dn, "ibm,req#msi-x", NULL);
|
|
|
|
if (p)
|
2013-12-12 12:59:40 +08:00
|
|
|
req = max(req, (int)be32_to_cpup(p));
|
powerpc/pseries: Implement a quota system for MSIs
There are hardware limitations on the number of available MSIs,
which firmware expresses using a property named "ibm,pe-total-#msi".
This property tells us how many MSIs are available for devices below
the point in the PCI tree where we find the property.
For old firmwares which don't have the property, we assume there are
8 MSIs available per "partitionable endpoint" (PE). The PE can be
found using existing EEH code, which uses the methods described in
PAPR. For our purposes we want the parent of the node that's
identified using this method.
When a driver requests n MSIs for a device, we first establish where
the "ibm,pe-total-#msi" property above that device is, or we find the
PE if the property is not found. In both cases we call this node
the "pe_dn".
We then count all non-bridge devices below the pe_dn, to establish
how many devices in total may need MSIs. The quota is then simply the
total available divided by the number of devices, if the request is
less than or equal to the quota, the request is fine and we're done.
If the request is greater than the quota, we try to determine if there
are any "spare" MSIs which we can give to this device. Spare MSIs are
found by looking for other devices which can never use their full
quota, because their "req#msi(-x)" property is less than the quota.
If we find any spare, we divide the spares by the number of devices
that could request more than their quota. This ensures the spare
MSIs are spread evenly amongst all over-quota requestors.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2009-02-17 08:21:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (req < counts->quota)
|
|
|
|
counts->spare += counts->quota - req;
|
|
|
|
else if (req > counts->quota)
|
|
|
|
counts->over_quota++;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int msi_quota_for_device(struct pci_dev *dev, int request)
|
|
|
|
{
|
|
|
|
struct device_node *pe_dn;
|
|
|
|
struct msi_counts counts;
|
|
|
|
int total;
|
|
|
|
|
|
|
|
pr_debug("rtas_msi: calc quota for %s, request %d\n", pci_name(dev),
|
|
|
|
request);
|
|
|
|
|
|
|
|
pe_dn = find_pe_total_msi(dev, &total);
|
|
|
|
if (!pe_dn)
|
|
|
|
pe_dn = find_pe_dn(dev, &total);
|
|
|
|
|
|
|
|
if (!pe_dn) {
|
|
|
|
pr_err("rtas_msi: couldn't find PE for %s\n", pci_name(dev));
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_debug("rtas_msi: found PE %s\n", pe_dn->full_name);
|
|
|
|
|
|
|
|
memset(&counts, 0, sizeof(struct msi_counts));
|
|
|
|
|
|
|
|
/* Work out how many devices we have below this PE */
|
|
|
|
traverse_pci_devices(pe_dn, count_non_bridge_devices, &counts);
|
|
|
|
|
|
|
|
if (counts.num_devices == 0) {
|
|
|
|
pr_err("rtas_msi: found 0 devices under PE for %s\n",
|
|
|
|
pci_name(dev));
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
counts.quota = total / counts.num_devices;
|
|
|
|
if (request <= counts.quota)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* else, we have some more calculating to do */
|
|
|
|
counts.requestor = pci_device_to_OF_node(dev);
|
|
|
|
counts.request = request;
|
|
|
|
traverse_pci_devices(pe_dn, count_spare_msis, &counts);
|
|
|
|
|
|
|
|
/* If the quota isn't an integer multiple of the total, we can
|
|
|
|
* use the remainder as spare MSIs for anyone that wants them. */
|
|
|
|
counts.spare += total % counts.num_devices;
|
|
|
|
|
|
|
|
/* Divide any spare by the number of over-quota requestors */
|
|
|
|
if (counts.over_quota)
|
|
|
|
counts.quota += counts.spare / counts.over_quota;
|
|
|
|
|
|
|
|
/* And finally clamp the request to the possibly adjusted quota */
|
|
|
|
request = min(counts.quota, request);
|
|
|
|
|
|
|
|
pr_debug("rtas_msi: request clamped to quota %d\n", request);
|
|
|
|
out:
|
|
|
|
of_node_put(pe_dn);
|
|
|
|
|
|
|
|
return request;
|
|
|
|
}
|
|
|
|
|
2009-03-05 22:44:26 +08:00
|
|
|
static int check_msix_entries(struct pci_dev *pdev)
|
|
|
|
{
|
|
|
|
struct msi_desc *entry;
|
|
|
|
int expected;
|
|
|
|
|
|
|
|
/* There's no way for us to express to firmware that we want
|
|
|
|
* a discontiguous, or non-zero based, range of MSI-X entries.
|
|
|
|
* So we must reject such requests. */
|
|
|
|
|
|
|
|
expected = 0;
|
|
|
|
list_for_each_entry(entry, &pdev->msi_list, list) {
|
|
|
|
if (entry->msi_attrib.entry_nr != expected) {
|
|
|
|
pr_debug("rtas_msi: bad MSI-X entries.\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
expected++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-05-22 19:07:46 +08:00
|
|
|
static void rtas_hack_32bit_msi_gen2(struct pci_dev *pdev)
|
|
|
|
{
|
|
|
|
u32 addr_hi, addr_lo;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We should only get in here for IODA1 configs. This is based on the
|
|
|
|
* fact that we using RTAS for MSIs, we don't have the 32 bit MSI RTAS
|
|
|
|
* support, and we are in a PCIe Gen2 slot.
|
|
|
|
*/
|
|
|
|
dev_info(&pdev->dev,
|
|
|
|
"rtas_msi: No 32 bit MSI firmware support, forcing 32 bit MSI\n");
|
|
|
|
pci_read_config_dword(pdev, pdev->msi_cap + PCI_MSI_ADDRESS_HI, &addr_hi);
|
|
|
|
addr_lo = 0xffff0000 | ((addr_hi >> (48 - 32)) << 4);
|
|
|
|
pci_write_config_dword(pdev, pdev->msi_cap + PCI_MSI_ADDRESS_LO, addr_lo);
|
|
|
|
pci_write_config_dword(pdev, pdev->msi_cap + PCI_MSI_ADDRESS_HI, 0);
|
|
|
|
}
|
|
|
|
|
2012-06-05 00:47:03 +08:00
|
|
|
static int rtas_setup_msi_irqs(struct pci_dev *pdev, int nvec_in, int type)
|
2007-05-08 10:58:35 +08:00
|
|
|
{
|
|
|
|
struct pci_dn *pdn;
|
2014-09-08 02:57:53 +08:00
|
|
|
int hwirq, virq, i, quota, rc;
|
2007-05-08 10:58:35 +08:00
|
|
|
struct msi_desc *entry;
|
2007-10-23 12:23:44 +08:00
|
|
|
struct msi_msg msg;
|
2012-06-05 00:47:03 +08:00
|
|
|
int nvec = nvec_in;
|
2013-05-22 19:07:46 +08:00
|
|
|
int use_32bit_msi_hack = 0;
|
2007-05-08 10:58:35 +08:00
|
|
|
|
2014-09-08 02:57:53 +08:00
|
|
|
if (type == PCI_CAP_ID_MSIX)
|
|
|
|
rc = check_req_msix(pdev, nvec);
|
|
|
|
else
|
|
|
|
rc = check_req_msi(pdev, nvec);
|
|
|
|
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
quota = msi_quota_for_device(pdev, nvec);
|
|
|
|
|
|
|
|
if (quota && quota < nvec)
|
|
|
|
return quota;
|
2007-05-08 10:58:35 +08:00
|
|
|
|
2009-03-05 22:44:26 +08:00
|
|
|
if (type == PCI_CAP_ID_MSIX && check_msix_entries(pdev))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2012-06-05 00:47:03 +08:00
|
|
|
/*
|
|
|
|
* Firmware currently refuse any non power of two allocation
|
|
|
|
* so we round up if the quota will allow it.
|
|
|
|
*/
|
|
|
|
if (type == PCI_CAP_ID_MSIX) {
|
|
|
|
int m = roundup_pow_of_two(nvec);
|
2014-09-08 02:57:53 +08:00
|
|
|
quota = msi_quota_for_device(pdev, m);
|
2012-06-05 00:47:03 +08:00
|
|
|
|
|
|
|
if (quota >= m)
|
|
|
|
nvec = m;
|
|
|
|
}
|
|
|
|
|
2014-09-08 02:57:53 +08:00
|
|
|
pdn = pci_get_pdn(pdev);
|
|
|
|
|
2007-05-08 10:58:35 +08:00
|
|
|
/*
|
|
|
|
* Try the new more explicit firmware interface, if that fails fall
|
|
|
|
* back to the old interface. The old interface is known to never
|
|
|
|
* return MSI-Xs.
|
|
|
|
*/
|
2012-06-05 00:47:03 +08:00
|
|
|
again:
|
2007-05-08 10:58:35 +08:00
|
|
|
if (type == PCI_CAP_ID_MSI) {
|
2014-10-07 13:12:55 +08:00
|
|
|
if (pdev->no_64bit_msi) {
|
2013-05-03 19:30:59 +08:00
|
|
|
rc = rtas_change_msi(pdn, RTAS_CHANGE_32MSI_FN, nvec);
|
2013-05-22 19:07:46 +08:00
|
|
|
if (rc < 0) {
|
|
|
|
/*
|
|
|
|
* We only want to run the 32 bit MSI hack below if
|
|
|
|
* the max bus speed is Gen2 speed
|
|
|
|
*/
|
|
|
|
if (pdev->bus->max_bus_speed != PCIE_SPEED_5_0GT)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
use_32bit_msi_hack = 1;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
rc = -1;
|
|
|
|
|
|
|
|
if (rc < 0)
|
2013-05-03 19:30:59 +08:00
|
|
|
rc = rtas_change_msi(pdn, RTAS_CHANGE_MSI_FN, nvec);
|
2007-05-08 10:58:35 +08:00
|
|
|
|
2013-05-22 19:07:46 +08:00
|
|
|
if (rc < 0) {
|
2007-05-08 10:58:35 +08:00
|
|
|
pr_debug("rtas_msi: trying the old firmware call.\n");
|
|
|
|
rc = rtas_change_msi(pdn, RTAS_CHANGE_FN, nvec);
|
|
|
|
}
|
2013-05-22 19:07:46 +08:00
|
|
|
|
|
|
|
if (use_32bit_msi_hack && rc > 0)
|
|
|
|
rtas_hack_32bit_msi_gen2(pdev);
|
2007-05-08 10:58:35 +08:00
|
|
|
} else
|
|
|
|
rc = rtas_change_msi(pdn, RTAS_CHANGE_MSIX_FN, nvec);
|
|
|
|
|
2009-01-23 04:54:33 +08:00
|
|
|
if (rc != nvec) {
|
2012-06-05 00:47:03 +08:00
|
|
|
if (nvec != nvec_in) {
|
|
|
|
nvec = nvec_in;
|
|
|
|
goto again;
|
|
|
|
}
|
2007-05-08 10:58:35 +08:00
|
|
|
pr_debug("rtas_msi: rtas_change_msi() failed\n");
|
2007-09-20 14:36:48 +08:00
|
|
|
return rc;
|
2007-05-08 10:58:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
list_for_each_entry(entry, &pdev->msi_list, list) {
|
2009-01-23 04:54:31 +08:00
|
|
|
hwirq = rtas_query_irq_number(pdn, i++);
|
2007-05-08 10:58:35 +08:00
|
|
|
if (hwirq < 0) {
|
|
|
|
pr_debug("rtas_msi: error (%d) getting hwirq\n", rc);
|
2007-09-20 14:36:48 +08:00
|
|
|
return hwirq;
|
2007-05-08 10:58:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
virq = irq_create_mapping(NULL, hwirq);
|
|
|
|
|
|
|
|
if (virq == NO_IRQ) {
|
|
|
|
pr_debug("rtas_msi: Failed mapping hwirq %d\n", hwirq);
|
2007-09-20 14:36:48 +08:00
|
|
|
return -ENOSPC;
|
2007-05-08 10:58:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
dev_dbg(&pdev->dev, "rtas_msi: allocated virq %d\n", virq);
|
2011-03-25 23:45:20 +08:00
|
|
|
irq_set_msi_desc(virq, entry);
|
2007-10-23 12:23:44 +08:00
|
|
|
|
|
|
|
/* Read config space back so we can restore after reset */
|
2014-11-09 23:10:33 +08:00
|
|
|
__pci_read_msi_msg(entry, &msg);
|
2007-10-23 12:23:44 +08:00
|
|
|
entry->msg = msg;
|
2007-05-08 10:58:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rtas_msi_pci_irq_fixup(struct pci_dev *pdev)
|
|
|
|
{
|
|
|
|
/* No LSI -> leave MSIs (if any) configured */
|
|
|
|
if (pdev->irq == NO_IRQ) {
|
|
|
|
dev_dbg(&pdev->dev, "rtas_msi: no LSI, nothing to do.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No MSI -> MSIs can't have been assigned by fw, leave LSI */
|
2009-01-23 04:54:32 +08:00
|
|
|
if (check_req_msi(pdev, 1) && check_req_msix(pdev, 1)) {
|
|
|
|
dev_dbg(&pdev->dev, "rtas_msi: no req#msi/x, nothing to do.\n");
|
2007-05-08 10:58:35 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_dbg(&pdev->dev, "rtas_msi: disabling existing MSI.\n");
|
|
|
|
rtas_disable_msi(pdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rtas_msi_init(void)
|
|
|
|
{
|
|
|
|
query_token = rtas_token("ibm,query-interrupt-source-number");
|
|
|
|
change_token = rtas_token("ibm,change-msi");
|
|
|
|
|
|
|
|
if ((query_token == RTAS_UNKNOWN_SERVICE) ||
|
|
|
|
(change_token == RTAS_UNKNOWN_SERVICE)) {
|
|
|
|
pr_debug("rtas_msi: no RTAS tokens, no MSI support.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_debug("rtas_msi: Registering RTAS MSI callbacks.\n");
|
|
|
|
|
|
|
|
WARN_ON(ppc_md.setup_msi_irqs);
|
|
|
|
ppc_md.setup_msi_irqs = rtas_setup_msi_irqs;
|
|
|
|
ppc_md.teardown_msi_irqs = rtas_teardown_msi_irqs;
|
|
|
|
|
|
|
|
WARN_ON(ppc_md.pci_irq_fixup);
|
|
|
|
ppc_md.pci_irq_fixup = rtas_msi_pci_irq_fixup;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2014-07-16 10:02:43 +08:00
|
|
|
machine_arch_initcall(pseries, rtas_msi_init);
|