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>
27583f8e4eSBen Skeggs #include <subdev/top.h>
28c39f472eSBen Skeggs 
2954dcadd5SBen Skeggs void
30d3981190SBen Skeggs nvkm_mc_unk260(struct nvkm_device *device, u32 data)
31c39f472eSBen Skeggs {
32d3981190SBen Skeggs 	struct nvkm_mc *mc = device->mc;
33d3981190SBen Skeggs 	if (likely(mc) && mc->func->unk260)
3454dcadd5SBen Skeggs 		mc->func->unk260(mc, data);
35c39f472eSBen Skeggs }
36c39f472eSBen Skeggs 
37d4c4cc83SBen Skeggs void
3866adbfb0SBen Skeggs nvkm_mc_intr_mask(struct nvkm_device *device, enum nvkm_devidx devidx, bool en)
3966adbfb0SBen Skeggs {
4066adbfb0SBen Skeggs 	struct nvkm_mc *mc = device->mc;
4166adbfb0SBen Skeggs 	const struct nvkm_mc_map *map;
4266adbfb0SBen Skeggs 	if (likely(mc) && mc->func->intr_mask) {
4366adbfb0SBen Skeggs 		u32 mask = nvkm_top_intr_mask(device, devidx);
4466adbfb0SBen Skeggs 		for (map = mc->func->intr; !mask && map->stat; map++) {
4566adbfb0SBen Skeggs 			if (map->unit == devidx)
4666adbfb0SBen Skeggs 				mask = map->stat;
4766adbfb0SBen Skeggs 		}
4866adbfb0SBen Skeggs 		mc->func->intr_mask(mc, mask, en ? mask : 0);
4966adbfb0SBen Skeggs 	}
5066adbfb0SBen Skeggs }
5166adbfb0SBen Skeggs 
5266adbfb0SBen Skeggs void
53d3981190SBen Skeggs nvkm_mc_intr_unarm(struct nvkm_device *device)
54d4c4cc83SBen Skeggs {
55d3981190SBen Skeggs 	struct nvkm_mc *mc = device->mc;
56d3981190SBen Skeggs 	if (likely(mc))
57d3981190SBen Skeggs 		mc->func->intr_unarm(mc);
58d4c4cc83SBen Skeggs }
59d4c4cc83SBen Skeggs 
60d4c4cc83SBen Skeggs void
61d3981190SBen Skeggs nvkm_mc_intr_rearm(struct nvkm_device *device)
62d4c4cc83SBen Skeggs {
63d3981190SBen Skeggs 	struct nvkm_mc *mc = device->mc;
64d3981190SBen Skeggs 	if (likely(mc))
65d3981190SBen Skeggs 		mc->func->intr_rearm(mc);
66d4c4cc83SBen Skeggs }
67d4c4cc83SBen Skeggs 
682b700825SBen Skeggs static u32
696e09a578SBen Skeggs nvkm_mc_intr_stat(struct nvkm_mc *mc)
70c39f472eSBen Skeggs {
716e09a578SBen Skeggs 	u32 intr = mc->func->intr_stat(mc);
72d4c4cc83SBen Skeggs 	if (WARN_ON_ONCE(intr == 0xffffffff))
73d4c4cc83SBen Skeggs 		intr = 0; /* likely fallen off the bus */
74c39f472eSBen Skeggs 	return intr;
75c39f472eSBen Skeggs }
76c39f472eSBen Skeggs 
772b700825SBen Skeggs void
78d3981190SBen Skeggs nvkm_mc_intr(struct nvkm_device *device, bool *handled)
79c39f472eSBen Skeggs {
80d3981190SBen Skeggs 	struct nvkm_mc *mc = device->mc;
812b700825SBen Skeggs 	struct nvkm_subdev *subdev;
82d3981190SBen Skeggs 	const struct nvkm_mc_map *map;
83d3981190SBen Skeggs 	u32 stat, intr;
84921be10dSBen Skeggs 	u64 subdevs;
85c39f472eSBen Skeggs 
86d3981190SBen Skeggs 	if (unlikely(!mc))
87d3981190SBen Skeggs 		return;
88d3981190SBen Skeggs 
896e09a578SBen Skeggs 	intr = nvkm_mc_intr_stat(mc);
90952eb819SBen Skeggs 	stat = nvkm_top_intr(device, intr, &subdevs);
91921be10dSBen Skeggs 	while (subdevs) {
92921be10dSBen Skeggs 		enum nvkm_devidx subidx = __ffs64(subdevs);
93921be10dSBen Skeggs 		subdev = nvkm_device_subdev(device, subidx);
94921be10dSBen Skeggs 		if (subdev)
95921be10dSBen Skeggs 			nvkm_subdev_intr(subdev);
96921be10dSBen Skeggs 		subdevs &= ~BIT_ULL(subidx);
97921be10dSBen Skeggs 	}
98921be10dSBen Skeggs 
99d3981190SBen Skeggs 	for (map = mc->func->intr; map->stat; map++) {
100c39f472eSBen Skeggs 		if (intr & map->stat) {
1012b700825SBen Skeggs 			subdev = nvkm_device_subdev(device, map->unit);
1022b700825SBen Skeggs 			if (subdev)
1032b700825SBen Skeggs 				nvkm_subdev_intr(subdev);
104c39f472eSBen Skeggs 			stat &= ~map->stat;
105c39f472eSBen Skeggs 		}
106c39f472eSBen Skeggs 	}
107c39f472eSBen Skeggs 
108c39f472eSBen Skeggs 	if (stat)
1092b700825SBen Skeggs 		nvkm_error(&mc->subdev, "intr %08x\n", stat);
1102b700825SBen Skeggs 	*handled = intr != 0;
111c39f472eSBen Skeggs }
112c39f472eSBen Skeggs 
1133c2a536bSBen Skeggs static u32
1143c2a536bSBen Skeggs nvkm_mc_reset_mask(struct nvkm_device *device, bool isauto,
1153c2a536bSBen Skeggs 		   enum nvkm_devidx devidx)
1166defde5aSBen Skeggs {
1173c2a536bSBen Skeggs 	struct nvkm_mc *mc = device->mc;
11870b01f07SBen Skeggs 	const struct nvkm_mc_map *map;
1193c2a536bSBen Skeggs 	u64 pmc_enable = 0;
1203c2a536bSBen Skeggs 	if (likely(mc)) {
121952eb819SBen Skeggs 		if (!(pmc_enable = nvkm_top_reset(device, devidx))) {
12270b01f07SBen Skeggs 			for (map = mc->func->reset; map && map->stat; map++) {
1233c2a536bSBen Skeggs 				if (!isauto || !map->noauto) {
12470b01f07SBen Skeggs 					if (map->unit == devidx) {
12570b01f07SBen Skeggs 						pmc_enable = map->stat;
12670b01f07SBen Skeggs 						break;
12770b01f07SBen Skeggs 					}
12870b01f07SBen Skeggs 				}
129583f8e4eSBen Skeggs 			}
1303c2a536bSBen Skeggs 		}
1313c2a536bSBen Skeggs 	}
1323c2a536bSBen Skeggs 	return pmc_enable;
1333c2a536bSBen Skeggs }
13470b01f07SBen Skeggs 
1353c2a536bSBen Skeggs void
1363c2a536bSBen Skeggs nvkm_mc_reset(struct nvkm_device *device, enum nvkm_devidx devidx)
1373c2a536bSBen Skeggs {
1383c2a536bSBen Skeggs 	u64 pmc_enable = nvkm_mc_reset_mask(device, true, devidx);
1396defde5aSBen Skeggs 	if (pmc_enable) {
1406defde5aSBen Skeggs 		nvkm_mask(device, 0x000200, pmc_enable, 0x00000000);
1416defde5aSBen Skeggs 		nvkm_mask(device, 0x000200, pmc_enable, pmc_enable);
1426defde5aSBen Skeggs 		nvkm_rd32(device, 0x000200);
1436defde5aSBen Skeggs 	}
1446defde5aSBen Skeggs }
1456defde5aSBen Skeggs 
1466defde5aSBen Skeggs void
1473c2a536bSBen Skeggs nvkm_mc_disable(struct nvkm_device *device, enum nvkm_devidx devidx)
1486defde5aSBen Skeggs {
1493c2a536bSBen Skeggs 	u64 pmc_enable = nvkm_mc_reset_mask(device, false, devidx);
1503c2a536bSBen Skeggs 	if (pmc_enable)
1513c2a536bSBen Skeggs 		nvkm_mask(device, 0x000200, pmc_enable, 0x00000000);
1523c2a536bSBen Skeggs }
1533c2a536bSBen Skeggs 
1543c2a536bSBen Skeggs void
1553c2a536bSBen Skeggs nvkm_mc_enable(struct nvkm_device *device, enum nvkm_devidx devidx)
1563c2a536bSBen Skeggs {
1573c2a536bSBen Skeggs 	u64 pmc_enable = nvkm_mc_reset_mask(device, false, devidx);
1583c2a536bSBen Skeggs 	if (pmc_enable) {
1593c2a536bSBen Skeggs 		nvkm_mask(device, 0x000200, pmc_enable, pmc_enable);
1603c2a536bSBen Skeggs 		nvkm_rd32(device, 0x000200);
1613c2a536bSBen Skeggs 	}
1626defde5aSBen Skeggs }
1636defde5aSBen Skeggs 
16454dcadd5SBen Skeggs static int
16554dcadd5SBen Skeggs nvkm_mc_fini(struct nvkm_subdev *subdev, bool suspend)
166c39f472eSBen Skeggs {
167d3981190SBen Skeggs 	nvkm_mc_intr_unarm(subdev->device);
16854dcadd5SBen Skeggs 	return 0;
169c39f472eSBen Skeggs }
170c39f472eSBen Skeggs 
17154dcadd5SBen Skeggs static int
17254dcadd5SBen Skeggs nvkm_mc_init(struct nvkm_subdev *subdev)
17354dcadd5SBen Skeggs {
17454dcadd5SBen Skeggs 	struct nvkm_mc *mc = nvkm_mc(subdev);
17554dcadd5SBen Skeggs 	if (mc->func->init)
17654dcadd5SBen Skeggs 		mc->func->init(mc);
177d3981190SBen Skeggs 	nvkm_mc_intr_rearm(subdev->device);
178c39f472eSBen Skeggs 	return 0;
179c39f472eSBen Skeggs }
180c39f472eSBen Skeggs 
18154dcadd5SBen Skeggs static void *
18254dcadd5SBen Skeggs nvkm_mc_dtor(struct nvkm_subdev *subdev)
183c39f472eSBen Skeggs {
1842b700825SBen Skeggs 	return nvkm_mc(subdev);
185c39f472eSBen Skeggs }
186c39f472eSBen Skeggs 
18754dcadd5SBen Skeggs static const struct nvkm_subdev_func
18854dcadd5SBen Skeggs nvkm_mc = {
18954dcadd5SBen Skeggs 	.dtor = nvkm_mc_dtor,
19054dcadd5SBen Skeggs 	.init = nvkm_mc_init,
19154dcadd5SBen Skeggs 	.fini = nvkm_mc_fini,
19254dcadd5SBen Skeggs };
19354dcadd5SBen Skeggs 
194d6adbe94SBen Skeggs void
195d6adbe94SBen Skeggs nvkm_mc_ctor(const struct nvkm_mc_func *func, struct nvkm_device *device,
196d6adbe94SBen Skeggs 	     int index, struct nvkm_mc *mc)
197d6adbe94SBen Skeggs {
198d6adbe94SBen Skeggs 	nvkm_subdev_ctor(&nvkm_mc, device, index, &mc->subdev);
199d6adbe94SBen Skeggs 	mc->func = func;
200d6adbe94SBen Skeggs }
201d6adbe94SBen Skeggs 
202c39f472eSBen Skeggs int
20354dcadd5SBen Skeggs nvkm_mc_new_(const struct nvkm_mc_func *func, struct nvkm_device *device,
20454dcadd5SBen Skeggs 	     int index, struct nvkm_mc **pmc)
205c39f472eSBen Skeggs {
2062ca0ddbcSBen Skeggs 	struct nvkm_mc *mc;
20754dcadd5SBen Skeggs 	if (!(mc = *pmc = kzalloc(sizeof(*mc), GFP_KERNEL)))
20854dcadd5SBen Skeggs 		return -ENOMEM;
209d6adbe94SBen Skeggs 	nvkm_mc_ctor(func, device, index, *pmc);
210c39f472eSBen Skeggs 	return 0;
211c39f472eSBen Skeggs }
212