1736759efSBjorn Helgaas // SPDX-License-Identifier: GPL-2.0+
266725152SGavin Shan /*
366725152SGavin Shan * PCI Hotplug Driver for PowerPC PowerNV platform.
466725152SGavin Shan *
566725152SGavin Shan * Copyright Gavin Shan, IBM Corporation 2016.
666725152SGavin Shan */
766725152SGavin Shan
8abaaac48SIlpo Järvinen #include <linux/bitfield.h>
966725152SGavin Shan #include <linux/libfdt.h>
1066725152SGavin Shan #include <linux/module.h>
1166725152SGavin Shan #include <linux/pci.h>
1266725152SGavin Shan #include <linux/pci_hotplug.h>
13b2851926SChristophe Leroy #include <linux/of_fdt.h>
1466725152SGavin Shan
1566725152SGavin Shan #include <asm/opal.h>
1666725152SGavin Shan #include <asm/pnv-pci.h>
1766725152SGavin Shan #include <asm/ppc-pci.h>
1866725152SGavin Shan
1966725152SGavin Shan #define DRIVER_VERSION "0.1"
2066725152SGavin Shan #define DRIVER_AUTHOR "Gavin Shan, IBM Corporation"
2166725152SGavin Shan #define DRIVER_DESC "PowerPC PowerNV PCI Hotplug Driver"
2266725152SGavin Shan
23748ac391SFrederic Barrat #define SLOT_WARN(sl, x...) \
24748ac391SFrederic Barrat ((sl)->pdev ? pci_warn((sl)->pdev, x) : dev_warn(&(sl)->bus->dev, x))
25748ac391SFrederic Barrat
26360aebd8SGavin Shan struct pnv_php_event {
27360aebd8SGavin Shan bool added;
28360aebd8SGavin Shan struct pnv_php_slot *php_slot;
29360aebd8SGavin Shan struct work_struct work;
30360aebd8SGavin Shan };
31360aebd8SGavin Shan
3266725152SGavin Shan static LIST_HEAD(pnv_php_slot_list);
3366725152SGavin Shan static DEFINE_SPINLOCK(pnv_php_lock);
3466725152SGavin Shan
3566725152SGavin Shan static void pnv_php_register(struct device_node *dn);
3666725152SGavin Shan static void pnv_php_unregister_one(struct device_node *dn);
3766725152SGavin Shan static void pnv_php_unregister(struct device_node *dn);
3866725152SGavin Shan
pnv_php_disable_irq(struct pnv_php_slot * php_slot,bool disable_device)3949f4b08eSGavin Shan static void pnv_php_disable_irq(struct pnv_php_slot *php_slot,
4049f4b08eSGavin Shan bool disable_device)
41360aebd8SGavin Shan {
42360aebd8SGavin Shan struct pci_dev *pdev = php_slot->pdev;
43360aebd8SGavin Shan u16 ctrl;
44360aebd8SGavin Shan
45360aebd8SGavin Shan if (php_slot->irq > 0) {
46360aebd8SGavin Shan pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &ctrl);
47360aebd8SGavin Shan ctrl &= ~(PCI_EXP_SLTCTL_HPIE |
48360aebd8SGavin Shan PCI_EXP_SLTCTL_PDCE |
49360aebd8SGavin Shan PCI_EXP_SLTCTL_DLLSCE);
50360aebd8SGavin Shan pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, ctrl);
51360aebd8SGavin Shan
52360aebd8SGavin Shan free_irq(php_slot->irq, php_slot);
53360aebd8SGavin Shan php_slot->irq = 0;
54360aebd8SGavin Shan }
55360aebd8SGavin Shan
56360aebd8SGavin Shan if (php_slot->wq) {
57360aebd8SGavin Shan destroy_workqueue(php_slot->wq);
58360aebd8SGavin Shan php_slot->wq = NULL;
59360aebd8SGavin Shan }
60360aebd8SGavin Shan
61*335e35b7SKrishna Kumar if (disable_device) {
62360aebd8SGavin Shan if (pdev->msix_enabled)
63360aebd8SGavin Shan pci_disable_msix(pdev);
64360aebd8SGavin Shan else if (pdev->msi_enabled)
65360aebd8SGavin Shan pci_disable_msi(pdev);
6649f4b08eSGavin Shan
6749f4b08eSGavin Shan pci_disable_device(pdev);
6849f4b08eSGavin Shan }
69360aebd8SGavin Shan }
70360aebd8SGavin Shan
pnv_php_free_slot(struct kref * kref)7166725152SGavin Shan static void pnv_php_free_slot(struct kref *kref)
7266725152SGavin Shan {
7366725152SGavin Shan struct pnv_php_slot *php_slot = container_of(kref,
7466725152SGavin Shan struct pnv_php_slot, kref);
7566725152SGavin Shan
7666725152SGavin Shan WARN_ON(!list_empty(&php_slot->children));
7749f4b08eSGavin Shan pnv_php_disable_irq(php_slot, false);
7866725152SGavin Shan kfree(php_slot->name);
7966725152SGavin Shan kfree(php_slot);
8066725152SGavin Shan }
8166725152SGavin Shan
pnv_php_put_slot(struct pnv_php_slot * php_slot)8266725152SGavin Shan static inline void pnv_php_put_slot(struct pnv_php_slot *php_slot)
8366725152SGavin Shan {
8466725152SGavin Shan
8536c7c9daSGavin Shan if (!php_slot)
8666725152SGavin Shan return;
8766725152SGavin Shan
8866725152SGavin Shan kref_put(&php_slot->kref, pnv_php_free_slot);
8966725152SGavin Shan }
9066725152SGavin Shan
pnv_php_match(struct device_node * dn,struct pnv_php_slot * php_slot)9166725152SGavin Shan static struct pnv_php_slot *pnv_php_match(struct device_node *dn,
9266725152SGavin Shan struct pnv_php_slot *php_slot)
9366725152SGavin Shan {
9466725152SGavin Shan struct pnv_php_slot *target, *tmp;
9566725152SGavin Shan
9666725152SGavin Shan if (php_slot->dn == dn) {
9766725152SGavin Shan kref_get(&php_slot->kref);
9866725152SGavin Shan return php_slot;
9966725152SGavin Shan }
10066725152SGavin Shan
10166725152SGavin Shan list_for_each_entry(tmp, &php_slot->children, link) {
10266725152SGavin Shan target = pnv_php_match(dn, tmp);
10366725152SGavin Shan if (target)
10466725152SGavin Shan return target;
10566725152SGavin Shan }
10666725152SGavin Shan
10766725152SGavin Shan return NULL;
10866725152SGavin Shan }
10966725152SGavin Shan
pnv_php_find_slot(struct device_node * dn)11089379f16SAndrew Donnellan struct pnv_php_slot *pnv_php_find_slot(struct device_node *dn)
11166725152SGavin Shan {
11266725152SGavin Shan struct pnv_php_slot *php_slot, *tmp;
11366725152SGavin Shan unsigned long flags;
11466725152SGavin Shan
11566725152SGavin Shan spin_lock_irqsave(&pnv_php_lock, flags);
11666725152SGavin Shan list_for_each_entry(tmp, &pnv_php_slot_list, link) {
11766725152SGavin Shan php_slot = pnv_php_match(dn, tmp);
11866725152SGavin Shan if (php_slot) {
11966725152SGavin Shan spin_unlock_irqrestore(&pnv_php_lock, flags);
12066725152SGavin Shan return php_slot;
12166725152SGavin Shan }
12266725152SGavin Shan }
12366725152SGavin Shan spin_unlock_irqrestore(&pnv_php_lock, flags);
12466725152SGavin Shan
12566725152SGavin Shan return NULL;
12666725152SGavin Shan }
12789379f16SAndrew Donnellan EXPORT_SYMBOL_GPL(pnv_php_find_slot);
12866725152SGavin Shan
12966725152SGavin Shan /*
13066725152SGavin Shan * Remove pdn for all children of the indicated device node.
13166725152SGavin Shan * The function should remove pdn in a depth-first manner.
13266725152SGavin Shan */
pnv_php_rmv_pdns(struct device_node * dn)13366725152SGavin Shan static void pnv_php_rmv_pdns(struct device_node *dn)
13466725152SGavin Shan {
13566725152SGavin Shan struct device_node *child;
13666725152SGavin Shan
13766725152SGavin Shan for_each_child_of_node(dn, child) {
13866725152SGavin Shan pnv_php_rmv_pdns(child);
13966725152SGavin Shan
14066725152SGavin Shan pci_remove_device_node_info(child);
14166725152SGavin Shan }
14266725152SGavin Shan }
14366725152SGavin Shan
14466725152SGavin Shan /*
14566725152SGavin Shan * Detach all child nodes of the indicated device nodes. The
14666725152SGavin Shan * function should handle device nodes in depth-first manner.
14766725152SGavin Shan *
14866725152SGavin Shan * We should not invoke of_node_release() as the memory for
14966725152SGavin Shan * individual device node is part of large memory block. The
15066725152SGavin Shan * large block is allocated from memblock (system bootup) or
15166725152SGavin Shan * kmalloc() when unflattening the device tree by OF changeset.
15266725152SGavin Shan * We can not free the large block allocated from memblock. For
15366725152SGavin Shan * later case, it should be released at once.
15466725152SGavin Shan */
pnv_php_detach_device_nodes(struct device_node * parent)15566725152SGavin Shan static void pnv_php_detach_device_nodes(struct device_node *parent)
15666725152SGavin Shan {
15766725152SGavin Shan struct device_node *dn;
15866725152SGavin Shan
15966725152SGavin Shan for_each_child_of_node(parent, dn) {
16066725152SGavin Shan pnv_php_detach_device_nodes(dn);
16166725152SGavin Shan
16266725152SGavin Shan of_node_put(dn);
16366725152SGavin Shan of_detach_node(dn);
16466725152SGavin Shan }
16566725152SGavin Shan }
16666725152SGavin Shan
pnv_php_rmv_devtree(struct pnv_php_slot * php_slot)16766725152SGavin Shan static void pnv_php_rmv_devtree(struct pnv_php_slot *php_slot)
16866725152SGavin Shan {
16966725152SGavin Shan pnv_php_rmv_pdns(php_slot->dn);
17066725152SGavin Shan
17166725152SGavin Shan /*
17266725152SGavin Shan * Decrease the refcount if the device nodes were created
17366725152SGavin Shan * through OF changeset before detaching them.
17466725152SGavin Shan */
17566725152SGavin Shan if (php_slot->fdt)
17666725152SGavin Shan of_changeset_destroy(&php_slot->ocs);
17766725152SGavin Shan pnv_php_detach_device_nodes(php_slot->dn);
17866725152SGavin Shan
17966725152SGavin Shan if (php_slot->fdt) {
18066725152SGavin Shan kfree(php_slot->dt);
18166725152SGavin Shan kfree(php_slot->fdt);
18266725152SGavin Shan php_slot->dt = NULL;
18366725152SGavin Shan php_slot->dn->child = NULL;
18466725152SGavin Shan php_slot->fdt = NULL;
18566725152SGavin Shan }
18666725152SGavin Shan }
18766725152SGavin Shan
18866725152SGavin Shan /*
18966725152SGavin Shan * As the nodes in OF changeset are applied in reverse order, we
19066725152SGavin Shan * need revert the nodes in advance so that we have correct node
19166725152SGavin Shan * order after the changeset is applied.
19266725152SGavin Shan */
pnv_php_reverse_nodes(struct device_node * parent)19366725152SGavin Shan static void pnv_php_reverse_nodes(struct device_node *parent)
19466725152SGavin Shan {
19566725152SGavin Shan struct device_node *child, *next;
19666725152SGavin Shan
19766725152SGavin Shan /* In-depth first */
19866725152SGavin Shan for_each_child_of_node(parent, child)
19966725152SGavin Shan pnv_php_reverse_nodes(child);
20066725152SGavin Shan
20166725152SGavin Shan /* Reverse the nodes in the child list */
20266725152SGavin Shan child = parent->child;
20366725152SGavin Shan parent->child = NULL;
20466725152SGavin Shan while (child) {
20566725152SGavin Shan next = child->sibling;
20666725152SGavin Shan
20766725152SGavin Shan child->sibling = parent->child;
20866725152SGavin Shan parent->child = child;
20966725152SGavin Shan child = next;
21066725152SGavin Shan }
21166725152SGavin Shan }
21266725152SGavin Shan
pnv_php_populate_changeset(struct of_changeset * ocs,struct device_node * dn)21366725152SGavin Shan static int pnv_php_populate_changeset(struct of_changeset *ocs,
21466725152SGavin Shan struct device_node *dn)
21566725152SGavin Shan {
21666725152SGavin Shan struct device_node *child;
21766725152SGavin Shan int ret = 0;
21866725152SGavin Shan
21966725152SGavin Shan for_each_child_of_node(dn, child) {
22066725152SGavin Shan ret = of_changeset_attach_node(ocs, child);
2215d9c6b8aSJulia Lawall if (ret) {
2225d9c6b8aSJulia Lawall of_node_put(child);
22366725152SGavin Shan break;
2245d9c6b8aSJulia Lawall }
22566725152SGavin Shan
22666725152SGavin Shan ret = pnv_php_populate_changeset(ocs, child);
2275d9c6b8aSJulia Lawall if (ret) {
2285d9c6b8aSJulia Lawall of_node_put(child);
22966725152SGavin Shan break;
23066725152SGavin Shan }
2315d9c6b8aSJulia Lawall }
23266725152SGavin Shan
23366725152SGavin Shan return ret;
23466725152SGavin Shan }
23566725152SGavin Shan
pnv_php_add_one_pdn(struct device_node * dn,void * data)23666725152SGavin Shan static void *pnv_php_add_one_pdn(struct device_node *dn, void *data)
23766725152SGavin Shan {
23866725152SGavin Shan struct pci_controller *hose = (struct pci_controller *)data;
23966725152SGavin Shan struct pci_dn *pdn;
24066725152SGavin Shan
24166725152SGavin Shan pdn = pci_add_device_node_info(hose, dn);
242149ba66aSGavin Shan if (!pdn)
24366725152SGavin Shan return ERR_PTR(-ENOMEM);
24466725152SGavin Shan
24566725152SGavin Shan return NULL;
24666725152SGavin Shan }
24766725152SGavin Shan
pnv_php_add_pdns(struct pnv_php_slot * slot)24866725152SGavin Shan static void pnv_php_add_pdns(struct pnv_php_slot *slot)
24966725152SGavin Shan {
25066725152SGavin Shan struct pci_controller *hose = pci_bus_to_host(slot->bus);
25166725152SGavin Shan
25266725152SGavin Shan pci_traverse_device_nodes(slot->dn, pnv_php_add_one_pdn, hose);
25366725152SGavin Shan }
25466725152SGavin Shan
pnv_php_add_devtree(struct pnv_php_slot * php_slot)25566725152SGavin Shan static int pnv_php_add_devtree(struct pnv_php_slot *php_slot)
25666725152SGavin Shan {
25766725152SGavin Shan void *fdt, *fdt1, *dt;
25866725152SGavin Shan int ret;
25966725152SGavin Shan
26066725152SGavin Shan /* We don't know the FDT blob size. We try to get it through
26166725152SGavin Shan * maximal memory chunk and then copy it to another chunk that
26266725152SGavin Shan * fits the real size.
26366725152SGavin Shan */
26466725152SGavin Shan fdt1 = kzalloc(0x10000, GFP_KERNEL);
265149ba66aSGavin Shan if (!fdt1) {
26666725152SGavin Shan ret = -ENOMEM;
26766725152SGavin Shan goto out;
26866725152SGavin Shan }
26966725152SGavin Shan
27066725152SGavin Shan ret = pnv_pci_get_device_tree(php_slot->dn->phandle, fdt1, 0x10000);
271149ba66aSGavin Shan if (ret) {
272748ac391SFrederic Barrat SLOT_WARN(php_slot, "Error %d getting FDT blob\n", ret);
27366725152SGavin Shan goto free_fdt1;
27466725152SGavin Shan }
27566725152SGavin Shan
27674171e9dSYueHaibing fdt = kmemdup(fdt1, fdt_totalsize(fdt1), GFP_KERNEL);
277149ba66aSGavin Shan if (!fdt) {
27866725152SGavin Shan ret = -ENOMEM;
27966725152SGavin Shan goto free_fdt1;
28066725152SGavin Shan }
28166725152SGavin Shan
28266725152SGavin Shan /* Unflatten device tree blob */
28366725152SGavin Shan dt = of_fdt_unflatten_tree(fdt, php_slot->dn, NULL);
284149ba66aSGavin Shan if (!dt) {
28566725152SGavin Shan ret = -EINVAL;
286748ac391SFrederic Barrat SLOT_WARN(php_slot, "Cannot unflatten FDT\n");
28766725152SGavin Shan goto free_fdt;
28866725152SGavin Shan }
28966725152SGavin Shan
29066725152SGavin Shan /* Initialize and apply the changeset */
29166725152SGavin Shan of_changeset_init(&php_slot->ocs);
29266725152SGavin Shan pnv_php_reverse_nodes(php_slot->dn);
29366725152SGavin Shan ret = pnv_php_populate_changeset(&php_slot->ocs, php_slot->dn);
294149ba66aSGavin Shan if (ret) {
29566725152SGavin Shan pnv_php_reverse_nodes(php_slot->dn);
296748ac391SFrederic Barrat SLOT_WARN(php_slot, "Error %d populating changeset\n",
29766725152SGavin Shan ret);
29866725152SGavin Shan goto free_dt;
29966725152SGavin Shan }
30066725152SGavin Shan
30166725152SGavin Shan php_slot->dn->child = NULL;
30266725152SGavin Shan ret = of_changeset_apply(&php_slot->ocs);
303149ba66aSGavin Shan if (ret) {
304748ac391SFrederic Barrat SLOT_WARN(php_slot, "Error %d applying changeset\n", ret);
30566725152SGavin Shan goto destroy_changeset;
30666725152SGavin Shan }
30766725152SGavin Shan
30866725152SGavin Shan /* Add device node firmware data */
30966725152SGavin Shan pnv_php_add_pdns(php_slot);
31066725152SGavin Shan php_slot->fdt = fdt;
31166725152SGavin Shan php_slot->dt = dt;
31266725152SGavin Shan kfree(fdt1);
31366725152SGavin Shan goto out;
31466725152SGavin Shan
31566725152SGavin Shan destroy_changeset:
31666725152SGavin Shan of_changeset_destroy(&php_slot->ocs);
31766725152SGavin Shan free_dt:
31866725152SGavin Shan kfree(dt);
31966725152SGavin Shan php_slot->dn->child = NULL;
32066725152SGavin Shan free_fdt:
32166725152SGavin Shan kfree(fdt);
32266725152SGavin Shan free_fdt1:
32366725152SGavin Shan kfree(fdt1);
32466725152SGavin Shan out:
32566725152SGavin Shan return ret;
32666725152SGavin Shan }
32766725152SGavin Shan
to_pnv_php_slot(struct hotplug_slot * slot)328a7da2161SLukas Wunner static inline struct pnv_php_slot *to_pnv_php_slot(struct hotplug_slot *slot)
329a7da2161SLukas Wunner {
330a7da2161SLukas Wunner return container_of(slot, struct pnv_php_slot, slot);
331a7da2161SLukas Wunner }
332a7da2161SLukas Wunner
pnv_php_set_slot_power_state(struct hotplug_slot * slot,uint8_t state)33389379f16SAndrew Donnellan int pnv_php_set_slot_power_state(struct hotplug_slot *slot,
33466725152SGavin Shan uint8_t state)
33566725152SGavin Shan {
336125450f8SLukas Wunner struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
33766725152SGavin Shan struct opal_msg msg;
33866725152SGavin Shan int ret;
33966725152SGavin Shan
34066725152SGavin Shan ret = pnv_pci_set_power_state(php_slot->id, state, &msg);
341149ba66aSGavin Shan if (ret > 0) {
34266725152SGavin Shan if (be64_to_cpu(msg.params[1]) != php_slot->dn->phandle ||
343323c2a26SFrederic Barrat be64_to_cpu(msg.params[2]) != state) {
344748ac391SFrederic Barrat SLOT_WARN(php_slot, "Wrong msg (%lld, %lld, %lld)\n",
34566725152SGavin Shan be64_to_cpu(msg.params[1]),
34666725152SGavin Shan be64_to_cpu(msg.params[2]),
34766725152SGavin Shan be64_to_cpu(msg.params[3]));
34866725152SGavin Shan return -ENOMSG;
34966725152SGavin Shan }
350323c2a26SFrederic Barrat if (be64_to_cpu(msg.params[3]) != OPAL_SUCCESS) {
351323c2a26SFrederic Barrat ret = -ENODEV;
352323c2a26SFrederic Barrat goto error;
353323c2a26SFrederic Barrat }
354149ba66aSGavin Shan } else if (ret < 0) {
355323c2a26SFrederic Barrat goto error;
35666725152SGavin Shan }
35766725152SGavin Shan
3585473a6bfSAndrew Donnellan if (state == OPAL_PCI_SLOT_POWER_OFF || state == OPAL_PCI_SLOT_OFFLINE)
35966725152SGavin Shan pnv_php_rmv_devtree(php_slot);
36066725152SGavin Shan else
36166725152SGavin Shan ret = pnv_php_add_devtree(php_slot);
36266725152SGavin Shan
36366725152SGavin Shan return ret;
364323c2a26SFrederic Barrat
365323c2a26SFrederic Barrat error:
366748ac391SFrederic Barrat SLOT_WARN(php_slot, "Error %d powering %s\n",
367323c2a26SFrederic Barrat ret, (state == OPAL_PCI_SLOT_POWER_ON) ? "on" : "off");
368323c2a26SFrederic Barrat return ret;
36966725152SGavin Shan }
37089379f16SAndrew Donnellan EXPORT_SYMBOL_GPL(pnv_php_set_slot_power_state);
37166725152SGavin Shan
pnv_php_get_power_state(struct hotplug_slot * slot,u8 * state)37266725152SGavin Shan static int pnv_php_get_power_state(struct hotplug_slot *slot, u8 *state)
37366725152SGavin Shan {
374125450f8SLukas Wunner struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
37566725152SGavin Shan uint8_t power_state = OPAL_PCI_SLOT_POWER_ON;
37666725152SGavin Shan int ret;
37766725152SGavin Shan
37866725152SGavin Shan /*
37966725152SGavin Shan * Retrieve power status from firmware. If we fail
38066725152SGavin Shan * getting that, the power status fails back to
38166725152SGavin Shan * be on.
38266725152SGavin Shan */
38366725152SGavin Shan ret = pnv_pci_get_power_state(php_slot->id, &power_state);
384149ba66aSGavin Shan if (ret) {
385748ac391SFrederic Barrat SLOT_WARN(php_slot, "Error %d getting power status\n",
38666725152SGavin Shan ret);
38766725152SGavin Shan } else {
38866725152SGavin Shan *state = power_state;
38966725152SGavin Shan }
39066725152SGavin Shan
39166725152SGavin Shan return 0;
39266725152SGavin Shan }
39366725152SGavin Shan
pnv_php_get_adapter_state(struct hotplug_slot * slot,u8 * state)39466725152SGavin Shan static int pnv_php_get_adapter_state(struct hotplug_slot *slot, u8 *state)
39566725152SGavin Shan {
396125450f8SLukas Wunner struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
39766725152SGavin Shan uint8_t presence = OPAL_PCI_SLOT_EMPTY;
39866725152SGavin Shan int ret;
39966725152SGavin Shan
40066725152SGavin Shan /*
40166725152SGavin Shan * Retrieve presence status from firmware. If we can't
40266725152SGavin Shan * get that, it will fail back to be empty.
40366725152SGavin Shan */
40466725152SGavin Shan ret = pnv_pci_get_presence_state(php_slot->id, &presence);
405149ba66aSGavin Shan if (ret >= 0) {
40666725152SGavin Shan *state = presence;
40766725152SGavin Shan ret = 0;
40866725152SGavin Shan } else {
409748ac391SFrederic Barrat SLOT_WARN(php_slot, "Error %d getting presence\n", ret);
41066725152SGavin Shan }
41166725152SGavin Shan
41266725152SGavin Shan return ret;
41366725152SGavin Shan }
41466725152SGavin Shan
pnv_php_get_attention_state(struct hotplug_slot * slot,u8 * state)415a7da2161SLukas Wunner static int pnv_php_get_attention_state(struct hotplug_slot *slot, u8 *state)
416a7da2161SLukas Wunner {
417a7da2161SLukas Wunner struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
418a7da2161SLukas Wunner
419a7da2161SLukas Wunner *state = php_slot->attention_state;
420a7da2161SLukas Wunner return 0;
421a7da2161SLukas Wunner }
422a7da2161SLukas Wunner
pnv_php_set_attention_state(struct hotplug_slot * slot,u8 state)42366725152SGavin Shan static int pnv_php_set_attention_state(struct hotplug_slot *slot, u8 state)
42466725152SGavin Shan {
425a7da2161SLukas Wunner struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
426018c49e9SOliver O'Halloran struct pci_dev *bridge = php_slot->pdev;
427018c49e9SOliver O'Halloran u16 new, mask;
428a7da2161SLukas Wunner
429a7da2161SLukas Wunner php_slot->attention_state = state;
430018c49e9SOliver O'Halloran if (!bridge)
431018c49e9SOliver O'Halloran return 0;
432018c49e9SOliver O'Halloran
433018c49e9SOliver O'Halloran mask = PCI_EXP_SLTCTL_AIC;
434018c49e9SOliver O'Halloran
435018c49e9SOliver O'Halloran if (state)
436018c49e9SOliver O'Halloran new = PCI_EXP_SLTCTL_ATTN_IND_ON;
437018c49e9SOliver O'Halloran else
438018c49e9SOliver O'Halloran new = PCI_EXP_SLTCTL_ATTN_IND_OFF;
439018c49e9SOliver O'Halloran
440018c49e9SOliver O'Halloran pcie_capability_clear_and_set_word(bridge, PCI_EXP_SLTCTL, mask, new);
44166725152SGavin Shan
44266725152SGavin Shan return 0;
44366725152SGavin Shan }
44466725152SGavin Shan
pnv_php_enable(struct pnv_php_slot * php_slot,bool rescan)44566725152SGavin Shan static int pnv_php_enable(struct pnv_php_slot *php_slot, bool rescan)
44666725152SGavin Shan {
44766725152SGavin Shan struct hotplug_slot *slot = &php_slot->slot;
44866725152SGavin Shan uint8_t presence = OPAL_PCI_SLOT_EMPTY;
44966725152SGavin Shan uint8_t power_status = OPAL_PCI_SLOT_POWER_ON;
45066725152SGavin Shan int ret;
45166725152SGavin Shan
45266725152SGavin Shan /* Check if the slot has been configured */
45366725152SGavin Shan if (php_slot->state != PNV_PHP_STATE_REGISTERED)
45466725152SGavin Shan return 0;
45566725152SGavin Shan
45666725152SGavin Shan /* Retrieve slot presence status */
45766725152SGavin Shan ret = pnv_php_get_adapter_state(slot, &presence);
458149ba66aSGavin Shan if (ret)
45966725152SGavin Shan return ret;
46066725152SGavin Shan
461d0c42497SGavin Shan /*
462d0c42497SGavin Shan * Proceed if there have nothing behind the slot. However,
463d0c42497SGavin Shan * we should leave the slot in registered state at the
464d0c42497SGavin Shan * beginning. Otherwise, the PCI devices inserted afterwards
465d0c42497SGavin Shan * won't be probed and populated.
466d0c42497SGavin Shan */
467d0c42497SGavin Shan if (presence == OPAL_PCI_SLOT_EMPTY) {
468d0c42497SGavin Shan if (!php_slot->power_state_check) {
469d0c42497SGavin Shan php_slot->power_state_check = true;
470d0c42497SGavin Shan
471d0c42497SGavin Shan return 0;
472d0c42497SGavin Shan }
473d0c42497SGavin Shan
47466725152SGavin Shan goto scan;
475d0c42497SGavin Shan }
47666725152SGavin Shan
47766725152SGavin Shan /*
47866725152SGavin Shan * If the power supply to the slot is off, we can't detect
47966725152SGavin Shan * adapter presence state. That means we have to turn the
48066725152SGavin Shan * slot on before going to probe slot's presence state.
48166725152SGavin Shan *
48266725152SGavin Shan * On the first time, we don't change the power status to
48366725152SGavin Shan * boost system boot with assumption that the firmware
48466725152SGavin Shan * supplies consistent slot power status: empty slot always
48566725152SGavin Shan * has its power off and non-empty slot has its power on.
48666725152SGavin Shan */
48766725152SGavin Shan if (!php_slot->power_state_check) {
48866725152SGavin Shan php_slot->power_state_check = true;
48966725152SGavin Shan
49066725152SGavin Shan ret = pnv_php_get_power_state(slot, &power_status);
491149ba66aSGavin Shan if (ret)
49266725152SGavin Shan return ret;
49366725152SGavin Shan
49466725152SGavin Shan if (power_status != OPAL_PCI_SLOT_POWER_ON)
49566725152SGavin Shan return 0;
49666725152SGavin Shan }
49766725152SGavin Shan
49866725152SGavin Shan /* Check the power status. Scan the slot if it is already on */
49966725152SGavin Shan ret = pnv_php_get_power_state(slot, &power_status);
500149ba66aSGavin Shan if (ret)
50166725152SGavin Shan return ret;
50266725152SGavin Shan
50366725152SGavin Shan if (power_status == OPAL_PCI_SLOT_POWER_ON)
50466725152SGavin Shan goto scan;
50566725152SGavin Shan
50666725152SGavin Shan /* Power is off, turn it on and then scan the slot */
50766725152SGavin Shan ret = pnv_php_set_slot_power_state(slot, OPAL_PCI_SLOT_POWER_ON);
508149ba66aSGavin Shan if (ret)
50966725152SGavin Shan return ret;
51066725152SGavin Shan
51166725152SGavin Shan scan:
51266725152SGavin Shan if (presence == OPAL_PCI_SLOT_PRESENT) {
51366725152SGavin Shan if (rescan) {
51466725152SGavin Shan pci_lock_rescan_remove();
51566725152SGavin Shan pci_hp_add_devices(php_slot->bus);
51666725152SGavin Shan pci_unlock_rescan_remove();
51766725152SGavin Shan }
51866725152SGavin Shan
51966725152SGavin Shan /* Rescan for child hotpluggable slots */
52066725152SGavin Shan php_slot->state = PNV_PHP_STATE_POPULATED;
52166725152SGavin Shan if (rescan)
52266725152SGavin Shan pnv_php_register(php_slot->dn);
52366725152SGavin Shan } else {
52466725152SGavin Shan php_slot->state = PNV_PHP_STATE_POPULATED;
52566725152SGavin Shan }
52666725152SGavin Shan
52766725152SGavin Shan return 0;
52866725152SGavin Shan }
52966725152SGavin Shan
pnv_php_reset_slot(struct hotplug_slot * slot,bool probe)5309bdc81ceSAmey Narkhede static int pnv_php_reset_slot(struct hotplug_slot *slot, bool probe)
5317fd1fe4eSOliver O'Halloran {
5327fd1fe4eSOliver O'Halloran struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
5337fd1fe4eSOliver O'Halloran struct pci_dev *bridge = php_slot->pdev;
5347fd1fe4eSOliver O'Halloran uint16_t sts;
5357fd1fe4eSOliver O'Halloran
5367fd1fe4eSOliver O'Halloran /*
5377fd1fe4eSOliver O'Halloran * The CAPI folks want pnv_php to drive OpenCAPI slots
5387fd1fe4eSOliver O'Halloran * which don't have a bridge. Only claim to support
5397fd1fe4eSOliver O'Halloran * reset_slot() if we have a bridge device (for now...)
5407fd1fe4eSOliver O'Halloran */
5417fd1fe4eSOliver O'Halloran if (probe)
5427fd1fe4eSOliver O'Halloran return !bridge;
5437fd1fe4eSOliver O'Halloran
5447fd1fe4eSOliver O'Halloran /* mask our interrupt while resetting the bridge */
5457fd1fe4eSOliver O'Halloran if (php_slot->irq > 0)
5467fd1fe4eSOliver O'Halloran disable_irq(php_slot->irq);
5477fd1fe4eSOliver O'Halloran
5487fd1fe4eSOliver O'Halloran pci_bridge_secondary_bus_reset(bridge);
5497fd1fe4eSOliver O'Halloran
5507fd1fe4eSOliver O'Halloran /* clear any state changes that happened due to the reset */
5517fd1fe4eSOliver O'Halloran pcie_capability_read_word(php_slot->pdev, PCI_EXP_SLTSTA, &sts);
5527fd1fe4eSOliver O'Halloran sts &= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC);
5537fd1fe4eSOliver O'Halloran pcie_capability_write_word(php_slot->pdev, PCI_EXP_SLTSTA, sts);
5547fd1fe4eSOliver O'Halloran
5557fd1fe4eSOliver O'Halloran if (php_slot->irq > 0)
5567fd1fe4eSOliver O'Halloran enable_irq(php_slot->irq);
5577fd1fe4eSOliver O'Halloran
5587fd1fe4eSOliver O'Halloran return 0;
5597fd1fe4eSOliver O'Halloran }
5607fd1fe4eSOliver O'Halloran
pnv_php_enable_slot(struct hotplug_slot * slot)56166725152SGavin Shan static int pnv_php_enable_slot(struct hotplug_slot *slot)
56266725152SGavin Shan {
563a7da2161SLukas Wunner struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
56466725152SGavin Shan
56566725152SGavin Shan return pnv_php_enable(php_slot, true);
56666725152SGavin Shan }
56766725152SGavin Shan
pnv_php_disable_slot(struct hotplug_slot * slot)56866725152SGavin Shan static int pnv_php_disable_slot(struct hotplug_slot *slot)
56966725152SGavin Shan {
570125450f8SLukas Wunner struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
57166725152SGavin Shan int ret;
57266725152SGavin Shan
573be1611e0SFrederic Barrat /*
574be1611e0SFrederic Barrat * Allow to disable a slot already in the registered state to
575be1611e0SFrederic Barrat * cover cases where the slot couldn't be enabled and never
576be1611e0SFrederic Barrat * reached the populated state
577be1611e0SFrederic Barrat */
578be1611e0SFrederic Barrat if (php_slot->state != PNV_PHP_STATE_POPULATED &&
579be1611e0SFrederic Barrat php_slot->state != PNV_PHP_STATE_REGISTERED)
58066725152SGavin Shan return 0;
58166725152SGavin Shan
58266725152SGavin Shan /* Remove all devices behind the slot */
58366725152SGavin Shan pci_lock_rescan_remove();
58466725152SGavin Shan pci_hp_remove_devices(php_slot->bus);
58566725152SGavin Shan pci_unlock_rescan_remove();
58666725152SGavin Shan
58766725152SGavin Shan /* Detach the child hotpluggable slots */
58866725152SGavin Shan pnv_php_unregister(php_slot->dn);
58966725152SGavin Shan
59066725152SGavin Shan /* Notify firmware and remove device nodes */
59166725152SGavin Shan ret = pnv_php_set_slot_power_state(slot, OPAL_PCI_SLOT_POWER_OFF);
59266725152SGavin Shan
59366725152SGavin Shan php_slot->state = PNV_PHP_STATE_REGISTERED;
59466725152SGavin Shan return ret;
59566725152SGavin Shan }
59666725152SGavin Shan
59781c4b5bfSLukas Wunner static const struct hotplug_slot_ops php_slot_ops = {
59866725152SGavin Shan .get_power_status = pnv_php_get_power_state,
59966725152SGavin Shan .get_adapter_status = pnv_php_get_adapter_state,
600a7da2161SLukas Wunner .get_attention_status = pnv_php_get_attention_state,
60166725152SGavin Shan .set_attention_status = pnv_php_set_attention_state,
60266725152SGavin Shan .enable_slot = pnv_php_enable_slot,
60366725152SGavin Shan .disable_slot = pnv_php_disable_slot,
6047fd1fe4eSOliver O'Halloran .reset_slot = pnv_php_reset_slot,
60566725152SGavin Shan };
60666725152SGavin Shan
pnv_php_release(struct pnv_php_slot * php_slot)60751bbf9beSLukas Wunner static void pnv_php_release(struct pnv_php_slot *php_slot)
60866725152SGavin Shan {
60966725152SGavin Shan unsigned long flags;
61066725152SGavin Shan
61166725152SGavin Shan /* Remove from global or child list */
61266725152SGavin Shan spin_lock_irqsave(&pnv_php_lock, flags);
61366725152SGavin Shan list_del(&php_slot->link);
61466725152SGavin Shan spin_unlock_irqrestore(&pnv_php_lock, flags);
61566725152SGavin Shan
61666725152SGavin Shan /* Detach from parent */
61766725152SGavin Shan pnv_php_put_slot(php_slot);
61866725152SGavin Shan pnv_php_put_slot(php_slot->parent);
61966725152SGavin Shan }
62066725152SGavin Shan
pnv_php_alloc_slot(struct device_node * dn)62166725152SGavin Shan static struct pnv_php_slot *pnv_php_alloc_slot(struct device_node *dn)
62266725152SGavin Shan {
62366725152SGavin Shan struct pnv_php_slot *php_slot;
62466725152SGavin Shan struct pci_bus *bus;
62566725152SGavin Shan const char *label;
62666725152SGavin Shan uint64_t id;
62739f0d6fbSGavin Shan int ret;
62866725152SGavin Shan
62939f0d6fbSGavin Shan ret = of_property_read_string(dn, "ibm,slot-label", &label);
63039f0d6fbSGavin Shan if (ret)
63166725152SGavin Shan return NULL;
63266725152SGavin Shan
63366725152SGavin Shan if (pnv_pci_get_slot_id(dn, &id))
63466725152SGavin Shan return NULL;
63566725152SGavin Shan
63666725152SGavin Shan bus = pci_find_bus_by_node(dn);
637149ba66aSGavin Shan if (!bus)
63866725152SGavin Shan return NULL;
63966725152SGavin Shan
64066725152SGavin Shan php_slot = kzalloc(sizeof(*php_slot), GFP_KERNEL);
641149ba66aSGavin Shan if (!php_slot)
64266725152SGavin Shan return NULL;
64366725152SGavin Shan
64466725152SGavin Shan php_slot->name = kstrdup(label, GFP_KERNEL);
645149ba66aSGavin Shan if (!php_slot->name) {
64666725152SGavin Shan kfree(php_slot);
64766725152SGavin Shan return NULL;
64866725152SGavin Shan }
64966725152SGavin Shan
650149ba66aSGavin Shan if (dn->child && PCI_DN(dn->child))
65166725152SGavin Shan php_slot->slot_no = PCI_SLOT(PCI_DN(dn->child)->devfn);
65266725152SGavin Shan else
65366725152SGavin Shan php_slot->slot_no = -1; /* Placeholder slot */
65466725152SGavin Shan
65566725152SGavin Shan kref_init(&php_slot->kref);
65666725152SGavin Shan php_slot->state = PNV_PHP_STATE_INITIALIZED;
65766725152SGavin Shan php_slot->dn = dn;
65866725152SGavin Shan php_slot->pdev = bus->self;
65966725152SGavin Shan php_slot->bus = bus;
66066725152SGavin Shan php_slot->id = id;
66166725152SGavin Shan php_slot->power_state_check = false;
66266725152SGavin Shan php_slot->slot.ops = &php_slot_ops;
66366725152SGavin Shan
66466725152SGavin Shan INIT_LIST_HEAD(&php_slot->children);
66566725152SGavin Shan INIT_LIST_HEAD(&php_slot->link);
66666725152SGavin Shan
66766725152SGavin Shan return php_slot;
66866725152SGavin Shan }
66966725152SGavin Shan
pnv_php_register_slot(struct pnv_php_slot * php_slot)67066725152SGavin Shan static int pnv_php_register_slot(struct pnv_php_slot *php_slot)
67166725152SGavin Shan {
67266725152SGavin Shan struct pnv_php_slot *parent;
67366725152SGavin Shan struct device_node *dn = php_slot->dn;
67466725152SGavin Shan unsigned long flags;
67566725152SGavin Shan int ret;
67666725152SGavin Shan
67766725152SGavin Shan /* Check if the slot is registered or not */
67866725152SGavin Shan parent = pnv_php_find_slot(php_slot->dn);
679149ba66aSGavin Shan if (parent) {
68066725152SGavin Shan pnv_php_put_slot(parent);
68166725152SGavin Shan return -EEXIST;
68266725152SGavin Shan }
68366725152SGavin Shan
68466725152SGavin Shan /* Register PCI slot */
68566725152SGavin Shan ret = pci_hp_register(&php_slot->slot, php_slot->bus,
68666725152SGavin Shan php_slot->slot_no, php_slot->name);
687149ba66aSGavin Shan if (ret) {
688748ac391SFrederic Barrat SLOT_WARN(php_slot, "Error %d registering slot\n", ret);
68966725152SGavin Shan return ret;
69066725152SGavin Shan }
69166725152SGavin Shan
69266725152SGavin Shan /* Attach to the parent's child list or global list */
69366725152SGavin Shan while ((dn = of_get_parent(dn))) {
69466725152SGavin Shan if (!PCI_DN(dn)) {
69566725152SGavin Shan of_node_put(dn);
69666725152SGavin Shan break;
69766725152SGavin Shan }
69866725152SGavin Shan
69966725152SGavin Shan parent = pnv_php_find_slot(dn);
70066725152SGavin Shan if (parent) {
70166725152SGavin Shan of_node_put(dn);
70266725152SGavin Shan break;
70366725152SGavin Shan }
70466725152SGavin Shan
70566725152SGavin Shan of_node_put(dn);
70666725152SGavin Shan }
70766725152SGavin Shan
70866725152SGavin Shan spin_lock_irqsave(&pnv_php_lock, flags);
70966725152SGavin Shan php_slot->parent = parent;
71066725152SGavin Shan if (parent)
71166725152SGavin Shan list_add_tail(&php_slot->link, &parent->children);
71266725152SGavin Shan else
71366725152SGavin Shan list_add_tail(&php_slot->link, &pnv_php_slot_list);
71466725152SGavin Shan spin_unlock_irqrestore(&pnv_php_lock, flags);
71566725152SGavin Shan
71666725152SGavin Shan php_slot->state = PNV_PHP_STATE_REGISTERED;
71766725152SGavin Shan return 0;
71866725152SGavin Shan }
71966725152SGavin Shan
pnv_php_enable_msix(struct pnv_php_slot * php_slot)720360aebd8SGavin Shan static int pnv_php_enable_msix(struct pnv_php_slot *php_slot)
721360aebd8SGavin Shan {
722360aebd8SGavin Shan struct pci_dev *pdev = php_slot->pdev;
723360aebd8SGavin Shan struct msix_entry entry;
724360aebd8SGavin Shan int nr_entries, ret;
725360aebd8SGavin Shan u16 pcie_flag;
726360aebd8SGavin Shan
727360aebd8SGavin Shan /* Get total number of MSIx entries */
728360aebd8SGavin Shan nr_entries = pci_msix_vec_count(pdev);
729360aebd8SGavin Shan if (nr_entries < 0)
730360aebd8SGavin Shan return nr_entries;
731360aebd8SGavin Shan
732360aebd8SGavin Shan /* Check hotplug MSIx entry is in range */
733360aebd8SGavin Shan pcie_capability_read_word(pdev, PCI_EXP_FLAGS, &pcie_flag);
734abaaac48SIlpo Järvinen entry.entry = FIELD_GET(PCI_EXP_FLAGS_IRQ, pcie_flag);
735360aebd8SGavin Shan if (entry.entry >= nr_entries)
736360aebd8SGavin Shan return -ERANGE;
737360aebd8SGavin Shan
738360aebd8SGavin Shan /* Enable MSIx */
739360aebd8SGavin Shan ret = pci_enable_msix_exact(pdev, &entry, 1);
740360aebd8SGavin Shan if (ret) {
741748ac391SFrederic Barrat SLOT_WARN(php_slot, "Error %d enabling MSIx\n", ret);
742360aebd8SGavin Shan return ret;
743360aebd8SGavin Shan }
744360aebd8SGavin Shan
745360aebd8SGavin Shan return entry.vector;
746360aebd8SGavin Shan }
747360aebd8SGavin Shan
pnv_php_event_handler(struct work_struct * work)748360aebd8SGavin Shan static void pnv_php_event_handler(struct work_struct *work)
749360aebd8SGavin Shan {
750360aebd8SGavin Shan struct pnv_php_event *event =
751360aebd8SGavin Shan container_of(work, struct pnv_php_event, work);
752360aebd8SGavin Shan struct pnv_php_slot *php_slot = event->php_slot;
753360aebd8SGavin Shan
754360aebd8SGavin Shan if (event->added)
755360aebd8SGavin Shan pnv_php_enable_slot(&php_slot->slot);
756360aebd8SGavin Shan else
757360aebd8SGavin Shan pnv_php_disable_slot(&php_slot->slot);
758360aebd8SGavin Shan
759360aebd8SGavin Shan kfree(event);
760360aebd8SGavin Shan }
761360aebd8SGavin Shan
pnv_php_interrupt(int irq,void * data)762360aebd8SGavin Shan static irqreturn_t pnv_php_interrupt(int irq, void *data)
763360aebd8SGavin Shan {
764360aebd8SGavin Shan struct pnv_php_slot *php_slot = data;
765360aebd8SGavin Shan struct pci_dev *pchild, *pdev = php_slot->pdev;
766360aebd8SGavin Shan struct eeh_dev *edev;
767360aebd8SGavin Shan struct eeh_pe *pe;
768360aebd8SGavin Shan struct pnv_php_event *event;
769360aebd8SGavin Shan u16 sts, lsts;
770360aebd8SGavin Shan u8 presence;
771360aebd8SGavin Shan bool added;
772360aebd8SGavin Shan unsigned long flags;
773360aebd8SGavin Shan int ret;
774360aebd8SGavin Shan
775360aebd8SGavin Shan pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &sts);
776360aebd8SGavin Shan sts &= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC);
777360aebd8SGavin Shan pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, sts);
7787fd1fe4eSOliver O'Halloran
7797fd1fe4eSOliver O'Halloran pci_dbg(pdev, "PCI slot [%s]: HP int! DLAct: %d, PresDet: %d\n",
7807fd1fe4eSOliver O'Halloran php_slot->name,
7817fd1fe4eSOliver O'Halloran !!(sts & PCI_EXP_SLTSTA_DLLSC),
7827fd1fe4eSOliver O'Halloran !!(sts & PCI_EXP_SLTSTA_PDC));
7837fd1fe4eSOliver O'Halloran
784360aebd8SGavin Shan if (sts & PCI_EXP_SLTSTA_DLLSC) {
785360aebd8SGavin Shan pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lsts);
786360aebd8SGavin Shan added = !!(lsts & PCI_EXP_LNKSTA_DLLLA);
787454593e5SGavin Shan } else if (!(php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC) &&
788454593e5SGavin Shan (sts & PCI_EXP_SLTSTA_PDC)) {
789360aebd8SGavin Shan ret = pnv_pci_get_presence_state(php_slot->id, &presence);
790d7d55536SGavin Shan if (ret) {
791748ac391SFrederic Barrat SLOT_WARN(php_slot,
792748ac391SFrederic Barrat "PCI slot [%s] error %d getting presence (0x%04x), to retry the operation.\n",
793d7d55536SGavin Shan php_slot->name, ret, sts);
794360aebd8SGavin Shan return IRQ_HANDLED;
795d7d55536SGavin Shan }
796d7d55536SGavin Shan
797360aebd8SGavin Shan added = !!(presence == OPAL_PCI_SLOT_PRESENT);
798360aebd8SGavin Shan } else {
7997fd1fe4eSOliver O'Halloran pci_dbg(pdev, "PCI slot [%s]: Spurious IRQ?\n", php_slot->name);
800360aebd8SGavin Shan return IRQ_NONE;
801360aebd8SGavin Shan }
802360aebd8SGavin Shan
803360aebd8SGavin Shan /* Freeze the removed PE to avoid unexpected error reporting */
804360aebd8SGavin Shan if (!added) {
805360aebd8SGavin Shan pchild = list_first_entry_or_null(&php_slot->bus->devices,
806360aebd8SGavin Shan struct pci_dev, bus_list);
807360aebd8SGavin Shan edev = pchild ? pci_dev_to_eeh_dev(pchild) : NULL;
808360aebd8SGavin Shan pe = edev ? edev->pe : NULL;
809360aebd8SGavin Shan if (pe) {
810360aebd8SGavin Shan eeh_serialize_lock(&flags);
811e762bb89SSam Bobroff eeh_pe_mark_isolated(pe);
812360aebd8SGavin Shan eeh_serialize_unlock(flags);
813360aebd8SGavin Shan eeh_pe_set_option(pe, EEH_OPT_FREEZE_PE);
814360aebd8SGavin Shan }
815360aebd8SGavin Shan }
816360aebd8SGavin Shan
817360aebd8SGavin Shan /*
818360aebd8SGavin Shan * The PE is left in frozen state if the event is missed. It's
819360aebd8SGavin Shan * fine as the PCI devices (PE) aren't functional any more.
820360aebd8SGavin Shan */
821360aebd8SGavin Shan event = kzalloc(sizeof(*event), GFP_ATOMIC);
822360aebd8SGavin Shan if (!event) {
823748ac391SFrederic Barrat SLOT_WARN(php_slot,
824748ac391SFrederic Barrat "PCI slot [%s] missed hotplug event 0x%04x\n",
825360aebd8SGavin Shan php_slot->name, sts);
826360aebd8SGavin Shan return IRQ_HANDLED;
827360aebd8SGavin Shan }
828360aebd8SGavin Shan
8297506dc79SFrederick Lawler pci_info(pdev, "PCI slot [%s] %s (IRQ: %d)\n",
830360aebd8SGavin Shan php_slot->name, added ? "added" : "removed", irq);
831360aebd8SGavin Shan INIT_WORK(&event->work, pnv_php_event_handler);
832360aebd8SGavin Shan event->added = added;
833360aebd8SGavin Shan event->php_slot = php_slot;
834360aebd8SGavin Shan queue_work(php_slot->wq, &event->work);
835360aebd8SGavin Shan
836360aebd8SGavin Shan return IRQ_HANDLED;
837360aebd8SGavin Shan }
838360aebd8SGavin Shan
pnv_php_init_irq(struct pnv_php_slot * php_slot,int irq)839360aebd8SGavin Shan static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
840360aebd8SGavin Shan {
841360aebd8SGavin Shan struct pci_dev *pdev = php_slot->pdev;
842454593e5SGavin Shan u32 broken_pdc = 0;
843360aebd8SGavin Shan u16 sts, ctrl;
844360aebd8SGavin Shan int ret;
845360aebd8SGavin Shan
846360aebd8SGavin Shan /* Allocate workqueue */
847360aebd8SGavin Shan php_slot->wq = alloc_workqueue("pciehp-%s", 0, 0, php_slot->name);
848360aebd8SGavin Shan if (!php_slot->wq) {
849748ac391SFrederic Barrat SLOT_WARN(php_slot, "Cannot alloc workqueue\n");
85049f4b08eSGavin Shan pnv_php_disable_irq(php_slot, true);
851360aebd8SGavin Shan return;
852360aebd8SGavin Shan }
853360aebd8SGavin Shan
854454593e5SGavin Shan /* Check PDC (Presence Detection Change) is broken or not */
855454593e5SGavin Shan ret = of_property_read_u32(php_slot->dn, "ibm,slot-broken-pdc",
856454593e5SGavin Shan &broken_pdc);
857454593e5SGavin Shan if (!ret && broken_pdc)
858454593e5SGavin Shan php_slot->flags |= PNV_PHP_FLAG_BROKEN_PDC;
859454593e5SGavin Shan
860360aebd8SGavin Shan /* Clear pending interrupts */
861360aebd8SGavin Shan pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &sts);
862454593e5SGavin Shan if (php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC)
863454593e5SGavin Shan sts |= PCI_EXP_SLTSTA_DLLSC;
864454593e5SGavin Shan else
865360aebd8SGavin Shan sts |= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC);
866360aebd8SGavin Shan pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, sts);
867360aebd8SGavin Shan
868360aebd8SGavin Shan /* Request the interrupt */
869360aebd8SGavin Shan ret = request_irq(irq, pnv_php_interrupt, IRQF_SHARED,
870360aebd8SGavin Shan php_slot->name, php_slot);
871360aebd8SGavin Shan if (ret) {
87249f4b08eSGavin Shan pnv_php_disable_irq(php_slot, true);
873748ac391SFrederic Barrat SLOT_WARN(php_slot, "Error %d enabling IRQ %d\n", ret, irq);
874360aebd8SGavin Shan return;
875360aebd8SGavin Shan }
876360aebd8SGavin Shan
877360aebd8SGavin Shan /* Enable the interrupts */
878360aebd8SGavin Shan pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &ctrl);
879454593e5SGavin Shan if (php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC) {
880454593e5SGavin Shan ctrl &= ~PCI_EXP_SLTCTL_PDCE;
881454593e5SGavin Shan ctrl |= (PCI_EXP_SLTCTL_HPIE |
882454593e5SGavin Shan PCI_EXP_SLTCTL_DLLSCE);
883454593e5SGavin Shan } else {
884360aebd8SGavin Shan ctrl |= (PCI_EXP_SLTCTL_HPIE |
885360aebd8SGavin Shan PCI_EXP_SLTCTL_PDCE |
886360aebd8SGavin Shan PCI_EXP_SLTCTL_DLLSCE);
887454593e5SGavin Shan }
888360aebd8SGavin Shan pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, ctrl);
889360aebd8SGavin Shan
890360aebd8SGavin Shan /* The interrupt is initialized successfully when @irq is valid */
891360aebd8SGavin Shan php_slot->irq = irq;
892360aebd8SGavin Shan }
893360aebd8SGavin Shan
pnv_php_enable_irq(struct pnv_php_slot * php_slot)894360aebd8SGavin Shan static void pnv_php_enable_irq(struct pnv_php_slot *php_slot)
895360aebd8SGavin Shan {
896360aebd8SGavin Shan struct pci_dev *pdev = php_slot->pdev;
897360aebd8SGavin Shan int irq, ret;
898360aebd8SGavin Shan
899303529d6SGavin Shan /*
900303529d6SGavin Shan * The MSI/MSIx interrupt might have been occupied by other
901303529d6SGavin Shan * drivers. Don't populate the surprise hotplug capability
902303529d6SGavin Shan * in that case.
903303529d6SGavin Shan */
904303529d6SGavin Shan if (pci_dev_msi_enabled(pdev))
905303529d6SGavin Shan return;
906303529d6SGavin Shan
907360aebd8SGavin Shan ret = pci_enable_device(pdev);
908360aebd8SGavin Shan if (ret) {
909748ac391SFrederic Barrat SLOT_WARN(php_slot, "Error %d enabling device\n", ret);
910360aebd8SGavin Shan return;
911360aebd8SGavin Shan }
912360aebd8SGavin Shan
913360aebd8SGavin Shan pci_set_master(pdev);
914360aebd8SGavin Shan
915360aebd8SGavin Shan /* Enable MSIx interrupt */
916360aebd8SGavin Shan irq = pnv_php_enable_msix(php_slot);
917360aebd8SGavin Shan if (irq > 0) {
918360aebd8SGavin Shan pnv_php_init_irq(php_slot, irq);
919360aebd8SGavin Shan return;
920360aebd8SGavin Shan }
921360aebd8SGavin Shan
922360aebd8SGavin Shan /*
923360aebd8SGavin Shan * Use MSI if MSIx doesn't work. Fail back to legacy INTx
924360aebd8SGavin Shan * if MSI doesn't work either
925360aebd8SGavin Shan */
926360aebd8SGavin Shan ret = pci_enable_msi(pdev);
927360aebd8SGavin Shan if (!ret || pdev->irq) {
928360aebd8SGavin Shan irq = pdev->irq;
929360aebd8SGavin Shan pnv_php_init_irq(php_slot, irq);
930360aebd8SGavin Shan }
931360aebd8SGavin Shan }
932360aebd8SGavin Shan
pnv_php_register_one(struct device_node * dn)93366725152SGavin Shan static int pnv_php_register_one(struct device_node *dn)
93466725152SGavin Shan {
93566725152SGavin Shan struct pnv_php_slot *php_slot;
93639f0d6fbSGavin Shan u32 prop32;
93766725152SGavin Shan int ret;
93866725152SGavin Shan
93966725152SGavin Shan /* Check if it's hotpluggable slot */
94039f0d6fbSGavin Shan ret = of_property_read_u32(dn, "ibm,slot-pluggable", &prop32);
94139f0d6fbSGavin Shan if (ret || !prop32)
94266725152SGavin Shan return -ENXIO;
94366725152SGavin Shan
94439f0d6fbSGavin Shan ret = of_property_read_u32(dn, "ibm,reset-by-firmware", &prop32);
94539f0d6fbSGavin Shan if (ret || !prop32)
94666725152SGavin Shan return -ENXIO;
94766725152SGavin Shan
94866725152SGavin Shan php_slot = pnv_php_alloc_slot(dn);
949149ba66aSGavin Shan if (!php_slot)
95066725152SGavin Shan return -ENODEV;
95166725152SGavin Shan
95266725152SGavin Shan ret = pnv_php_register_slot(php_slot);
953149ba66aSGavin Shan if (ret)
95466725152SGavin Shan goto free_slot;
95566725152SGavin Shan
95666725152SGavin Shan ret = pnv_php_enable(php_slot, false);
957149ba66aSGavin Shan if (ret)
95866725152SGavin Shan goto unregister_slot;
95966725152SGavin Shan
960360aebd8SGavin Shan /* Enable interrupt if the slot supports surprise hotplug */
96139f0d6fbSGavin Shan ret = of_property_read_u32(dn, "ibm,slot-surprise-pluggable", &prop32);
96239f0d6fbSGavin Shan if (!ret && prop32)
963360aebd8SGavin Shan pnv_php_enable_irq(php_slot);
964360aebd8SGavin Shan
96566725152SGavin Shan return 0;
96666725152SGavin Shan
96766725152SGavin Shan unregister_slot:
96866725152SGavin Shan pnv_php_unregister_one(php_slot->dn);
96966725152SGavin Shan free_slot:
97066725152SGavin Shan pnv_php_put_slot(php_slot);
97166725152SGavin Shan return ret;
97266725152SGavin Shan }
97366725152SGavin Shan
pnv_php_register(struct device_node * dn)97466725152SGavin Shan static void pnv_php_register(struct device_node *dn)
97566725152SGavin Shan {
97666725152SGavin Shan struct device_node *child;
97766725152SGavin Shan
97866725152SGavin Shan /*
97966725152SGavin Shan * The parent slots should be registered before their
98066725152SGavin Shan * child slots.
98166725152SGavin Shan */
98266725152SGavin Shan for_each_child_of_node(dn, child) {
98366725152SGavin Shan pnv_php_register_one(child);
98466725152SGavin Shan pnv_php_register(child);
98566725152SGavin Shan }
98666725152SGavin Shan }
98766725152SGavin Shan
pnv_php_unregister_one(struct device_node * dn)98866725152SGavin Shan static void pnv_php_unregister_one(struct device_node *dn)
98966725152SGavin Shan {
99066725152SGavin Shan struct pnv_php_slot *php_slot;
99166725152SGavin Shan
99266725152SGavin Shan php_slot = pnv_php_find_slot(dn);
99366725152SGavin Shan if (!php_slot)
99466725152SGavin Shan return;
99566725152SGavin Shan
99666725152SGavin Shan php_slot->state = PNV_PHP_STATE_OFFLINE;
99766725152SGavin Shan pci_hp_deregister(&php_slot->slot);
99851bbf9beSLukas Wunner pnv_php_release(php_slot);
99997c6f25dSSimon Guo pnv_php_put_slot(php_slot);
100066725152SGavin Shan }
100166725152SGavin Shan
pnv_php_unregister(struct device_node * dn)100266725152SGavin Shan static void pnv_php_unregister(struct device_node *dn)
100366725152SGavin Shan {
100466725152SGavin Shan struct device_node *child;
100566725152SGavin Shan
100666725152SGavin Shan /* The child slots should go before their parent slots */
100766725152SGavin Shan for_each_child_of_node(dn, child) {
100866725152SGavin Shan pnv_php_unregister(child);
100966725152SGavin Shan pnv_php_unregister_one(child);
101066725152SGavin Shan }
101166725152SGavin Shan }
101266725152SGavin Shan
pnv_php_init(void)101366725152SGavin Shan static int __init pnv_php_init(void)
101466725152SGavin Shan {
101566725152SGavin Shan struct device_node *dn;
101666725152SGavin Shan
101766725152SGavin Shan pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
101866725152SGavin Shan for_each_compatible_node(dn, NULL, "ibm,ioda2-phb")
101966725152SGavin Shan pnv_php_register(dn);
102066725152SGavin Shan
1021a839bd87SOliver O'Halloran for_each_compatible_node(dn, NULL, "ibm,ioda3-phb")
1022a839bd87SOliver O'Halloran pnv_php_register(dn);
1023a839bd87SOliver O'Halloran
1024ea53919bSFrederic Barrat for_each_compatible_node(dn, NULL, "ibm,ioda2-npu2-opencapi-phb")
1025ea53919bSFrederic Barrat pnv_php_register_one(dn); /* slot directly under the PHB */
102666725152SGavin Shan return 0;
102766725152SGavin Shan }
102866725152SGavin Shan
pnv_php_exit(void)102966725152SGavin Shan static void __exit pnv_php_exit(void)
103066725152SGavin Shan {
103166725152SGavin Shan struct device_node *dn;
103266725152SGavin Shan
103366725152SGavin Shan for_each_compatible_node(dn, NULL, "ibm,ioda2-phb")
103466725152SGavin Shan pnv_php_unregister(dn);
1035a839bd87SOliver O'Halloran
1036a839bd87SOliver O'Halloran for_each_compatible_node(dn, NULL, "ibm,ioda3-phb")
1037a839bd87SOliver O'Halloran pnv_php_unregister(dn);
1038ea53919bSFrederic Barrat
1039ea53919bSFrederic Barrat for_each_compatible_node(dn, NULL, "ibm,ioda2-npu2-opencapi-phb")
1040ea53919bSFrederic Barrat pnv_php_unregister_one(dn); /* slot directly under the PHB */
104166725152SGavin Shan }
104266725152SGavin Shan
104366725152SGavin Shan module_init(pnv_php_init);
104466725152SGavin Shan module_exit(pnv_php_exit);
104566725152SGavin Shan
104666725152SGavin Shan MODULE_VERSION(DRIVER_VERSION);
104766725152SGavin Shan MODULE_LICENSE("GPL v2");
104866725152SGavin Shan MODULE_AUTHOR(DRIVER_AUTHOR);
104966725152SGavin Shan MODULE_DESCRIPTION(DRIVER_DESC);
1050