Merge tag 'mmc-v4.7-rc1' of git://git.linaro.org/people/ulf.hansson/mmc
[linux-2.6-block.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_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 }