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 
28c39f472eSBen Skeggs static inline void
292ca0ddbcSBen Skeggs nvkm_mc_unk260(struct nvkm_mc *mc, u32 data)
30c39f472eSBen Skeggs {
312ca0ddbcSBen Skeggs 	const struct nvkm_mc_oclass *impl = (void *)nv_oclass(mc);
32c39f472eSBen Skeggs 	if (impl->unk260)
332ca0ddbcSBen Skeggs 		impl->unk260(mc, data);
34c39f472eSBen Skeggs }
35c39f472eSBen Skeggs 
36c39f472eSBen Skeggs static inline u32
372ca0ddbcSBen Skeggs nvkm_mc_intr_mask(struct nvkm_mc *mc)
38c39f472eSBen Skeggs {
3925e3a463SBen Skeggs 	struct nvkm_device *device = mc->subdev.device;
4025e3a463SBen Skeggs 	u32 intr = nvkm_rd32(device, 0x000100);
41c39f472eSBen Skeggs 	if (intr == 0xffffffff) /* likely fallen off the bus */
42c39f472eSBen Skeggs 		intr = 0x00000000;
43c39f472eSBen Skeggs 	return intr;
44c39f472eSBen Skeggs }
45c39f472eSBen Skeggs 
46c39f472eSBen Skeggs static irqreturn_t
47d7e5fcd2SBen Skeggs nvkm_mc_intr(int irq, void *arg)
48c39f472eSBen Skeggs {
492ca0ddbcSBen Skeggs 	struct nvkm_mc *mc = arg;
5025e3a463SBen Skeggs 	struct nvkm_device *device = mc->subdev.device;
512ca0ddbcSBen Skeggs 	const struct nvkm_mc_oclass *oclass = (void *)nv_object(mc)->oclass;
52d7e5fcd2SBen Skeggs 	const struct nvkm_mc_intr *map = oclass->intr;
53d7e5fcd2SBen Skeggs 	struct nvkm_subdev *unit;
54c39f472eSBen Skeggs 	u32 intr;
55c39f472eSBen Skeggs 
5625e3a463SBen Skeggs 	nvkm_wr32(device, 0x000140, 0x00000000);
5725e3a463SBen Skeggs 	nvkm_rd32(device, 0x000140);
582ca0ddbcSBen Skeggs 	intr = nvkm_mc_intr_mask(mc);
592ca0ddbcSBen Skeggs 	if (mc->use_msi)
602ca0ddbcSBen Skeggs 		oclass->msi_rearm(mc);
61c39f472eSBen Skeggs 
62c39f472eSBen Skeggs 	if (intr) {
632ca0ddbcSBen Skeggs 		u32 stat = intr = nvkm_mc_intr_mask(mc);
64c39f472eSBen Skeggs 		while (map->stat) {
65c39f472eSBen Skeggs 			if (intr & map->stat) {
662ca0ddbcSBen Skeggs 				unit = nvkm_subdev(mc, map->unit);
67c39f472eSBen Skeggs 				if (unit && unit->intr)
68c39f472eSBen Skeggs 					unit->intr(unit);
69c39f472eSBen Skeggs 				stat &= ~map->stat;
70c39f472eSBen Skeggs 			}
71c39f472eSBen Skeggs 			map++;
72c39f472eSBen Skeggs 		}
73c39f472eSBen Skeggs 
74c39f472eSBen Skeggs 		if (stat)
752ca0ddbcSBen Skeggs 			nv_error(mc, "unknown intr 0x%08x\n", stat);
76c39f472eSBen Skeggs 	}
77c39f472eSBen Skeggs 
7825e3a463SBen Skeggs 	nvkm_wr32(device, 0x000140, 0x00000001);
79c39f472eSBen Skeggs 	return intr ? IRQ_HANDLED : IRQ_NONE;
80c39f472eSBen Skeggs }
81c39f472eSBen Skeggs 
82c39f472eSBen Skeggs int
83d7e5fcd2SBen Skeggs _nvkm_mc_fini(struct nvkm_object *object, bool suspend)
84c39f472eSBen Skeggs {
852ca0ddbcSBen Skeggs 	struct nvkm_mc *mc = (void *)object;
8625e3a463SBen Skeggs 	struct nvkm_device *device = mc->subdev.device;
8725e3a463SBen Skeggs 	nvkm_wr32(device, 0x000140, 0x00000000);
882ca0ddbcSBen Skeggs 	return nvkm_subdev_fini(&mc->subdev, suspend);
89c39f472eSBen Skeggs }
90c39f472eSBen Skeggs 
91c39f472eSBen Skeggs int
92d7e5fcd2SBen Skeggs _nvkm_mc_init(struct nvkm_object *object)
93c39f472eSBen Skeggs {
942ca0ddbcSBen Skeggs 	struct nvkm_mc *mc = (void *)object;
9525e3a463SBen Skeggs 	struct nvkm_device *device = mc->subdev.device;
962ca0ddbcSBen Skeggs 	int ret = nvkm_subdev_init(&mc->subdev);
97c39f472eSBen Skeggs 	if (ret)
98c39f472eSBen Skeggs 		return ret;
9925e3a463SBen Skeggs 	nvkm_wr32(device, 0x000140, 0x00000001);
100c39f472eSBen Skeggs 	return 0;
101c39f472eSBen Skeggs }
102c39f472eSBen Skeggs 
103c39f472eSBen Skeggs void
104d7e5fcd2SBen Skeggs _nvkm_mc_dtor(struct nvkm_object *object)
105c39f472eSBen Skeggs {
1062ca0ddbcSBen Skeggs 	struct nvkm_mc *mc = (void *)object;
10725e3a463SBen Skeggs 	struct nvkm_device *device = mc->subdev.device;
1082ca0ddbcSBen Skeggs 	free_irq(mc->irq, mc);
1092ca0ddbcSBen Skeggs 	if (mc->use_msi)
110c39f472eSBen Skeggs 		pci_disable_msi(device->pdev);
1112ca0ddbcSBen Skeggs 	nvkm_subdev_destroy(&mc->subdev);
112c39f472eSBen Skeggs }
113c39f472eSBen Skeggs 
114c39f472eSBen Skeggs int
115d7e5fcd2SBen Skeggs nvkm_mc_create_(struct nvkm_object *parent, struct nvkm_object *engine,
116d7e5fcd2SBen Skeggs 		struct nvkm_oclass *bclass, int length, void **pobject)
117c39f472eSBen Skeggs {
118d7e5fcd2SBen Skeggs 	const struct nvkm_mc_oclass *oclass = (void *)bclass;
11925e3a463SBen Skeggs 	struct nvkm_device *device = (void *)parent;
1202ca0ddbcSBen Skeggs 	struct nvkm_mc *mc;
121c39f472eSBen Skeggs 	int ret;
122c39f472eSBen Skeggs 
123d7e5fcd2SBen Skeggs 	ret = nvkm_subdev_create_(parent, engine, bclass, 0, "PMC",
124c39f472eSBen Skeggs 				  "master", length, pobject);
1252ca0ddbcSBen Skeggs 	mc = *pobject;
126c39f472eSBen Skeggs 	if (ret)
127c39f472eSBen Skeggs 		return ret;
128c39f472eSBen Skeggs 
1292ca0ddbcSBen Skeggs 	mc->unk260 = nvkm_mc_unk260;
130c39f472eSBen Skeggs 
131c39f472eSBen Skeggs 	if (nv_device_is_pci(device)) {
132c39f472eSBen Skeggs 		switch (device->pdev->device & 0x0ff0) {
133c39f472eSBen Skeggs 		case 0x00f0:
134c39f472eSBen Skeggs 		case 0x02e0:
135c39f472eSBen Skeggs 			/* BR02? NFI how these would be handled yet exactly */
136c39f472eSBen Skeggs 			break;
137c39f472eSBen Skeggs 		default:
138c39f472eSBen Skeggs 			switch (device->chipset) {
139c39f472eSBen Skeggs 			case 0xaa:
140c39f472eSBen Skeggs 				/* reported broken, nv also disable it */
141c39f472eSBen Skeggs 				break;
142c39f472eSBen Skeggs 			default:
1432ca0ddbcSBen Skeggs 				mc->use_msi = true;
144c39f472eSBen Skeggs 				break;
145c39f472eSBen Skeggs 			}
146c39f472eSBen Skeggs 		}
147c39f472eSBen Skeggs 
1482ca0ddbcSBen Skeggs 		mc->use_msi = nvkm_boolopt(device->cfgopt, "NvMSI",
1492ca0ddbcSBen Skeggs 					    mc->use_msi);
150c39f472eSBen Skeggs 
1512ca0ddbcSBen Skeggs 		if (mc->use_msi && oclass->msi_rearm) {
1522ca0ddbcSBen Skeggs 			mc->use_msi = pci_enable_msi(device->pdev) == 0;
1532ca0ddbcSBen Skeggs 			if (mc->use_msi) {
1542ca0ddbcSBen Skeggs 				nv_info(mc, "MSI interrupts enabled\n");
1552ca0ddbcSBen Skeggs 				oclass->msi_rearm(mc);
156c39f472eSBen Skeggs 			}
157c39f472eSBen Skeggs 		} else {
1582ca0ddbcSBen Skeggs 			mc->use_msi = false;
159c39f472eSBen Skeggs 		}
160c39f472eSBen Skeggs 	}
161c39f472eSBen Skeggs 
162c39f472eSBen Skeggs 	ret = nv_device_get_irq(device, true);
163c39f472eSBen Skeggs 	if (ret < 0)
164c39f472eSBen Skeggs 		return ret;
1652ca0ddbcSBen Skeggs 	mc->irq = ret;
166c39f472eSBen Skeggs 
1672ca0ddbcSBen Skeggs 	ret = request_irq(mc->irq, nvkm_mc_intr, IRQF_SHARED, "nvkm", mc);
168c39f472eSBen Skeggs 	if (ret < 0)
169c39f472eSBen Skeggs 		return ret;
170c39f472eSBen Skeggs 
171c39f472eSBen Skeggs 	return 0;
172c39f472eSBen Skeggs }
173