1/*
2 * Copyright (c) 2005 Cisco Systems.  All rights reserved.
3 *
4 * This software is available to you under a choice of one of two
5 * licenses.  You may choose to be licensed under the terms of the GNU
6 * General Public License (GPL) Version 2, available from the file
7 * COPYING in the main directory of this source tree, or the
8 * OpenIB.org BSD license below:
9 *
10 *     Redistribution and use in source and binary forms, with or
11 *     without modification, are permitted provided that the following
12 *     conditions are met:
13 *
14 *      - Redistributions of source code must retain the above
15 *        copyright notice, this list of conditions and the following
16 *        disclaimer.
17 *
18 *      - Redistributions in binary form must reproduce the above
19 *        copyright notice, this list of conditions and the following
20 *        disclaimer in the documentation and/or other materials
21 *        provided with the distribution.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 * SOFTWARE.
31 */
32
33#define	LINUXKPI_PARAM_PREFIX mthca_
34
35#include <linux/jiffies.h>
36#include <linux/module.h>
37#include <linux/timer.h>
38#include <linux/workqueue.h>
39
40#include "mthca_dev.h"
41
42#define	MTHCA_CATAS_POLL_INTERVAL	(5 * HZ)
43
44enum {
45	MTHCA_CATAS_TYPE_INTERNAL	= 0,
46	MTHCA_CATAS_TYPE_UPLINK		= 3,
47	MTHCA_CATAS_TYPE_DDR		= 4,
48	MTHCA_CATAS_TYPE_PARITY		= 5,
49};
50
51static DEFINE_SPINLOCK(catas_lock);
52
53static LIST_HEAD(catas_list);
54static struct workqueue_struct *catas_wq;
55static struct work_struct catas_work;
56
57static int catas_reset_disable;
58module_param_named(catas_reset_disable, catas_reset_disable, int, 0644);
59MODULE_PARM_DESC(catas_reset_disable, "disable reset on catastrophic event if nonzero");
60
61static void catas_reset(struct work_struct *work)
62{
63	struct mthca_dev *dev, *tmpdev;
64	LIST_HEAD(tlist);
65	int ret;
66
67	mutex_lock(&mthca_device_mutex);
68
69	spin_lock_irq(&catas_lock);
70	list_splice_init(&catas_list, &tlist);
71	spin_unlock_irq(&catas_lock);
72
73	list_for_each_entry_safe(dev, tmpdev, &tlist, catas_err.list) {
74		struct pci_dev *pdev = dev->pdev;
75		ret = __mthca_restart_one(dev->pdev);
76		/* 'dev' now is not valid */
77		if (ret)
78			printk(KERN_ERR "mthca %s: Reset failed (%d)\n",
79			       pci_name(pdev), ret);
80		else {
81			struct mthca_dev *d = pci_get_drvdata(pdev);
82			mthca_dbg(d, "Reset succeeded\n");
83		}
84	}
85
86	mutex_unlock(&mthca_device_mutex);
87}
88
89static void handle_catas(struct mthca_dev *dev)
90{
91	struct ib_event event;
92	unsigned long flags;
93	const char *type;
94	int i;
95
96	event.device = &dev->ib_dev;
97	event.event  = IB_EVENT_DEVICE_FATAL;
98	event.element.port_num = 0;
99	dev->active = false;
100
101	ib_dispatch_event(&event);
102
103	switch (swab32(readl(dev->catas_err.map)) >> 24) {
104	case MTHCA_CATAS_TYPE_INTERNAL:
105		type = "internal error";
106		break;
107	case MTHCA_CATAS_TYPE_UPLINK:
108		type = "uplink bus error";
109		break;
110	case MTHCA_CATAS_TYPE_DDR:
111		type = "DDR data error";
112		break;
113	case MTHCA_CATAS_TYPE_PARITY:
114		type = "internal parity error";
115		break;
116	default:
117		type = "unknown error";
118		break;
119	}
120
121	mthca_err(dev, "Catastrophic error detected: %s\n", type);
122	for (i = 0; i < dev->catas_err.size; ++i)
123		mthca_err(dev, "  buf[%02x]: %08x\n",
124			  i, swab32(readl(dev->catas_err.map + i)));
125
126	if (catas_reset_disable)
127		return;
128
129	spin_lock_irqsave(&catas_lock, flags);
130	list_add(&dev->catas_err.list, &catas_list);
131	queue_work(catas_wq, &catas_work);
132	spin_unlock_irqrestore(&catas_lock, flags);
133}
134
135static void poll_catas(unsigned long dev_ptr)
136{
137	struct mthca_dev *dev = (struct mthca_dev *) dev_ptr;
138	int i;
139
140	for (i = 0; i < dev->catas_err.size; ++i)
141		if (readl(dev->catas_err.map + i)) {
142			handle_catas(dev);
143			return;
144		}
145
146	mod_timer(&dev->catas_err.timer,
147		  round_jiffies(jiffies + MTHCA_CATAS_POLL_INTERVAL));
148}
149
150void mthca_start_catas_poll(struct mthca_dev *dev)
151{
152	phys_addr_t addr;
153
154	init_timer(&dev->catas_err.timer);
155	dev->catas_err.map  = NULL;
156
157	addr = pci_resource_start(dev->pdev, 0) +
158		((pci_resource_len(dev->pdev, 0) - 1) &
159		 dev->catas_err.addr);
160
161	dev->catas_err.map = ioremap(addr, dev->catas_err.size * 4);
162	if (!dev->catas_err.map) {
163		mthca_warn(dev, "couldn't map catastrophic error region "
164			   "at 0x%llx/0x%x\n", (unsigned long long) addr,
165			   dev->catas_err.size * 4);
166		return;
167	}
168
169	dev->catas_err.timer.data     = (unsigned long) dev;
170	dev->catas_err.timer.function = poll_catas;
171	dev->catas_err.timer.expires  = jiffies + MTHCA_CATAS_POLL_INTERVAL;
172	INIT_LIST_HEAD(&dev->catas_err.list);
173	add_timer(&dev->catas_err.timer);
174}
175
176void mthca_stop_catas_poll(struct mthca_dev *dev)
177{
178	del_timer_sync(&dev->catas_err.timer);
179
180	if (dev->catas_err.map)
181		iounmap(dev->catas_err.map);
182
183	spin_lock_irq(&catas_lock);
184	list_del(&dev->catas_err.list);
185	spin_unlock_irq(&catas_lock);
186}
187
188int __init mthca_catas_init(void)
189{
190	INIT_WORK(&catas_work, catas_reset);
191
192	catas_wq = alloc_ordered_workqueue("mthca_catas", WQ_MEM_RECLAIM);
193	if (!catas_wq)
194		return -ENOMEM;
195
196	return 0;
197}
198
199void mthca_catas_cleanup(void)
200{
201	destroy_workqueue(catas_wq);
202}
203