diff options
Diffstat (limited to 'drivers/pci/pcie/aer/aerdrv.c')
-rw-r--r-- | drivers/pci/pcie/aer/aerdrv.c | 179 |
1 files changed, 133 insertions, 46 deletions
diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index 7a711ee314b7..484cc55194b8 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c | |||
@@ -72,13 +72,120 @@ void pci_no_aer(void) | |||
72 | pcie_aer_disable = 1; /* has priority over 'forceload' */ | 72 | pcie_aer_disable = 1; /* has priority over 'forceload' */ |
73 | } | 73 | } |
74 | 74 | ||
75 | static int set_device_error_reporting(struct pci_dev *dev, void *data) | ||
76 | { | ||
77 | bool enable = *((bool *)data); | ||
78 | |||
79 | if ((dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) || | ||
80 | (dev->pcie_type == PCI_EXP_TYPE_UPSTREAM) || | ||
81 | (dev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM)) { | ||
82 | if (enable) | ||
83 | pci_enable_pcie_error_reporting(dev); | ||
84 | else | ||
85 | pci_disable_pcie_error_reporting(dev); | ||
86 | } | ||
87 | |||
88 | if (enable) | ||
89 | pcie_set_ecrc_checking(dev); | ||
90 | |||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * set_downstream_devices_error_reporting - enable/disable the error reporting bits on the root port and its downstream ports. | ||
96 | * @dev: pointer to root port's pci_dev data structure | ||
97 | * @enable: true = enable error reporting, false = disable error reporting. | ||
98 | */ | ||
99 | static void set_downstream_devices_error_reporting(struct pci_dev *dev, | ||
100 | bool enable) | ||
101 | { | ||
102 | set_device_error_reporting(dev, &enable); | ||
103 | |||
104 | if (!dev->subordinate) | ||
105 | return; | ||
106 | pci_walk_bus(dev->subordinate, set_device_error_reporting, &enable); | ||
107 | } | ||
108 | |||
109 | /** | ||
110 | * aer_enable_rootport - enable Root Port's interrupts when receiving messages | ||
111 | * @rpc: pointer to a Root Port data structure | ||
112 | * | ||
113 | * Invoked when PCIe bus loads AER service driver. | ||
114 | */ | ||
115 | static void aer_enable_rootport(struct aer_rpc *rpc) | ||
116 | { | ||
117 | struct pci_dev *pdev = rpc->rpd->port; | ||
118 | int pos, aer_pos; | ||
119 | u16 reg16; | ||
120 | u32 reg32; | ||
121 | |||
122 | pos = pci_pcie_cap(pdev); | ||
123 | /* Clear PCIe Capability's Device Status */ | ||
124 | pci_read_config_word(pdev, pos+PCI_EXP_DEVSTA, ®16); | ||
125 | pci_write_config_word(pdev, pos+PCI_EXP_DEVSTA, reg16); | ||
126 | |||
127 | /* Disable system error generation in response to error messages */ | ||
128 | pci_read_config_word(pdev, pos + PCI_EXP_RTCTL, ®16); | ||
129 | reg16 &= ~(SYSTEM_ERROR_INTR_ON_MESG_MASK); | ||
130 | pci_write_config_word(pdev, pos + PCI_EXP_RTCTL, reg16); | ||
131 | |||
132 | aer_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); | ||
133 | /* Clear error status */ | ||
134 | pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, ®32); | ||
135 | pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32); | ||
136 | pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, ®32); | ||
137 | pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32); | ||
138 | pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, ®32); | ||
139 | pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32); | ||
140 | |||
141 | /* | ||
142 | * Enable error reporting for the root port device and downstream port | ||
143 | * devices. | ||
144 | */ | ||
145 | set_downstream_devices_error_reporting(pdev, true); | ||
146 | |||
147 | /* Enable Root Port's interrupt in response to error messages */ | ||
148 | pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, ®32); | ||
149 | reg32 |= ROOT_PORT_INTR_ON_MESG_MASK; | ||
150 | pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, reg32); | ||
151 | } | ||
152 | |||
153 | /** | ||
154 | * aer_disable_rootport - disable Root Port's interrupts when receiving messages | ||
155 | * @rpc: pointer to a Root Port data structure | ||
156 | * | ||
157 | * Invoked when PCIe bus unloads AER service driver. | ||
158 | */ | ||
159 | static void aer_disable_rootport(struct aer_rpc *rpc) | ||
160 | { | ||
161 | struct pci_dev *pdev = rpc->rpd->port; | ||
162 | u32 reg32; | ||
163 | int pos; | ||
164 | |||
165 | /* | ||
166 | * Disable error reporting for the root port device and downstream port | ||
167 | * devices. | ||
168 | */ | ||
169 | set_downstream_devices_error_reporting(pdev, false); | ||
170 | |||
171 | pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); | ||
172 | /* Disable Root's interrupt in response to error messages */ | ||
173 | pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, ®32); | ||
174 | reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK; | ||
175 | pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, reg32); | ||
176 | |||
177 | /* Clear Root's error status reg */ | ||
178 | pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, ®32); | ||
179 | pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32); | ||
180 | } | ||
181 | |||
75 | /** | 182 | /** |
76 | * aer_irq - Root Port's ISR | 183 | * aer_irq - Root Port's ISR |
77 | * @irq: IRQ assigned to Root Port | 184 | * @irq: IRQ assigned to Root Port |
78 | * @context: pointer to Root Port data structure | 185 | * @context: pointer to Root Port data structure |
79 | * | 186 | * |
80 | * Invoked when Root Port detects AER messages. | 187 | * Invoked when Root Port detects AER messages. |
81 | **/ | 188 | */ |
82 | irqreturn_t aer_irq(int irq, void *context) | 189 | irqreturn_t aer_irq(int irq, void *context) |
83 | { | 190 | { |
84 | unsigned int status, id; | 191 | unsigned int status, id; |
@@ -97,13 +204,13 @@ irqreturn_t aer_irq(int irq, void *context) | |||
97 | 204 | ||
98 | /* Read error status */ | 205 | /* Read error status */ |
99 | pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status); | 206 | pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status); |
100 | if (!(status & ROOT_ERR_STATUS_MASKS)) { | 207 | if (!(status & (PCI_ERR_ROOT_UNCOR_RCV|PCI_ERR_ROOT_COR_RCV))) { |
101 | spin_unlock_irqrestore(&rpc->e_lock, flags); | 208 | spin_unlock_irqrestore(&rpc->e_lock, flags); |
102 | return IRQ_NONE; | 209 | return IRQ_NONE; |
103 | } | 210 | } |
104 | 211 | ||
105 | /* Read error source and clear error status */ | 212 | /* Read error source and clear error status */ |
106 | pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_COR_SRC, &id); | 213 | pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_ERR_SRC, &id); |
107 | pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status); | 214 | pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status); |
108 | 215 | ||
109 | /* Store error source for later DPC handler */ | 216 | /* Store error source for later DPC handler */ |
@@ -135,7 +242,7 @@ EXPORT_SYMBOL_GPL(aer_irq); | |||
135 | * @dev: pointer to the pcie_dev data structure | 242 | * @dev: pointer to the pcie_dev data structure |
136 | * | 243 | * |
137 | * Invoked when Root Port's AER service is loaded. | 244 | * Invoked when Root Port's AER service is loaded. |
138 | **/ | 245 | */ |
139 | static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev) | 246 | static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev) |
140 | { | 247 | { |
141 | struct aer_rpc *rpc; | 248 | struct aer_rpc *rpc; |
@@ -144,15 +251,11 @@ static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev) | |||
144 | if (!rpc) | 251 | if (!rpc) |
145 | return NULL; | 252 | return NULL; |
146 | 253 | ||
147 | /* | 254 | /* Initialize Root lock access, e_lock, to Root Error Status Reg */ |
148 | * Initialize Root lock access, e_lock, to Root Error Status Reg, | ||
149 | * Root Error ID Reg, and Root error producer/consumer index. | ||
150 | */ | ||
151 | spin_lock_init(&rpc->e_lock); | 255 | spin_lock_init(&rpc->e_lock); |
152 | 256 | ||
153 | rpc->rpd = dev; | 257 | rpc->rpd = dev; |
154 | INIT_WORK(&rpc->dpc_handler, aer_isr); | 258 | INIT_WORK(&rpc->dpc_handler, aer_isr); |
155 | rpc->prod_idx = rpc->cons_idx = 0; | ||
156 | mutex_init(&rpc->rpc_mutex); | 259 | mutex_init(&rpc->rpc_mutex); |
157 | init_waitqueue_head(&rpc->wait_release); | 260 | init_waitqueue_head(&rpc->wait_release); |
158 | 261 | ||
@@ -167,7 +270,7 @@ static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev) | |||
167 | * @dev: pointer to the pcie_dev data structure | 270 | * @dev: pointer to the pcie_dev data structure |
168 | * | 271 | * |
169 | * Invoked when PCI Express bus unloads or AER probe fails. | 272 | * Invoked when PCI Express bus unloads or AER probe fails. |
170 | **/ | 273 | */ |
171 | static void aer_remove(struct pcie_device *dev) | 274 | static void aer_remove(struct pcie_device *dev) |
172 | { | 275 | { |
173 | struct aer_rpc *rpc = get_service_data(dev); | 276 | struct aer_rpc *rpc = get_service_data(dev); |
@@ -179,7 +282,8 @@ static void aer_remove(struct pcie_device *dev) | |||
179 | 282 | ||
180 | wait_event(rpc->wait_release, rpc->prod_idx == rpc->cons_idx); | 283 | wait_event(rpc->wait_release, rpc->prod_idx == rpc->cons_idx); |
181 | 284 | ||
182 | aer_delete_rootport(rpc); | 285 | aer_disable_rootport(rpc); |
286 | kfree(rpc); | ||
183 | set_service_data(dev, NULL); | 287 | set_service_data(dev, NULL); |
184 | } | 288 | } |
185 | } | 289 | } |
@@ -190,7 +294,7 @@ static void aer_remove(struct pcie_device *dev) | |||
190 | * @id: pointer to the service id data structure | 294 | * @id: pointer to the service id data structure |
191 | * | 295 | * |
192 | * Invoked when PCI Express bus loads AER service driver. | 296 | * Invoked when PCI Express bus loads AER service driver. |
193 | **/ | 297 | */ |
194 | static int __devinit aer_probe(struct pcie_device *dev) | 298 | static int __devinit aer_probe(struct pcie_device *dev) |
195 | { | 299 | { |
196 | int status; | 300 | int status; |
@@ -230,47 +334,30 @@ static int __devinit aer_probe(struct pcie_device *dev) | |||
230 | * @dev: pointer to Root Port's pci_dev data structure | 334 | * @dev: pointer to Root Port's pci_dev data structure |
231 | * | 335 | * |
232 | * Invoked by Port Bus driver when performing link reset at Root Port. | 336 | * Invoked by Port Bus driver when performing link reset at Root Port. |
233 | **/ | 337 | */ |
234 | static pci_ers_result_t aer_root_reset(struct pci_dev *dev) | 338 | static pci_ers_result_t aer_root_reset(struct pci_dev *dev) |
235 | { | 339 | { |
236 | u16 p2p_ctrl; | 340 | u32 reg32; |
237 | u32 status; | ||
238 | int pos; | 341 | int pos; |
239 | 342 | ||
240 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); | 343 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); |
241 | 344 | ||
242 | /* Disable Root's interrupt in response to error messages */ | 345 | /* Disable Root's interrupt in response to error messages */ |
243 | pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, 0); | 346 | pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, ®32); |
244 | 347 | reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK; | |
245 | /* Assert Secondary Bus Reset */ | 348 | pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32); |
246 | pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl); | ||
247 | p2p_ctrl |= PCI_BRIDGE_CTL_BUS_RESET; | ||
248 | pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl); | ||
249 | |||
250 | /* | ||
251 | * we should send hot reset message for 2ms to allow it time to | ||
252 | * propogate to all downstream ports | ||
253 | */ | ||
254 | msleep(2); | ||
255 | |||
256 | /* De-assert Secondary Bus Reset */ | ||
257 | p2p_ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET; | ||
258 | pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl); | ||
259 | 349 | ||
260 | /* | 350 | aer_do_secondary_bus_reset(dev); |
261 | * System software must wait for at least 100ms from the end | ||
262 | * of a reset of one or more device before it is permitted | ||
263 | * to issue Configuration Requests to those devices. | ||
264 | */ | ||
265 | msleep(200); | ||
266 | dev_printk(KERN_DEBUG, &dev->dev, "Root Port link has been reset\n"); | 351 | dev_printk(KERN_DEBUG, &dev->dev, "Root Port link has been reset\n"); |
267 | 352 | ||
353 | /* Clear Root Error Status */ | ||
354 | pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, ®32); | ||
355 | pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, reg32); | ||
356 | |||
268 | /* Enable Root Port's interrupt in response to error messages */ | 357 | /* Enable Root Port's interrupt in response to error messages */ |
269 | pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &status); | 358 | pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, ®32); |
270 | pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, status); | 359 | reg32 |= ROOT_PORT_INTR_ON_MESG_MASK; |
271 | pci_write_config_dword(dev, | 360 | pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32); |
272 | pos + PCI_ERR_ROOT_COMMAND, | ||
273 | ROOT_PORT_INTR_ON_MESG_MASK); | ||
274 | 361 | ||
275 | return PCI_ERS_RESULT_RECOVERED; | 362 | return PCI_ERS_RESULT_RECOVERED; |
276 | } | 363 | } |
@@ -281,7 +368,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev) | |||
281 | * @error: error severity being notified by port bus | 368 | * @error: error severity being notified by port bus |
282 | * | 369 | * |
283 | * Invoked by Port Bus driver during error recovery. | 370 | * Invoked by Port Bus driver during error recovery. |
284 | **/ | 371 | */ |
285 | static pci_ers_result_t aer_error_detected(struct pci_dev *dev, | 372 | static pci_ers_result_t aer_error_detected(struct pci_dev *dev, |
286 | enum pci_channel_state error) | 373 | enum pci_channel_state error) |
287 | { | 374 | { |
@@ -294,7 +381,7 @@ static pci_ers_result_t aer_error_detected(struct pci_dev *dev, | |||
294 | * @dev: pointer to Root Port's pci_dev data structure | 381 | * @dev: pointer to Root Port's pci_dev data structure |
295 | * | 382 | * |
296 | * Invoked by Port Bus driver during nonfatal recovery. | 383 | * Invoked by Port Bus driver during nonfatal recovery. |
297 | **/ | 384 | */ |
298 | static void aer_error_resume(struct pci_dev *dev) | 385 | static void aer_error_resume(struct pci_dev *dev) |
299 | { | 386 | { |
300 | int pos; | 387 | int pos; |
@@ -321,7 +408,7 @@ static void aer_error_resume(struct pci_dev *dev) | |||
321 | * aer_service_init - register AER root service driver | 408 | * aer_service_init - register AER root service driver |
322 | * | 409 | * |
323 | * Invoked when AER root service driver is loaded. | 410 | * Invoked when AER root service driver is loaded. |
324 | **/ | 411 | */ |
325 | static int __init aer_service_init(void) | 412 | static int __init aer_service_init(void) |
326 | { | 413 | { |
327 | if (pcie_aer_disable) | 414 | if (pcie_aer_disable) |
@@ -335,7 +422,7 @@ static int __init aer_service_init(void) | |||
335 | * aer_service_exit - unregister AER root service driver | 422 | * aer_service_exit - unregister AER root service driver |
336 | * | 423 | * |
337 | * Invoked when AER root service driver is unloaded. | 424 | * Invoked when AER root service driver is unloaded. |
338 | **/ | 425 | */ |
339 | static void __exit aer_service_exit(void) | 426 | static void __exit aer_service_exit(void) |
340 | { | 427 | { |
341 | pcie_port_service_unregister(&aerdriver); | 428 | pcie_port_service_unregister(&aerdriver); |