lkml.org 
[lkml]   [2003]   [Jan]   [24]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subject[patch 2.5] pci_{save,restore}_extended_state
Existing pci_{save,restore}_state() functions take care only
of first 64 bytes of the PCI config space. It's not sufficient
for modern (especially PCI-X) hardware, which usually have extra
config registers (defined by Capabilities List) located above
64-byte boundary.

The patch adds 3 new functions:
- pci_alloc_extended_state - allocates buffer large enough to hold
standard PCI 2.1 configuration registers (first 64 bytes of configuration
header) plus space for extended configuration registers defined by
Capabilities List (if present);
- pci_save_extended_state - save the PCI configuration space of a device
before suspending or reset, including extended configuration registers;
- pci_restore_extended_state - restore the saved state of a PCI device,
including extended configuration. Note that it doesn't restore Power
Management registers - in conjunction with pci_set_power_state() it
could produce nasty effects.

pci.h: added missing SIZEOFs for each capability as per PCI specs.
I'm not 100% sure about PCI_CHSWP_SIZEOF and PCI_X_SIZEOF - somebody
with respective specs in hands (Matthew?) ought to verify this...

Ivan.

--- 2.5.59/drivers/pci/pci.c Fri Jan 17 05:21:48 2003
+++ linux/drivers/pci/pci.c Tue Jan 21 15:32:25 2003
@@ -67,6 +67,29 @@ pci_max_busnr(void)
return max;
}

+static u8
+get_cap_pos(struct pci_dev *dev)
+{
+ u8 pos;
+ u16 status;
+
+ pci_read_config_word(dev, PCI_STATUS, &status);
+ if (!(status & PCI_STATUS_CAP_LIST))
+ return 0;
+ switch (dev->hdr_type) {
+ case PCI_HEADER_TYPE_NORMAL:
+ case PCI_HEADER_TYPE_BRIDGE:
+ pci_read_config_byte(dev, PCI_CAPABILITY_LIST, &pos);
+ break;
+ case PCI_HEADER_TYPE_CARDBUS:
+ pci_read_config_byte(dev, PCI_CB_CAPABILITY_LIST, &pos);
+ break;
+ default:
+ return 0;
+ }
+ return pos;
+}
+
/**
* pci_find_capability - query for devices' capabilities
* @dev: PCI device to query
@@ -94,24 +117,10 @@ pci_max_busnr(void)
int
pci_find_capability(struct pci_dev *dev, int cap)
{
- u16 status;
u8 pos, id;
int ttl = 48;

- pci_read_config_word(dev, PCI_STATUS, &status);
- if (!(status & PCI_STATUS_CAP_LIST))
- return 0;
- switch (dev->hdr_type) {
- case PCI_HEADER_TYPE_NORMAL:
- case PCI_HEADER_TYPE_BRIDGE:
- pci_read_config_byte(dev, PCI_CAPABILITY_LIST, &pos);
- break;
- case PCI_HEADER_TYPE_CARDBUS:
- pci_read_config_byte(dev, PCI_CB_CAPABILITY_LIST, &pos);
- break;
- default:
- return 0;
- }
+ pos = get_cap_pos(dev);
while (ttl-- && pos >= 0x40) {
pos &= ~3;
pci_read_config_byte(dev, pos + PCI_CAP_LIST_ID, &id);
@@ -327,6 +336,116 @@ pci_restore_state(struct pci_dev *dev, u
return 0;
}

+static int
+get_cap_size(u32 cap)
+{
+ switch (cap & 0xff) {
+ case PCI_CAP_ID_PM:
+ return PCI_PM_SIZEOF;
+ case PCI_CAP_ID_AGP:
+ return PCI_AGP_SIZEOF;
+ case PCI_CAP_ID_VPD:
+ return PCI_VPD_SIZEOF;
+ case PCI_CAP_ID_SLOTID:
+ return PCI_SID_SIZEOF;
+ case PCI_CAP_ID_MSI:
+ return cap & (PCI_MSI_FLAGS_64BIT << 16) ? PCI_MSI64_SIZEOF :
+ PCI_MSI32_SIZEOF;
+ case PCI_CAP_ID_CHSWP:
+ return PCI_CHSWP_SIZEOF;
+ case PCI_CAP_ID_PCIX:
+ return PCI_X_SIZEOF;
+ default:
+ return PCI_CAP_SIZEOF;
+ }
+}
+
+/**
+ * pci_alloc_extended_state - allocates buffer large enough to hold
+ * standard PCI 2.1 configuration registers (first 64 bytes of configuration
+ * header) plus space for extended configuration registers defined by
+ * Capabilities List (if present)
+ * @dev: - PCI device that we're dealing with
+ */
+u32 *
+pci_alloc_extended_state(struct pci_dev *dev)
+{
+ u8 pos;
+ u32 cap;
+ int ttl = 48, size = 64;
+
+ pos = get_cap_pos(dev) & ~3;
+ while (ttl-- && pos >= 0x40) {
+ pci_read_config_dword(dev, pos, &cap);
+ size += (get_cap_size(cap) + 3) & ~3; /* round up to dword */
+ pos = (cap >> 8) & 0xfc;
+ }
+ return kmalloc(size, GFP_KERNEL);
+}
+
+/**
+ * pci_save_extended_state - save the PCI configuration space of a device
+ * before suspending, including extended configuration registers defined by
+ * Capabilities List
+ * @dev: - PCI device that we're dealing with
+ * @buffer: - buffer to hold config space context
+ *
+ * @buffer must be allocated by pci_alloc_extended_state
+ * (>= 64 bytes).
+ */
+int
+pci_save_extended_state(struct pci_dev *dev, u32 *buf)
+{
+ u32 *ptr = buf + 16;
+ u8 pos, next_pos;
+ int ttl = 48, i, cap_size;
+
+ if (!buf)
+ return 0;
+ pci_save_state(dev, buf);
+ pos = get_cap_pos(dev) & ~3;
+ while (ttl-- && pos >= 0x40) {
+ pci_read_config_dword(dev, pos, ptr);
+ next_pos = (*ptr >> 8) & 0xfc;
+ cap_size = get_cap_size(*ptr++);
+ for (i = 4; i < cap_size; i += 4, ptr++)
+ pci_read_config_dword(dev, pos + i, ptr);
+ pos = next_pos;
+ }
+ return 0;
+}
+
+/**
+ * pci_restore_extended_state - restore the saved state of a PCI device,
+ * including extended configuration registers defined by Capabilities List
+ * @dev: - PCI device that we're dealing with
+ * @buffer: - saved PCI config space
+ */
+int
+pci_restore_extended_state(struct pci_dev *dev, u32 *buf)
+{
+ u32 *ptr = buf + 16;
+ u8 pos, next_pos;
+ int i, cap_size;
+
+ pci_restore_state(dev, buf);
+ if (!buf)
+ return 0;
+ pos = get_cap_pos(dev) & ~3;
+ while (pos >= 0x40) {
+ u8 cap_id = *ptr & 0xff;
+
+ cap_size = get_cap_size(*ptr);
+ next_pos = (*ptr >> 8) & 0xfc;
+ for (i = 0; i < cap_size; i += 4, ptr++)
+ /* Don't restore PM state! */
+ if (cap_id != PCI_CAP_ID_PM)
+ pci_write_config_dword(dev, pos + i, *ptr);
+ pos = next_pos;
+ }
+ return 0;
+}
+
/**
* pci_enable_device_bars - Initialize some of a device for use
* @dev: PCI device to be initialized
@@ -757,6 +876,9 @@ EXPORT_SYMBOL(pci_find_parent_resource);
EXPORT_SYMBOL(pci_set_power_state);
EXPORT_SYMBOL(pci_save_state);
EXPORT_SYMBOL(pci_restore_state);
+EXPORT_SYMBOL(pci_alloc_extended_state);
+EXPORT_SYMBOL(pci_save_extended_state);
+EXPORT_SYMBOL(pci_restore_extended_state);
EXPORT_SYMBOL(pci_enable_wake);

/* Quirk info */
--- 2.5.59/include/linux/pci.h Fri Jan 17 05:22:08 2003
+++ linux/include/linux/pci.h Tue Jan 21 15:56:54 2003
@@ -258,6 +258,7 @@
#define PCI_VPD_ADDR_MASK 0x7fff /* Address mask */
#define PCI_VPD_ADDR_F 0x8000 /* Write 0, 1 indicates completion */
#define PCI_VPD_DATA 4 /* 32-bits of data returned here */
+#define PCI_VPD_SIZEOF 8

/* Slot Identification */

@@ -265,6 +266,7 @@
#define PCI_SID_ESR_NSLOTS 0x1f /* Number of expansion slots available */
#define PCI_SID_ESR_FIC 0x20 /* First In Chassis Flag */
#define PCI_SID_CHASSIS_NR 3 /* Chassis Number */
+#define PCI_SID_SIZEOF 4

/* Message Signalled Interrupts registers */

@@ -278,6 +280,8 @@
#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */
#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */
#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */
+#define PCI_MSI32_SIZEOF 10
+#define PCI_MSI64_SIZEOF 14

/* CompactPCI Hotswap Register */

@@ -289,6 +293,7 @@
#define PCI_CHSWP_PI 0x30 /* Programming Interface */
#define PCI_CHSWP_EXT 0x40 /* ENUM# status - extraction */
#define PCI_CHSWP_INS 0x80 /* ENUM# status - insertion */
+#define PCI_CHSWP_SIZEOF 4

/* PCI-X registers */

@@ -309,6 +314,7 @@
#define PCI_X_STATUS_MAX_SPLIT 0x0380 /* Design Max Outstanding Split Trans */
#define PCI_X_STATUS_MAX_CUM 0x1c00 /* Designed Max Cumulative Read Size */
#define PCI_X_STATUS_SPL_ERR 0x2000 /* Rcvd Split Completion Error Msg */
+#define PCI_X_SIZEOF 9

/* Include the ID list */

@@ -629,6 +635,9 @@ int pci_assign_resource(struct pci_dev *
/* Power management related routines */
int pci_save_state(struct pci_dev *dev, u32 *buffer);
int pci_restore_state(struct pci_dev *dev, u32 *buffer);
+u32 *pci_alloc_extended_state(struct pci_dev *dev);
+int pci_save_extended_state(struct pci_dev *dev, u32 *buffer);
+int pci_restore_extended_state(struct pci_dev *dev, u32 *buffer);
int pci_set_power_state(struct pci_dev *dev, int state);
int pci_enable_wake(struct pci_dev *dev, u32 state, int enable);

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
\
 
 \ /
  Last update: 2005-03-22 13:32    [W:0.038 / U:3.712 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site