pcie-dpc.c 10.3 KB
Newer Older
1 2
/*
 * PCI Express Downstream Port Containment services driver
3 4
 * Author: Keith Busch <keith.busch@intel.com>
 *
5 6 7 8 9 10 11 12 13
 * Copyright (C) 2016 Intel Corp.
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 */

#include <linux/delay.h>
#include <linux/interrupt.h>
14
#include <linux/init.h>
15 16
#include <linux/pci.h>
#include <linux/pcieport_if.h>
K
Keith Busch 已提交
17
#include "../pci.h"
18
#include "aer/aerdrv.h"
19

D
Dongdong Liu 已提交
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
struct rp_pio_header_log_regs {
	u32 dw0;
	u32 dw1;
	u32 dw2;
	u32 dw3;
};

struct dpc_rp_pio_regs {
	u32 status;
	u32 mask;
	u32 severity;
	u32 syserror;
	u32 exception;

	struct rp_pio_header_log_regs header_log;
	u32 impspec_log;
	u32 tlp_prefix_log[4];
	u16 first_error;
};

40 41
struct dpc_dev {
	struct pcie_device	*dev;
42
	struct work_struct	work;
43
	u16			cap_pos;
44
	bool			rp_extensions;
D
Dongdong Liu 已提交
45
	u32			rp_pio_status;
46
	u8			rp_log_size;
D
Dongdong Liu 已提交
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
};

static const char * const rp_pio_error_string[] = {
	"Configuration Request received UR Completion",	 /* Bit Position 0  */
	"Configuration Request received CA Completion",	 /* Bit Position 1  */
	"Configuration Request Completion Timeout",	 /* Bit Position 2  */
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	"I/O Request received UR Completion",		 /* Bit Position 8  */
	"I/O Request received CA Completion",		 /* Bit Position 9  */
	"I/O Request Completion Timeout",		 /* Bit Position 10 */
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	"Memory Request received UR Completion",	 /* Bit Position 16 */
	"Memory Request received CA Completion",	 /* Bit Position 17 */
	"Memory Request Completion Timeout",		 /* Bit Position 18 */
69 70
};

71 72 73 74
static int dpc_wait_rp_inactive(struct dpc_dev *dpc)
{
	unsigned long timeout = jiffies + HZ;
	struct pci_dev *pdev = dpc->dev->port;
75
	struct device *dev = &dpc->dev->device;
76
	u16 cap = dpc->cap_pos, status;
77

78
	pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status);
79 80 81
	while (status & PCI_EXP_DPC_RP_BUSY &&
					!time_after(jiffies, timeout)) {
		msleep(10);
82
		pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status);
83 84
	}
	if (status & PCI_EXP_DPC_RP_BUSY) {
85
		dev_warn(dev, "DPC root port still busy\n");
86 87 88 89 90
		return -EBUSY;
	}
	return 0;
}

91
static void dpc_wait_link_inactive(struct dpc_dev *dpc)
92 93
{
	unsigned long timeout = jiffies + HZ;
94 95
	struct pci_dev *pdev = dpc->dev->port;
	struct device *dev = &dpc->dev->device;
96 97 98 99 100 101 102 103 104
	u16 lnk_status;

	pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
	while (lnk_status & PCI_EXP_LNKSTA_DLLLA &&
					!time_after(jiffies, timeout)) {
		msleep(10);
		pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
	}
	if (lnk_status & PCI_EXP_LNKSTA_DLLLA)
105
		dev_warn(dev, "Link state not disabled for DPC event\n");
106 107
}

108
static void dpc_work(struct work_struct *work)
109 110 111 112
{
	struct dpc_dev *dpc = container_of(work, struct dpc_dev, work);
	struct pci_dev *dev, *temp, *pdev = dpc->dev->port;
	struct pci_bus *parent = pdev->subordinate;
113
	u16 cap = dpc->cap_pos, ctl;
114 115 116 117 118

	pci_lock_rescan_remove();
	list_for_each_entry_safe_reverse(dev, temp, &parent->devices,
					 bus_list) {
		pci_dev_get(dev);
K
Keith Busch 已提交
119 120 121 122
		pci_dev_set_disconnected(dev, NULL);
		if (pci_has_subordinate(dev))
			pci_walk_bus(dev->subordinate,
				     pci_dev_set_disconnected, NULL);
123 124 125 126 127
		pci_stop_and_remove_bus_device(dev);
		pci_dev_put(dev);
	}
	pci_unlock_rescan_remove();

128
	dpc_wait_link_inactive(dpc);
129
	if (dpc->rp_extensions && dpc_wait_rp_inactive(dpc))
130
		return;
131
	if (dpc->rp_extensions && dpc->rp_pio_status) {
132 133
		pci_write_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS,
				       dpc->rp_pio_status);
D
Dongdong Liu 已提交
134 135 136
		dpc->rp_pio_status = 0;
	}

137
	pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
138
		PCI_EXP_DPC_STATUS_TRIGGER | PCI_EXP_DPC_STATUS_INTERRUPT);
139

140 141
	pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl);
	pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL,
142
			      ctl | PCI_EXP_DPC_CTL_INT_EN);
143 144
}

D
Dongdong Liu 已提交
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
static void dpc_rp_pio_print_tlp_header(struct device *dev,
					struct rp_pio_header_log_regs *t)
{
	dev_err(dev, "TLP Header: %#010x %#010x %#010x %#010x\n",
		t->dw0, t->dw1, t->dw2, t->dw3);
}

static void dpc_rp_pio_print_error(struct dpc_dev *dpc,
				   struct dpc_rp_pio_regs *rp_pio)
{
	struct device *dev = &dpc->dev->device;
	int i;
	u32 status;

	dev_err(dev, "rp_pio_status: %#010x, rp_pio_mask: %#010x\n",
		rp_pio->status, rp_pio->mask);

	dev_err(dev, "RP PIO severity=%#010x, syserror=%#010x, exception=%#010x\n",
		rp_pio->severity, rp_pio->syserror, rp_pio->exception);

	status = (rp_pio->status & ~rp_pio->mask);

	for (i = 0; i < ARRAY_SIZE(rp_pio_error_string); i++) {
		if (!(status & (1 << i)))
			continue;

		dev_err(dev, "[%2d] %s%s\n", i, rp_pio_error_string[i],
			rp_pio->first_error == i ? " (First)" : "");
	}

	dpc_rp_pio_print_tlp_header(dev, &rp_pio->header_log);
176
	if (dpc->rp_log_size == 4)
D
Dongdong Liu 已提交
177
		return;
178

D
Dongdong Liu 已提交
179 180
	dev_err(dev, "RP PIO ImpSpec Log %#010x\n", rp_pio->impspec_log);

181
	for (i = 0; i < dpc->rp_log_size - 5; i++)
D
Dongdong Liu 已提交
182 183 184 185 186 187 188 189 190
		dev_err(dev, "TLP Prefix Header: dw%d, %#010x\n", i,
			rp_pio->tlp_prefix_log[i]);
}

static void dpc_rp_pio_get_info(struct dpc_dev *dpc,
				struct dpc_rp_pio_regs *rp_pio)
{
	struct pci_dev *pdev = dpc->dev->port;
	int i;
191
	u16 cap = dpc->cap_pos, status;
D
Dongdong Liu 已提交
192

193
	pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS,
D
Dongdong Liu 已提交
194
			      &rp_pio->status);
195
	pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_MASK,
D
Dongdong Liu 已提交
196 197
			      &rp_pio->mask);

198
	pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SEVERITY,
D
Dongdong Liu 已提交
199
			      &rp_pio->severity);
200
	pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SYSERROR,
D
Dongdong Liu 已提交
201
			      &rp_pio->syserror);
202
	pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_EXCEPTION,
D
Dongdong Liu 已提交
203 204 205
			      &rp_pio->exception);

	/* Get First Error Pointer */
206
	pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status);
D
Dongdong Liu 已提交
207 208
	rp_pio->first_error = (status & 0x1f00) >> 8;

209
	if (dpc->rp_log_size < 4)
D
Dongdong Liu 已提交
210 211
		return;

212
	pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG,
D
Dongdong Liu 已提交
213
			      &rp_pio->header_log.dw0);
214
	pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 4,
D
Dongdong Liu 已提交
215
			      &rp_pio->header_log.dw1);
216
	pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 8,
D
Dongdong Liu 已提交
217
			      &rp_pio->header_log.dw2);
218
	pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 12,
D
Dongdong Liu 已提交
219
			      &rp_pio->header_log.dw3);
220
	if (dpc->rp_log_size == 4)
D
Dongdong Liu 已提交
221 222
		return;

223
	pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_IMPSPEC_LOG,
D
Dongdong Liu 已提交
224
			      &rp_pio->impspec_log);
225
	for (i = 0; i < dpc->rp_log_size - 5; i++)
D
Dongdong Liu 已提交
226
		pci_read_config_dword(pdev,
227
			cap + PCI_EXP_DPC_RP_PIO_TLPPREFIX_LOG,
D
Dongdong Liu 已提交
228 229 230 231 232 233 234 235 236 237 238 239 240
			&rp_pio->tlp_prefix_log[i]);
}

static void dpc_process_rp_pio_error(struct dpc_dev *dpc)
{
	struct dpc_rp_pio_regs rp_pio_regs;

	dpc_rp_pio_get_info(dpc, &rp_pio_regs);
	dpc_rp_pio_print_error(dpc, &rp_pio_regs);

	dpc->rp_pio_status = rp_pio_regs.status;
}

241 242 243 244
static irqreturn_t dpc_irq(int irq, void *context)
{
	struct dpc_dev *dpc = (struct dpc_dev *)context;
	struct pci_dev *pdev = dpc->dev->port;
245
	struct device *dev = &dpc->dev->device;
246
	u16 cap = dpc->cap_pos, ctl, status, source, reason, ext_reason;
247

248
	pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl);
249 250 251

	if (!(ctl & PCI_EXP_DPC_CTL_INT_EN) || ctl == (u16)(~0))
		return IRQ_NONE;
252

253
	pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status);
254 255 256 257 258

	if (!(status & PCI_EXP_DPC_STATUS_INTERRUPT))
		return IRQ_NONE;

	if (!(status & PCI_EXP_DPC_STATUS_TRIGGER)) {
259
		pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
260 261 262 263
				      PCI_EXP_DPC_STATUS_INTERRUPT);
		return IRQ_HANDLED;
	}

264
	pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL,
265 266
			      ctl & ~PCI_EXP_DPC_CTL_INT_EN);

267
	pci_read_config_word(pdev, cap + PCI_EXP_DPC_SOURCE_ID,
268 269
			     &source);

270
	dev_info(dev, "DPC containment event, status:%#06x source:%#06x\n",
271 272
		status, source);

273 274 275 276 277 278 279 280 281 282 283
	reason = (status >> 1) & 0x3;
	ext_reason = (status >> 5) & 0x3;

	dev_warn(dev, "DPC %s detected, remove downstream devices\n",
		 (reason == 0) ? "unmasked uncorrectable error" :
		 (reason == 1) ? "ERR_NONFATAL" :
		 (reason == 2) ? "ERR_FATAL" :
		 (ext_reason == 0) ? "RP PIO error" :
		 (ext_reason == 1) ? "software trigger" :
				     "reserved error");
	/* show RP PIO error detail information */
284
	if (dpc->rp_extensions && reason == 3 && ext_reason == 0)
285 286 287 288
		dpc_process_rp_pio_error(dpc);

	schedule_work(&dpc->work);

289 290 291 292 293 294 295 296
	return IRQ_HANDLED;
}

#define FLAG(x, y) (((x) & (y)) ? '+' : '-')
static int dpc_probe(struct pcie_device *dev)
{
	struct dpc_dev *dpc;
	struct pci_dev *pdev = dev->port;
297
	struct device *device = &dev->device;
298 299 300
	int status;
	u16 ctl, cap;

301 302 303
	if (pcie_aer_get_firmware_first(pdev))
		return -ENOTSUPP;

304
	dpc = devm_kzalloc(device, sizeof(*dpc), GFP_KERNEL);
305 306 307 308 309
	if (!dpc)
		return -ENOMEM;

	dpc->cap_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DPC);
	dpc->dev = dev;
310
	INIT_WORK(&dpc->work, dpc_work);
311 312
	set_service_data(dev, dpc);

313
	status = devm_request_irq(device, dev->irq, dpc_irq, IRQF_SHARED,
314
				  "pcie-dpc", dpc);
315
	if (status) {
316
		dev_warn(device, "request IRQ%d failed: %d\n", dev->irq,
317
			 status);
318
		return status;
319 320 321 322 323
	}

	pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CAP, &cap);
	pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);

324
	dpc->rp_extensions = (cap & PCI_EXP_DPC_CAP_RP_EXT);
325 326 327 328 329 330 331 332
	if (dpc->rp_extensions) {
		dpc->rp_log_size = (cap & PCI_EXP_DPC_RP_PIO_LOG_SIZE) >> 8;
		if (dpc->rp_log_size < 4 || dpc->rp_log_size > 9) {
			dev_err(device, "RP PIO log size %u is invalid\n",
				dpc->rp_log_size);
			dpc->rp_log_size = 0;
		}
	}
333

334
	ctl = (ctl & 0xfff4) | PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN;
335 336
	pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);

337
	dev_info(device, "DPC error containment capabilities: Int Msg #%d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c\n",
338
		cap & PCI_EXP_DPC_IRQ, FLAG(cap, PCI_EXP_DPC_CAP_RP_EXT),
339
		FLAG(cap, PCI_EXP_DPC_CAP_POISONED_TLP),
340
		FLAG(cap, PCI_EXP_DPC_CAP_SW_TRIGGER), dpc->rp_log_size,
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
		FLAG(cap, PCI_EXP_DPC_CAP_DL_ACTIVE));
	return status;
}

static void dpc_remove(struct pcie_device *dev)
{
	struct dpc_dev *dpc = get_service_data(dev);
	struct pci_dev *pdev = dev->port;
	u16 ctl;

	pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);
	ctl &= ~(PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN);
	pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);
}

static struct pcie_port_service_driver dpcdriver = {
	.name		= "dpc",
358
	.port_type	= PCIE_ANY_PORT,
359 360 361 362 363 364 365 366 367
	.service	= PCIE_PORT_SERVICE_DPC,
	.probe		= dpc_probe,
	.remove		= dpc_remove,
};

static int __init dpc_service_init(void)
{
	return pcie_port_service_register(&dpcdriver);
}
368
device_initcall(dpc_service_init);