summaryrefslogtreecommitdiffstats
path: root/arch/s390/pci
diff options
context:
space:
mode:
Diffstat (limited to 'arch/s390/pci')
-rw-r--r--arch/s390/pci/pci.c202
-rw-r--r--arch/s390/pci/pci_clp.c8
-rw-r--r--arch/s390/pci/pci_event.c79
3 files changed, 169 insertions, 120 deletions
diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c
index 0c9a17780e4b..bf7c73d71eef 100644
--- a/arch/s390/pci/pci.c
+++ b/arch/s390/pci/pci.c
@@ -530,20 +530,6 @@ static void zpci_unmap_resources(struct zpci_dev *zdev)
}
}
-struct zpci_dev *zpci_alloc_device(void)
-{
- struct zpci_dev *zdev;
-
- /* Alloc memory for our private pci device data */
- zdev = kzalloc(sizeof(*zdev), GFP_KERNEL);
- return zdev ? : ERR_PTR(-ENOMEM);
-}
-
-void zpci_free_device(struct zpci_dev *zdev)
-{
- kfree(zdev);
-}
-
int pcibios_add_platform_entries(struct pci_dev *pdev)
{
return zpci_sysfs_add_device(&pdev->dev);
@@ -579,37 +565,6 @@ static void zpci_irq_exit(void)
unregister_adapter_interrupt(&zpci_airq);
}
-static struct resource *zpci_alloc_bus_resource(unsigned long start, unsigned long size,
- unsigned long flags, int domain)
-{
- struct resource *r;
- char *name;
- int rc;
-
- r = kzalloc(sizeof(*r), GFP_KERNEL);
- if (!r)
- return ERR_PTR(-ENOMEM);
- r->start = start;
- r->end = r->start + size - 1;
- r->flags = flags;
- r->parent = &iomem_resource;
- name = kmalloc(18, GFP_KERNEL);
- if (!name) {
- kfree(r);
- return ERR_PTR(-ENOMEM);
- }
- sprintf(name, "PCI Bus: %04x:%02x", domain, ZPCI_BUS_NR);
- r->name = name;
-
- rc = request_resource(&iomem_resource, r);
- if (rc) {
- kfree(r->name);
- kfree(r);
- return ERR_PTR(-ENOMEM);
- }
- return r;
-}
-
static int zpci_alloc_iomap(struct zpci_dev *zdev)
{
int entry;
@@ -633,6 +588,82 @@ static void zpci_free_iomap(struct zpci_dev *zdev, int entry)
spin_unlock(&zpci_iomap_lock);
}
+static struct resource *__alloc_res(struct zpci_dev *zdev, unsigned long start,
+ unsigned long size, unsigned long flags)
+{
+ struct resource *r;
+
+ r = kzalloc(sizeof(*r), GFP_KERNEL);
+ if (!r)
+ return NULL;
+
+ r->start = start;
+ r->end = r->start + size - 1;
+ r->flags = flags;
+ r->name = zdev->res_name;
+
+ if (request_resource(&iomem_resource, r)) {
+ kfree(r);
+ return NULL;
+ }
+ return r;
+}
+
+static int zpci_setup_bus_resources(struct zpci_dev *zdev,
+ struct list_head *resources)
+{
+ unsigned long addr, size, flags;
+ struct resource *res;
+ int i, entry;
+
+ snprintf(zdev->res_name, sizeof(zdev->res_name),
+ "PCI Bus %04x:%02x", zdev->domain, ZPCI_BUS_NR);
+
+ for (i = 0; i < PCI_BAR_COUNT; i++) {
+ if (!zdev->bars[i].size)
+ continue;
+ entry = zpci_alloc_iomap(zdev);
+ if (entry < 0)
+ return entry;
+ zdev->bars[i].map_idx = entry;
+
+ /* only MMIO is supported */
+ flags = IORESOURCE_MEM;
+ if (zdev->bars[i].val & 8)
+ flags |= IORESOURCE_PREFETCH;
+ if (zdev->bars[i].val & 4)
+ flags |= IORESOURCE_MEM_64;
+
+ addr = ZPCI_IOMAP_ADDR_BASE + ((u64) entry << 48);
+
+ size = 1UL << zdev->bars[i].size;
+
+ res = __alloc_res(zdev, addr, size, flags);
+ if (!res) {
+ zpci_free_iomap(zdev, entry);
+ return -ENOMEM;
+ }
+ zdev->bars[i].res = res;
+ pci_add_resource(resources, res);
+ }
+
+ return 0;
+}
+
+static void zpci_cleanup_bus_resources(struct zpci_dev *zdev)
+{
+ int i;
+
+ for (i = 0; i < PCI_BAR_COUNT; i++) {
+ if (!zdev->bars[i].size)
+ continue;
+
+ zpci_free_iomap(zdev, zdev->bars[i].map_idx);
+ release_resource(zdev->bars[i].res);
+ kfree(zdev->bars[i].res);
+ }
+}
+
int pcibios_add_device(struct pci_dev *pdev)
{
struct zpci_dev *zdev = get_zdev(pdev);
@@ -729,52 +760,6 @@ struct dev_pm_ops pcibios_pm_ops = {
};
#endif /* CONFIG_HIBERNATE_CALLBACKS */
-static int zpci_scan_bus(struct zpci_dev *zdev)
-{
- struct resource *res;
- LIST_HEAD(resources);
- int i;
-
- /* allocate mapping entry for each used bar */
- for (i = 0; i < PCI_BAR_COUNT; i++) {
- unsigned long addr, size, flags;
- int entry;
-
- if (!zdev->bars[i].size)
- continue;
- entry = zpci_alloc_iomap(zdev);
- if (entry < 0)
- return entry;
- zdev->bars[i].map_idx = entry;
-
- /* only MMIO is supported */
- flags = IORESOURCE_MEM;
- if (zdev->bars[i].val & 8)
- flags |= IORESOURCE_PREFETCH;
- if (zdev->bars[i].val & 4)
- flags |= IORESOURCE_MEM_64;
-
- addr = ZPCI_IOMAP_ADDR_BASE + ((u64) entry << 48);
-
- size = 1UL << zdev->bars[i].size;
-
- res = zpci_alloc_bus_resource(addr, size, flags, zdev->domain);
- if (IS_ERR(res)) {
- zpci_free_iomap(zdev, entry);
- return PTR_ERR(res);
- }
- pci_add_resource(&resources, res);
- }
-
- zdev->bus = pci_scan_root_bus(NULL, ZPCI_BUS_NR, &pci_root_ops,
- zdev, &resources);
- if (!zdev->bus)
- return -EIO;
-
- zdev->bus->max_bus_speed = zdev->max_bus_speed;
- return 0;
-}
-
static int zpci_alloc_domain(struct zpci_dev *zdev)
{
spin_lock(&zpci_domain_lock);
@@ -795,6 +780,41 @@ static void zpci_free_domain(struct zpci_dev *zdev)
spin_unlock(&zpci_domain_lock);
}
+void pcibios_remove_bus(struct pci_bus *bus)
+{
+ struct zpci_dev *zdev = get_zdev_by_bus(bus);
+
+ zpci_exit_slot(zdev);
+ zpci_cleanup_bus_resources(zdev);
+ zpci_free_domain(zdev);
+
+ spin_lock(&zpci_list_lock);
+ list_del(&zdev->entry);
+ spin_unlock(&zpci_list_lock);
+
+ kfree(zdev);
+}
+
+static int zpci_scan_bus(struct zpci_dev *zdev)
+{
+ LIST_HEAD(resources);
+ int ret;
+
+ ret = zpci_setup_bus_resources(zdev, &resources);
+ if (ret)
+ return ret;
+
+ zdev->bus = pci_scan_root_bus(NULL, ZPCI_BUS_NR, &pci_root_ops,
+ zdev, &resources);
+ if (!zdev->bus) {
+ zpci_cleanup_bus_resources(zdev);
+ return -EIO;
+ }
+
+ zdev->bus->max_bus_speed = zdev->max_bus_speed;
+ return 0;
+}
+
int zpci_enable_device(struct zpci_dev *zdev)
{
int rc;
diff --git a/arch/s390/pci/pci_clp.c b/arch/s390/pci/pci_clp.c
index 84147984224a..c747394029ee 100644
--- a/arch/s390/pci/pci_clp.c
+++ b/arch/s390/pci/pci_clp.c
@@ -155,9 +155,9 @@ int clp_add_pci_device(u32 fid, u32 fh, int configured)
int rc;
zpci_dbg(3, "add fid:%x, fh:%x, c:%d\n", fid, fh, configured);
- zdev = zpci_alloc_device();
- if (IS_ERR(zdev))
- return PTR_ERR(zdev);
+ zdev = kzalloc(sizeof(*zdev), GFP_KERNEL);
+ if (!zdev)
+ return -ENOMEM;
zdev->fh = fh;
zdev->fid = fid;
@@ -178,7 +178,7 @@ int clp_add_pci_device(u32 fid, u32 fh, int configured)
return 0;
error:
- zpci_free_device(zdev);
+ kfree(zdev);
return rc;
}
diff --git a/arch/s390/pci/pci_event.c b/arch/s390/pci/pci_event.c
index 278e671ec9ac..800f064b0da7 100644
--- a/arch/s390/pci/pci_event.c
+++ b/arch/s390/pci/pci_event.c
@@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/pci.h>
#include <asm/pci_debug.h>
+#include <asm/sclp.h>
/* Content Code Description for PCI Function Error */
struct zpci_ccdf_err {
@@ -42,10 +43,27 @@ struct zpci_ccdf_avail {
u16 pec; /* PCI event code */
} __packed;
-static void zpci_event_log_avail(struct zpci_ccdf_avail *ccdf)
+void zpci_event_error(void *data)
{
+ struct zpci_ccdf_err *ccdf = data;
+ struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid);
+
+ zpci_err("error CCDF:\n");
+ zpci_err_hex(ccdf, sizeof(*ccdf));
+
+ if (!zdev)
+ return;
+
+ pr_err("%s: Event 0x%x reports an error for PCI function 0x%x\n",
+ pci_name(zdev->pdev), ccdf->pec, ccdf->fid);
+}
+
+void zpci_event_availability(void *data)
+{
+ struct zpci_ccdf_avail *ccdf = data;
struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid);
struct pci_dev *pdev = zdev ? zdev->pdev : NULL;
+ int ret;
pr_info("%s: Event 0x%x reconfigured PCI function 0x%x\n",
pdev ? pci_name(pdev) : "n/a", ccdf->pec, ccdf->fid);
@@ -53,36 +71,47 @@ static void zpci_event_log_avail(struct zpci_ccdf_avail *ccdf)
zpci_err_hex(ccdf, sizeof(*ccdf));
switch (ccdf->pec) {
- case 0x0301:
- zpci_enable_device(zdev);
+ case 0x0301: /* Standby -> Configured */
+ if (!zdev || zdev->state == ZPCI_FN_STATE_CONFIGURED)
+ break;
+ zdev->state = ZPCI_FN_STATE_CONFIGURED;
+ ret = zpci_enable_device(zdev);
+ if (ret)
+ break;
+ pci_rescan_bus(zdev->bus);
break;
- case 0x0302:
+ case 0x0302: /* Reserved -> Standby */
clp_add_pci_device(ccdf->fid, ccdf->fh, 0);
break;
- case 0x0306:
+ case 0x0303: /* Deconfiguration requested */
+ if (pdev)
+ pci_stop_and_remove_bus_device(pdev);
+
+ ret = zpci_disable_device(zdev);
+ if (ret)
+ break;
+
+ ret = sclp_pci_deconfigure(zdev->fid);
+ zpci_dbg(3, "deconf fid:%x, rc:%d\n", zdev->fid, ret);
+ if (!ret)
+ zdev->state = ZPCI_FN_STATE_STANDBY;
+
+ break;
+ case 0x0304: /* Configured -> Standby */
+ if (pdev)
+ pci_stop_and_remove_bus_device(pdev);
+
+ zpci_disable_device(zdev);
+ zdev->state = ZPCI_FN_STATE_STANDBY;
+ break;
+ case 0x0306: /* 0x308 or 0x302 for multiple devices */
clp_rescan_pci_devices();
break;
+ case 0x0308: /* Standby -> Reserved */
+ pci_stop_root_bus(zdev->bus);
+ pci_remove_root_bus(zdev->bus);
+ break;
default:
break;
}
}
-
-void zpci_event_error(void *data)
-{
- struct zpci_ccdf_err *ccdf = data;
- struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid);
-
- zpci_err("error CCDF:\n");
- zpci_err_hex(ccdf, sizeof(*ccdf));
-
- if (!zdev)
- return;
-
- pr_err("%s: Event 0x%x reports an error for PCI function 0x%x\n",
- pci_name(zdev->pdev), ccdf->pec, ccdf->fid);
-}
-
-void zpci_event_availability(void *data)
-{
- zpci_event_log_avail(data);
-}
OpenPOWER on IntegriCloud