GNU Linux-libre 4.9.330-gnu1
[releases.git] / drivers / gpu / drm / nouveau / nvkm / subdev / mc / base.c
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_device *device, u32 data)
31 {
32         struct nvkm_mc *mc = device->mc;
33         if (likely(mc) && mc->func->unk260)
34                 mc->func->unk260(mc, data);
35 }
36
37 void
38 nvkm_mc_intr_mask(struct nvkm_device *device, enum nvkm_devidx devidx, bool en)
39 {
40         struct nvkm_mc *mc = device->mc;
41         const struct nvkm_mc_map *map;
42         if (likely(mc) && mc->func->intr_mask) {
43                 u32 mask = nvkm_top_intr_mask(device, devidx);
44                 for (map = mc->func->intr; !mask && map->stat; map++) {
45                         if (map->unit == devidx)
46                                 mask = map->stat;
47                 }
48                 mc->func->intr_mask(mc, mask, en ? mask : 0);
49         }
50 }
51
52 void
53 nvkm_mc_intr_unarm(struct nvkm_device *device)
54 {
55         struct nvkm_mc *mc = device->mc;
56         if (likely(mc))
57                 mc->func->intr_unarm(mc);
58 }
59
60 void
61 nvkm_mc_intr_rearm(struct nvkm_device *device)
62 {
63         struct nvkm_mc *mc = device->mc;
64         if (likely(mc))
65                 mc->func->intr_rearm(mc);
66 }
67
68 static u32
69 nvkm_mc_intr_stat(struct nvkm_mc *mc)
70 {
71         u32 intr = mc->func->intr_stat(mc);
72         if (WARN_ON_ONCE(intr == 0xffffffff))
73                 intr = 0; /* likely fallen off the bus */
74         return intr;
75 }
76
77 void
78 nvkm_mc_intr(struct nvkm_device *device, bool *handled)
79 {
80         struct nvkm_mc *mc = device->mc;
81         struct nvkm_subdev *subdev;
82         const struct nvkm_mc_map *map;
83         u32 stat, intr;
84         u64 subdevs;
85
86         if (unlikely(!mc))
87                 return;
88
89         intr = nvkm_mc_intr_stat(mc);
90         stat = nvkm_top_intr(device, intr, &subdevs);
91         while (subdevs) {
92                 enum nvkm_devidx subidx = __ffs64(subdevs);
93                 subdev = nvkm_device_subdev(device, subidx);
94                 if (subdev)
95                         nvkm_subdev_intr(subdev);
96                 subdevs &= ~BIT_ULL(subidx);
97         }
98
99         for (map = mc->func->intr; map->stat; map++) {
100                 if (intr & map->stat) {
101                         subdev = nvkm_device_subdev(device, map->unit);
102                         if (subdev)
103                                 nvkm_subdev_intr(subdev);
104                         stat &= ~map->stat;
105                 }
106         }
107
108         if (stat)
109                 nvkm_error(&mc->subdev, "intr %08x\n", stat);
110         *handled = intr != 0;
111 }
112
113 static u32
114 nvkm_mc_reset_mask(struct nvkm_device *device, bool isauto,
115                    enum nvkm_devidx devidx)
116 {
117         struct nvkm_mc *mc = device->mc;
118         const struct nvkm_mc_map *map;
119         u64 pmc_enable = 0;
120         if (likely(mc)) {
121                 if (!(pmc_enable = nvkm_top_reset(device, devidx))) {
122                         for (map = mc->func->reset; map && map->stat; map++) {
123                                 if (!isauto || !map->noauto) {
124                                         if (map->unit == devidx) {
125                                                 pmc_enable = map->stat;
126                                                 break;
127                                         }
128                                 }
129                         }
130                 }
131         }
132         return pmc_enable;
133 }
134
135 void
136 nvkm_mc_reset(struct nvkm_device *device, enum nvkm_devidx devidx)
137 {
138         u64 pmc_enable = nvkm_mc_reset_mask(device, true, devidx);
139         if (pmc_enable) {
140                 nvkm_mask(device, 0x000200, pmc_enable, 0x00000000);
141                 nvkm_mask(device, 0x000200, pmc_enable, pmc_enable);
142                 nvkm_rd32(device, 0x000200);
143         }
144 }
145
146 void
147 nvkm_mc_disable(struct nvkm_device *device, enum nvkm_devidx devidx)
148 {
149         u64 pmc_enable = nvkm_mc_reset_mask(device, false, devidx);
150         if (pmc_enable)
151                 nvkm_mask(device, 0x000200, pmc_enable, 0x00000000);
152 }
153
154 void
155 nvkm_mc_enable(struct nvkm_device *device, enum nvkm_devidx devidx)
156 {
157         u64 pmc_enable = nvkm_mc_reset_mask(device, false, devidx);
158         if (pmc_enable) {
159                 nvkm_mask(device, 0x000200, pmc_enable, pmc_enable);
160                 nvkm_rd32(device, 0x000200);
161         }
162 }
163
164 static int
165 nvkm_mc_fini(struct nvkm_subdev *subdev, bool suspend)
166 {
167         nvkm_mc_intr_unarm(subdev->device);
168         return 0;
169 }
170
171 static int
172 nvkm_mc_init(struct nvkm_subdev *subdev)
173 {
174         struct nvkm_mc *mc = nvkm_mc(subdev);
175         if (mc->func->init)
176                 mc->func->init(mc);
177         nvkm_mc_intr_rearm(subdev->device);
178         return 0;
179 }
180
181 static void *
182 nvkm_mc_dtor(struct nvkm_subdev *subdev)
183 {
184         return nvkm_mc(subdev);
185 }
186
187 static const struct nvkm_subdev_func
188 nvkm_mc = {
189         .dtor = nvkm_mc_dtor,
190         .init = nvkm_mc_init,
191         .fini = nvkm_mc_fini,
192 };
193
194 void
195 nvkm_mc_ctor(const struct nvkm_mc_func *func, struct nvkm_device *device,
196              int index, struct nvkm_mc *mc)
197 {
198         nvkm_subdev_ctor(&nvkm_mc, device, index, &mc->subdev);
199         mc->func = func;
200 }
201
202 int
203 nvkm_mc_new_(const struct nvkm_mc_func *func, struct nvkm_device *device,
204              int index, struct nvkm_mc **pmc)
205 {
206         struct nvkm_mc *mc;
207         if (!(mc = *pmc = kzalloc(sizeof(*mc), GFP_KERNEL)))
208                 return -ENOMEM;
209         nvkm_mc_ctor(func, device, index, *pmc);
210         return 0;
211 }