1/* $NetBSD: nouveau_subdev_mc_base.c,v 1.5 2015/10/22 23:17:08 jmcneill Exp $ */
2
3/*
4 * Copyright 2012 Red Hat Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 *
24 * Authors: Ben Skeggs
25 */
26
27#include <sys/cdefs.h>
28__KERNEL_RCSID(0, "$NetBSD: nouveau_subdev_mc_base.c,v 1.5 2015/10/22 23:17:08 jmcneill Exp $");
29
30#include <subdev/mc.h>
31#include <core/option.h>
32
33#if defined(__NetBSD__) && defined(__arm__)
34/* XXX nouveau platform kludge */
35#include <arm/nvidia/tegra_intr.h>
36#endif
37
38static inline u32
39nouveau_mc_intr_mask(struct nouveau_mc *pmc)
40{
41 u32 intr = nv_rd32(pmc, 0x000100);
42 if (intr == 0xffffffff) /* likely fallen off the bus */
43 intr = 0x00000000;
44 return intr;
45}
46
47#ifdef __NetBSD__
48static int
49nouveau_mc_intr(void *arg)
50#else
51static irqreturn_t
52nouveau_mc_intr(int irq, void *arg)
53#endif
54{
55 struct nouveau_mc *pmc = arg;
56 const struct nouveau_mc_oclass *oclass = (void *)nv_object(pmc)->oclass;
57 const struct nouveau_mc_intr *map = oclass->intr;
58 struct nouveau_subdev *unit;
59 u32 intr;
60
61 nv_wr32(pmc, 0x000140, 0x00000000);
62 nv_rd32(pmc, 0x000140);
63 intr = nouveau_mc_intr_mask(pmc);
64 if (pmc->use_msi)
65 oclass->msi_rearm(pmc);
66
67 if (intr) {
68 u32 stat = intr = nouveau_mc_intr_mask(pmc);
69 while (map->stat) {
70 if (intr & map->stat) {
71 unit = nouveau_subdev(pmc, map->unit);
72 if (unit && unit->intr)
73 unit->intr(unit);
74 stat &= ~map->stat;
75 }
76 map++;
77 }
78
79 if (stat)
80 nv_error(pmc, "unknown intr 0x%08x\n", stat);
81 }
82
83 nv_wr32(pmc, 0x000140, 0x00000001);
84#ifdef __NetBSD__
85 return intr ? 1 : 0;
86#else
87 return intr ? IRQ_HANDLED : IRQ_NONE;
88#endif
89}
90
91int
92_nouveau_mc_fini(struct nouveau_object *object, bool suspend)
93{
94 struct nouveau_mc *pmc = (void *)object;
95 nv_wr32(pmc, 0x000140, 0x00000000);
96 return nouveau_subdev_fini(&pmc->base, suspend);
97}
98
99int
100_nouveau_mc_init(struct nouveau_object *object)
101{
102 struct nouveau_mc *pmc = (void *)object;
103 int ret = nouveau_subdev_init(&pmc->base);
104 if (ret)
105 return ret;
106 nv_wr32(pmc, 0x000140, 0x00000001);
107 return 0;
108}
109
110void
111_nouveau_mc_dtor(struct nouveau_object *object)
112{
113 struct nouveau_device *device = nv_device(object);
114 struct nouveau_mc *pmc = (void *)object;
115#if defined(__NetBSD__)
116 if (nv_device_is_pci(device)) {
117 pci_intr_disestablish(device->pdev->pd_pa.pa_pc, pmc->irq_cookie);
118#if defined(__arm__)
119 } else {
120 intr_disestablish(pmc->irq_cookie);
121#endif
122 }
123#else
124 free_irq(pmc->irq, pmc);
125#endif
126 if (pmc->use_msi)
127 pci_disable_msi(device->pdev);
128 nouveau_subdev_destroy(&pmc->base);
129}
130
131int
132nouveau_mc_create_(struct nouveau_object *parent, struct nouveau_object *engine,
133 struct nouveau_oclass *bclass, int length, void **pobject)
134{
135 const struct nouveau_mc_oclass *oclass = (void *)bclass;
136 struct nouveau_device *device = nv_device(parent);
137 struct nouveau_mc *pmc;
138 int ret;
139
140 ret = nouveau_subdev_create_(parent, engine, bclass, 0, "PMC",
141 "master", length, pobject);
142 pmc = *pobject;
143 if (ret)
144 return ret;
145
146 if (nv_device_is_pci(device))
147 switch (device->pdev->device & 0x0ff0) {
148 case 0x00f0:
149 case 0x02e0:
150 /* BR02? NFI how these would be handled yet exactly */
151 break;
152 default:
153 switch (device->chipset) {
154 case 0xaa:
155 /* reported broken, nv also disable it */
156 break;
157 default:
158 pmc->use_msi = true;
159 break;
160 }
161
162 pmc->use_msi = nouveau_boolopt(device->cfgopt, "NvMSI",
163 pmc->use_msi);
164
165 if (pmc->use_msi && oclass->msi_rearm) {
166 pmc->use_msi = pci_enable_msi(device->pdev) == 0;
167 if (pmc->use_msi) {
168 nv_info(pmc, "MSI interrupts enabled\n");
169 oclass->msi_rearm(pmc);
170 }
171 } else {
172 pmc->use_msi = false;
173 }
174 }
175
176#if defined(__NetBSD__)
177 if (nv_device_is_pci(device)) {
178 const pci_chipset_tag_t pc = device->pdev->pd_pa.pa_pc;
179 pci_intr_handle_t ih;
180
181 if (pci_intr_map(&device->pdev->pd_pa, &ih))
182 return -EIO;
183
184 pmc->irq_cookie = pci_intr_establish(pc, ih, IPL_VM,
185 &nouveau_mc_intr, pmc);
186 if (pmc->irq_cookie == NULL)
187 return -EIO;
188#if defined (__arm__)
189 } else {
190 pmc->irq_cookie = intr_establish(TEGRA_INTR_GPU,
191 IPL_VM, IST_LEVEL, nouveau_mc_intr, pmc);
192 if (pmc->irq_cookie == NULL)
193 return -EIO;
194#endif
195 }
196#else
197 ret = nv_device_get_irq(device, true);
198 if (ret < 0)
199 return ret;
200 pmc->irq = ret;
201
202 ret = request_irq(pmc->irq, nouveau_mc_intr, IRQF_SHARED, "nouveau",
203 pmc);
204
205 if (ret < 0)
206 return ret;
207#endif
208
209 return 0;
210}
211