blob: 9066d6e976d472f08d809f174a92ee0f3fafb41a [file] [log] [blame]
Bjorn Helgaase1e86ee2018-01-26 14:12:23 -06001// SPDX-License-Identifier: GPL-2.0
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +08002/*
Bjorn Helgaasdf62ab52018-03-09 16:36:33 -06003 * Implement the AER root port service driver. The driver registers an IRQ
4 * handler. When a root port triggers an AER interrupt, the IRQ handler
5 * collects root port status and schedules work.
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +08006 *
7 * Copyright (C) 2006 Intel Corp.
8 * Tom Long Nguyen (tom.l.nguyen@intel.com)
9 * Zhang Yanmin (yanmin.zhang@intel.com)
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +080010 */
11
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +080012#include <linux/pci.h>
Rafael J. Wysocki415e12b2011-01-07 00:55:09 +010013#include <linux/pci-acpi.h>
Alexey Dobriyand43c36d2009-10-07 17:09:06 +040014#include <linux/sched.h>
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +080015#include <linux/kernel.h>
16#include <linux/errno.h>
17#include <linux/pm.h>
18#include <linux/init.h>
19#include <linux/interrupt.h>
20#include <linux/delay.h>
21#include <linux/pcieport_if.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090022#include <linux/slab.h>
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +080023
24#include "aerdrv.h"
Alex Chiang5d9526d2008-06-04 11:39:07 -060025#include "../../pci.h"
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +080026
Bill Pemberton15856ad2012-11-21 15:35:00 -050027static int aer_probe(struct pcie_device *dev);
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +080028static void aer_remove(struct pcie_device *dev);
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +080029static void aer_error_resume(struct pci_dev *dev);
30static pci_ers_result_t aer_root_reset(struct pci_dev *dev);
31
Sam Ravnborgc1996c22007-02-27 10:22:00 +010032static struct pcie_port_service_driver aerdriver = {
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +080033 .name = "aer",
Kenji Kaneshige694f88e2009-11-25 21:06:15 +090034 .port_type = PCI_EXP_TYPE_ROOT_PORT,
Rafael J. Wysocki22106362009-01-13 14:46:46 +010035 .service = PCIE_PORT_SERVICE_AER,
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +080036
37 .probe = aer_probe,
38 .remove = aer_remove,
Christoph Hellwigc5dc3c62017-06-19 20:04:58 +020039 .error_resume = aer_error_resume,
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +080040 .reset_link = aer_root_reset,
41};
42
Randy Dunlap7f785762007-10-05 13:17:58 -070043static int pcie_aer_disable;
44
45void pci_no_aer(void)
46{
Bjorn Helgaas7ece1412016-09-06 16:24:37 -050047 pcie_aer_disable = 1;
Randy Dunlap7f785762007-10-05 13:17:58 -070048}
49
Rafael J. Wysockif1a7bfa2010-08-21 01:50:52 +020050bool pci_aer_available(void)
51{
52 return !pcie_aer_disable && pci_msi_enabled();
53}
54
Hidetoshi Seto843f4692010-04-15 13:10:53 +090055static int set_device_error_reporting(struct pci_dev *dev, void *data)
56{
57 bool enable = *((bool *)data);
Yijing Wang62f87c02012-07-24 17:20:03 +080058 int type = pci_pcie_type(dev);
Hidetoshi Seto843f4692010-04-15 13:10:53 +090059
Yijing Wang62f87c02012-07-24 17:20:03 +080060 if ((type == PCI_EXP_TYPE_ROOT_PORT) ||
61 (type == PCI_EXP_TYPE_UPSTREAM) ||
62 (type == PCI_EXP_TYPE_DOWNSTREAM)) {
Hidetoshi Seto843f4692010-04-15 13:10:53 +090063 if (enable)
64 pci_enable_pcie_error_reporting(dev);
65 else
66 pci_disable_pcie_error_reporting(dev);
67 }
68
69 if (enable)
70 pcie_set_ecrc_checking(dev);
71
72 return 0;
73}
74
75/**
76 * set_downstream_devices_error_reporting - enable/disable the error reporting bits on the root port and its downstream ports.
77 * @dev: pointer to root port's pci_dev data structure
78 * @enable: true = enable error reporting, false = disable error reporting.
79 */
80static void set_downstream_devices_error_reporting(struct pci_dev *dev,
81 bool enable)
82{
83 set_device_error_reporting(dev, &enable);
84
85 if (!dev->subordinate)
86 return;
87 pci_walk_bus(dev->subordinate, set_device_error_reporting, &enable);
88}
89
90/**
91 * aer_enable_rootport - enable Root Port's interrupts when receiving messages
92 * @rpc: pointer to a Root Port data structure
93 *
94 * Invoked when PCIe bus loads AER service driver.
95 */
96static void aer_enable_rootport(struct aer_rpc *rpc)
97{
98 struct pci_dev *pdev = rpc->rpd->port;
Jiang Liu43bd4ee2012-07-24 17:20:11 +080099 int aer_pos;
Hidetoshi Seto843f4692010-04-15 13:10:53 +0900100 u16 reg16;
101 u32 reg32;
102
Hidetoshi Seto843f4692010-04-15 13:10:53 +0900103 /* Clear PCIe Capability's Device Status */
Jiang Liu43bd4ee2012-07-24 17:20:11 +0800104 pcie_capability_read_word(pdev, PCI_EXP_DEVSTA, &reg16);
105 pcie_capability_write_word(pdev, PCI_EXP_DEVSTA, reg16);
Hidetoshi Seto843f4692010-04-15 13:10:53 +0900106
107 /* Disable system error generation in response to error messages */
Jiang Liu43bd4ee2012-07-24 17:20:11 +0800108 pcie_capability_clear_word(pdev, PCI_EXP_RTCTL,
109 SYSTEM_ERROR_INTR_ON_MESG_MASK);
Hidetoshi Seto843f4692010-04-15 13:10:53 +0900110
Keith Busch66b80802016-09-27 16:23:34 -0400111 aer_pos = pdev->aer_cap;
Hidetoshi Seto843f4692010-04-15 13:10:53 +0900112 /* Clear error status */
113 pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, &reg32);
114 pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32);
115 pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, &reg32);
116 pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32);
117 pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, &reg32);
118 pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32);
119
120 /*
121 * Enable error reporting for the root port device and downstream port
122 * devices.
123 */
124 set_downstream_devices_error_reporting(pdev, true);
125
126 /* Enable Root Port's interrupt in response to error messages */
127 pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, &reg32);
128 reg32 |= ROOT_PORT_INTR_ON_MESG_MASK;
129 pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, reg32);
130}
131
132/**
133 * aer_disable_rootport - disable Root Port's interrupts when receiving messages
134 * @rpc: pointer to a Root Port data structure
135 *
136 * Invoked when PCIe bus unloads AER service driver.
137 */
138static void aer_disable_rootport(struct aer_rpc *rpc)
139{
140 struct pci_dev *pdev = rpc->rpd->port;
141 u32 reg32;
142 int pos;
143
144 /*
145 * Disable error reporting for the root port device and downstream port
146 * devices.
147 */
148 set_downstream_devices_error_reporting(pdev, false);
149
Keith Busch66b80802016-09-27 16:23:34 -0400150 pos = pdev->aer_cap;
Hidetoshi Seto843f4692010-04-15 13:10:53 +0900151 /* Disable Root's interrupt in response to error messages */
152 pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, &reg32);
153 reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
154 pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, reg32);
155
156 /* Clear Root's error status reg */
157 pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, &reg32);
158 pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32);
159}
160
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800161/**
162 * aer_irq - Root Port's ISR
163 * @irq: IRQ assigned to Root Port
164 * @context: pointer to Root Port data structure
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800165 *
166 * Invoked when Root Port detects AER messages.
Hidetoshi Setof6d37802010-04-15 13:22:11 +0900167 */
Huang Ying634deb02009-04-24 10:45:23 +0800168irqreturn_t aer_irq(int irq, void *context)
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800169{
170 unsigned int status, id;
171 struct pcie_device *pdev = (struct pcie_device *)context;
172 struct aer_rpc *rpc = get_service_data(pdev);
173 int next_prod_idx;
174 unsigned long flags;
175 int pos;
176
Keith Busch66b80802016-09-27 16:23:34 -0400177 pos = pdev->port->aer_cap;
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800178 /*
179 * Must lock access to Root Error Status Reg, Root Error ID Reg,
180 * and Root error producer/consumer index
181 */
182 spin_lock_irqsave(&rpc->e_lock, flags);
183
184 /* Read error status */
185 pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status);
Hidetoshi Setoe167bfc2010-04-15 13:18:26 +0900186 if (!(status & (PCI_ERR_ROOT_UNCOR_RCV|PCI_ERR_ROOT_COR_RCV))) {
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800187 spin_unlock_irqrestore(&rpc->e_lock, flags);
188 return IRQ_NONE;
189 }
190
191 /* Read error source and clear error status */
Hidetoshi Setof647a442010-04-15 13:17:33 +0900192 pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_ERR_SRC, &id);
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800193 pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status);
194
195 /* Store error source for later DPC handler */
196 next_prod_idx = rpc->prod_idx + 1;
197 if (next_prod_idx == AER_ERROR_SOURCES_MAX)
198 next_prod_idx = 0;
199 if (next_prod_idx == rpc->cons_idx) {
200 /*
201 * Error Storm Condition - possibly the same error occurred.
202 * Drop the error.
203 */
204 spin_unlock_irqrestore(&rpc->e_lock, flags);
205 return IRQ_HANDLED;
206 }
207 rpc->e_sources[rpc->prod_idx].status = status;
208 rpc->e_sources[rpc->prod_idx].id = id;
209 rpc->prod_idx = next_prod_idx;
210 spin_unlock_irqrestore(&rpc->e_lock, flags);
211
212 /* Invoke DPC handler */
213 schedule_work(&rpc->dpc_handler);
214
215 return IRQ_HANDLED;
216}
Huang Ying634deb02009-04-24 10:45:23 +0800217EXPORT_SYMBOL_GPL(aer_irq);
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800218
219/**
220 * aer_alloc_rpc - allocate Root Port data structure
221 * @dev: pointer to the pcie_dev data structure
222 *
223 * Invoked when Root Port's AER service is loaded.
Hidetoshi Setof6d37802010-04-15 13:22:11 +0900224 */
Hidetoshi Setoc9a91882009-09-07 17:07:29 +0900225static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev)
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800226{
227 struct aer_rpc *rpc;
228
Hidetoshi Setoc9a91882009-09-07 17:07:29 +0900229 rpc = kzalloc(sizeof(struct aer_rpc), GFP_KERNEL);
230 if (!rpc)
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800231 return NULL;
232
Hidetoshi Setof6d37802010-04-15 13:22:11 +0900233 /* Initialize Root lock access, e_lock, to Root Error Status Reg */
Milind Arun Choudharyf5609d72007-07-09 11:55:54 -0700234 spin_lock_init(&rpc->e_lock);
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800235
236 rpc->rpd = dev;
David Howells65f27f32006-11-22 14:55:48 +0000237 INIT_WORK(&rpc->dpc_handler, aer_isr);
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800238 mutex_init(&rpc->rpc_mutex);
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800239
Stefan Assmann45e829e2009-12-03 06:49:24 -0500240 /* Use PCIe bus function to store rpc into PCIe device */
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800241 set_service_data(dev, rpc);
242
243 return rpc;
244}
245
246/**
247 * aer_remove - clean up resources
248 * @dev: pointer to the pcie_dev data structure
249 *
250 * Invoked when PCI Express bus unloads or AER probe fails.
Hidetoshi Setof6d37802010-04-15 13:22:11 +0900251 */
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800252static void aer_remove(struct pcie_device *dev)
253{
254 struct aer_rpc *rpc = get_service_data(dev);
255
256 if (rpc) {
257 /* If register interrupt service, it must be free. */
258 if (rpc->isr)
259 free_irq(dev->irq, dev);
260
Sebastian Andrzej Siewior4ae21822016-01-25 10:08:00 -0600261 flush_work(&rpc->dpc_handler);
Hidetoshi Seto460d2982010-04-15 13:10:03 +0900262 aer_disable_rootport(rpc);
263 kfree(rpc);
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800264 set_service_data(dev, NULL);
265 }
266}
267
268/**
269 * aer_probe - initialize resources
270 * @dev: pointer to the pcie_dev data structure
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800271 *
272 * Invoked when PCI Express bus loads AER service driver.
Hidetoshi Setof6d37802010-04-15 13:22:11 +0900273 */
Bill Pemberton15856ad2012-11-21 15:35:00 -0500274static int aer_probe(struct pcie_device *dev)
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800275{
276 int status;
277 struct aer_rpc *rpc;
Bjorn Helgaas576700b2016-11-21 15:24:25 -0600278 struct device *device = &dev->port->dev;
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800279
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800280 /* Alloc rpc data structure */
Hidetoshi Setoc9a91882009-09-07 17:07:29 +0900281 rpc = aer_alloc_rpc(dev);
282 if (!rpc) {
Bjorn Helgaas576700b2016-11-21 15:24:25 -0600283 dev_printk(KERN_DEBUG, device, "alloc AER rpc failed\n");
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800284 aer_remove(dev);
285 return -ENOMEM;
286 }
287
288 /* Request IRQ ISR */
Hidetoshi Setoc9a91882009-09-07 17:07:29 +0900289 status = request_irq(dev->irq, aer_irq, IRQF_SHARED, "aerdrv", dev);
290 if (status) {
Bjorn Helgaas576700b2016-11-21 15:24:25 -0600291 dev_printk(KERN_DEBUG, device, "request AER IRQ %d failed\n",
292 dev->irq);
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800293 aer_remove(dev);
294 return status;
295 }
296
297 rpc->isr = 1;
298
299 aer_enable_rootport(rpc);
Bjorn Helgaas68a55ae2016-11-21 15:34:02 -0600300 dev_info(device, "AER enabled with IRQ %d\n", dev->irq);
301 return 0;
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800302}
303
304/**
305 * aer_root_reset - reset link on Root Port
306 * @dev: pointer to Root Port's pci_dev data structure
307 *
308 * Invoked by Port Bus driver when performing link reset at Root Port.
Hidetoshi Setof6d37802010-04-15 13:22:11 +0900309 */
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800310static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
311{
Hidetoshi Setoc6d34ed2010-04-15 13:09:13 +0900312 u32 reg32;
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800313 int pos;
314
Keith Busch66b80802016-09-27 16:23:34 -0400315 pos = dev->aer_cap;
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800316
317 /* Disable Root's interrupt in response to error messages */
Hidetoshi Setoc6d34ed2010-04-15 13:09:13 +0900318 pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, &reg32);
319 reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
320 pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800321
Alex Williamson1b95ce82013-08-08 14:10:20 -0600322 pci_reset_bridge_secondary_bus(dev);
Frederick Lawler7506dc72018-01-18 12:55:24 -0600323 pci_printk(KERN_DEBUG, dev, "Root Port link has been reset\n");
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800324
Hidetoshi Setoc6d34ed2010-04-15 13:09:13 +0900325 /* Clear Root Error Status */
326 pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &reg32);
327 pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, reg32);
328
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800329 /* Enable Root Port's interrupt in response to error messages */
Hidetoshi Setoc6d34ed2010-04-15 13:09:13 +0900330 pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, &reg32);
331 reg32 |= ROOT_PORT_INTR_ON_MESG_MASK;
332 pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800333
334 return PCI_ERS_RESULT_RECOVERED;
335}
336
337/**
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800338 * aer_error_resume - clean up corresponding error status bits
339 * @dev: pointer to Root Port's pci_dev data structure
340 *
341 * Invoked by Port Bus driver during nonfatal recovery.
Hidetoshi Setof6d37802010-04-15 13:22:11 +0900342 */
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800343static void aer_error_resume(struct pci_dev *dev)
344{
345 int pos;
346 u32 status, mask;
347 u16 reg16;
348
349 /* Clean up Root device status */
Jiang Liu43bd4ee2012-07-24 17:20:11 +0800350 pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &reg16);
351 pcie_capability_write_word(dev, PCI_EXP_DEVSTA, reg16);
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800352
353 /* Clean AER Root Error Status */
Keith Busch66b80802016-09-27 16:23:34 -0400354 pos = dev->aer_cap;
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800355 pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
356 pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask);
357 if (dev->error_state == pci_channel_io_normal)
358 status &= ~mask; /* Clear corresponding nonfatal bits */
359 else
360 status &= mask; /* Clear corresponding fatal bits */
361 pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
362}
363
364/**
365 * aer_service_init - register AER root service driver
366 *
367 * Invoked when AER root service driver is loaded.
Hidetoshi Setof6d37802010-04-15 13:22:11 +0900368 */
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800369static int __init aer_service_init(void)
370{
Rafael J. Wysockib22c3d82010-09-20 18:50:00 +0200371 if (!pci_aer_available() || aer_acpi_firmware_first())
Andi Kleen3e77a3f2009-09-16 22:40:22 +0200372 return -ENXIO;
Sam Ravnborgc1996c22007-02-27 10:22:00 +0100373 return pcie_port_service_register(&aerdriver);
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +0800374}
Paul Gortmaker87563362016-08-24 16:57:46 -0400375device_initcall(aer_service_init);