1 /*
2  * Copyright 2012 Red Hat Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Authors: Ben Skeggs
23  */
24 #include "priv.h"
25 
26 #include <core/option.h>
27 #include <subdev/top.h>
28 
29 void
30 nvkm_mc_unk260(struct nvkm_mc *mc, u32 data)
31 {
32 	if (mc->func->unk260)
33 		mc->func->unk260(mc, data);
34 }
35 
36 void
37 nvkm_mc_intr_unarm(struct nvkm_mc *mc)
38 {
39 	return mc->func->intr_unarm(mc);
40 }
41 
42 void
43 nvkm_mc_intr_rearm(struct nvkm_mc *mc)
44 {
45 	return mc->func->intr_rearm(mc);
46 }
47 
48 static u32
49 nvkm_mc_intr_mask(struct nvkm_mc *mc)
50 {
51 	u32 intr = mc->func->intr_mask(mc);
52 	if (WARN_ON_ONCE(intr == 0xffffffff))
53 		intr = 0; /* likely fallen off the bus */
54 	return intr;
55 }
56 
57 void
58 nvkm_mc_intr(struct nvkm_mc *mc, bool *handled)
59 {
60 	struct nvkm_device *device = mc->subdev.device;
61 	struct nvkm_subdev *subdev;
62 	const struct nvkm_mc_map *map = mc->func->intr;
63 	u32 stat, intr = nvkm_mc_intr_mask(mc);
64 	u64 subdevs;
65 
66 	stat = nvkm_top_intr(device->top, intr, &subdevs);
67 	while (subdevs) {
68 		enum nvkm_devidx subidx = __ffs64(subdevs);
69 		subdev = nvkm_device_subdev(device, subidx);
70 		if (subdev)
71 			nvkm_subdev_intr(subdev);
72 		subdevs &= ~BIT_ULL(subidx);
73 	}
74 
75 	while (map->stat) {
76 		if (intr & map->stat) {
77 			subdev = nvkm_device_subdev(device, map->unit);
78 			if (subdev)
79 				nvkm_subdev_intr(subdev);
80 			stat &= ~map->stat;
81 		}
82 		map++;
83 	}
84 
85 	if (stat)
86 		nvkm_error(&mc->subdev, "intr %08x\n", stat);
87 	*handled = intr != 0;
88 }
89 
90 static void
91 nvkm_mc_reset_(struct nvkm_mc *mc, enum nvkm_devidx devidx)
92 {
93 	struct nvkm_device *device = mc->subdev.device;
94 	const struct nvkm_mc_map *map;
95 	u64 pmc_enable;
96 
97 	if (!(pmc_enable = nvkm_top_reset(device->top, devidx))) {
98 		for (map = mc->func->reset; map && map->stat; map++) {
99 			if (map->unit == devidx) {
100 				pmc_enable = map->stat;
101 				break;
102 			}
103 		}
104 	}
105 
106 	if (pmc_enable) {
107 		nvkm_mask(device, 0x000200, pmc_enable, 0x00000000);
108 		nvkm_mask(device, 0x000200, pmc_enable, pmc_enable);
109 		nvkm_rd32(device, 0x000200);
110 	}
111 }
112 
113 void
114 nvkm_mc_reset(struct nvkm_mc *mc, enum nvkm_devidx devidx)
115 {
116 	if (likely(mc))
117 		nvkm_mc_reset_(mc, devidx);
118 }
119 
120 static int
121 nvkm_mc_fini(struct nvkm_subdev *subdev, bool suspend)
122 {
123 	struct nvkm_mc *mc = nvkm_mc(subdev);
124 	nvkm_mc_intr_unarm(mc);
125 	return 0;
126 }
127 
128 static int
129 nvkm_mc_init(struct nvkm_subdev *subdev)
130 {
131 	struct nvkm_mc *mc = nvkm_mc(subdev);
132 	if (mc->func->init)
133 		mc->func->init(mc);
134 	nvkm_mc_intr_rearm(mc);
135 	return 0;
136 }
137 
138 static void *
139 nvkm_mc_dtor(struct nvkm_subdev *subdev)
140 {
141 	return nvkm_mc(subdev);
142 }
143 
144 static const struct nvkm_subdev_func
145 nvkm_mc = {
146 	.dtor = nvkm_mc_dtor,
147 	.init = nvkm_mc_init,
148 	.fini = nvkm_mc_fini,
149 };
150 
151 int
152 nvkm_mc_new_(const struct nvkm_mc_func *func, struct nvkm_device *device,
153 	     int index, struct nvkm_mc **pmc)
154 {
155 	struct nvkm_mc *mc;
156 
157 	if (!(mc = *pmc = kzalloc(sizeof(*mc), GFP_KERNEL)))
158 		return -ENOMEM;
159 
160 	nvkm_subdev_ctor(&nvkm_mc, device, index, &mc->subdev);
161 	mc->func = func;
162 	return 0;
163 }
164