1c39f472eSBen Skeggs /*
2c39f472eSBen Skeggs  * Copyright 2012 Red Hat Inc.
3c39f472eSBen Skeggs  *
4c39f472eSBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining a
5c39f472eSBen Skeggs  * copy of this software and associated documentation files (the "Software"),
6c39f472eSBen Skeggs  * to deal in the Software without restriction, including without limitation
7c39f472eSBen Skeggs  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8c39f472eSBen Skeggs  * and/or sell copies of the Software, and to permit persons to whom the
9c39f472eSBen Skeggs  * Software is furnished to do so, subject to the following conditions:
10c39f472eSBen Skeggs  *
11c39f472eSBen Skeggs  * The above copyright notice and this permission notice shall be included in
12c39f472eSBen Skeggs  * all copies or substantial portions of the Software.
13c39f472eSBen Skeggs  *
14c39f472eSBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15c39f472eSBen Skeggs  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16c39f472eSBen Skeggs  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17c39f472eSBen Skeggs  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18c39f472eSBen Skeggs  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19c39f472eSBen Skeggs  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20c39f472eSBen Skeggs  * OTHER DEALINGS IN THE SOFTWARE.
21c39f472eSBen Skeggs  *
22c39f472eSBen Skeggs  * Authors: Ben Skeggs
23c39f472eSBen Skeggs  */
24c39f472eSBen Skeggs #include "priv.h"
25d7e5fcd2SBen Skeggs 
26c39f472eSBen Skeggs #include <core/option.h>
27c39f472eSBen Skeggs 
2854dcadd5SBen Skeggs void
292ca0ddbcSBen Skeggs nvkm_mc_unk260(struct nvkm_mc *mc, u32 data)
30c39f472eSBen Skeggs {
3154dcadd5SBen Skeggs 	if (mc->func->unk260)
3254dcadd5SBen Skeggs 		mc->func->unk260(mc, data);
33c39f472eSBen Skeggs }
34c39f472eSBen Skeggs 
35c39f472eSBen Skeggs static inline u32
362ca0ddbcSBen Skeggs nvkm_mc_intr_mask(struct nvkm_mc *mc)
37c39f472eSBen Skeggs {
3825e3a463SBen Skeggs 	struct nvkm_device *device = mc->subdev.device;
3925e3a463SBen Skeggs 	u32 intr = nvkm_rd32(device, 0x000100);
40c39f472eSBen Skeggs 	if (intr == 0xffffffff) /* likely fallen off the bus */
41c39f472eSBen Skeggs 		intr = 0x00000000;
42c39f472eSBen Skeggs 	return intr;
43c39f472eSBen Skeggs }
44c39f472eSBen Skeggs 
45c39f472eSBen Skeggs static irqreturn_t
46d7e5fcd2SBen Skeggs nvkm_mc_intr(int irq, void *arg)
47c39f472eSBen Skeggs {
482ca0ddbcSBen Skeggs 	struct nvkm_mc *mc = arg;
49c47a48a5SBen Skeggs 	struct nvkm_subdev *subdev = &mc->subdev;
50c47a48a5SBen Skeggs 	struct nvkm_device *device = subdev->device;
5154dcadd5SBen Skeggs 	const struct nvkm_mc_intr *map = mc->func->intr;
52d7e5fcd2SBen Skeggs 	struct nvkm_subdev *unit;
53c39f472eSBen Skeggs 	u32 intr;
54c39f472eSBen Skeggs 
5525e3a463SBen Skeggs 	nvkm_wr32(device, 0x000140, 0x00000000);
5625e3a463SBen Skeggs 	nvkm_rd32(device, 0x000140);
572ca0ddbcSBen Skeggs 	intr = nvkm_mc_intr_mask(mc);
582ca0ddbcSBen Skeggs 	if (mc->use_msi)
5954dcadd5SBen Skeggs 		mc->func->msi_rearm(mc);
60c39f472eSBen Skeggs 
61c39f472eSBen Skeggs 	if (intr) {
622ca0ddbcSBen Skeggs 		u32 stat = intr = nvkm_mc_intr_mask(mc);
63c39f472eSBen Skeggs 		while (map->stat) {
64c39f472eSBen Skeggs 			if (intr & map->stat) {
6554dcadd5SBen Skeggs 				unit = nvkm_device_subdev(device, map->unit);
666cf813fbSBen Skeggs 				if (unit)
676cf813fbSBen Skeggs 					nvkm_subdev_intr(unit);
68c39f472eSBen Skeggs 				stat &= ~map->stat;
69c39f472eSBen Skeggs 			}
70c39f472eSBen Skeggs 			map++;
71c39f472eSBen Skeggs 		}
72c39f472eSBen Skeggs 
73c39f472eSBen Skeggs 		if (stat)
74c47a48a5SBen Skeggs 			nvkm_error(subdev, "unknown intr %08x\n", stat);
75c39f472eSBen Skeggs 	}
76c39f472eSBen Skeggs 
7725e3a463SBen Skeggs 	nvkm_wr32(device, 0x000140, 0x00000001);
78c39f472eSBen Skeggs 	return intr ? IRQ_HANDLED : IRQ_NONE;
79c39f472eSBen Skeggs }
80c39f472eSBen Skeggs 
8154dcadd5SBen Skeggs static int
8254dcadd5SBen Skeggs nvkm_mc_fini(struct nvkm_subdev *subdev, bool suspend)
83c39f472eSBen Skeggs {
8454dcadd5SBen Skeggs 	nvkm_wr32(subdev->device, 0x000140, 0x00000000);
8554dcadd5SBen Skeggs 	return 0;
86c39f472eSBen Skeggs }
87c39f472eSBen Skeggs 
8854dcadd5SBen Skeggs static int
8954dcadd5SBen Skeggs nvkm_mc_oneinit(struct nvkm_subdev *subdev)
90c39f472eSBen Skeggs {
9154dcadd5SBen Skeggs 	struct nvkm_mc *mc = nvkm_mc(subdev);
9254dcadd5SBen Skeggs 	return request_irq(mc->irq, nvkm_mc_intr, IRQF_SHARED, "nvkm", mc);
9354dcadd5SBen Skeggs }
9454dcadd5SBen Skeggs 
9554dcadd5SBen Skeggs static int
9654dcadd5SBen Skeggs nvkm_mc_init(struct nvkm_subdev *subdev)
9754dcadd5SBen Skeggs {
9854dcadd5SBen Skeggs 	struct nvkm_mc *mc = nvkm_mc(subdev);
9925e3a463SBen Skeggs 	struct nvkm_device *device = mc->subdev.device;
10054dcadd5SBen Skeggs 	if (mc->func->init)
10154dcadd5SBen Skeggs 		mc->func->init(mc);
10225e3a463SBen Skeggs 	nvkm_wr32(device, 0x000140, 0x00000001);
103c39f472eSBen Skeggs 	return 0;
104c39f472eSBen Skeggs }
105c39f472eSBen Skeggs 
10654dcadd5SBen Skeggs static void *
10754dcadd5SBen Skeggs nvkm_mc_dtor(struct nvkm_subdev *subdev)
108c39f472eSBen Skeggs {
10954dcadd5SBen Skeggs 	struct nvkm_mc *mc = nvkm_mc(subdev);
11025e3a463SBen Skeggs 	struct nvkm_device *device = mc->subdev.device;
1112ca0ddbcSBen Skeggs 	free_irq(mc->irq, mc);
1122ca0ddbcSBen Skeggs 	if (mc->use_msi)
113c39f472eSBen Skeggs 		pci_disable_msi(device->pdev);
11454dcadd5SBen Skeggs 	return mc;
115c39f472eSBen Skeggs }
116c39f472eSBen Skeggs 
11754dcadd5SBen Skeggs static const struct nvkm_subdev_func
11854dcadd5SBen Skeggs nvkm_mc = {
11954dcadd5SBen Skeggs 	.dtor = nvkm_mc_dtor,
12054dcadd5SBen Skeggs 	.oneinit = nvkm_mc_oneinit,
12154dcadd5SBen Skeggs 	.init = nvkm_mc_init,
12254dcadd5SBen Skeggs 	.fini = nvkm_mc_fini,
12354dcadd5SBen Skeggs };
12454dcadd5SBen Skeggs 
125c39f472eSBen Skeggs int
12654dcadd5SBen Skeggs nvkm_mc_new_(const struct nvkm_mc_func *func, struct nvkm_device *device,
12754dcadd5SBen Skeggs 	     int index, struct nvkm_mc **pmc)
128c39f472eSBen Skeggs {
1292ca0ddbcSBen Skeggs 	struct nvkm_mc *mc;
130c39f472eSBen Skeggs 	int ret;
131c39f472eSBen Skeggs 
13254dcadd5SBen Skeggs 	if (!(mc = *pmc = kzalloc(sizeof(*mc), GFP_KERNEL)))
13354dcadd5SBen Skeggs 		return -ENOMEM;
134c39f472eSBen Skeggs 
13554dcadd5SBen Skeggs 	nvkm_subdev_ctor(&nvkm_mc, device, index, 0, &mc->subdev);
13654dcadd5SBen Skeggs 	mc->func = func;
137c39f472eSBen Skeggs 
138c39f472eSBen Skeggs 	if (nv_device_is_pci(device)) {
139c39f472eSBen Skeggs 		switch (device->pdev->device & 0x0ff0) {
140c39f472eSBen Skeggs 		case 0x00f0:
141c39f472eSBen Skeggs 		case 0x02e0:
142c39f472eSBen Skeggs 			/* BR02? NFI how these would be handled yet exactly */
143c39f472eSBen Skeggs 			break;
144c39f472eSBen Skeggs 		default:
145c39f472eSBen Skeggs 			switch (device->chipset) {
146c39f472eSBen Skeggs 			case 0xaa:
147c39f472eSBen Skeggs 				/* reported broken, nv also disable it */
148c39f472eSBen Skeggs 				break;
149c39f472eSBen Skeggs 			default:
1502ca0ddbcSBen Skeggs 				mc->use_msi = true;
151c39f472eSBen Skeggs 				break;
152c39f472eSBen Skeggs 			}
153c39f472eSBen Skeggs 		}
154c39f472eSBen Skeggs 
1552ca0ddbcSBen Skeggs 		mc->use_msi = nvkm_boolopt(device->cfgopt, "NvMSI",
1562ca0ddbcSBen Skeggs 					    mc->use_msi);
157c39f472eSBen Skeggs 
15854dcadd5SBen Skeggs 		if (mc->use_msi && mc->func->msi_rearm) {
1592ca0ddbcSBen Skeggs 			mc->use_msi = pci_enable_msi(device->pdev) == 0;
1602ca0ddbcSBen Skeggs 			if (mc->use_msi) {
161c47a48a5SBen Skeggs 				nvkm_debug(&mc->subdev, "MSI enabled\n");
16254dcadd5SBen Skeggs 				mc->func->msi_rearm(mc);
163c39f472eSBen Skeggs 			}
164c39f472eSBen Skeggs 		} else {
1652ca0ddbcSBen Skeggs 			mc->use_msi = false;
166c39f472eSBen Skeggs 		}
167c39f472eSBen Skeggs 	}
168c39f472eSBen Skeggs 
169c39f472eSBen Skeggs 	ret = nv_device_get_irq(device, true);
170c39f472eSBen Skeggs 	if (ret < 0)
171c39f472eSBen Skeggs 		return ret;
1722ca0ddbcSBen Skeggs 	mc->irq = ret;
173c39f472eSBen Skeggs 	return 0;
174c39f472eSBen Skeggs }
175