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