@@ -463,15 +463,17 @@ pci_read_config(struct file *filp, struc
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
+ struct device *dev = container_of(kobj,struct device,kobj);
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct device *parent = dev->parent;
unsigned int size = 64;
loff_t init_off = off;
u8 *data = (u8*) buf;
/* Several chips lock up trying to read undefined config space */
if (security_capable(filp->f_cred, &init_user_ns, CAP_SYS_ADMIN) == 0) {
- size = dev->cfg_size;
- } else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) {
+ size = pdev->cfg_size;
+ } else if (pdev->hdr_type == PCI_HEADER_TYPE_CARDBUS) {
size = 128;
}
@@ -484,9 +486,20 @@ pci_read_config(struct file *filp, struc
size = count;
}
+ if (parent)
+ pm_runtime_get_sync(parent);
+ pm_runtime_get_noresume(dev);
+ /*
+ * pdev->current_state is set to PCI_D3cold during suspending,
+ * so wait until suspending completes
+ */
+ pm_runtime_barrier(dev);
+ if (pdev->current_state == PCI_D3cold)
+ pm_runtime_resume(dev);
+
if ((off & 1) && size) {
u8 val;
- pci_user_read_config_byte(dev, off, &val);
+ pci_user_read_config_byte(pdev, off, &val);
data[off - init_off] = val;
off++;
size--;
@@ -494,7 +507,7 @@ pci_read_config(struct file *filp, struc
if ((off & 3) && size > 2) {
u16 val;
- pci_user_read_config_word(dev, off, &val);
+ pci_user_read_config_word(pdev, off, &val);
data[off - init_off] = val & 0xff;
data[off - init_off + 1] = (val >> 8) & 0xff;
off += 2;
@@ -503,7 +516,7 @@ pci_read_config(struct file *filp, struc
while (size > 3) {
u32 val;
- pci_user_read_config_dword(dev, off, &val);
+ pci_user_read_config_dword(pdev, off, &val);
data[off - init_off] = val & 0xff;
data[off - init_off + 1] = (val >> 8) & 0xff;
data[off - init_off + 2] = (val >> 16) & 0xff;
@@ -514,7 +527,7 @@ pci_read_config(struct file *filp, struc
if (size >= 2) {
u16 val;
- pci_user_read_config_word(dev, off, &val);
+ pci_user_read_config_word(pdev, off, &val);
data[off - init_off] = val & 0xff;
data[off - init_off + 1] = (val >> 8) & 0xff;
off += 2;
@@ -523,12 +536,16 @@ pci_read_config(struct file *filp, struc
if (size > 0) {
u8 val;
- pci_user_read_config_byte(dev, off, &val);
+ pci_user_read_config_byte(pdev, off, &val);
data[off - init_off] = val;
off++;
--size;
}
+ pm_runtime_put(dev);
+ if (parent)
+ pm_runtime_put(parent);
+
return count;
}
@@ -537,20 +554,33 @@ pci_write_config(struct file* filp, stru
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
+ struct device *dev = container_of(kobj,struct device,kobj);
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct device *parent = dev->parent;
unsigned int size = count;
loff_t init_off = off;
u8 *data = (u8*) buf;
- if (off > dev->cfg_size)
+ if (off > pdev->cfg_size)
return 0;
- if (off + count > dev->cfg_size) {
- size = dev->cfg_size - off;
+ if (off + count > pdev->cfg_size) {
+ size = pdev->cfg_size - off;
count = size;
}
+ if (parent)
+ pm_runtime_get_sync(parent);
+ pm_runtime_get_noresume(dev);
+ /*
+ * pdev->current_state is set to PCI_D3cold during suspending,
+ * so wait until suspending completes
+ */
+ pm_runtime_barrier(dev);
+ if (pdev->current_state == PCI_D3cold)
+ pm_runtime_resume(dev);
+
if ((off & 1) && size) {
- pci_user_write_config_byte(dev, off, data[off - init_off]);
+ pci_user_write_config_byte(pdev, off, data[off - init_off]);
off++;
size--;
}
@@ -558,7 +588,7 @@ pci_write_config(struct file* filp, stru
if ((off & 3) && size > 2) {
u16 val = data[off - init_off];
val |= (u16) data[off - init_off + 1] << 8;
- pci_user_write_config_word(dev, off, val);
+ pci_user_write_config_word(pdev, off, val);
off += 2;
size -= 2;
}
@@ -568,7 +598,7 @@ pci_write_config(struct file* filp, stru
val |= (u32) data[off - init_off + 1] << 8;
val |= (u32) data[off - init_off + 2] << 16;
val |= (u32) data[off - init_off + 3] << 24;
- pci_user_write_config_dword(dev, off, val);
+ pci_user_write_config_dword(pdev, off, val);
off += 4;
size -= 4;
}
@@ -576,17 +606,21 @@ pci_write_config(struct file* filp, stru
if (size >= 2) {
u16 val = data[off - init_off];
val |= (u16) data[off - init_off + 1] << 8;
- pci_user_write_config_word(dev, off, val);
+ pci_user_write_config_word(pdev, off, val);
off += 2;
size -= 2;
}
if (size) {
- pci_user_write_config_byte(dev, off, data[off - init_off]);
+ pci_user_write_config_byte(pdev, off, data[off - init_off]);
off++;
--size;
}
+ pm_runtime_put(dev);
+ if (parent)
+ pm_runtime_put(parent);
+
return count;
}
@@ -140,9 +140,17 @@ static int pcie_port_runtime_resume(stru
{
return 0;
}
+
+static int pcie_port_runtime_idle(struct device *dev)
+{
+ /* Delay for a short while to prevent too frequent suspend/resume */
+ pm_schedule_suspend(dev, 10);
+ return -EBUSY;
+}
#else
#define pcie_port_runtime_suspend NULL
#define pcie_port_runtime_resume NULL
+#define pcie_port_runtime_idle NULL
#endif
static const struct dev_pm_ops pcie_portdrv_pm_ops = {
@@ -155,6 +163,7 @@ static const struct dev_pm_ops pcie_port
.resume_noirq = pcie_port_resume_noirq,
.runtime_suspend = pcie_port_runtime_suspend,
.runtime_resume = pcie_port_runtime_resume,
+ .runtime_idle = pcie_port_runtime_idle,
};
#define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops)