Commit e522a7126c7c144a1dd14c6f217ac31e71082b1d
Committed by
Jesse Barnes
1 parent
a246670dde
Exists in
master
and in
7 other branches
PCI: Set PCIE maxpayload for card during hotplug insertion
The following patch sets the MaxPayload setting to match the parent reading when inserting a PCIE card into a hotplug slot. On our system, the upstream bridge is set to 256, but when inserting a card, the card setting defaults to 128. As soon as I/O is performed to the card it starts receiving errors since the payload size is too small. Reviewed-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com> Signed-off-by: Jordan Hargrave <jordan_hargrave@dell.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Showing 1 changed file with 45 additions and 0 deletions Side-by-side Diff
drivers/pci/hotplug/pcihp_slot.c
... | ... | @@ -158,6 +158,47 @@ |
158 | 158 | */ |
159 | 159 | } |
160 | 160 | |
161 | +/* Program PCIE MaxPayload setting on device: ensure parent maxpayload <= device */ | |
162 | +static int pci_set_payload(struct pci_dev *dev) | |
163 | +{ | |
164 | + int pos, ppos; | |
165 | + u16 pctl, psz; | |
166 | + u16 dctl, dsz, dcap, dmax; | |
167 | + struct pci_dev *parent; | |
168 | + | |
169 | + parent = dev->bus->self; | |
170 | + pos = pci_find_capability(dev, PCI_CAP_ID_EXP); | |
171 | + if (!pos) | |
172 | + return 0; | |
173 | + | |
174 | + /* Read Device MaxPayload capability and setting */ | |
175 | + pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &dctl); | |
176 | + pci_read_config_word(dev, pos + PCI_EXP_DEVCAP, &dcap); | |
177 | + dsz = (dctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5; | |
178 | + dmax = (dcap & PCI_EXP_DEVCAP_PAYLOAD); | |
179 | + | |
180 | + /* Read Parent MaxPayload setting */ | |
181 | + ppos = pci_find_capability(parent, PCI_CAP_ID_EXP); | |
182 | + if (!ppos) | |
183 | + return 0; | |
184 | + pci_read_config_word(parent, ppos + PCI_EXP_DEVCTL, &pctl); | |
185 | + psz = (pctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5; | |
186 | + | |
187 | + /* If parent payload > device max payload -> error | |
188 | + * If parent payload > device payload -> set speed | |
189 | + * If parent payload <= device payload -> do nothing | |
190 | + */ | |
191 | + if (psz > dmax) | |
192 | + return -1; | |
193 | + else if (psz > dsz) { | |
194 | + dev_info(&dev->dev, "Setting MaxPayload to %d\n", 128 << psz); | |
195 | + pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, | |
196 | + (dctl & ~PCI_EXP_DEVCTL_PAYLOAD) + | |
197 | + (psz << 5)); | |
198 | + } | |
199 | + return 0; | |
200 | +} | |
201 | + | |
161 | 202 | void pci_configure_slot(struct pci_dev *dev) |
162 | 203 | { |
163 | 204 | struct pci_dev *cdev; |
... | ... | @@ -168,6 +209,10 @@ |
168 | 209 | (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && |
169 | 210 | (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI))) |
170 | 211 | return; |
212 | + | |
213 | + ret = pci_set_payload(dev); | |
214 | + if (ret) | |
215 | + dev_warn(&dev->dev, "could not set device max payload\n"); | |
171 | 216 | |
172 | 217 | memset(&hpp, 0, sizeof(hpp)); |
173 | 218 | ret = pci_get_hp_params(dev, &hpp); |