ia64/linux-2.6.18-xen.hg

changeset 738:1b68d09b868f

Save/restore PCI MSI state across S3.

Signed-off-by: Haitao Shan <haitao.shan@intel.com>
author Keir Fraser <keir.fraser@citrix.com>
date Mon Nov 24 14:15:58 2008 +0000 (2008-11-24)
parents 163a3807cb1f
children 6bb7f500d5e4
files drivers/pci/msi-xen.c
line diff
     1.1 --- a/drivers/pci/msi-xen.c	Mon Nov 24 11:13:20 2008 +0000
     1.2 +++ b/drivers/pci/msi-xen.c	Mon Nov 24 14:15:58 2008 +0000
     1.3 @@ -48,6 +48,13 @@ struct msi_pirq_entry {
     1.4  	struct list_head list;
     1.5  	int pirq;
     1.6  	int entry_nr;
     1.7 +#ifdef CONFIG_PM
     1.8 +	/* PM save area for MSIX address/data */
     1.9 +	void __iomem *mask_base;
    1.10 +	u32	address_hi_save;
    1.11 +	u32	address_lo_save;
    1.12 +	u32	data_save;
    1.13 +#endif
    1.14  };
    1.15  
    1.16  static struct msi_dev_list *get_msi_dev_pirq_list(struct pci_dev *dev)
    1.17 @@ -83,7 +90,7 @@ static struct msi_dev_list *get_msi_dev_
    1.18  	return ret;
    1.19  }
    1.20  
    1.21 -static int attach_pirq_entry(int pirq, int entry_nr,
    1.22 +static int attach_pirq_entry(int pirq, int entry_nr, u64 table_base,
    1.23                               struct msi_dev_list *msi_dev_entry)
    1.24  {
    1.25  	struct msi_pirq_entry *entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
    1.26 @@ -93,6 +100,9 @@ static int attach_pirq_entry(int pirq, i
    1.27  		return -ENOMEM;
    1.28  	entry->pirq = pirq;
    1.29  	entry->entry_nr = entry_nr;
    1.30 +#ifdef COMFIG_PM
    1.31 +	entry->mask_base = table_base;
    1.32 +#endif
    1.33  	spin_lock_irqsave(&msi_dev_entry->pirq_list_lock, flags);
    1.34  	list_add_tail(&entry->list, &msi_dev_entry->pirq_list_head);
    1.35  	spin_unlock_irqrestore(&msi_dev_entry->pirq_list_lock, flags);
    1.36 @@ -299,104 +309,173 @@ static void enable_msi_mode(struct pci_d
    1.37  #ifdef CONFIG_PM
    1.38  int pci_save_msi_state(struct pci_dev *dev)
    1.39  {
    1.40 -	int pos;
    1.41 +	int pos, i = 0;
    1.42 +	u16 control;
    1.43 +	struct pci_cap_saved_state *save_state;
    1.44 +	u32 *cap;
    1.45  
    1.46  	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
    1.47  	if (pos <= 0 || dev->no_msi)
    1.48  		return 0;
    1.49  
    1.50 -	if (!dev->msi_enabled)
    1.51 +	pci_read_config_word(dev, msi_control_reg(pos), &control);
    1.52 +	if (!(control & PCI_MSI_FLAGS_ENABLE))
    1.53  		return 0;
    1.54  
    1.55 -	/* Restore dev->irq to its default pin-assertion vector */
    1.56 -	msi_unmap_pirq(dev, dev->irq);
    1.57 -	/* Disable MSI mode */
    1.58 -	disable_msi_mode(dev, pos, PCI_CAP_ID_MSI);
    1.59 -	/* Set the flags for use of restore */
    1.60 -	dev->msi_enabled = 1;
    1.61 +	save_state = kzalloc(sizeof(struct pci_cap_saved_state) + sizeof(u32) * 5,
    1.62 +		GFP_KERNEL);
    1.63 +	if (!save_state) {
    1.64 +		printk(KERN_ERR "Out of memory in pci_save_msi_state\n");
    1.65 +		return -ENOMEM;
    1.66 +	}
    1.67 +	cap = &save_state->data[0];
    1.68 +
    1.69 +	pci_read_config_dword(dev, pos, &cap[i++]);
    1.70 +	control = cap[0] >> 16;
    1.71 +	pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, &cap[i++]);
    1.72 +	if (control & PCI_MSI_FLAGS_64BIT) {
    1.73 +		pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_HI, &cap[i++]);
    1.74 +		pci_read_config_dword(dev, pos + PCI_MSI_DATA_64, &cap[i++]);
    1.75 +	} else
    1.76 +		pci_read_config_dword(dev, pos + PCI_MSI_DATA_32, &cap[i++]);
    1.77 +	if (control & PCI_MSI_FLAGS_MASKBIT)
    1.78 +		pci_read_config_dword(dev, pos + PCI_MSI_MASK_BIT, &cap[i++]);
    1.79 +	save_state->cap_nr = PCI_CAP_ID_MSI;
    1.80 +	pci_add_saved_cap(dev, save_state);
    1.81  	return 0;
    1.82  }
    1.83  
    1.84  void pci_restore_msi_state(struct pci_dev *dev)
    1.85  {
    1.86 -	int pos, pirq;
    1.87 -
    1.88 -	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
    1.89 -	if (pos <= 0)
    1.90 -		return;
    1.91 +	int i = 0, pos;
    1.92 +	u16 control;
    1.93 +	struct pci_cap_saved_state *save_state;
    1.94 +	u32 *cap;
    1.95  
    1.96 -	if (!dev->msi_enabled)
    1.97 +	save_state = pci_find_saved_cap(dev, PCI_CAP_ID_MSI);
    1.98 +	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
    1.99 +	if (!save_state || pos <= 0)
   1.100  		return;
   1.101 +	cap = &save_state->data[0];
   1.102  
   1.103 -	pirq = msi_map_pirq_to_vector(dev, dev->irq, 0, 0);
   1.104 -	if (pirq < 0)
   1.105 -		return;
   1.106 +	control = cap[i++] >> 16;
   1.107 +	pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, cap[i++]);
   1.108 +	if (control & PCI_MSI_FLAGS_64BIT) {
   1.109 +		pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_HI, cap[i++]);
   1.110 +		pci_write_config_dword(dev, pos + PCI_MSI_DATA_64, cap[i++]);
   1.111 +	} else
   1.112 +		pci_write_config_dword(dev, pos + PCI_MSI_DATA_32, cap[i++]);
   1.113 +	if (control & PCI_MSI_FLAGS_MASKBIT)
   1.114 +		pci_write_config_dword(dev, pos + PCI_MSI_MASK_BIT, cap[i++]);
   1.115 +	pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control);
   1.116  	enable_msi_mode(dev, pos, PCI_CAP_ID_MSI);
   1.117 +	pci_remove_saved_cap(save_state);
   1.118 +	kfree(save_state);
   1.119  }
   1.120  
   1.121  int pci_save_msix_state(struct pci_dev *dev)
   1.122  {
   1.123  	int pos;
   1.124 +	u16 control;
   1.125 +	struct pci_cap_saved_state *save_state;
   1.126  	unsigned long flags;
   1.127  	struct msi_dev_list *msi_dev_entry;
   1.128 -	struct msi_pirq_entry *pirq_entry, *tmp;
   1.129 +	struct msi_pirq_entry *pirq_entry;
   1.130  
   1.131  	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
   1.132  	if (pos <= 0 || dev->no_msi)
   1.133  		return 0;
   1.134  
   1.135 +	printk(KERN_CRIT "Saving MSIX cap\n");
   1.136 +
   1.137  	/* save the capability */
   1.138 -	if (!dev->msix_enabled)
   1.139 +	pci_read_config_word(dev, msi_control_reg(pos), &control);
   1.140 +	if (!(control & PCI_MSIX_FLAGS_ENABLE))
   1.141  		return 0;
   1.142 +	save_state = kzalloc(sizeof(struct pci_cap_saved_state) + sizeof(u16),
   1.143 +		GFP_KERNEL);
   1.144 +	if (!save_state) {
   1.145 +		printk(KERN_ERR "Out of memory in pci_save_msix_state\n");
   1.146 +		return -ENOMEM;
   1.147 +	}
   1.148 +	*((u16 *)&save_state->data[0]) = control;
   1.149  
   1.150  	msi_dev_entry = get_msi_dev_pirq_list(dev);
   1.151  
   1.152  	spin_lock_irqsave(&msi_dev_entry->pirq_list_lock, flags);
   1.153 -        list_for_each_entry_safe(pirq_entry, tmp,
   1.154 -                                 &msi_dev_entry->pirq_list_head, list)
   1.155 -		msi_unmap_pirq(dev, pirq_entry->pirq);
   1.156 +	list_for_each_entry(pirq_entry, &msi_dev_entry->pirq_list_head, list) {
   1.157 +		int j;
   1.158 +		void __iomem *base;
   1.159 +
   1.160 +		/* save the table */
   1.161 +		base = pirq_entry->mask_base;
   1.162 +		j = pirq_entry->entry_nr;
   1.163 +		printk(KERN_CRIT "Save msix table entry %d pirq %x base %p\n",
   1.164 +		       j, pirq_entry->pirq, base);
   1.165 +
   1.166 +		pirq_entry->address_lo_save =
   1.167 +			readl(base + j * PCI_MSIX_ENTRY_SIZE +
   1.168 +			      PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
   1.169 +		pirq_entry->address_hi_save =
   1.170 +			readl(base + j * PCI_MSIX_ENTRY_SIZE +
   1.171 +			      PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
   1.172 +		pirq_entry->data_save =
   1.173 +			readl(base + j * PCI_MSIX_ENTRY_SIZE +
   1.174 +			      PCI_MSIX_ENTRY_DATA_OFFSET);
   1.175 +	}
   1.176  	spin_unlock_irqrestore(&msi_dev_entry->pirq_list_lock, flags);
   1.177  
   1.178 -	disable_msi_mode(dev, pos, PCI_CAP_ID_MSIX);
   1.179 -	/* Set the flags for use of restore */
   1.180 -	dev->msix_enabled = 1;
   1.181 -
   1.182 +	save_state->cap_nr = PCI_CAP_ID_MSIX;
   1.183 +	pci_add_saved_cap(dev, save_state);
   1.184  	return 0;
   1.185  }
   1.186  
   1.187  void pci_restore_msix_state(struct pci_dev *dev)
   1.188  {
   1.189 -	int pos;
   1.190 +	u16 save;
   1.191 +	int pos, j;
   1.192 +	void __iomem *base;
   1.193 +	struct pci_cap_saved_state *save_state;
   1.194  	unsigned long flags;
   1.195 -	u64 table_base;
   1.196  	struct msi_dev_list *msi_dev_entry;
   1.197 -	struct msi_pirq_entry *pirq_entry, *tmp;
   1.198 +	struct msi_pirq_entry *pirq_entry;
   1.199 +
   1.200 +	save_state = pci_find_saved_cap(dev, PCI_CAP_ID_MSIX);
   1.201 +	if (!save_state)
   1.202 +		return;
   1.203 +	printk(KERN_CRIT "Restoring MSIX cap\n");
   1.204 +
   1.205 +	save = *((u16 *)&save_state->data[0]);
   1.206 +	pci_remove_saved_cap(save_state);
   1.207 +	kfree(save_state);
   1.208  
   1.209  	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
   1.210  	if (pos <= 0)
   1.211  		return;
   1.212  
   1.213 -	if (!dev->msix_enabled)
   1.214 -		return;
   1.215 -
   1.216  	msi_dev_entry = get_msi_dev_pirq_list(dev);
   1.217 -	table_base = find_table_base(dev, pos);
   1.218 -	if (!table_base)
   1.219 -		return;
   1.220  
   1.221  	spin_lock_irqsave(&msi_dev_entry->pirq_list_lock, flags);
   1.222 -	list_for_each_entry_safe(pirq_entry, tmp,
   1.223 -				 &msi_dev_entry->pirq_list_head, list) {
   1.224 -		int rc = msi_map_pirq_to_vector(dev, pirq_entry->pirq,
   1.225 -						pirq_entry->entry_nr, table_base);
   1.226 -		if (rc < 0)
   1.227 -			printk(KERN_WARNING
   1.228 -			       "%s: re-mapping irq #%d (pirq%d) failed: %d\n",
   1.229 -			       pci_name(dev), pirq_entry->entry_nr,
   1.230 -			       pirq_entry->pirq, rc);
   1.231 +	list_for_each_entry(pirq_entry, &msi_dev_entry->pirq_list_head, list) {
   1.232 +		/* route the table */
   1.233 +		base = pirq_entry->mask_base;
   1.234 +		j = pirq_entry->entry_nr;
   1.235 +
   1.236 +		printk(KERN_CRIT "Restore msix table entry %d pirq %x base %p\n",
   1.237 +		       j, pirq_entry->pirq, base);
   1.238 +		writel(pirq_entry->address_lo_save,
   1.239 +			base + j * PCI_MSIX_ENTRY_SIZE +
   1.240 +			PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
   1.241 +		writel(pirq_entry->address_hi_save,
   1.242 +			base + j * PCI_MSIX_ENTRY_SIZE +
   1.243 +			PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
   1.244 +		writel(pirq_entry->data_save,
   1.245 +			base + j * PCI_MSIX_ENTRY_SIZE +
   1.246 +			PCI_MSIX_ENTRY_DATA_OFFSET);
   1.247  	}
   1.248  	spin_unlock_irqrestore(&msi_dev_entry->pirq_list_lock, flags);
   1.249  
   1.250 +	pci_write_config_word(dev, msi_control_reg(pos), save);
   1.251  	enable_msi_mode(dev, pos, PCI_CAP_ID_MSIX);
   1.252  }
   1.253  #endif
   1.254 @@ -475,7 +554,7 @@ static int msix_capability_init(struct p
   1.255  		pirq = msi_map_vector(dev, entries[i].entry, table_base);
   1.256  		if (pirq < 0)
   1.257  			break;
   1.258 -		attach_pirq_entry(pirq, entries[i].entry, msi_dev_entry);
   1.259 +		attach_pirq_entry(pirq, entries[i].entry, table_base, msi_dev_entry);
   1.260  		(entries + i)->vector = pirq;
   1.261  	}
   1.262  
   1.263 @@ -660,7 +739,7 @@ int pci_enable_msix(struct pci_dev* dev,
   1.264  			if (mapped)
   1.265  				continue;
   1.266  			irq = evtchn_map_pirq(-1, entries[i].vector);
   1.267 -			attach_pirq_entry(irq, entries[i].entry, msi_dev_entry);
   1.268 +			attach_pirq_entry(irq, entries[i].entry, 0, msi_dev_entry);
   1.269  			entries[i].vector = irq;
   1.270  		}
   1.271          return 0;